diff options
Diffstat (limited to 'drivers/net/wireless/mwifiex/cfg80211.c')
| -rw-r--r-- | drivers/net/wireless/mwifiex/cfg80211.c | 2375 |
1 files changed, 1971 insertions, 404 deletions
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 2210a0f9af2..b511613bba2 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -20,6 +20,58 @@ #include "cfg80211.h" #include "main.h" +static char *reg_alpha2; +module_param(reg_alpha2, charp, 0); + +static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = { + { + .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT), + }, + { + .max = 1, .types = BIT(NL80211_IFTYPE_AP), + }, +}; + +static const struct ieee80211_iface_combination mwifiex_iface_comb_ap_sta = { + .limits = mwifiex_ap_sta_limits, + .num_different_channels = 1, + .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), + .max_interfaces = MWIFIEX_MAX_BSS_NUM, + .beacon_int_infra_match = true, +}; + +static const struct ieee80211_regdomain mwifiex_world_regdom_custom = { + .n_reg_rules = 7, + .alpha2 = "99", + .reg_rules = { + /* Channel 1 - 11 */ + REG_RULE(2412-10, 2462+10, 40, 3, 20, 0), + /* Channel 12 - 13 */ + REG_RULE(2467-10, 2472+10, 20, 3, 20, + NL80211_RRF_NO_IR), + /* Channel 14 */ + REG_RULE(2484-10, 2484+10, 20, 3, 20, + NL80211_RRF_NO_IR | + NL80211_RRF_NO_OFDM), + /* Channel 36 - 48 */ + REG_RULE(5180-10, 5240+10, 40, 3, 20, + NL80211_RRF_NO_IR), + /* Channel 149 - 165 */ + REG_RULE(5745-10, 5825+10, 40, 3, 20, + NL80211_RRF_NO_IR), + /* Channel 52 - 64 */ + REG_RULE(5260-10, 5320+10, 40, 3, 30, + NL80211_RRF_NO_IR | + NL80211_RRF_DFS), + /* Channel 100 - 140 */ + REG_RULE(5500-10, 5700+10, 40, 3, 30, + NL80211_RRF_NO_IR | + NL80211_RRF_DFS), + } +}; + /* * This function maps the nl802.11 channel type into driver channel type. * @@ -30,11 +82,9 @@ * NL80211_CHAN_HT40MINUS -> IEEE80211_HT_PARAM_CHA_SEC_BELOW * Others -> IEEE80211_HT_PARAM_CHA_SEC_NONE */ -static u8 -mwifiex_cfg80211_channel_type_to_sec_chan_offset(enum nl80211_channel_type - channel_type) +u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type) { - switch (channel_type) { + switch (chan_type) { case NL80211_CHAN_NO_HT: case NL80211_CHAN_HT20: return IEEE80211_HT_PARAM_CHA_SEC_NONE; @@ -67,7 +117,7 @@ mwifiex_is_alg_wep(u32 cipher) /* * This function retrieves the private structure from kernel wiphy structure. */ -static void *mwifiex_cfg80211_get_priv(struct wiphy *wiphy) +static void *mwifiex_cfg80211_get_adapter(struct wiphy *wiphy) { return (void *) (*(unsigned long *) wiphy_priv(wiphy)); } @@ -79,9 +129,11 @@ static int mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool pairwise, const u8 *mac_addr) { - struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); + struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); + const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const u8 *peer_mac = pairwise ? mac_addr : bc_mac; - if (mwifiex_set_encode(priv, NULL, 0, key_index, 1)) { + if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, peer_mac, 1)) { wiphy_err(wiphy, "deleting the crypto keys\n"); return -EFAULT; } @@ -91,14 +143,203 @@ mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, } /* + * This function forms an skb for management frame. + */ +static int +mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) +{ + u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + u16 pkt_len; + u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT; + struct timeval tv; + + pkt_len = len + ETH_ALEN; + + skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); + memcpy(skb_push(skb, sizeof(pkt_len)), &pkt_len, sizeof(pkt_len)); + + memcpy(skb_push(skb, sizeof(tx_control)), + &tx_control, sizeof(tx_control)); + + memcpy(skb_push(skb, sizeof(pkt_type)), &pkt_type, sizeof(pkt_type)); + + /* Add packet data and address4 */ + memcpy(skb_put(skb, sizeof(struct ieee80211_hdr_3addr)), buf, + sizeof(struct ieee80211_hdr_3addr)); + memcpy(skb_put(skb, ETH_ALEN), addr, ETH_ALEN); + memcpy(skb_put(skb, len - sizeof(struct ieee80211_hdr_3addr)), + buf + sizeof(struct ieee80211_hdr_3addr), + len - sizeof(struct ieee80211_hdr_3addr)); + + skb->priority = LOW_PRIO_TID; + do_gettimeofday(&tv); + skb->tstamp = timeval_to_ktime(tv); + + return 0; +} + +/* + * CFG802.11 operation handler to transmit a management frame. + */ +static int +mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, u64 *cookie) +{ + const u8 *buf = params->buf; + size_t len = params->len; + struct sk_buff *skb; + u16 pkt_len; + const struct ieee80211_mgmt *mgmt; + struct mwifiex_txinfo *tx_info; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + + if (!buf || !len) { + wiphy_err(wiphy, "invalid buffer and length\n"); + return -EFAULT; + } + + mgmt = (const struct ieee80211_mgmt *)buf; + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA && + ieee80211_is_probe_resp(mgmt->frame_control)) { + /* Since we support offload probe resp, we need to skip probe + * resp in AP or GO mode */ + wiphy_dbg(wiphy, + "info: skip to send probe resp in AP or GO mode\n"); + return 0; + } + + pkt_len = len + ETH_ALEN; + skb = dev_alloc_skb(MWIFIEX_MIN_DATA_HEADER_LEN + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + + pkt_len + sizeof(pkt_len)); + + if (!skb) { + wiphy_err(wiphy, "allocate skb failed for management frame\n"); + return -ENOMEM; + } + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = pkt_len; + + mwifiex_form_mgmt_frame(skb, buf, len); + mwifiex_queue_tx_pkt(priv, skb); + + *cookie = prandom_u32() | 1; + cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, GFP_ATOMIC); + + wiphy_dbg(wiphy, "info: management frame transmitted\n"); + return 0; +} + +/* + * CFG802.11 operation handler to register a mgmt frame. + */ +static void +mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy, + struct wireless_dev *wdev, + u16 frame_type, bool reg) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + u32 mask; + + if (reg) + mask = priv->mgmt_frame_mask | BIT(frame_type >> 4); + else + mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4); + + if (mask != priv->mgmt_frame_mask) { + priv->mgmt_frame_mask = mask; + mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG, + HostCmd_ACT_GEN_SET, 0, + &priv->mgmt_frame_mask, false); + wiphy_dbg(wiphy, "info: mgmt frame registered\n"); + } +} + +/* + * CFG802.11 operation handler to remain on channel. + */ +static int +mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, + unsigned int duration, u64 *cookie) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + int ret; + + if (!chan || !cookie) { + wiphy_err(wiphy, "Invalid parameter for ROC\n"); + return -EINVAL; + } + + if (priv->roc_cfg.cookie) { + wiphy_dbg(wiphy, "info: ongoing ROC, cookie = 0x%llu\n", + priv->roc_cfg.cookie); + return -EBUSY; + } + + ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET, chan, + duration); + + if (!ret) { + *cookie = prandom_u32() | 1; + priv->roc_cfg.cookie = *cookie; + priv->roc_cfg.chan = *chan; + + cfg80211_ready_on_channel(wdev, *cookie, chan, + duration, GFP_ATOMIC); + + wiphy_dbg(wiphy, "info: ROC, cookie = 0x%llx\n", *cookie); + } + + return ret; +} + +/* + * CFG802.11 operation handler to cancel remain on channel. + */ +static int +mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, u64 cookie) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + int ret; + + if (cookie != priv->roc_cfg.cookie) + return -ENOENT; + + ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_REMOVE, + &priv->roc_cfg.chan, 0); + + if (!ret) { + cfg80211_remain_on_channel_expired(wdev, cookie, + &priv->roc_cfg.chan, + GFP_ATOMIC); + + memset(&priv->roc_cfg, 0, sizeof(struct mwifiex_roc_cfg)); + + wiphy_dbg(wiphy, "info: cancel ROC, cookie = 0x%llx\n", cookie); + } + + return ret; +} + +/* * CFG802.11 operation handler to set Tx power. */ static int mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { - struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv; struct mwifiex_power_cfg power_cfg; int dbm = MBM_TO_DBM(mbm); @@ -109,6 +350,8 @@ mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy, power_cfg.is_power_auto = 1; } + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + return mwifiex_set_tx_power(priv, &power_cfg); } @@ -122,13 +365,12 @@ mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout) { - struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); u32 ps_mode; if (timeout) wiphy_dbg(wiphy, - "info: ignoring the timeout value" - " for IEEE power save\n"); + "info: ignore timeout value for IEEE Power Save\n"); ps_mode = enabled; @@ -143,13 +385,16 @@ mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool unicast, bool multicast) { - struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); + struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); /* Return if WEP key not configured */ - if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED) + if (!priv->sec_info.wep_enabled) return 0; - if (mwifiex_set_encode(priv, NULL, 0, key_index, 0)) { + if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) { + priv->wep_key_curr_index = key_index; + } else if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, + NULL, 0)) { wiphy_err(wiphy, "set default Tx key index\n"); return -EFAULT; } @@ -165,10 +410,28 @@ mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool pairwise, const u8 *mac_addr, struct key_params *params) { - struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); + struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); + struct mwifiex_wep_key *wep_key; + const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const u8 *peer_mac = pairwise ? mac_addr : bc_mac; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP && + (params->cipher == WLAN_CIPHER_SUITE_WEP40 || + params->cipher == WLAN_CIPHER_SUITE_WEP104)) { + if (params->key && params->key_len) { + wep_key = &priv->wep_key[key_index]; + memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); + memcpy(wep_key->key_material, params->key, + params->key_len); + wep_key->key_index = key_index; + wep_key->key_length = params->key_len; + priv->sec_info.wep_enabled = 1; + } + return 0; + } - if (mwifiex_set_encode(priv, params->key, params->key_len, - key_index, 0)) { + if (mwifiex_set_encode(priv, params, params->key, params->key_len, + key_index, peer_mac, 0)) { wiphy_err(wiphy, "crypto keys added\n"); return -EFAULT; } @@ -193,13 +456,13 @@ static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) enum ieee80211_band band; struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; - struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); - struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv; struct mwifiex_802_11d_domain_reg *domain_info = &adapter->domain_reg; /* Set country code */ - domain_info->country_code[0] = priv->country_code[0]; - domain_info->country_code[1] = priv->country_code[1]; + domain_info->country_code[0] = adapter->country_code[0]; + domain_info->country_code[1] = adapter->country_code[1]; domain_info->country_code[2] = ' '; band = mwifiex_band_to_radio_type(adapter->config_bands); @@ -225,7 +488,7 @@ static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) } if (ch->hw_value == next_chan + 1 && - ch->max_power == max_pwr) { + ch->max_power == max_pwr) { next_chan++; no_of_parsed_chan++; } else { @@ -251,8 +514,10 @@ static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) domain_info->no_of_triplet = no_of_triplet; - if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, - HostCmd_ACT_GEN_SET, 0, NULL)) { + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, + HostCmd_ACT_GEN_SET, 0, NULL, false)) { wiphy_err(wiphy, "11D: setting domain info in FW\n"); return -1; } @@ -270,185 +535,230 @@ static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) * - Set by user * - Set bt Country IE */ -static int mwifiex_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) +static void mwifiex_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) { - struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); - - wiphy_dbg(wiphy, "info: cfg80211 regulatory domain callback for domain" - " %c%c\n", request->alpha2[0], request->alpha2[1]); + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv = mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY); - memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2)); + wiphy_dbg(wiphy, "info: cfg80211 regulatory domain callback for %c%c\n", + request->alpha2[0], request->alpha2[1]); switch (request->initiator) { case NL80211_REGDOM_SET_BY_DRIVER: case NL80211_REGDOM_SET_BY_CORE: case NL80211_REGDOM_SET_BY_USER: - break; - /* Todo: apply driver specific changes in channel flags based - on the request initiator if necessary. */ case NL80211_REGDOM_SET_BY_COUNTRY_IE: break; + default: + wiphy_err(wiphy, "unknown regdom initiator: %d\n", + request->initiator); + return; } - mwifiex_send_domain_info_cmd_fw(wiphy); - return 0; + /* Don't send world or same regdom info to firmware */ + if (strncmp(request->alpha2, "00", 2) && + strncmp(request->alpha2, adapter->country_code, + sizeof(request->alpha2))) { + memcpy(adapter->country_code, request->alpha2, + sizeof(request->alpha2)); + mwifiex_send_domain_info_cmd_fw(wiphy); + mwifiex_dnld_txpwr_table(priv); + } } /* - * This function sets the RF channel. + * This function sets the fragmentation threshold. * - * This function creates multiple IOCTL requests, populates them accordingly - * and issues them to set the band/channel and frequency. + * The fragmentation threshold value must lie between MWIFIEX_FRAG_MIN_VALUE + * and MWIFIEX_FRAG_MAX_VALUE. */ static int -mwifiex_set_rf_channel(struct mwifiex_private *priv, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) +mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr) { - struct mwifiex_chan_freq_power cfp; - u32 config_bands = 0; - struct wiphy *wiphy = priv->wdev->wiphy; - struct mwifiex_adapter *adapter = priv->adapter; - - if (chan) { - /* Set appropriate bands */ - if (chan->band == IEEE80211_BAND_2GHZ) { - if (channel_type == NL80211_CHAN_NO_HT) - if (priv->adapter->config_bands == BAND_B || - priv->adapter->config_bands == BAND_G) - config_bands = - priv->adapter->config_bands; - else - config_bands = BAND_B | BAND_G; - else - config_bands = BAND_B | BAND_G | BAND_GN; - } else { - if (channel_type == NL80211_CHAN_NO_HT) - config_bands = BAND_A; - else - config_bands = BAND_AN | BAND_A; - } + if (frag_thr < MWIFIEX_FRAG_MIN_VALUE || + frag_thr > MWIFIEX_FRAG_MAX_VALUE) + frag_thr = MWIFIEX_FRAG_MAX_VALUE; - if (!((config_bands | adapter->fw_bands) & - ~adapter->fw_bands)) { - adapter->config_bands = config_bands; - if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { - adapter->adhoc_start_band = config_bands; - if ((config_bands & BAND_GN) || - (config_bands & BAND_AN)) - adapter->adhoc_11n_enabled = true; - else - adapter->adhoc_11n_enabled = false; - } - } - adapter->sec_chan_offset = - mwifiex_cfg80211_channel_type_to_sec_chan_offset - (channel_type); - adapter->channel_type = channel_type; - - mwifiex_send_domain_info_cmd_fw(wiphy); - } - - wiphy_dbg(wiphy, "info: setting band %d, channel offset %d and " - "mode %d\n", config_bands, adapter->sec_chan_offset, - priv->bss_mode); - if (!chan) - return 0; + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, FRAG_THRESH_I, + &frag_thr, true); +} - memset(&cfp, 0, sizeof(cfp)); - cfp.freq = chan->center_freq; - cfp.channel = ieee80211_frequency_to_channel(chan->center_freq); +/* + * This function sets the RTS threshold. - if (mwifiex_bss_set_channel(priv, &cfp)) - return -EFAULT; + * The rts value must lie between MWIFIEX_RTS_MIN_VALUE + * and MWIFIEX_RTS_MAX_VALUE. + */ +static int +mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr) +{ + if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE) + rts_thr = MWIFIEX_RTS_MAX_VALUE; - return mwifiex_drv_change_adhoc_chan(priv, cfp.channel); + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, RTS_THRESH_I, + &rts_thr, true); } /* - * CFG802.11 operation handler to set channel. + * CFG802.11 operation handler to set wiphy parameters. * - * This function can only be used when station is not connected. + * This function can be used to set the RTS threshold and the + * Fragmentation threshold of the driver. */ static int -mwifiex_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) +mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) { - struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv; + struct mwifiex_uap_bss_param *bss_cfg; + int ret, bss_started, i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + + switch (priv->bss_role) { + case MWIFIEX_BSS_ROLE_UAP: + bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), + GFP_KERNEL); + if (!bss_cfg) + return -ENOMEM; + + mwifiex_set_sys_config_invalid_data(bss_cfg); + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) + bss_cfg->rts_threshold = wiphy->rts_threshold; + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) + bss_cfg->frag_threshold = wiphy->frag_threshold; + if (changed & WIPHY_PARAM_RETRY_LONG) + bss_cfg->retry_limit = wiphy->retry_long; + + bss_started = priv->bss_started; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, + NULL, true); + if (ret) { + wiphy_err(wiphy, "Failed to stop the BSS\n"); + kfree(bss_cfg); + return ret; + } - if (priv->media_connected) { - wiphy_err(wiphy, "This setting is valid only when station " - "is not connected\n"); - return -EINVAL; + ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, + HostCmd_ACT_GEN_SET, + UAP_BSS_PARAMS_I, bss_cfg, + false); + + kfree(bss_cfg); + + if (ret) { + wiphy_err(wiphy, "Failed to set bss config\n"); + return ret; + } + + if (!bss_started) + break; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START, + HostCmd_ACT_GEN_SET, 0, + NULL, false); + if (ret) { + wiphy_err(wiphy, "Failed to start BSS\n"); + return ret; + } + + break; + case MWIFIEX_BSS_ROLE_STA: + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + ret = mwifiex_set_rts(priv, + wiphy->rts_threshold); + if (ret) + return ret; + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + ret = mwifiex_set_frag(priv, + wiphy->frag_threshold); + if (ret) + return ret; + } + break; + } } - return mwifiex_set_rf_channel(priv, chan, channel_type); + return 0; } -/* - * This function sets the fragmentation threshold. - * - * The fragmentation threshold value must lie between MWIFIEX_FRAG_MIN_VALUE - * and MWIFIEX_FRAG_MAX_VALUE. - */ static int -mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr) +mwifiex_cfg80211_deinit_p2p(struct mwifiex_private *priv) { - int ret; + u16 mode = P2P_MODE_DISABLE; - if (frag_thr < MWIFIEX_FRAG_MIN_VALUE - || frag_thr > MWIFIEX_FRAG_MAX_VALUE) - return -EINVAL; + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) + mwifiex_set_bss_role(priv, MWIFIEX_BSS_ROLE_STA); - /* Send request to firmware */ - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, FRAG_THRESH_I, - &frag_thr); + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; - return ret; + return 0; } /* - * This function sets the RTS threshold. - - * The rts value must lie between MWIFIEX_RTS_MIN_VALUE - * and MWIFIEX_RTS_MAX_VALUE. + * This function initializes the functionalities for P2P client. + * The P2P client initialization sequence is: + * disable -> device -> client */ static int -mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr) +mwifiex_cfg80211_init_p2p_client(struct mwifiex_private *priv) { - if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE) - rts_thr = MWIFIEX_RTS_MAX_VALUE; + u16 mode; - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, RTS_THRESH_I, - &rts_thr); + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -1; + + mode = P2P_MODE_DEVICE; + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; + + mode = P2P_MODE_CLIENT; + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; + + return 0; } /* - * CFG802.11 operation handler to set wiphy parameters. - * - * This function can be used to set the RTS threshold and the - * Fragmentation threshold of the driver. + * This function initializes the functionalities for P2P GO. + * The P2P GO initialization sequence is: + * disable -> device -> GO */ static int -mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +mwifiex_cfg80211_init_p2p_go(struct mwifiex_private *priv) { - struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); - int ret = 0; + u16 mode; - if (changed & WIPHY_PARAM_RTS_THRESHOLD) { - ret = mwifiex_set_rts(priv, wiphy->rts_threshold); - if (ret) - return ret; - } + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -1; + + mode = P2P_MODE_DEVICE; + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; - if (changed & WIPHY_PARAM_FRAG_THRESHOLD) - ret = mwifiex_set_frag(priv, wiphy->frag_threshold); + mode = P2P_MODE_GO; + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; - return ret; + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) + mwifiex_set_bss_role(priv, MWIFIEX_BSS_ROLE_UAP); + + return 0; } /* @@ -463,41 +773,151 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, int ret; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - if (priv->bss_mode == type) { - wiphy_warn(wiphy, "already set to required type\n"); - return 0; - } - - priv->bss_mode = type; - - switch (type) { + switch (dev->ieee80211_ptr->iftype) { case NL80211_IFTYPE_ADHOC: - dev->ieee80211_ptr->iftype = NL80211_IFTYPE_ADHOC; - wiphy_dbg(wiphy, "info: setting interface type to adhoc\n"); + switch (type) { + case NL80211_IFTYPE_STATION: + break; + case NL80211_IFTYPE_UNSPECIFIED: + wiphy_warn(wiphy, "%s: kept type as IBSS\n", dev->name); + case NL80211_IFTYPE_ADHOC: /* This shouldn't happen */ + return 0; + case NL80211_IFTYPE_AP: + default: + wiphy_err(wiphy, "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } break; case NL80211_IFTYPE_STATION: - dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; - wiphy_dbg(wiphy, "info: setting interface type to managed\n"); + switch (type) { + case NL80211_IFTYPE_ADHOC: + break; + case NL80211_IFTYPE_P2P_CLIENT: + if (mwifiex_cfg80211_init_p2p_client(priv)) + return -EFAULT; + dev->ieee80211_ptr->iftype = type; + return 0; + case NL80211_IFTYPE_P2P_GO: + if (mwifiex_cfg80211_init_p2p_go(priv)) + return -EFAULT; + dev->ieee80211_ptr->iftype = type; + return 0; + case NL80211_IFTYPE_UNSPECIFIED: + wiphy_warn(wiphy, "%s: kept type as STA\n", dev->name); + case NL80211_IFTYPE_STATION: /* This shouldn't happen */ + return 0; + case NL80211_IFTYPE_AP: + default: + wiphy_err(wiphy, "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + break; + case NL80211_IFTYPE_AP: + switch (type) { + case NL80211_IFTYPE_UNSPECIFIED: + wiphy_warn(wiphy, "%s: kept type as AP\n", dev->name); + case NL80211_IFTYPE_AP: /* This shouldn't happen */ + return 0; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + default: + wiphy_err(wiphy, "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + switch (type) { + case NL80211_IFTYPE_STATION: + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -EFAULT; + dev->ieee80211_ptr->iftype = type; + return 0; + default: + return -EOPNOTSUPP; + } break; - case NL80211_IFTYPE_UNSPECIFIED: - dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; - wiphy_dbg(wiphy, "info: setting interface type to auto\n"); - return 0; default: - wiphy_err(wiphy, "unknown interface type: %d\n", type); - return -EINVAL; + wiphy_err(wiphy, "%s: unknown iftype: %d\n", + dev->name, dev->ieee80211_ptr->iftype); + return -EOPNOTSUPP; } + dev->ieee80211_ptr->iftype = type; + priv->bss_mode = type; mwifiex_deauthenticate(priv, NULL); priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, true); return ret; } +static void +mwifiex_parse_htinfo(struct mwifiex_private *priv, u8 tx_htinfo, + struct rate_info *rate) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + if (adapter->is_hw_11ac_capable) { + /* bit[1-0]: 00=LG 01=HT 10=VHT */ + if (tx_htinfo & BIT(0)) { + /* HT */ + rate->mcs = priv->tx_rate; + rate->flags |= RATE_INFO_FLAGS_MCS; + } + if (tx_htinfo & BIT(1)) { + /* VHT */ + rate->mcs = priv->tx_rate & 0x0F; + rate->flags |= RATE_INFO_FLAGS_VHT_MCS; + } + + if (tx_htinfo & (BIT(1) | BIT(0))) { + /* HT or VHT */ + switch (tx_htinfo & (BIT(3) | BIT(2))) { + case 0: + /* This will be 20MHz */ + break; + case (BIT(2)): + rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + break; + case (BIT(3)): + rate->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; + break; + case (BIT(3) | BIT(2)): + rate->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; + break; + } + + if (tx_htinfo & BIT(4)) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + + if ((priv->tx_rate >> 4) == 1) + rate->nss = 2; + else + rate->nss = 1; + } + } else { + /* + * Bit 0 in tx_htinfo indicates that current Tx rate + * is 11n rate. Valid MCS index values for us are 0 to 15. + */ + if ((tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) { + rate->mcs = priv->tx_rate; + rate->flags |= RATE_INFO_FLAGS_MCS; + if (tx_htinfo & BIT(1)) + rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + if (tx_htinfo & BIT(2)) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + } + } +} + /* * This function dumps the station information on a buffer. * @@ -513,51 +933,58 @@ static int mwifiex_dump_station_info(struct mwifiex_private *priv, struct station_info *sinfo) { - struct mwifiex_ds_get_signal signal; - struct mwifiex_rate_cfg rate; - int ret = 0; + u32 rate; sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | - STATION_INFO_RX_PACKETS | - STATION_INFO_TX_PACKETS - | STATION_INFO_SIGNAL | STATION_INFO_TX_BITRATE; + STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS | + STATION_INFO_TX_BITRATE | + STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG; /* Get signal information from the firmware */ - memset(&signal, 0, sizeof(struct mwifiex_ds_get_signal)); - if (mwifiex_get_signal_info(priv, &signal)) { - dev_err(priv->adapter->dev, "getting signal information\n"); - ret = -EFAULT; + if (mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, + HostCmd_ACT_GEN_GET, 0, NULL, true)) { + dev_err(priv->adapter->dev, "failed to get signal information\n"); + return -EFAULT; } if (mwifiex_drv_get_data_rate(priv, &rate)) { dev_err(priv->adapter->dev, "getting data rate\n"); - ret = -EFAULT; + return -EFAULT; } - /* - * Bit 0 in tx_htinfo indicates that current Tx rate is 11n rate. Valid - * MCS index values for us are 0 to 7. - */ - if ((priv->tx_htinfo & BIT(0)) && (priv->tx_rate < 8)) { - sinfo->txrate.mcs = priv->tx_rate; - sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; - /* 40MHz rate */ - if (priv->tx_htinfo & BIT(1)) - sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; - /* SGI enabled */ - if (priv->tx_htinfo & BIT(2)) - sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; - } + /* Get DTIM period information from firmware */ + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, DTIM_PERIOD_I, + &priv->dtim_period, true); + + mwifiex_parse_htinfo(priv, priv->tx_htinfo, &sinfo->txrate); + sinfo->signal_avg = priv->bcn_rssi_avg; sinfo->rx_bytes = priv->stats.rx_bytes; sinfo->tx_bytes = priv->stats.tx_bytes; sinfo->rx_packets = priv->stats.rx_packets; sinfo->tx_packets = priv->stats.tx_packets; - sinfo->signal = priv->qual_level; + sinfo->signal = priv->bcn_rssi_avg; /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */ - sinfo->txrate.legacy = rate.rate * 5; + sinfo->txrate.legacy = rate * 5; + + if (priv->bss_mode == NL80211_IFTYPE_STATION) { + sinfo->filled |= STATION_INFO_BSS_PARAM; + sinfo->bss_param.flags = 0; + if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & + WLAN_CAPABILITY_SHORT_PREAMBLE) + sinfo->bss_param.flags |= + BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & + WLAN_CAPABILITY_SHORT_SLOT_TIME) + sinfo->bss_param.flags |= + BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + sinfo->bss_param.dtim_period = priv->dtim_period; + sinfo->bss_param.beacon_interval = + priv->curr_bss_params.bss_descriptor.beacon_period; + } - return ret; + return 0; } /* @@ -568,7 +995,7 @@ mwifiex_dump_station_info(struct mwifiex_private *priv, */ static int mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); @@ -580,14 +1007,29 @@ mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, return mwifiex_dump_station_info(priv, sinfo); } -/* Supported rates to be advertised to the cfg80211 */ +/* + * CFG802.11 operation handler to dump station information. + */ +static int +mwifiex_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *mac, struct station_info *sinfo) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (!priv->media_connected || idx) + return -ENOENT; + + memcpy(mac, priv->cfg_bssid, ETH_ALEN); + + return mwifiex_dump_station_info(priv, sinfo); +} +/* Supported rates to be advertised to the cfg80211 */ static struct ieee80211_rate mwifiex_rates[] = { {.bitrate = 10, .hw_value = 2, }, {.bitrate = 20, .hw_value = 4, }, {.bitrate = 55, .hw_value = 11, }, {.bitrate = 110, .hw_value = 22, }, - {.bitrate = 220, .hw_value = 44, }, {.bitrate = 60, .hw_value = 12, }, {.bitrate = 90, .hw_value = 18, }, {.bitrate = 120, .hw_value = 24, }, @@ -596,11 +1038,9 @@ static struct ieee80211_rate mwifiex_rates[] = { {.bitrate = 360, .hw_value = 72, }, {.bitrate = 480, .hw_value = 96, }, {.bitrate = 540, .hw_value = 108, }, - {.bitrate = 720, .hw_value = 144, }, }; /* Channel definitions to be advertised to cfg80211 */ - static struct ieee80211_channel mwifiex_channels_2ghz[] = { {.center_freq = 2412, .hw_value = 1, }, {.center_freq = 2417, .hw_value = 2, }, @@ -622,7 +1062,7 @@ static struct ieee80211_supported_band mwifiex_band_2ghz = { .channels = mwifiex_channels_2ghz, .n_channels = ARRAY_SIZE(mwifiex_channels_2ghz), .bitrates = mwifiex_rates, - .n_bitrates = 14, + .n_bitrates = ARRAY_SIZE(mwifiex_rates), }; static struct ieee80211_channel mwifiex_channels_5ghz[] = { @@ -662,25 +1102,54 @@ static struct ieee80211_channel mwifiex_channels_5ghz[] = { static struct ieee80211_supported_band mwifiex_band_5ghz = { .channels = mwifiex_channels_5ghz, .n_channels = ARRAY_SIZE(mwifiex_channels_5ghz), - .bitrates = mwifiex_rates - 4, - .n_bitrates = ARRAY_SIZE(mwifiex_rates) + 4, + .bitrates = mwifiex_rates + 4, + .n_bitrates = ARRAY_SIZE(mwifiex_rates) - 4, }; /* Supported crypto cipher suits to be advertised to cfg80211 */ - static const u32 mwifiex_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_AES_CMAC, +}; + +/* Supported mgmt frame types to be advertised to cfg80211 */ +static const struct ieee80211_txrx_stypes +mwifiex_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_AP] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, }; /* * CFG802.11 operation handler for setting bit rates. * - * Function selects legacy bang B/G/BG from corresponding bitrates selection. - * Currently only 2.4GHz band is supported. + * Function configures data rates to firmware using bitrate mask + * provided by cfg80211. */ static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, @@ -688,42 +1157,374 @@ static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy, const struct cfg80211_bitrate_mask *mask) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - int index = 0, mode = 0, i; + u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + enum ieee80211_band band; struct mwifiex_adapter *adapter = priv->adapter; - /* Currently only 2.4GHz is supported */ - for (i = 0; i < mwifiex_band_2ghz.n_bitrates; i++) { - /* - * Rates below 6 Mbps in the table are CCK rates; 802.11b - * and from 6 they are OFDM; 802.11G + if (!priv->media_connected) { + dev_err(adapter->dev, + "Can not set Tx data rate in disconnected state\n"); + return -EINVAL; + } + + band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + + memset(bitmap_rates, 0, sizeof(bitmap_rates)); + + /* Fill HR/DSSS rates. */ + if (band == IEEE80211_BAND_2GHZ) + bitmap_rates[0] = mask->control[band].legacy & 0x000f; + + /* Fill OFDM rates */ + if (band == IEEE80211_BAND_2GHZ) + bitmap_rates[1] = (mask->control[band].legacy & 0x0ff0) >> 4; + else + bitmap_rates[1] = mask->control[band].legacy; + + /* Fill HT MCS rates */ + bitmap_rates[2] = mask->control[band].ht_mcs[0]; + if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) + bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8; + + /* Fill VHT MCS rates */ + if (adapter->fw_api_ver == MWIFIEX_FW_V15) { + bitmap_rates[10] = mask->control[band].vht_mcs[0]; + if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) + bitmap_rates[11] = mask->control[band].vht_mcs[1]; + } + + return mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_SET, 0, bitmap_rates, true); +} + +/* + * CFG802.11 operation handler for connection quality monitoring. + * + * This function subscribes/unsubscribes HIGH_RSSI and LOW_RSSI + * events to FW. + */ +static int mwifiex_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_ds_misc_subsc_evt subsc_evt; + + priv->cqm_rssi_thold = rssi_thold; + priv->cqm_rssi_hyst = rssi_hyst; + + memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt)); + subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; + + /* Subscribe/unsubscribe low and high rssi events */ + if (rssi_thold && rssi_hyst) { + subsc_evt.action = HostCmd_ACT_BITWISE_SET; + subsc_evt.bcn_l_rssi_cfg.abs_value = abs(rssi_thold); + subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold); + subsc_evt.bcn_l_rssi_cfg.evt_freq = 1; + subsc_evt.bcn_h_rssi_cfg.evt_freq = 1; + return mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, &subsc_evt, true); + } else { + subsc_evt.action = HostCmd_ACT_BITWISE_CLR; + return mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, &subsc_evt, true); + } + + return 0; +} + +/* cfg80211 operation handler for change_beacon. + * Function retrieves and sets modified management IEs to FW. + */ +static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_beacon_data *data) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) { + wiphy_err(wiphy, "%s: bss_type mismatched\n", __func__); + return -EINVAL; + } + + if (!priv->bss_started) { + wiphy_err(wiphy, "%s: bss not started\n", __func__); + return -EINVAL; + } + + if (mwifiex_set_mgmt_ies(priv, data)) { + wiphy_err(wiphy, "%s: setting mgmt ies failed\n", __func__); + return -EFAULT; + } + + return 0; +} + +/* cfg80211 operation handler for del_station. + * Function deauthenticates station which value is provided in mac parameter. + * If mac is NULL/broadcast, all stations in associated station list are + * deauthenticated. If bss is not started or there are no stations in + * associated stations list, no action is taken. + */ +static int +mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_sta_node *sta_node; + unsigned long flags; + + if (list_empty(&priv->sta_list) || !priv->bss_started) + return 0; + + if (!mac || is_broadcast_ether_addr(mac)) { + wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__); + list_for_each_entry(sta_node, &priv->sta_list, list) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH, + HostCmd_ACT_GEN_SET, 0, + sta_node->mac_addr, true)) + return -1; + mwifiex_uap_del_sta_data(priv, sta_node); + } + } else { + wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, mac); + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_node = mwifiex_get_sta_entry(priv, mac); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + if (sta_node) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH, + HostCmd_ACT_GEN_SET, 0, + sta_node->mac_addr, true)) + return -1; + mwifiex_uap_del_sta_data(priv, sta_node); + } + } + + return 0; +} + +static int +mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv = mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY); + struct mwifiex_ds_ant_cfg ant_cfg; + + if (!tx_ant || !rx_ant) + return -EOPNOTSUPP; + + if (adapter->hw_dev_mcs_support != HT_STREAM_2X2) { + /* Not a MIMO chip. User should provide specific antenna number + * for Tx/Rx path or enable all antennas for diversity */ - if (mwifiex_rates[i].bitrate == 60) { - index = 1 << i; - break; + if (tx_ant != rx_ant) + return -EOPNOTSUPP; + + if ((tx_ant & (tx_ant - 1)) && + (tx_ant != BIT(adapter->number_of_antenna) - 1)) + return -EOPNOTSUPP; + + if ((tx_ant == BIT(adapter->number_of_antenna) - 1) && + (priv->adapter->number_of_antenna > 1)) { + tx_ant = RF_ANTENNA_AUTO; + rx_ant = RF_ANTENNA_AUTO; + } + } else { + struct ieee80211_sta_ht_cap *ht_info; + int rx_mcs_supp; + enum ieee80211_band band; + + if ((tx_ant == 0x1 && rx_ant == 0x1)) { + adapter->user_dev_mcs_support = HT_STREAM_1X1; + if (adapter->is_hw_11ac_capable) + adapter->usr_dot_11ac_mcs_support = + MWIFIEX_11AC_MCS_MAP_1X1; + } else { + adapter->user_dev_mcs_support = HT_STREAM_2X2; + if (adapter->is_hw_11ac_capable) + adapter->usr_dot_11ac_mcs_support = + MWIFIEX_11AC_MCS_MAP_2X2; } + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!adapter->wiphy->bands[band]) + continue; + + ht_info = &adapter->wiphy->bands[band]->ht_cap; + rx_mcs_supp = + GET_RXMCSSUPP(adapter->user_dev_mcs_support); + memset(&ht_info->mcs, 0, adapter->number_of_antenna); + memset(&ht_info->mcs, 0xff, rx_mcs_supp); + } + } + + ant_cfg.tx_ant = tx_ant; + ant_cfg.rx_ant = rx_ant; + + return mwifiex_send_cmd(priv, HostCmd_CMD_RF_ANTENNA, + HostCmd_ACT_GEN_SET, 0, &ant_cfg, true); +} + +/* cfg80211 operation handler for stop ap. + * Function stops BSS running at uAP interface. + */ +static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (mwifiex_del_mgmt_ies(priv)) + wiphy_err(wiphy, "Failed to delete mgmt IEs!\n"); + + priv->ap_11n_enabled = 0; + + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, NULL, true)) { + wiphy_err(wiphy, "Failed to stop the BSS\n"); + return -1; } - if (mask->control[IEEE80211_BAND_2GHZ].legacy < index) { - mode = BAND_B; + return 0; +} + +/* cfg80211 operation handler for start_ap. + * Function sets beacon period, DTIM period, SSID and security into + * AP config structure. + * AP is configured with these settings and BSS is started. + */ +static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ap_settings *params) +{ + struct mwifiex_uap_bss_param *bss_cfg; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + u8 config_bands = 0; + + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) + return -1; + if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon)) + return -1; + + bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), GFP_KERNEL); + if (!bss_cfg) + return -ENOMEM; + + mwifiex_set_sys_config_invalid_data(bss_cfg); + + if (params->beacon_interval) + bss_cfg->beacon_period = params->beacon_interval; + if (params->dtim_period) + bss_cfg->dtim_period = params->dtim_period; + + if (params->ssid && params->ssid_len) { + memcpy(bss_cfg->ssid.ssid, params->ssid, params->ssid_len); + bss_cfg->ssid.ssid_len = params->ssid_len; + } + + switch (params->hidden_ssid) { + case NL80211_HIDDEN_SSID_NOT_IN_USE: + bss_cfg->bcast_ssid_ctl = 1; + break; + case NL80211_HIDDEN_SSID_ZERO_LEN: + bss_cfg->bcast_ssid_ctl = 0; + break; + case NL80211_HIDDEN_SSID_ZERO_CONTENTS: + /* firmware doesn't support this type of hidden SSID */ + default: + kfree(bss_cfg); + return -EINVAL; + } + + bss_cfg->channel = ieee80211_frequency_to_channel( + params->chandef.chan->center_freq); + + /* Set appropriate bands */ + if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) { + bss_cfg->band_cfg = BAND_CONFIG_BG; + config_bands = BAND_B | BAND_G; + + if (params->chandef.width > NL80211_CHAN_WIDTH_20_NOHT) + config_bands |= BAND_GN; } else { - mode = BAND_G; - if (mask->control[IEEE80211_BAND_2GHZ].legacy % index) - mode |= BAND_B; + bss_cfg->band_cfg = BAND_CONFIG_A; + config_bands = BAND_A; + + if (params->chandef.width > NL80211_CHAN_WIDTH_20_NOHT) + config_bands |= BAND_AN; + + if (params->chandef.width > NL80211_CHAN_WIDTH_40) + config_bands |= BAND_AAC; } - if (!((mode | adapter->fw_bands) & ~adapter->fw_bands)) { - adapter->config_bands = mode; - if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { - adapter->adhoc_start_band = mode; - adapter->adhoc_11n_enabled = false; - } + if (!((config_bands | priv->adapter->fw_bands) & + ~priv->adapter->fw_bands)) + priv->adapter->config_bands = config_bands; + + mwifiex_set_uap_rates(bss_cfg, params); + mwifiex_send_domain_info_cmd_fw(wiphy); + + if (mwifiex_set_secure_params(priv, bss_cfg, params)) { + kfree(bss_cfg); + wiphy_err(wiphy, "Failed to parse secuirty parameters!\n"); + return -1; } - adapter->sec_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; - adapter->channel_type = NL80211_CHAN_NO_HT; - wiphy_debug(wiphy, "info: device configured in 802.11%s%s mode\n", - (mode & BAND_B) ? "b" : "", - (mode & BAND_G) ? "g" : ""); + mwifiex_set_ht_params(priv, bss_cfg, params); + + if (priv->adapter->is_hw_11ac_capable) { + mwifiex_set_vht_params(priv, bss_cfg, params); + mwifiex_set_vht_width(priv, params->chandef.width, + priv->ap_11ac_enabled); + } + + if (priv->ap_11ac_enabled) + mwifiex_set_11ac_ba_params(priv); + else + mwifiex_set_ba_params(priv); + + mwifiex_set_wmm_params(priv, bss_cfg, params); + + if (params->inactivity_timeout > 0) { + /* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */ + bss_cfg->sta_ao_timer = 10 * params->inactivity_timeout; + bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout; + } + + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, NULL, true)) { + wiphy_err(wiphy, "Failed to stop the BSS\n"); + kfree(bss_cfg); + return -1; + } + + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, + HostCmd_ACT_GEN_SET, + UAP_BSS_PARAMS_I, bss_cfg, false)) { + wiphy_err(wiphy, "Failed to set the SSID\n"); + kfree(bss_cfg); + return -1; + } + + kfree(bss_cfg); + + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START, + HostCmd_ACT_GEN_SET, 0, NULL, false)) { + wiphy_err(wiphy, "Failed to start the BSS\n"); + return -1; + } + + if (priv->sec_info.wep_enabled) + priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; + else + priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; + + if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true)) + return -1; return 0; } @@ -747,6 +1548,7 @@ mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, " reason code %d\n", priv->cfg_bssid, reason_code); memset(priv->cfg_bssid, 0, ETH_ALEN); + priv->hs2_enabled = false; return 0; } @@ -778,8 +1580,7 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) ie_buf[1] = bss_info.ssid.ssid_len; memcpy(&ie_buf[sizeof(struct ieee_types_header)], - &bss_info.ssid.ssid, - bss_info.ssid.ssid_len); + &bss_info.ssid.ssid, bss_info.ssid.ssid_len); ie_len = ie_buf[1] + sizeof(struct ieee_types_header); band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); @@ -788,9 +1589,9 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) band)); bss = cfg80211_inform_bss(priv->wdev->wiphy, chan, - bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, - 0, ie_buf, ie_len, 0, GFP_KERNEL); - cfg80211_put_bss(bss); + bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, + 0, ie_buf, ie_len, 0, GFP_KERNEL); + cfg80211_put_bss(priv->wdev->wiphy, bss); memcpy(priv->cfg_bssid, bss_info.bssid, ETH_ALEN); return 0; @@ -811,16 +1612,17 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) * the function notifies the CFG802.11 subsystem of the new BSS connection. */ static int -mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, - u8 *bssid, int mode, struct ieee80211_channel *channel, +mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, + const u8 *ssid, const u8 *bssid, int mode, + struct ieee80211_channel *channel, struct cfg80211_connect_params *sme, bool privacy) { - struct mwifiex_802_11_ssid req_ssid; + struct cfg80211_ssid req_ssid; int ret, auth_type = 0; struct cfg80211_bss *bss = NULL; u8 is_scanning_required = 0; - memset(&req_ssid, 0, sizeof(struct mwifiex_802_11_ssid)); + memset(&req_ssid, 0, sizeof(struct cfg80211_ssid)); req_ssid.ssid_len = ssid_len; if (ssid_len > IEEE80211_MAX_SSID_LEN) { @@ -837,17 +1639,14 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, /* disconnect before try to associate */ mwifiex_deauthenticate(priv, NULL); - if (channel) - ret = mwifiex_set_rf_channel(priv, channel, - priv->adapter->channel_type); - /* As this is new association, clear locally stored * keys and security related flags */ priv->sec_info.wpa_enabled = false; priv->sec_info.wpa2_enabled = false; priv->wep_key_curr_index = 0; priv->sec_info.encryption_mode = 0; - ret = mwifiex_set_encode(priv, NULL, 0, 0, 1); + priv->sec_info.is_authtype_auto = 0; + ret = mwifiex_set_encode(priv, NULL, NULL, 0, 0, NULL, 1); if (mode == NL80211_IFTYPE_ADHOC) { /* "privacy" is set only for ad-hoc mode */ @@ -868,11 +1667,12 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, } /* Now handle infra mode. "sme" is valid for infra mode only */ - if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC - || sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) + if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; - else if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) - auth_type = NL80211_AUTHTYPE_SHARED_KEY; + priv->sec_info.is_authtype_auto = 1; + } else { + auth_type = sme->auth_type; + } if (sme->crypto.n_ciphers_pairwise) { priv->sec_info.encryption_mode = @@ -893,17 +1693,12 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, "info: setting wep encryption" " with key len %d\n", sme->key_len); priv->wep_key_curr_index = sme->key_idx; - ret = mwifiex_set_encode(priv, sme->key, sme->key_len, - sme->key_idx, 0); + ret = mwifiex_set_encode(priv, NULL, sme->key, + sme->key_len, sme->key_idx, + NULL, 0); } } done: - /* Do specific SSID scanning */ - if (mwifiex_request_scan(priv, &req_ssid)) { - dev_err(priv->adapter->dev, "scan error\n"); - return -EFAULT; - } - /* * Scan entries are valid for some time (15 sec). So we can save one * active scan time if we just try cfg80211_get_bss first. If it fails @@ -932,21 +1727,23 @@ done: if (!bss) { if (is_scanning_required) { - dev_warn(priv->adapter->dev, "assoc: requested " - "bss not found in scan results\n"); + dev_warn(priv->adapter->dev, + "assoc: requested bss not found in scan results\n"); break; } is_scanning_required = 1; } else { - dev_dbg(priv->adapter->dev, "info: trying to associate to %s and bssid %pM\n", - (char *) req_ssid.ssid, bss->bssid); + dev_dbg(priv->adapter->dev, + "info: trying to associate to '%s' bssid %pM\n", + (char *) req_ssid.ssid, bss->bssid); memcpy(&priv->cfg_bssid, bss->bssid, ETH_ALEN); break; } } - if (mwifiex_bss_start(priv, bss, &req_ssid)) - return -EFAULT; + ret = mwifiex_bss_start(priv, bss, &req_ssid); + if (ret) + return ret; if (mode == NL80211_IFTYPE_ADHOC) { /* Inform the BSS information to kernel, otherwise @@ -970,20 +1767,20 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - int ret = 0; + int ret; - if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { - wiphy_err(wiphy, "received infra assoc request " - "when station is in ibss mode\n"); - goto done; + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) { + wiphy_err(wiphy, + "%s: reject infra assoc request in non-STA role\n", + dev->name); + return -EINVAL; } wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n", - (char *) sme->ssid, sme->bssid); + (char *) sme->ssid, sme->bssid); ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, priv->bss_mode, sme->channel, sme, 0); -done: if (!ret) { cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0, NULL, 0, WLAN_STATUS_SUCCESS, @@ -996,9 +1793,92 @@ done: "info: association to bssid %pM failed\n", priv->cfg_bssid); memset(priv->cfg_bssid, 0, ETH_ALEN); + + if (ret > 0) + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, + NULL, 0, NULL, 0, ret, + GFP_KERNEL); + else + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); } - return ret; + return 0; +} + +/* + * This function sets following parameters for ibss network. + * - channel + * - start band + * - 11n flag + * - secondary channel offset + */ +static int mwifiex_set_ibss_params(struct mwifiex_private *priv, + struct cfg80211_ibss_params *params) +{ + struct wiphy *wiphy = priv->wdev->wiphy; + struct mwifiex_adapter *adapter = priv->adapter; + int index = 0, i; + u8 config_bands = 0; + + if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) { + if (!params->basic_rates) { + config_bands = BAND_B | BAND_G; + } else { + for (i = 0; i < mwifiex_band_2ghz.n_bitrates; i++) { + /* + * Rates below 6 Mbps in the table are CCK + * rates; 802.11b and from 6 they are OFDM; + * 802.11G + */ + if (mwifiex_rates[i].bitrate == 60) { + index = 1 << i; + break; + } + } + + if (params->basic_rates < index) { + config_bands = BAND_B; + } else { + config_bands = BAND_G; + if (params->basic_rates % index) + config_bands |= BAND_B; + } + } + + if (cfg80211_get_chandef_type(¶ms->chandef) != + NL80211_CHAN_NO_HT) + config_bands |= BAND_G | BAND_GN; + } else { + if (cfg80211_get_chandef_type(¶ms->chandef) == + NL80211_CHAN_NO_HT) + config_bands = BAND_A; + else + config_bands = BAND_AN | BAND_A; + } + + if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) { + adapter->config_bands = config_bands; + adapter->adhoc_start_band = config_bands; + + if ((config_bands & BAND_GN) || (config_bands & BAND_AN)) + adapter->adhoc_11n_enabled = true; + else + adapter->adhoc_11n_enabled = false; + } + + adapter->sec_chan_offset = + mwifiex_chan_type_to_sec_chan_offset( + cfg80211_get_chandef_type(¶ms->chandef)); + priv->adhoc_channel = ieee80211_frequency_to_channel( + params->chandef.chan->center_freq); + + wiphy_dbg(wiphy, "info: set ibss band %d, chan %d, chan offset %d\n", + config_bands, priv->adhoc_channel, adapter->sec_chan_offset); + + return 0; } /* @@ -1011,7 +1891,7 @@ static int mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) { - struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); int ret = 0; if (priv->bss_mode != NL80211_IFTYPE_ADHOC) { @@ -1021,14 +1901,18 @@ mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, } wiphy_dbg(wiphy, "info: trying to join to %s and bssid %pM\n", - (char *) params->ssid, params->bssid); + (char *) params->ssid, params->bssid); + + mwifiex_set_ibss_params(priv, params); ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid, - params->bssid, priv->bss_mode, - params->channel, NULL, params->privacy); + params->bssid, priv->bss_mode, + params->chandef.chan, NULL, + params->privacy); done: if (!ret) { - cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL); + cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, + params->chandef.chan, GFP_KERNEL); dev_dbg(priv->adapter->dev, "info: joined/created adhoc network with bssid" " %pM successfully\n", priv->cfg_bssid); @@ -1049,10 +1933,10 @@ done: static int mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { - struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); wiphy_dbg(wiphy, "info: disconnecting from essid %pM\n", - priv->cfg_bssid); + priv->cfg_bssid); if (mwifiex_deauthenticate(priv, NULL)) return -EFAULT; @@ -1069,49 +1953,111 @@ mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) * it also informs the results. */ static int -mwifiex_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, +mwifiex_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { + struct net_device *dev = request->wdev->netdev; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - int i; + int i, offset, ret; struct ieee80211_channel *chan; + struct ieee_types_header *ie; + struct mwifiex_user_scan_cfg *user_scan_cfg; wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name); - priv->scan_request = request; + if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && + atomic_read(&priv->wmm.tx_pkts_queued) >= + MWIFIEX_MIN_TX_PENDING_TO_CANCEL_SCAN) { + dev_dbg(priv->adapter->dev, "scan rejected due to traffic\n"); + return -EBUSY; + } - priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), - GFP_KERNEL); - if (!priv->user_scan_cfg) { - dev_err(priv->adapter->dev, "failed to alloc scan_req\n"); - return -ENOMEM; + /* Block scan request if scan operation or scan cleanup when interface + * is disabled is in process + */ + if (priv->scan_request || priv->scan_aborting) { + dev_err(priv->adapter->dev, "cmd: Scan already in process..\n"); + return -EBUSY; } - for (i = 0; i < request->n_ssids; i++) { - memcpy(priv->user_scan_cfg->ssid_list[i].ssid, - request->ssids[i].ssid, request->ssids[i].ssid_len); - priv->user_scan_cfg->ssid_list[i].max_len = - request->ssids[i].ssid_len; + + user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); + if (!user_scan_cfg) + return -ENOMEM; + + priv->scan_request = request; + + user_scan_cfg->num_ssids = request->n_ssids; + user_scan_cfg->ssid_list = request->ssids; + + if (request->ie && request->ie_len) { + offset = 0; + for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { + if (priv->vs_ie[i].mask != MWIFIEX_VSIE_MASK_CLEAR) + continue; + priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_SCAN; + ie = (struct ieee_types_header *)(request->ie + offset); + memcpy(&priv->vs_ie[i].ie, ie, sizeof(*ie) + ie->len); + offset += sizeof(*ie) + ie->len; + + if (offset >= request->ie_len) + break; + } } - for (i = 0; i < request->n_channels; i++) { + + for (i = 0; i < min_t(u32, request->n_channels, + MWIFIEX_USER_SCAN_CHAN_MAX); i++) { chan = request->channels[i]; - priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value; - priv->user_scan_cfg->chan_list[i].radio_type = chan->band; + user_scan_cfg->chan_list[i].chan_number = chan->hw_value; + user_scan_cfg->chan_list[i].radio_type = chan->band; - if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) - priv->user_scan_cfg->chan_list[i].scan_type = - MWIFIEX_SCAN_TYPE_PASSIVE; + if (chan->flags & IEEE80211_CHAN_NO_IR) + user_scan_cfg->chan_list[i].scan_type = + MWIFIEX_SCAN_TYPE_PASSIVE; else - priv->user_scan_cfg->chan_list[i].scan_type = - MWIFIEX_SCAN_TYPE_ACTIVE; + user_scan_cfg->chan_list[i].scan_type = + MWIFIEX_SCAN_TYPE_ACTIVE; - priv->user_scan_cfg->chan_list[i].scan_time = 0; + user_scan_cfg->chan_list[i].scan_time = 0; } - if (mwifiex_set_user_scan_ioctl(priv, priv->user_scan_cfg)) - return -EFAULT; + ret = mwifiex_scan_networks(priv, user_scan_cfg); + kfree(user_scan_cfg); + if (ret) { + dev_err(priv->adapter->dev, "scan failed: %d\n", ret); + priv->scan_aborting = false; + priv->scan_request = NULL; + return ret; + } + + if (request->ie && request->ie_len) { + for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { + if (priv->vs_ie[i].mask == MWIFIEX_VSIE_MASK_SCAN) { + priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_CLEAR; + memset(&priv->vs_ie[i].ie, 0, + MWIFIEX_MAX_VSIE_LEN); + } + } + } return 0; } +static void mwifiex_setup_vht_caps(struct ieee80211_sta_vht_cap *vht_info, + struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + vht_info->vht_supported = true; + + vht_info->cap = adapter->hw_dot_11ac_dev_cap; + /* Update MCS support for VHT */ + vht_info->vht_mcs.rx_mcs_map = cpu_to_le16( + adapter->hw_dot_11ac_mcs_support & 0xFFFF); + vht_info->vht_mcs.rx_highest = 0; + vht_info->vht_mcs.tx_mcs_map = cpu_to_le16( + adapter->hw_dot_11ac_mcs_support >> 16); + vht_info->vht_mcs.tx_highest = 0; +} + /* * This function sets up the CFG802.11 specific HT capability fields * with default values. @@ -1155,27 +2101,42 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, else ht_info->cap &= ~IEEE80211_HT_CAP_SGI_40; - if (ISSUPP_RXSTBC(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT; + if (adapter->user_dev_mcs_support == HT_STREAM_2X2) + ht_info->cap |= 3 << IEEE80211_HT_CAP_RX_STBC_SHIFT; else - ht_info->cap &= ~(3 << IEEE80211_HT_CAP_RX_STBC_SHIFT); + ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT; if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap)) ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; else ht_info->cap &= ~IEEE80211_HT_CAP_TX_STBC; + if (ISSUPP_GREENFIELD(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; + else + ht_info->cap &= ~IEEE80211_HT_CAP_GRN_FLD; + + if (ISENABLED_40MHZ_INTOLERANT(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_40MHZ_INTOLERANT; + else + ht_info->cap &= ~IEEE80211_HT_CAP_40MHZ_INTOLERANT; + + if (ISSUPP_RXLDPC(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; + else + ht_info->cap &= ~IEEE80211_HT_CAP_LDPC_CODING; + ht_info->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU; ht_info->cap |= IEEE80211_HT_CAP_SM_PS; - rx_mcs_supp = GET_RXMCSSUPP(adapter->hw_dev_mcs_support); - /* Set MCS for 1x1 */ + rx_mcs_supp = GET_RXMCSSUPP(adapter->user_dev_mcs_support); + /* Set MCS for 1x1/2x2 */ memset(mcs, 0xff, rx_mcs_supp); /* Clear all the other values */ memset(&mcs[rx_mcs_supp], 0, - sizeof(struct ieee80211_mcs_info) - rx_mcs_supp); + sizeof(struct ieee80211_mcs_info) - rx_mcs_supp); if (priv->bss_mode == NL80211_IFTYPE_STATION || - ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) + ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ SETHT_MCS32(mcs_set.rx_mask); @@ -1187,34 +2148,40 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, /* * create a new virtual interface with the given name */ -struct net_device *mwifiex_add_virtual_intf(struct wiphy *wiphy, - char *name, - enum nl80211_iftype type, - u32 *flags, - struct vif_params *params) +struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, + const char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) { - struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); - struct mwifiex_adapter *adapter; + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv; struct net_device *dev; void *mdev_priv; + struct wireless_dev *wdev; - if (!priv) - return NULL; - - adapter = priv->adapter; if (!adapter) - return NULL; + return ERR_PTR(-EFAULT); switch (type) { case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: + priv = adapter->priv[MWIFIEX_BSS_TYPE_STA]; if (priv->bss_mode) { - wiphy_err(wiphy, "cannot create multiple" - " station/adhoc interfaces\n"); - return NULL; + wiphy_err(wiphy, + "cannot create multiple sta/adhoc ifaces\n"); + return ERR_PTR(-EINVAL); } + wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!wdev) + return ERR_PTR(-ENOMEM); + + wdev->wiphy = wiphy; + priv->wdev = wdev; + wdev->iftype = NL80211_IFTYPE_STATION; + if (type == NL80211_IFTYPE_UNSPECIFIED) priv->bss_mode = NL80211_IFTYPE_STATION; else @@ -1224,93 +2191,171 @@ struct net_device *mwifiex_add_virtual_intf(struct wiphy *wiphy, priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; priv->bss_priority = 0; priv->bss_role = MWIFIEX_BSS_ROLE_STA; - priv->bss_index = 0; priv->bss_num = 0; break; + case NL80211_IFTYPE_AP: + priv = adapter->priv[MWIFIEX_BSS_TYPE_UAP]; + + if (priv->bss_mode) { + wiphy_err(wiphy, "Can't create multiple AP interfaces"); + return ERR_PTR(-EINVAL); + } + + wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!wdev) + return ERR_PTR(-ENOMEM); + + priv->wdev = wdev; + wdev->wiphy = wiphy; + wdev->iftype = NL80211_IFTYPE_AP; + + priv->bss_type = MWIFIEX_BSS_TYPE_UAP; + priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority = 0; + priv->bss_role = MWIFIEX_BSS_ROLE_UAP; + priv->bss_started = 0; + priv->bss_num = 0; + priv->bss_mode = type; + + break; + case NL80211_IFTYPE_P2P_CLIENT: + priv = adapter->priv[MWIFIEX_BSS_TYPE_P2P]; + + if (priv->bss_mode) { + wiphy_err(wiphy, "Can't create multiple P2P ifaces"); + return ERR_PTR(-EINVAL); + } + + wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!wdev) + return ERR_PTR(-ENOMEM); + + priv->wdev = wdev; + wdev->wiphy = wiphy; + + /* At start-up, wpa_supplicant tries to change the interface + * to NL80211_IFTYPE_STATION if it is not managed mode. + */ + wdev->iftype = NL80211_IFTYPE_P2P_CLIENT; + priv->bss_mode = NL80211_IFTYPE_P2P_CLIENT; + + /* Setting bss_type to P2P tells firmware that this interface + * is receiving P2P peers found during find phase and doing + * action frame handshake. + */ + priv->bss_type = MWIFIEX_BSS_TYPE_P2P; + + priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority = MWIFIEX_BSS_ROLE_STA; + priv->bss_role = MWIFIEX_BSS_ROLE_STA; + priv->bss_started = 0; + priv->bss_num = 0; + + if (mwifiex_cfg80211_init_p2p_client(priv)) { + wdev = ERR_PTR(-EFAULT); + goto done; + } + + break; default: wiphy_err(wiphy, "type not supported\n"); - return NULL; + return ERR_PTR(-EINVAL); } - dev = alloc_netdev_mq(sizeof(struct mwifiex_private *), name, - ether_setup, 1); + dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name, + ether_setup, IEEE80211_NUM_ACS, 1); if (!dev) { wiphy_err(wiphy, "no memory available for netdevice\n"); - goto error; + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + wdev = ERR_PTR(-ENOMEM); + goto done; } + mwifiex_init_priv_params(priv, dev); + priv->netdev = dev; + + mwifiex_setup_ht_caps(&wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, priv); + if (adapter->is_hw_11ac_capable) + mwifiex_setup_vht_caps( + &wiphy->bands[IEEE80211_BAND_2GHZ]->vht_cap, priv); + + if (adapter->config_bands & BAND_A) + mwifiex_setup_ht_caps( + &wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, priv); + + if ((adapter->config_bands & BAND_A) && adapter->is_hw_11ac_capable) + mwifiex_setup_vht_caps( + &wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap, priv); + dev_net_set(dev, wiphy_net(wiphy)); dev->ieee80211_ptr = priv->wdev; dev->ieee80211_ptr->iftype = priv->bss_mode; memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); - memcpy(dev->perm_addr, wiphy->perm_addr, ETH_ALEN); SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); dev->flags |= IFF_BROADCAST | IFF_MULTICAST; dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT; dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN; + dev->ethtool_ops = &mwifiex_ethtool_ops; mdev_priv = netdev_priv(dev); *((unsigned long *) mdev_priv) = (unsigned long) priv; - priv->netdev = dev; - mwifiex_init_priv_params(priv, dev); - SET_NETDEV_DEV(dev, adapter->dev); /* Register network device */ if (register_netdevice(dev)) { wiphy_err(wiphy, "cannot register virtual network device\n"); - goto error; + free_netdev(dev); + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->netdev = NULL; + wdev = ERR_PTR(-EFAULT); + goto done; } sema_init(&priv->async_sem, 1); - priv->scan_pending_on_block = false; dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name); #ifdef CONFIG_DEBUG_FS mwifiex_dev_debugfs_init(priv); #endif - return dev; -error: - if (dev && (dev->reg_state == NETREG_UNREGISTERED)) - free_netdev(dev); - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - return NULL; +done: + if (IS_ERR(wdev)) { + kfree(priv->wdev); + priv->wdev = NULL; + } + + return wdev; } EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf); /* * del_virtual_intf: remove the virtual interface determined by dev */ -int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev) +int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) { - struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); - - if (!priv || !dev) - return 0; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); #ifdef CONFIG_DEBUG_FS mwifiex_dev_debugfs_remove(priv); #endif - if (!netif_queue_stopped(priv->netdev)) - netif_stop_queue(priv->netdev); + mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); - if (dev->reg_state == NETREG_REGISTERED) - unregister_netdevice(dev); - - if (dev->reg_state == NETREG_UNREGISTERED) - free_netdev(dev); + if (wdev->netdev->reg_state == NETREG_REGISTERED) + unregister_netdevice(wdev->netdev); /* Clear the priv in adapter */ + priv->netdev->ieee80211_ptr = NULL; priv->netdev = NULL; + kfree(wdev); + priv->wdev = NULL; priv->media_connected = false; @@ -1320,6 +2365,429 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev) } EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf); +static bool +mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq, + u8 max_byte_seq) +{ + int j, k, valid_byte_cnt = 0; + bool dont_care_byte = false; + + for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) { + for (k = 0; k < 8; k++) { + if (pat->mask[j] & 1 << k) { + memcpy(byte_seq + valid_byte_cnt, + &pat->pattern[j * 8 + k], 1); + valid_byte_cnt++; + if (dont_care_byte) + return false; + } else { + if (valid_byte_cnt) + dont_care_byte = true; + } + + if (valid_byte_cnt > max_byte_seq) + return false; + } + } + + byte_seq[max_byte_seq] = valid_byte_cnt; + + return true; +} + +#ifdef CONFIG_PM +static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, + struct cfg80211_wowlan *wowlan) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_ds_mef_cfg mef_cfg; + struct mwifiex_mef_entry *mef_entry; + int i, filt_num = 0, ret; + bool first_pat = true; + u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; + const u8 ipv4_mc_mac[] = {0x33, 0x33}; + const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); + + if (!wowlan) { + dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n"); + return 0; + } + + if (!priv->media_connected) { + dev_warn(adapter->dev, + "Can not configure WOWLAN in disconnected state\n"); + return 0; + } + + mef_entry = kzalloc(sizeof(*mef_entry), GFP_KERNEL); + if (!mef_entry) + return -ENOMEM; + + memset(&mef_cfg, 0, sizeof(mef_cfg)); + mef_cfg.num_entries = 1; + mef_cfg.mef_entry = mef_entry; + mef_entry->mode = MEF_MODE_HOST_SLEEP; + mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST; + + for (i = 0; i < wowlan->n_patterns; i++) { + memset(byte_seq, 0, sizeof(byte_seq)); + if (!mwifiex_is_pattern_supported(&wowlan->patterns[i], + byte_seq, + MWIFIEX_MEF_MAX_BYTESEQ)) { + wiphy_err(wiphy, "Pattern not supported\n"); + kfree(mef_entry); + return -EOPNOTSUPP; + } + + if (!wowlan->patterns[i].pkt_offset) { + if (!(byte_seq[0] & 0x01) && + (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) { + mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST; + continue; + } else if (is_broadcast_ether_addr(byte_seq)) { + mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST; + continue; + } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && + (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) || + (!memcmp(byte_seq, ipv6_mc_mac, 3) && + (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) { + mef_cfg.criteria |= MWIFIEX_CRITERIA_MULTICAST; + continue; + } + } + + mef_entry->filter[filt_num].repeat = 1; + mef_entry->filter[filt_num].offset = + wowlan->patterns[i].pkt_offset; + memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq, + sizeof(byte_seq)); + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + + if (first_pat) + first_pat = false; + else + mef_entry->filter[filt_num].filt_action = TYPE_AND; + + filt_num++; + } + + if (wowlan->magic_pkt) { + mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST; + mef_entry->filter[filt_num].repeat = 16; + memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, + ETH_ALEN); + mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = + ETH_ALEN; + mef_entry->filter[filt_num].offset = 28; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + if (filt_num) + mef_entry->filter[filt_num].filt_action = TYPE_OR; + } + + if (!mef_cfg.criteria) + mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST | + MWIFIEX_CRITERIA_UNICAST | + MWIFIEX_CRITERIA_MULTICAST; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG, + HostCmd_ACT_GEN_SET, 0, &mef_cfg, true); + + kfree(mef_entry); + return ret; +} + +static int mwifiex_cfg80211_resume(struct wiphy *wiphy) +{ + return 0; +} + +static void mwifiex_cfg80211_set_wakeup(struct wiphy *wiphy, + bool enabled) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + + device_set_wakeup_enable(adapter->dev, enabled); +} +#endif + +static int mwifiex_get_coalesce_pkt_type(u8 *byte_seq) +{ + const u8 ipv4_mc_mac[] = {0x33, 0x33}; + const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; + const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff}; + + if ((byte_seq[0] & 0x01) && + (byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 1)) + return PACKET_TYPE_UNICAST; + else if (!memcmp(byte_seq, bc_mac, 4)) + return PACKET_TYPE_BROADCAST; + else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && + byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 2) || + (!memcmp(byte_seq, ipv6_mc_mac, 3) && + byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 3)) + return PACKET_TYPE_MULTICAST; + + return 0; +} + +static int +mwifiex_fill_coalesce_rule_info(struct mwifiex_private *priv, + struct cfg80211_coalesce_rules *crule, + struct mwifiex_coalesce_rule *mrule) +{ + u8 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ + 1]; + struct filt_field_param *param; + int i; + + mrule->max_coalescing_delay = crule->delay; + + param = mrule->params; + + for (i = 0; i < crule->n_patterns; i++) { + memset(byte_seq, 0, sizeof(byte_seq)); + if (!mwifiex_is_pattern_supported(&crule->patterns[i], + byte_seq, + MWIFIEX_COALESCE_MAX_BYTESEQ)) { + dev_err(priv->adapter->dev, "Pattern not supported\n"); + return -EOPNOTSUPP; + } + + if (!crule->patterns[i].pkt_offset) { + u8 pkt_type; + + pkt_type = mwifiex_get_coalesce_pkt_type(byte_seq); + if (pkt_type && mrule->pkt_type) { + dev_err(priv->adapter->dev, + "Multiple packet types not allowed\n"); + return -EOPNOTSUPP; + } else if (pkt_type) { + mrule->pkt_type = pkt_type; + continue; + } + } + + if (crule->condition == NL80211_COALESCE_CONDITION_MATCH) + param->operation = RECV_FILTER_MATCH_TYPE_EQ; + else + param->operation = RECV_FILTER_MATCH_TYPE_NE; + + param->operand_len = byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ]; + memcpy(param->operand_byte_stream, byte_seq, + param->operand_len); + param->offset = crule->patterns[i].pkt_offset; + param++; + + mrule->num_of_fields++; + } + + if (!mrule->pkt_type) { + dev_err(priv->adapter->dev, + "Packet type can not be determined\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy, + struct cfg80211_coalesce *coalesce) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + int i, ret; + struct mwifiex_ds_coalesce_cfg coalesce_cfg; + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); + + memset(&coalesce_cfg, 0, sizeof(coalesce_cfg)); + if (!coalesce) { + dev_dbg(adapter->dev, + "Disable coalesce and reset all previous rules\n"); + return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG, + HostCmd_ACT_GEN_SET, 0, + &coalesce_cfg, true); + } + + coalesce_cfg.num_of_rules = coalesce->n_rules; + for (i = 0; i < coalesce->n_rules; i++) { + ret = mwifiex_fill_coalesce_rule_info(priv, &coalesce->rules[i], + &coalesce_cfg.rule[i]); + if (ret) { + dev_err(priv->adapter->dev, + "Recheck the patterns provided for rule %d\n", + i + 1); + return ret; + } + } + + return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG, + HostCmd_ACT_GEN_SET, 0, &coalesce_cfg, true); +} + +/* cfg80211 ops handler for tdls_mgmt. + * Function prepares TDLS action frame packets and forwards them to FW + */ +static int +mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capability, + const u8 *extra_ies, size_t extra_ies_len) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + int ret; + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return -ENOTSUPP; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + dev_dbg(priv->adapter->dev, + "Send TDLS Setup Request to %pM status_code=%d\n", peer, + status_code); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_SETUP_RESPONSE: + dev_dbg(priv->adapter->dev, + "Send TDLS Setup Response to %pM status_code=%d\n", + peer, status_code); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_SETUP_CONFIRM: + dev_dbg(priv->adapter->dev, + "Send TDLS Confirm to %pM status_code=%d\n", peer, + status_code); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_TEARDOWN: + dev_dbg(priv->adapter->dev, "Send TDLS Tear down to %pM\n", + peer); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_DISCOVERY_REQUEST: + dev_dbg(priv->adapter->dev, + "Send TDLS Discovery Request to %pM\n", peer); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + dev_dbg(priv->adapter->dev, + "Send TDLS Discovery Response to %pM\n", peer); + ret = mwifiex_send_tdls_action_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + default: + dev_warn(priv->adapter->dev, + "Unknown TDLS mgmt/action frame %pM\n", peer); + ret = -EINVAL; + break; + } + + return ret; +} + +static int +mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, enum nl80211_tdls_operation action) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) || + !(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return -ENOTSUPP; + + dev_dbg(priv->adapter->dev, + "TDLS peer=%pM, oper=%d\n", peer, action); + + switch (action) { + case NL80211_TDLS_ENABLE_LINK: + action = MWIFIEX_TDLS_ENABLE_LINK; + break; + case NL80211_TDLS_DISABLE_LINK: + action = MWIFIEX_TDLS_DISABLE_LINK; + break; + case NL80211_TDLS_TEARDOWN: + /* shouldn't happen!*/ + dev_warn(priv->adapter->dev, + "tdls_oper: teardown from driver not supported\n"); + return -EINVAL; + case NL80211_TDLS_SETUP: + /* shouldn't happen!*/ + dev_warn(priv->adapter->dev, + "tdls_oper: setup from driver not supported\n"); + return -EINVAL; + case NL80211_TDLS_DISCOVERY_REQ: + /* shouldn't happen!*/ + dev_warn(priv->adapter->dev, + "tdls_oper: discovery from driver not supported\n"); + return -EINVAL; + default: + dev_err(priv->adapter->dev, + "tdls_oper: operation not supported\n"); + return -ENOTSUPP; + } + + return mwifiex_tdls_oper(priv, peer, action); +} + +static int +mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_parameters *params) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) + return -ENOTSUPP; + + return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK); +} + +static int +mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, + struct station_parameters *params) +{ + int ret; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + /* we support change_station handler only for TDLS peers*/ + if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) + return -ENOTSUPP; + + priv->sta_params = params; + + ret = mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CONFIG_LINK); + priv->sta_params = NULL; + + return ret; +} + /* station cfg80211 operations */ static struct cfg80211_ops mwifiex_cfg80211_ops = { .add_virtual_intf = mwifiex_add_virtual_intf, @@ -1329,16 +2797,66 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { .connect = mwifiex_cfg80211_connect, .disconnect = mwifiex_cfg80211_disconnect, .get_station = mwifiex_cfg80211_get_station, + .dump_station = mwifiex_cfg80211_dump_station, .set_wiphy_params = mwifiex_cfg80211_set_wiphy_params, - .set_channel = mwifiex_cfg80211_set_channel, .join_ibss = mwifiex_cfg80211_join_ibss, .leave_ibss = mwifiex_cfg80211_leave_ibss, .add_key = mwifiex_cfg80211_add_key, .del_key = mwifiex_cfg80211_del_key, + .mgmt_tx = mwifiex_cfg80211_mgmt_tx, + .mgmt_frame_register = mwifiex_cfg80211_mgmt_frame_register, + .remain_on_channel = mwifiex_cfg80211_remain_on_channel, + .cancel_remain_on_channel = mwifiex_cfg80211_cancel_remain_on_channel, .set_default_key = mwifiex_cfg80211_set_default_key, .set_power_mgmt = mwifiex_cfg80211_set_power_mgmt, .set_tx_power = mwifiex_cfg80211_set_tx_power, .set_bitrate_mask = mwifiex_cfg80211_set_bitrate_mask, + .start_ap = mwifiex_cfg80211_start_ap, + .stop_ap = mwifiex_cfg80211_stop_ap, + .change_beacon = mwifiex_cfg80211_change_beacon, + .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config, + .set_antenna = mwifiex_cfg80211_set_antenna, + .del_station = mwifiex_cfg80211_del_station, +#ifdef CONFIG_PM + .suspend = mwifiex_cfg80211_suspend, + .resume = mwifiex_cfg80211_resume, + .set_wakeup = mwifiex_cfg80211_set_wakeup, +#endif + .set_coalesce = mwifiex_cfg80211_set_coalesce, + .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt, + .tdls_oper = mwifiex_cfg80211_tdls_oper, + .add_station = mwifiex_cfg80211_add_station, + .change_station = mwifiex_cfg80211_change_station, +}; + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support mwifiex_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT, + .n_patterns = MWIFIEX_MEF_MAX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, + .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, +}; +#endif + +static bool mwifiex_is_valid_alpha2(const char *alpha2) +{ + if (!alpha2 || strlen(alpha2) != 2) + return false; + + if (isalpha(alpha2[0]) && isalpha(alpha2[1])) + return true; + + return false; +} + +static const struct wiphy_coalesce_support mwifiex_coalesce_support = { + .n_rules = MWIFIEX_COALESCE_MAX_RULES, + .max_delay = MWIFIEX_MAX_COALESCING_DELAY, + .n_patterns = MWIFIEX_COALESCE_MAX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, + .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, }; /* @@ -1348,74 +2866,123 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { * default parameters and handler function pointers, and finally * registers the device. */ -int mwifiex_register_cfg80211(struct mwifiex_private *priv) + +int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) { int ret; void *wdev_priv; - struct wireless_dev *wdev; - - wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); - if (!wdev) { - dev_err(priv->adapter->dev, "%s: allocating wireless device\n", - __func__); + struct wiphy *wiphy; + struct mwifiex_private *priv = adapter->priv[MWIFIEX_BSS_TYPE_STA]; + u8 *country_code; + u32 thr, retry; + + /* create a new wiphy for use with cfg80211 */ + wiphy = wiphy_new(&mwifiex_cfg80211_ops, + sizeof(struct mwifiex_adapter *)); + if (!wiphy) { + dev_err(adapter->dev, "%s: creating new wiphy\n", __func__); return -ENOMEM; } - wdev->wiphy = - wiphy_new(&mwifiex_cfg80211_ops, - sizeof(struct mwifiex_private *)); - if (!wdev->wiphy) { - kfree(wdev); - return -ENOMEM; - } - wdev->iftype = NL80211_IFTYPE_STATION; - wdev->wiphy->max_scan_ssids = 10; - wdev->wiphy->interface_modes = - BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); - - wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz; - mwifiex_setup_ht_caps( - &wdev->wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, priv); + wiphy->max_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH; + wiphy->max_scan_ie_len = MWIFIEX_MAX_VSIE_LEN; + wiphy->mgmt_stypes = mwifiex_mgmt_stypes; + wiphy->max_remain_on_channel_duration = 5000; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_AP); + + wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz; + if (adapter->config_bands & BAND_A) + wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz; + else + wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; - if (priv->adapter->config_bands & BAND_A) { - wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz; - mwifiex_setup_ht_caps( - &wdev->wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, priv); - } else { - wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; - } + wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta; + wiphy->n_iface_combinations = 1; /* Initialize cipher suits */ - wdev->wiphy->cipher_suites = mwifiex_cipher_suites; - wdev->wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites); + wiphy->cipher_suites = mwifiex_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites); + + memcpy(wiphy->perm_addr, priv->curr_addr, ETH_ALEN); + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | + WIPHY_FLAG_AP_UAPSD | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; - memcpy(wdev->wiphy->perm_addr, priv->curr_addr, ETH_ALEN); - wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | + WIPHY_FLAG_TDLS_EXTERNAL_SETUP; - /* Reserve space for bss band information */ - wdev->wiphy->bss_priv_size = sizeof(u8); + wiphy->regulatory_flags |= + REGULATORY_CUSTOM_REG | + REGULATORY_STRICT_REG; - wdev->wiphy->reg_notifier = mwifiex_reg_notifier; + wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom); - /* Set struct mwifiex_private pointer in wiphy_priv */ - wdev_priv = wiphy_priv(wdev->wiphy); +#ifdef CONFIG_PM + wiphy->wowlan = &mwifiex_wowlan_support; +#endif + + wiphy->coalesce = &mwifiex_coalesce_support; + + wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; + + wiphy->available_antennas_tx = BIT(adapter->number_of_antenna) - 1; + wiphy->available_antennas_rx = BIT(adapter->number_of_antenna) - 1; + + wiphy->features |= NL80211_FEATURE_HT_IBSS | + NL80211_FEATURE_INACTIVITY_TIMER | + NL80211_FEATURE_LOW_PRIORITY_SCAN | + NL80211_FEATURE_NEED_OBSS_SCAN; + + /* Reserve space for mwifiex specific private data for BSS */ + wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv); - *(unsigned long *) wdev_priv = (unsigned long) priv; + wiphy->reg_notifier = mwifiex_reg_notifier; - set_wiphy_dev(wdev->wiphy, (struct device *) priv->adapter->dev); + /* Set struct mwifiex_adapter pointer in wiphy_priv */ + wdev_priv = wiphy_priv(wiphy); + *(unsigned long *)wdev_priv = (unsigned long)adapter; - ret = wiphy_register(wdev->wiphy); + set_wiphy_dev(wiphy, priv->adapter->dev); + + ret = wiphy_register(wiphy); if (ret < 0) { - dev_err(priv->adapter->dev, "%s: registering cfg80211 device\n", - __func__); - wiphy_free(wdev->wiphy); - kfree(wdev); + dev_err(adapter->dev, + "%s: wiphy_register failed: %d\n", __func__, ret); + wiphy_free(wiphy); return ret; - } else { - dev_dbg(priv->adapter->dev, - "info: successfully registered wiphy device\n"); } - priv->wdev = wdev; + if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) { + wiphy_info(wiphy, "driver hint alpha2: %2.2s\n", reg_alpha2); + regulatory_hint(wiphy, reg_alpha2); + } else { + country_code = mwifiex_11d_code_2_region(adapter->region_code); + if (country_code) + wiphy_info(wiphy, "ignoring F/W country code %2.2s\n", + country_code); + } + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, FRAG_THRESH_I, &thr, true); + wiphy->frag_threshold = thr; + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, RTS_THRESH_I, &thr, true); + wiphy->rts_threshold = thr; + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry, true); + wiphy->retry_short = (u8) retry; + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, LONG_RETRY_LIM_I, &retry, true); + wiphy->retry_long = (u8) retry; + + adapter->wiphy = wiphy; return ret; } |
