diff options
Diffstat (limited to 'drivers/net/wireless/mwifiex')
25 files changed, 1152 insertions, 204 deletions
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index d2732736f86..245a371f1a4 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -176,23 +176,6 @@ int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, } /* - * This function handles the command response of 11n configuration request. - * - * Handling includes changing the header fields into CPU format. - */ -int mwifiex_ret_11n_cfg(struct host_cmd_ds_command *resp, - struct mwifiex_ds_11n_tx_cfg *tx_cfg) -{ - struct host_cmd_ds_11n_cfg *htcfg = &resp->params.htcfg; - - if (tx_cfg) { - tx_cfg->tx_htcap = le16_to_cpu(htcfg->ht_tx_cap); - tx_cfg->tx_htinfo = le16_to_cpu(htcfg->ht_tx_info); - } - return 0; -} - -/* * This function prepares command of reconfigure Tx buffer. * * Preparation includes - @@ -258,27 +241,6 @@ int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, } /* - * This function handles the command response of AMSDU aggregation - * control request. - * - * Handling includes changing the header fields into CPU format. - */ -int mwifiex_ret_amsdu_aggr_ctrl(struct host_cmd_ds_command *resp, - struct mwifiex_ds_11n_amsdu_aggr_ctrl - *amsdu_aggr_ctrl) -{ - struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl = - &resp->params.amsdu_aggr_ctrl; - - if (amsdu_aggr_ctrl) { - amsdu_aggr_ctrl->enable = le16_to_cpu(amsdu_ctrl->enable); - amsdu_aggr_ctrl->curr_buf_size = - le16_to_cpu(amsdu_ctrl->curr_buf_size); - } - return 0; -} - -/* * This function prepares 11n configuration command. * * Preparation includes - diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h index 67c087cf9dc..46006a54a65 100644 --- a/drivers/net/wireless/mwifiex/11n.h +++ b/drivers/net/wireless/mwifiex/11n.h @@ -28,8 +28,6 @@ int mwifiex_ret_11n_delba(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); -int mwifiex_ret_11n_cfg(struct host_cmd_ds_command *resp, - struct mwifiex_ds_11n_tx_cfg *tx_cfg); int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd, u16 cmd_action, struct mwifiex_ds_11n_tx_cfg *txcfg); @@ -60,9 +58,6 @@ int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, struct mwifiex_ds_rx_reorder_tbl *buf); int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, struct mwifiex_ds_tx_ba_stream_tbl *buf); -int mwifiex_ret_amsdu_aggr_ctrl(struct host_cmd_ds_command *resp, - struct mwifiex_ds_11n_amsdu_aggr_ctrl - *amsdu_aggr_ctrl); int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, int cmd_action, u16 *buf_size); diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index 24e2582b467..9402b93b9a3 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c @@ -302,6 +302,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, new_node->start_win = last_seq + 1; new_node->win_size = win_size; + new_node->flags = 0; new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size, GFP_KERNEL); @@ -457,13 +458,20 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, * If seq_num is less then starting win then ignore and drop the * packet */ - if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {/* Wrap */ - if (seq_num >= ((start_win + TWOPOW11) & - (MAX_TID_VALUE - 1)) && (seq_num < start_win)) + if (tbl->flags & RXREOR_FORCE_NO_DROP) { + dev_dbg(priv->adapter->dev, + "RXREOR_FORCE_NO_DROP when HS is activated\n"); + tbl->flags &= ~RXREOR_FORCE_NO_DROP; + } else { + if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { + if (seq_num >= ((start_win + TWOPOW11) & + (MAX_TID_VALUE - 1)) && + seq_num < start_win) + return -1; + } else if ((seq_num < start_win) || + (seq_num > (start_win + TWOPOW11))) { return -1; - } else if ((seq_num < start_win) || - (seq_num > (start_win + TWOPOW11))) { - return -1; + } } /* @@ -474,8 +482,7 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1); if (((end_win < start_win) && - (seq_num < (TWOPOW11 - (MAX_TID_VALUE - start_win))) && - (seq_num > end_win)) || + (seq_num < start_win) && (seq_num > end_win)) || ((end_win > start_win) && ((seq_num > end_win) || (seq_num < start_win)))) { end_win = seq_num; @@ -637,3 +644,29 @@ void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); mwifiex_reset_11n_rx_seq_num(priv); } + +/* + * This function updates all rx_reorder_tbl's flags. + */ +void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags) +{ + struct mwifiex_private *priv; + struct mwifiex_rx_reorder_tbl *tbl; + unsigned long lock_flags; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (list_empty(&priv->rx_reorder_tbl_ptr)) + continue; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags); + list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) + tbl->flags = flags; + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags); + } + + return; +} diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h index 72848591691..4064041ac85 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.h +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.h @@ -41,6 +41,10 @@ #define BA_SETUP_MAX_PACKET_THRESHOLD 16 #define BA_SETUP_PACKET_OFFSET 16 +enum mwifiex_rxreor_flags { + RXREOR_FORCE_NO_DROP = 1<<0, +}; + static inline void mwifiex_reset_11n_rx_seq_num(struct mwifiex_private *priv) { memset(priv->rx_seq, 0xff, sizeof(priv->rx_seq)); @@ -73,5 +77,6 @@ struct mwifiex_rx_reorder_tbl *mwifiex_11n_get_rxreorder_tbl(struct struct mwifiex_rx_reorder_tbl * mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta); void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta); +void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags); #endif /* _MWIFIEX_11N_RXREORDER_H_ */ diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e57f543413d..2691620393e 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -22,7 +22,7 @@ static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = { { - .max = 1, .types = BIT(NL80211_IFTYPE_STATION), + .max = 2, .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 1, .types = BIT(NL80211_IFTYPE_AP), @@ -37,6 +37,36 @@ static const struct ieee80211_iface_combination mwifiex_iface_comb_ap_sta = { .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_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), + /* Channel 14 */ + REG_RULE(2484-10, 2484+10, 20, 3, 20, + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | + NL80211_RRF_NO_OFDM), + /* Channel 36 - 48 */ + REG_RULE(5180-10, 5240+10, 40, 3, 20, + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), + /* Channel 149 - 165 */ + REG_RULE(5745-10, 5825+10, 40, 3, 20, + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), + /* Channel 52 - 64 */ + REG_RULE(5260-10, 5320+10, 40, 3, 30, + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | + NL80211_RRF_DFS), + /* Channel 100 - 140 */ + REG_RULE(5500-10, 5700+10, 40, 3, 30, + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | + NL80211_RRF_DFS), + } +}; + /* * This function maps the nl802.11 channel type into driver channel type. * @@ -47,8 +77,7 @@ static const struct ieee80211_iface_combination mwifiex_iface_comb_ap_sta = { * NL80211_CHAN_HT40MINUS -> IEEE80211_HT_PARAM_CHA_SEC_BELOW * Others -> IEEE80211_HT_PARAM_CHA_SEC_NONE */ -static u8 -mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type) +u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type) { switch (chan_type) { case NL80211_CHAN_NO_HT: @@ -109,6 +138,188 @@ 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 ieee80211_channel *chan, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, u64 *cookie) +{ + struct sk_buff *skb; + u16 pkt_len; + const struct ieee80211_mgmt *mgmt; + 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; + } + + mwifiex_form_mgmt_frame(skb, buf, len); + mwifiex_queue_tx_pkt(priv, skb); + + *cookie = random32() | 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); + + if (reg) + priv->mgmt_frame_mask |= BIT(frame_type >> 4); + else + priv->mgmt_frame_mask &= ~BIT(frame_type >> 4); + + mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG, + HostCmd_ACT_GEN_SET, 0, &priv->mgmt_frame_mask); + + 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, + enum nl80211_channel_type channel_type, + 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, + &channel_type, duration); + + if (!ret) { + *cookie = random32() | 1; + priv->roc_cfg.cookie = *cookie; + priv->roc_cfg.chan = *chan; + priv->roc_cfg.chan_type = channel_type; + + cfg80211_ready_on_channel(wdev, *cookie, chan, channel_type, + 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, + &priv->roc_cfg.chan_type, 0); + + if (!ret) { + cfg80211_remain_on_channel_expired(wdev, cookie, + &priv->roc_cfg.chan, + priv->roc_cfg.chan_type, + 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 @@ -463,6 +674,76 @@ mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) return 0; } +static int +mwifiex_cfg80211_deinit_p2p(struct mwifiex_private *priv) +{ + u16 mode = P2P_MODE_DISABLE; + + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) + mwifiex_set_bss_role(priv, MWIFIEX_BSS_ROLE_STA); + + if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode)) + return -1; + + return 0; +} + +/* + * This function initializes the functionalities for P2P client. + * The P2P client initialization sequence is: + * disable -> device -> client + */ +static int +mwifiex_cfg80211_init_p2p_client(struct mwifiex_private *priv) +{ + u16 mode; + + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -1; + + mode = P2P_MODE_DEVICE; + if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode)) + return -1; + + mode = P2P_MODE_CLIENT; + if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode)) + return -1; + + return 0; +} + +/* + * This function initializes the functionalities for P2P GO. + * The P2P GO initialization sequence is: + * disable -> device -> GO + */ +static int +mwifiex_cfg80211_init_p2p_go(struct mwifiex_private *priv) +{ + u16 mode; + + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -1; + + mode = P2P_MODE_DEVICE; + if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode)) + return -1; + + mode = P2P_MODE_GO; + if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode)) + return -1; + + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) + mwifiex_set_bss_role(priv, MWIFIEX_BSS_ROLE_UAP); + + return 0; +} + /* * CFG802.11 operation handler to change interface type. */ @@ -495,6 +776,16 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, 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 */ @@ -520,6 +811,18 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, 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; default: wiphy_err(wiphy, "%s: unknown iftype: %d\n", dev->name, dev->ieee80211_ptr->iftype); @@ -658,7 +961,6 @@ mwifiex_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev, } /* Supported rates to be advertised to the cfg80211 */ - static struct ieee80211_rate mwifiex_rates[] = { {.bitrate = 10, .hw_value = 2, }, {.bitrate = 20, .hw_value = 4, }, @@ -675,7 +977,6 @@ static struct ieee80211_rate mwifiex_rates[] = { }; /* 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, }, @@ -743,7 +1044,6 @@ static struct ieee80211_supported_band mwifiex_band_5ghz = { /* Supported crypto cipher suits to be advertised to cfg80211 */ - static const u32 mwifiex_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, @@ -752,6 +1052,35 @@ static const u32 mwifiex_cipher_suites[] = { 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. * @@ -844,7 +1173,7 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy, { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) { wiphy_err(wiphy, "%s: bss_type mismatched\n", __func__); return -EINVAL; } @@ -932,7 +1261,7 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); u8 config_bands = 0; - if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) return -1; if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon)) return -1; @@ -969,15 +1298,18 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, bss_cfg->channel = (u8)ieee80211_frequency_to_channel(params->channel->center_freq); - bss_cfg->band_cfg = BAND_CONFIG_MANUAL; /* Set appropriate bands */ if (params->channel->band == IEEE80211_BAND_2GHZ) { + bss_cfg->band_cfg = BAND_CONFIG_BG; + if (params->channel_type == NL80211_CHAN_NO_HT) config_bands = BAND_B | BAND_G; else config_bands = BAND_B | BAND_G | BAND_GN; } else { + bss_cfg->band_cfg = BAND_CONFIG_A; + if (params->channel_type == NL80211_CHAN_NO_HT) config_bands = BAND_A; else @@ -988,6 +1320,7 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, ~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)) { @@ -998,6 +1331,12 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, mwifiex_set_ht_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_sync(priv, HostCmd_CMD_UAP_BSS_STOP, HostCmd_ACT_GEN_SET, 0, NULL)) { wiphy_err(wiphy, "Failed to stop the BSS\n"); @@ -1153,7 +1492,6 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, ~priv->adapter->fw_bands)) priv->adapter->config_bands = config_bands; } - mwifiex_send_domain_info_cmd_fw(priv->wdev->wiphy); } /* As this is new association, clear locally stored @@ -1464,11 +1802,18 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, { struct net_device *dev = request->wdev->netdev; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - int i; + int i, offset; struct ieee80211_channel *chan; + struct ieee_types_header *ie; wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name); + if (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->scan_request = request; priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), @@ -1482,13 +1827,17 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, priv->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; - memcpy(&priv->vs_ie[i].ie, request->ie, - request->ie_len); - break; + 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; } } @@ -1597,7 +1946,7 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, * create a new virtual interface with the given name */ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, - char *name, + const char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params) @@ -1637,7 +1986,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, priv->bss_type = MWIFIEX_BSS_TYPE_STA; priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; - priv->bss_priority = MWIFIEX_BSS_ROLE_STA; + priv->bss_priority = 0; priv->bss_role = MWIFIEX_BSS_ROLE_STA; priv->bss_num = 0; @@ -1660,13 +2009,48 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, priv->bss_type = MWIFIEX_BSS_TYPE_UAP; priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; - priv->bss_priority = MWIFIEX_BSS_ROLE_UAP; + 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. + * So, we initialize it to STA mode. + */ + wdev->iftype = NL80211_IFTYPE_STATION; + priv->bss_mode = NL80211_IFTYPE_STATION; + + /* 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; + + break; default: wiphy_err(wiphy, "type not supported\n"); return ERR_PTR(-EINVAL); @@ -1774,6 +2158,10 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { .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, @@ -1810,8 +2198,12 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) } 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; @@ -1830,15 +2222,21 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) 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_PROBE_RESP_OFFLOAD | + WIPHY_FLAG_CUSTOM_REGULATORY | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + + wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom); wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | - NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2; + 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; + wiphy->features = NL80211_FEATURE_HT_IBSS | + NL80211_FEATURE_INACTIVITY_TIMER; /* Reserve space for mwifiex specific private data for BSS */ wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv); @@ -1859,8 +2257,9 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) return ret; } country_code = mwifiex_11d_code_2_region(priv->adapter->region_code); - if (country_code && regulatory_hint(wiphy, country_code)) - dev_err(adapter->dev, "regulatory_hint() failed\n"); + if (country_code) + dev_info(adapter->dev, + "ignoring F/W country code %2.2s\n", country_code); adapter->wiphy = wiphy; return ret; diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index c229dddcf1c..8d465107f52 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -170,7 +170,20 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, cmd_code = le16_to_cpu(host_cmd->command); cmd_size = le16_to_cpu(host_cmd->size); - skb_trim(cmd_node->cmd_skb, cmd_size); + /* Adjust skb length */ + if (cmd_node->cmd_skb->len > cmd_size) + /* + * cmd_size is less than sizeof(struct host_cmd_ds_command). + * Trim off the unused portion. + */ + skb_trim(cmd_node->cmd_skb, cmd_size); + else if (cmd_node->cmd_skb->len < cmd_size) + /* + * cmd_size is larger than sizeof(struct host_cmd_ds_command) + * because we have appended custom IE TLV. Increase skb length + * accordingly. + */ + skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len); do_gettimeofday(&tstamp); dev_dbg(adapter->dev, "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d," @@ -1075,6 +1088,8 @@ mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated) if (activated) { if (priv->adapter->is_hs_configured) { priv->adapter->hs_activated = true; + mwifiex_update_rxreor_flags(priv->adapter, + RXREOR_FORCE_NO_DROP); dev_dbg(priv->adapter->dev, "event: hs_activated\n"); priv->adapter->hs_activate_wait_q_woken = true; wake_up_interruptible( diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index 400d360ac91..e9357d87d32 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -28,11 +28,14 @@ #include <linux/ieee80211.h> -#define MWIFIEX_MAX_BSS_NUM (2) +#define MWIFIEX_MAX_BSS_NUM (3) #define MWIFIEX_MIN_DATA_HEADER_LEN 36 /* sizeof(mwifiex_txpd) * + 4 byte alignment */ +#define MWIFIEX_MGMT_FRAME_HEADER_SIZE 8 /* sizeof(pkt_type) + * + sizeof(tx_control) + */ #define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2 #define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16 @@ -67,6 +70,7 @@ enum mwifiex_bss_type { MWIFIEX_BSS_TYPE_STA = 0, MWIFIEX_BSS_TYPE_UAP = 1, + MWIFIEX_BSS_TYPE_P2P = 2, MWIFIEX_BSS_TYPE_ANY = 0xff, }; diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index ae06f31c683..dda588b3557 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -94,6 +94,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { }; #define CAL_SNR(RSSI, NF) ((s16)((s16)(RSSI)-(s16)(NF))) +#define CAL_RSSI(SNR, NF) ((s16)((s16)(SNR)+(s16)(NF))) #define UAP_BSS_PARAMS_I 0 #define UAP_CUSTOM_IE_I 1 @@ -108,6 +109,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define MGMT_MASK_BEACON 0x100 #define TLV_TYPE_UAP_SSID 0x0000 +#define TLV_TYPE_UAP_RATES 0x0001 #define PROPRIETARY_TLV_BASE_ID 0x0100 #define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) @@ -126,6 +128,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45) #define TLV_TYPE_UAP_BCAST_SSID (PROPRIETARY_TLV_BASE_ID + 48) #define TLV_TYPE_UAP_RTS_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 51) +#define TLV_TYPE_UAP_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 57) #define TLV_TYPE_UAP_WEP_KEY (PROPRIETARY_TLV_BASE_ID + 59) #define TLV_TYPE_UAP_WPA_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 60) #define TLV_TYPE_UAP_ENCRY_PROTOCOL (PROPRIETARY_TLV_BASE_ID + 64) @@ -140,6 +143,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_MGMT_IE (PROPRIETARY_TLV_BASE_ID + 105) #define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 113) #define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 114) +#define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123) #define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145) #define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146) @@ -259,9 +263,12 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_TX_RATE_CFG 0x00d6 #define HostCmd_CMD_802_11_PS_MODE_ENH 0x00e4 #define HostCmd_CMD_802_11_HS_CFG_ENH 0x00e5 +#define HostCmd_CMD_P2P_MODE_CFG 0x00eb #define HostCmd_CMD_CAU_REG_ACCESS 0x00ed #define HostCmd_CMD_SET_BSS_MODE 0x00f7 #define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa +#define HostCmd_CMD_MGMT_FRAME_REG 0x010c +#define HostCmd_CMD_REMAIN_ON_CHAN 0x010d #define PROTOCOL_NO_SECURITY 0x01 #define PROTOCOL_STATIC_WEP 0x02 @@ -287,9 +294,17 @@ enum ENH_PS_MODES { DIS_AUTO_PS = 0xfe, }; +enum P2P_MODES { + P2P_MODE_DISABLE = 0, + P2P_MODE_DEVICE = 1, + P2P_MODE_GO = 2, + P2P_MODE_CLIENT = 3, +}; + #define HostCmd_RET_BIT 0x8000 #define HostCmd_ACT_GEN_GET 0x0000 #define HostCmd_ACT_GEN_SET 0x0001 +#define HostCmd_ACT_GEN_REMOVE 0x0004 #define HostCmd_ACT_BITWISE_SET 0x0002 #define HostCmd_ACT_BITWISE_CLR 0x0003 #define HostCmd_RESULT_OK 0x0000 @@ -309,7 +324,7 @@ enum ENH_PS_MODES { #define HostCmd_SCAN_RADIO_TYPE_A 1 #define HOST_SLEEP_CFG_CANCEL 0xffffffff -#define HOST_SLEEP_CFG_COND_DEF 0x0000000f +#define HOST_SLEEP_CFG_COND_DEF 0x00000000 #define HOST_SLEEP_CFG_GPIO_DEF 0xff #define HOST_SLEEP_CFG_GAP_DEF 0 @@ -387,6 +402,7 @@ enum ENH_PS_MODES { #define EVENT_BW_CHANGE 0x00000048 #define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c #define EVENT_HOSTWAKE_STAIE 0x0000004d +#define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f #define EVENT_ID_MASK 0xffff #define BSS_NUM_MASK 0xf @@ -1284,6 +1300,11 @@ struct host_cmd_tlv_ssid { u8 ssid[0]; } __packed; +struct host_cmd_tlv_rates { + struct host_cmd_tlv tlv; + u8 rates[0]; +} __packed; + struct host_cmd_tlv_bcast_ssid { struct host_cmd_tlv tlv; u8 bcast_ctl; @@ -1325,11 +1346,35 @@ struct host_cmd_tlv_channel_band { u8 channel; } __packed; +struct host_cmd_tlv_ageout_timer { + struct host_cmd_tlv tlv; + __le32 sta_ao_timer; +} __packed; + struct host_cmd_ds_version_ext { u8 version_str_sel; char version_str[128]; } __packed; +struct host_cmd_ds_mgmt_frame_reg { + __le16 action; + __le32 mask; +} __packed; + +struct host_cmd_ds_p2p_mode_cfg { + __le16 action; + __le16 mode; +} __packed; + +struct host_cmd_ds_remain_on_chan { + __le16 action; + u8 status; + u8 reserved; + u8 band_cfg; + u8 channel; + __le32 duration; +} __packed; + struct host_cmd_ds_802_11_ibss_status { __le16 action; __le16 enable; @@ -1341,6 +1386,7 @@ struct host_cmd_ds_802_11_ibss_status { #define CONNECTION_TYPE_INFRA 0 #define CONNECTION_TYPE_ADHOC 1 +#define CONNECTION_TYPE_AP 2 struct host_cmd_ds_set_bss_mode { u8 con_type; @@ -1438,6 +1484,9 @@ struct host_cmd_ds_command { struct host_cmd_ds_wmm_get_status get_wmm_status; struct host_cmd_ds_802_11_key_material key_material; struct host_cmd_ds_version_ext verext; + struct host_cmd_ds_mgmt_frame_reg reg_mask; + struct host_cmd_ds_remain_on_chan roc_cfg; + struct host_cmd_ds_p2p_mode_cfg mode_cfg; struct host_cmd_ds_802_11_ibss_status ibss_coalescing; struct host_cmd_ds_mac_reg_access mac_reg; struct host_cmd_ds_bbp_reg_access bbp_reg; diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c index 1d8dd003e39..e38342f86c5 100644 --- a/drivers/net/wireless/mwifiex/ie.c +++ b/drivers/net/wireless/mwifiex/ie.c @@ -114,9 +114,6 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv, cpu_to_le16(mask); ie->ie_index = cpu_to_le16(index); - ie->ie_length = priv->mgmt_ie[index].ie_length; - memcpy(&ie->ie_buffer, &priv->mgmt_ie[index].ie_buffer, - le16_to_cpu(priv->mgmt_ie[index].ie_length)); } else { if (mask != MWIFIEX_DELETE_MASK) return -1; @@ -160,7 +157,7 @@ mwifiex_update_uap_custom_ie(struct mwifiex_private *priv, u16 len; int ret; - ap_custom_ie = kzalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); + ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL); if (!ap_custom_ie) return -ENOMEM; @@ -214,30 +211,35 @@ mwifiex_update_uap_custom_ie(struct mwifiex_private *priv, return ret; } -/* This function checks if WPS IE is present in passed buffer and copies it to - * mwifiex_ie structure. +/* This function checks if the vendor specified IE is present in passed buffer + * and copies it to mwifiex_ie structure. * Function takes pointer to struct mwifiex_ie pointer as argument. - * If WPS IE is present memory is allocated for mwifiex_ie pointer and filled - * in with WPS IE. Caller should take care of freeing this memory. + * If the vendor specified IE is present then memory is allocated for + * mwifiex_ie pointer and filled in with IE. Caller should take care of freeing + * this memory. */ -static int mwifiex_update_wps_ie(const u8 *ies, int ies_len, - struct mwifiex_ie **ie_ptr, u16 mask) +static int mwifiex_update_vs_ie(const u8 *ies, int ies_len, + struct mwifiex_ie **ie_ptr, u16 mask, + unsigned int oui, u8 oui_type) { - struct ieee_types_header *wps_ie; - struct mwifiex_ie *ie = NULL; + struct ieee_types_header *vs_ie; + struct mwifiex_ie *ie = *ie_ptr; const u8 *vendor_ie; - vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, - WLAN_OUI_TYPE_MICROSOFT_WPS, - ies, ies_len); + vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len); if (vendor_ie) { - ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); - if (!ie) - return -ENOMEM; + if (!*ie_ptr) { + *ie_ptr = kzalloc(sizeof(struct mwifiex_ie), + GFP_KERNEL); + if (!*ie_ptr) + return -ENOMEM; + ie = *ie_ptr; + } - wps_ie = (struct ieee_types_header *)vendor_ie; - memcpy(ie->ie_buffer, wps_ie, wps_ie->len + 2); - ie->ie_length = cpu_to_le16(wps_ie->len + 2); + vs_ie = (struct ieee_types_header *)vendor_ie; + memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length), + vs_ie, vs_ie->len + 2); + le16_add_cpu(&ie->ie_length, vs_ie->len + 2); ie->mgmt_subtype_mask = cpu_to_le16(mask); ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK); } @@ -257,20 +259,40 @@ static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv, u16 ar_idx = MWIFIEX_AUTO_IDX_MASK; int ret = 0; - if (data->beacon_ies && data->beacon_ies_len) - mwifiex_update_wps_ie(data->beacon_ies, data->beacon_ies_len, - &beacon_ie, MGMT_MASK_BEACON); + if (data->beacon_ies && data->beacon_ies_len) { + mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len, + &beacon_ie, MGMT_MASK_BEACON, + WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPS); + mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len, + &beacon_ie, MGMT_MASK_BEACON, + WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P); + } - if (data->proberesp_ies && data->proberesp_ies_len) - mwifiex_update_wps_ie(data->proberesp_ies, - data->proberesp_ies_len, &pr_ie, - MGMT_MASK_PROBE_RESP); + if (data->proberesp_ies && data->proberesp_ies_len) { + mwifiex_update_vs_ie(data->proberesp_ies, + data->proberesp_ies_len, &pr_ie, + MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPS); + mwifiex_update_vs_ie(data->proberesp_ies, + data->proberesp_ies_len, &pr_ie, + MGMT_MASK_PROBE_RESP, + WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P); + } - if (data->assocresp_ies && data->assocresp_ies_len) - mwifiex_update_wps_ie(data->assocresp_ies, - data->assocresp_ies_len, &ar_ie, - MGMT_MASK_ASSOC_RESP | - MGMT_MASK_REASSOC_RESP); + if (data->assocresp_ies && data->assocresp_ies_len) { + mwifiex_update_vs_ie(data->assocresp_ies, + data->assocresp_ies_len, &ar_ie, + MGMT_MASK_ASSOC_RESP | + MGMT_MASK_REASSOC_RESP, + WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPS); + mwifiex_update_vs_ie(data->assocresp_ies, + data->assocresp_ies_len, &ar_ie, + MGMT_MASK_ASSOC_RESP | + MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA, + WLAN_OUI_TYPE_WFA_P2P); + } if (beacon_ie || pr_ie || ar_ie) { ret = mwifiex_update_uap_custom_ie(priv, beacon_ie, diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index fad2c8d2bdd..b5d37a8caa0 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -73,7 +73,6 @@ static void scan_delay_timer_fn(unsigned long data) list_for_each_entry_safe(cmd_node, tmp_node, &adapter->scan_pending_q, list) { list_del(&cmd_node->list); - cmd_node->wait_q_enabled = false; mwifiex_insert_cmd_to_free_q(adapter, cmd_node); } spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); @@ -92,6 +91,11 @@ static void scan_delay_timer_fn(unsigned long data) kfree(priv->user_scan_cfg); priv->user_scan_cfg = NULL; } + + if (priv->scan_pending_on_block) { + priv->scan_pending_on_block = false; + up(&priv->async_sem); + } goto done; } @@ -114,6 +118,7 @@ static void scan_delay_timer_fn(unsigned long data) mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); + queue_work(adapter->workqueue, &adapter->main_work); goto done; } } else { @@ -139,7 +144,7 @@ done: * Additionally, it also initializes all the locks and sets up all the * lists. */ -static int mwifiex_init_priv(struct mwifiex_private *priv) +int mwifiex_init_priv(struct mwifiex_private *priv) { u32 i; @@ -209,6 +214,7 @@ static int mwifiex_init_priv(struct mwifiex_private *priv) priv->wps_ie = NULL; priv->wps_ie_len = 0; priv->ap_11n_enabled = 0; + memset(&priv->roc_cfg, 0, sizeof(priv->roc_cfg)); priv->scan_block = false; @@ -643,6 +649,17 @@ static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv) } /* + * This function frees the private structure, including cleans + * up the TX and RX queues and frees the BSS priority tables. + */ +void mwifiex_free_priv(struct mwifiex_private *priv) +{ + mwifiex_clean_txrx(priv); + mwifiex_delete_bss_prio_tbl(priv); + mwifiex_free_curr_bcn(priv); +} + +/* * This function is used to shutdown the driver. * * The following operations are performed sequentially - diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index 6a5eded3be1..4e31c6013eb 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -81,7 +81,11 @@ struct wep_key { #define KEY_MGMT_ON_HOST 0x03 #define MWIFIEX_AUTH_MODE_AUTO 0xFF -#define BAND_CONFIG_MANUAL 0x00 +#define BAND_CONFIG_BG 0x00 +#define BAND_CONFIG_A 0x01 +#define MWIFIEX_SUPPORTED_RATES 14 +#define MWIFIEX_SUPPORTED_RATES_EXT 32 + struct mwifiex_uap_bss_param { u8 channel; u8 band_cfg; @@ -100,6 +104,9 @@ struct mwifiex_uap_bss_param { struct wpa_param wpa_cfg; struct wep_key wep_cfg[NUM_WEP_KEYS]; struct ieee80211_ht_cap ht_cap; + u8 rates[MWIFIEX_SUPPORTED_RATES]; + u32 sta_ao_timer; + u32 ps_sta_ao_timer; }; enum { diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index cb1155286e0..eb22dd248d5 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -72,7 +72,6 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, goto error; adapter->priv[i]->adapter = adapter; - adapter->priv[i]->bss_priority = i; adapter->priv_num++; } mwifiex_init_lock_list(adapter); @@ -370,6 +369,13 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) dev_err(adapter->dev, "cannot create default AP interface\n"); goto err_add_intf; } + + /* Create P2P interface by default */ + if (!mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", + NL80211_IFTYPE_P2P_CLIENT, NULL, NULL)) { + dev_err(adapter->dev, "cannot create default P2P interface\n"); + goto err_add_intf; + } rtnl_unlock(); mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1); @@ -470,6 +476,27 @@ mwifiex_close(struct net_device *dev) } /* + * Add buffer into wmm tx queue and queue work to transmit it. + */ +int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb) +{ + mwifiex_wmm_add_buf_txqueue(priv, skb); + atomic_inc(&priv->adapter->tx_pending); + + if (priv->adapter->scan_delay_cnt) + atomic_set(&priv->adapter->is_tx_received, true); + + if (atomic_read(&priv->adapter->tx_pending) >= MAX_TX_PENDING) { + mwifiex_set_trans_start(priv->netdev); + mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); + } + + queue_work(priv->adapter->workqueue, &priv->adapter->main_work); + + return 0; +} + +/* * CFG802.11 network device handler for data transmission. */ static int @@ -517,18 +544,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) tx_info->bss_type = priv->bss_type; mwifiex_fill_buffer(skb); - mwifiex_wmm_add_buf_txqueue(priv, skb); - atomic_inc(&priv->adapter->tx_pending); - - if (priv->adapter->scan_delay_cnt) - atomic_set(&priv->adapter->is_tx_received, true); - - if (atomic_read(&priv->adapter->tx_pending) >= MAX_TX_PENDING) { - mwifiex_set_trans_start(dev); - mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); - } - - queue_work(priv->adapter->workqueue, &priv->adapter->main_work); + mwifiex_queue_tx_pkt(priv, skb); return 0; } diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 994bc4fc263..bfb3fa69805 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -91,11 +91,15 @@ enum { #define MWIFIEX_MAX_EMPTY_TX_Q_CNT 10 #define MWIFIEX_SCAN_DELAY_MSEC 20 +#define MWIFIEX_MIN_TX_PENDING_TO_CANCEL_SCAN 2 + #define RSN_GTK_OUI_OFFSET 2 #define MWIFIEX_OUI_NOT_PRESENT 0 #define MWIFIEX_OUI_PRESENT 1 +#define PKT_TYPE_MGMT 0xE5 + /* * Do not check for data_received for USB, as data_received * is handled in mwifiex_usb_recv for USB @@ -116,6 +120,7 @@ enum { #define MAX_BITMAP_RATES_SIZE 10 #define MAX_CHANNEL_BAND_BG 14 +#define MAX_CHANNEL_BAND_A 165 #define MAX_FREQUENCY_BAND_BG 2484 @@ -249,10 +254,6 @@ struct ieee_types_header { u8 len; } __packed; -#define MWIFIEX_SUPPORTED_RATES 14 - -#define MWIFIEX_SUPPORTED_RATES_EXT 32 - struct ieee_types_vendor_specific { struct ieee_types_vendor_header vend_hdr; u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_vendor_header)]; @@ -369,6 +370,12 @@ struct wps { u8 session_enable; }; +struct mwifiex_roc_cfg { + u64 cookie; + struct ieee80211_channel chan; + enum nl80211_channel_type chan_type; +}; + struct mwifiex_adapter; struct mwifiex_private; @@ -487,6 +494,7 @@ struct mwifiex_private { s32 cqm_rssi_thold; u32 cqm_rssi_hyst; u8 subsc_evt_rssi_state; + struct mwifiex_ds_misc_subsc_evt async_subsc_evt_storage; struct mwifiex_ie mgmt_ie[MAX_MGMT_IE_INDEX]; u16 beacon_idx; u16 proberesp_idx; @@ -494,6 +502,8 @@ struct mwifiex_private { u16 rsn_idx; struct timer_list scan_delay_timer; u8 ap_11n_enabled; + u32 mgmt_frame_mask; + struct mwifiex_roc_cfg roc_cfg; }; enum mwifiex_ba_status { @@ -525,6 +535,7 @@ struct mwifiex_rx_reorder_tbl { int win_size; void **rx_reorder_ptr; struct reorder_tmr_cnxt timer_context; + u8 flags; }; struct mwifiex_bss_prio_node { @@ -726,6 +737,9 @@ void mwifiex_stop_net_dev_queue(struct net_device *netdev, void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, struct mwifiex_adapter *adapter); +int mwifiex_init_priv(struct mwifiex_private *priv); +void mwifiex_free_priv(struct mwifiex_private *priv); + int mwifiex_init_fw(struct mwifiex_adapter *adapter); int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter); @@ -738,6 +752,9 @@ int mwifiex_dnld_fw(struct mwifiex_adapter *, struct mwifiex_fw_image *); int mwifiex_recv_packet(struct mwifiex_adapter *, struct sk_buff *skb); +int mwifiex_process_mgmt_packet(struct mwifiex_adapter *adapter, + struct sk_buff *skb); + int mwifiex_process_event(struct mwifiex_adapter *adapter); int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, @@ -814,6 +831,7 @@ struct mwifiex_sta_node * mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac); void mwifiex_delete_all_station_list(struct mwifiex_private *priv); void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb); +void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb); int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta); int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, struct mwifiex_scan_cmd_config *scan_cfg); @@ -872,6 +890,8 @@ int mwifiex_set_secure_params(struct mwifiex_private *priv, void mwifiex_set_ht_params(struct mwifiex_private *priv, struct mwifiex_uap_bss_param *bss_cfg, struct cfg80211_ap_settings *params); +void mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); /* * This function checks if the queuing is RA based or not. @@ -957,6 +977,14 @@ mwifiex_netdev_get_priv(struct net_device *dev) return (struct mwifiex_private *) (*(unsigned long *) netdev_priv(dev)); } +/* + * This function checks if a skb holds a management frame. + */ +static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb) +{ + return (*(u32 *)skb->data == PKT_TYPE_MGMT); +} + int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, u32 func_init_shutdown); int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8); @@ -989,6 +1017,13 @@ int mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len); int mwifiex_get_ver_ext(struct mwifiex_private *priv); +int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, + struct ieee80211_channel *chan, + enum nl80211_channel_type *channel_type, + unsigned int duration); + +int mwifiex_set_bss_role(struct mwifiex_private *priv, u8 bss_role); + int mwifiex_get_stats_info(struct mwifiex_private *priv, struct mwifiex_ds_get_stats *log); @@ -1019,6 +1054,8 @@ int mwifiex_set_tx_power(struct mwifiex_private *priv, int mwifiex_main_process(struct mwifiex_adapter *); +int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb); + int mwifiex_get_bss_info(struct mwifiex_private *, struct mwifiex_bss_info *); int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, @@ -1029,8 +1066,10 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, int mwifiex_check_network_compatibility(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc); +u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type); + struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, - char *name, + const char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params); diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 215d07e6c46..e36a75988f8 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -614,9 +614,8 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, /* Increment the TLV header length by the size appended */ - chan_tlv_out->header.len = - cpu_to_le16(le16_to_cpu(chan_tlv_out->header.len) + - (sizeof(chan_tlv_out->chan_scan_param))); + le16_add_cpu(&chan_tlv_out->header.len, + sizeof(chan_tlv_out->chan_scan_param)); /* * The tlv buffer length is set to the number of bytes @@ -726,7 +725,6 @@ mwifiex_config_scan(struct mwifiex_private *priv, struct mwifiex_ie_types_num_probes *num_probes_tlv; struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; struct mwifiex_ie_types_rates_param_set *rates_tlv; - const u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; u8 *tlv_pos; u32 num_probes; u32 ssid_len; @@ -840,8 +838,7 @@ mwifiex_config_scan(struct mwifiex_private *priv, * or BSSID filter applied to the scan results in the firmware. */ if ((i && ssid_filter) || - memcmp(scan_cfg_out->specific_bssid, &zero_mac, - sizeof(zero_mac))) + !is_zero_ether_addr(scan_cfg_out->specific_bssid)) *filtered_scan = true; } else { scan_cfg_out->bss_mode = (u8) adapter->scan_mode; diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 0cc3406050d..5d87195390f 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -551,7 +551,6 @@ mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, struct host_cmd_tlv_mac_addr *tlv_mac; u16 key_param_len = 0, cmd_size; int ret = 0; - const u8 bc_mac[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); key_material->action = cpu_to_le16(cmd_action); @@ -593,7 +592,7 @@ mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, /* set 0 when re-key */ key_material->key_param_set.key[1] = 0; - if (0 != memcmp(enc_key->mac_addr, bc_mac, sizeof(bc_mac))) { + if (!is_broadcast_ether_addr(enc_key->mac_addr)) { /* WAPI pairwise key: unicast */ key_material->key_param_set.key_info |= cpu_to_le16(KEY_UNICAST); @@ -1168,6 +1167,31 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, S_DS_GEN); ret = 0; break; + case HostCmd_CMD_MGMT_FRAME_REG: + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->params.reg_mask.action = cpu_to_le16(cmd_action); + cmd_ptr->params.reg_mask.mask = cpu_to_le32(*(u32 *)data_buf); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_mgmt_frame_reg) + + S_DS_GEN); + ret = 0; + break; + case HostCmd_CMD_REMAIN_ON_CHAN: + cmd_ptr->command = cpu_to_le16(cmd_no); + memcpy(&cmd_ptr->params, data_buf, + sizeof(struct host_cmd_ds_remain_on_chan)); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_remain_on_chan) + + S_DS_GEN); + break; + case HostCmd_CMD_P2P_MODE_CFG: + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->params.mode_cfg.action = cpu_to_le16(cmd_action); + cmd_ptr->params.mode_cfg.mode = cpu_to_le16(*(u16 *)data_buf); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_p2p_mode_cfg) + + S_DS_GEN); + break; case HostCmd_CMD_FUNC_INIT: if (priv->adapter->hw_status == MWIFIEX_HW_STATUS_RESET) priv->adapter->hw_status = MWIFIEX_HW_STATUS_READY; @@ -1237,6 +1261,8 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, else if (priv->bss_mode == NL80211_IFTYPE_STATION) cmd_ptr->params.bss_mode.con_type = CONNECTION_TYPE_INFRA; + else if (priv->bss_mode == NL80211_IFTYPE_AP) + cmd_ptr->params.bss_mode.con_type = CONNECTION_TYPE_AP; cmd_ptr->size = cpu_to_le16(sizeof(struct host_cmd_ds_set_bss_mode) + S_DS_GEN); ret = 0; @@ -1286,35 +1312,35 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) if (first_sta) { if (priv->adapter->iface_type == MWIFIEX_PCIE) { - ret = mwifiex_send_cmd_async(priv, + ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_PCIE_DESC_DETAILS, HostCmd_ACT_GEN_SET, 0, NULL); if (ret) return -1; } - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_FUNC_INIT, - HostCmd_ACT_GEN_SET, 0, NULL); + ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_FUNC_INIT, + HostCmd_ACT_GEN_SET, 0, NULL); if (ret) return -1; /* Read MAC address from HW */ - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_GET_HW_SPEC, - HostCmd_ACT_GEN_GET, 0, NULL); + ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_GET_HW_SPEC, + HostCmd_ACT_GEN_GET, 0, NULL); if (ret) return -1; /* Reconfigure tx buf size */ - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_RECONFIGURE_TX_BUFF, - HostCmd_ACT_GEN_SET, 0, - &priv->adapter->tx_buf_size); + ret = mwifiex_send_cmd_sync(priv, + HostCmd_CMD_RECONFIGURE_TX_BUFF, + HostCmd_ACT_GEN_SET, 0, + &priv->adapter->tx_buf_size); if (ret) return -1; if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { /* Enable IEEE PS by default */ priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; - ret = mwifiex_send_cmd_async( + ret = mwifiex_send_cmd_sync( priv, HostCmd_CMD_802_11_PS_MODE_ENH, EN_AUTO_PS, BITMAP_STA_PS, NULL); if (ret) @@ -1323,21 +1349,21 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) } /* get tx rate */ - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_TX_RATE_CFG, - HostCmd_ACT_GEN_GET, 0, NULL); + ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_GET, 0, NULL); if (ret) return -1; priv->data_rate = 0; /* get tx power */ - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_RF_TX_PWR, - HostCmd_ACT_GEN_GET, 0, NULL); + ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_RF_TX_PWR, + HostCmd_ACT_GEN_GET, 0, NULL); if (ret) return -1; if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) { /* set ibss coalescing_status */ - ret = mwifiex_send_cmd_async( + ret = mwifiex_send_cmd_sync( priv, HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, HostCmd_ACT_GEN_SET, 0, &enable); if (ret) @@ -1347,16 +1373,16 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl)); amsdu_aggr_ctrl.enable = true; /* Send request to firmware */ - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_AMSDU_AGGR_CTRL, - HostCmd_ACT_GEN_SET, 0, - &amsdu_aggr_ctrl); + ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_AMSDU_AGGR_CTRL, + HostCmd_ACT_GEN_SET, 0, + &amsdu_aggr_ctrl); if (ret) return -1; /* MAC Control must be the last command in init_fw */ /* set MAC Control */ - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter); + ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter); if (ret) return -1; @@ -1365,10 +1391,10 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) /* Enable auto deep sleep */ auto_ds.auto_ds = DEEP_SLEEP_ON; auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME; - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_802_11_PS_MODE_ENH, - EN_AUTO_PS, BITMAP_AUTO_DS, - &auto_ds); + ret = mwifiex_send_cmd_sync(priv, + HostCmd_CMD_802_11_PS_MODE_ENH, + EN_AUTO_PS, BITMAP_AUTO_DS, + &auto_ds); if (ret) return -1; } @@ -1376,23 +1402,24 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { /* Send cmd to FW to enable/disable 11D function */ state_11d = ENABLE_11D; - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, DOT11D_I, - &state_11d); + ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, DOT11D_I, + &state_11d); if (ret) dev_err(priv->adapter->dev, "11D: failed to enable 11D\n"); } + /* set last_init_cmd before sending the command */ + priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG; + /* Send cmd to FW to configure 11n specific configuration * (Short GI, Channel BW, Green field support etc.) for transmit */ tx_cfg.tx_htcap = MWIFIEX_FW_DEF_HTTXCFG; - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_CFG, - HostCmd_ACT_GEN_SET, 0, &tx_cfg); + ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_11N_CFG, + HostCmd_ACT_GEN_SET, 0, &tx_cfg); - /* set last_init_cmd */ - priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG; ret = -EINPROGRESS; return ret; diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 0b09004ebb2..e380171c4c5 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -123,7 +123,8 @@ static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv, { struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp = &resp->params.rssi_info_rsp; - struct mwifiex_ds_misc_subsc_evt subsc_evt; + struct mwifiex_ds_misc_subsc_evt *subsc_evt = + &priv->async_subsc_evt_storage; priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last); priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last); @@ -140,26 +141,27 @@ static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv, if (priv->subsc_evt_rssi_state == EVENT_HANDLED) return 0; + memset(subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt)); + /* Resubscribe low and high rssi events with new thresholds */ - memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt)); - subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; - subsc_evt.action = HostCmd_ACT_BITWISE_SET; + subsc_evt->events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; + subsc_evt->action = HostCmd_ACT_BITWISE_SET; if (priv->subsc_evt_rssi_state == RSSI_LOW_RECVD) { - subsc_evt.bcn_l_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg - + subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg - priv->cqm_rssi_hyst); - subsc_evt.bcn_h_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); + subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); } else if (priv->subsc_evt_rssi_state == RSSI_HIGH_RECVD) { - subsc_evt.bcn_l_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); - subsc_evt.bcn_h_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg + + subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); + subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg + priv->cqm_rssi_hyst); } - subsc_evt.bcn_l_rssi_cfg.evt_freq = 1; - subsc_evt.bcn_h_rssi_cfg.evt_freq = 1; + subsc_evt->bcn_l_rssi_cfg.evt_freq = 1; + subsc_evt->bcn_h_rssi_cfg.evt_freq = 1; priv->subsc_evt_rssi_state = EVENT_HANDLED; mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT, - 0, 0, &subsc_evt); + 0, 0, subsc_evt); return 0; } @@ -652,6 +654,38 @@ static int mwifiex_ret_ver_ext(struct mwifiex_private *priv, } /* + * This function handles the command response of remain on channel. + */ +static int +mwifiex_ret_remain_on_chan(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + struct host_cmd_ds_remain_on_chan *roc_cfg) +{ + struct host_cmd_ds_remain_on_chan *resp_cfg = &resp->params.roc_cfg; + + if (roc_cfg) + memcpy(roc_cfg, resp_cfg, sizeof(*roc_cfg)); + + return 0; +} + +/* + * This function handles the command response of P2P mode cfg. + */ +static int +mwifiex_ret_p2p_mode_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + void *data_buf) +{ + struct host_cmd_ds_p2p_mode_cfg *mode_cfg = &resp->params.mode_cfg; + + if (data_buf) + *((u16 *)data_buf) = le16_to_cpu(mode_cfg->mode); + + return 0; +} + +/* * This function handles the command response of register access. * * The register value and offset are returned to the user. For EEPROM @@ -736,7 +770,6 @@ static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv, { struct host_cmd_ds_802_11_ibss_status *ibss_coal_resp = &(resp->params.ibss_coalescing); - u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; if (le16_to_cpu(ibss_coal_resp->action) == HostCmd_ACT_GEN_SET) return 0; @@ -745,7 +778,7 @@ static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv, "info: new BSSID %pM\n", ibss_coal_resp->bssid); /* If rsp has NULL BSSID, Just return..... No Action */ - if (!memcmp(ibss_coal_resp->bssid, zero_mac, ETH_ALEN)) { + if (is_zero_ether_addr(ibss_coal_resp->bssid)) { dev_warn(priv->adapter->dev, "new BSSID is NULL\n"); return 0; } @@ -775,8 +808,7 @@ static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv, * This function handles the command response for subscribe event command. */ static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - struct mwifiex_ds_misc_subsc_evt *sub_event) + struct host_cmd_ds_command *resp) { struct host_cmd_ds_802_11_subsc_evt *cmd_sub_event = &resp->params.subsc_evt; @@ -786,10 +818,6 @@ static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv, dev_dbg(priv->adapter->dev, "Bitmap of currently subscribed events: %16x\n", le16_to_cpu(cmd_sub_event->events)); - /*Return the subscribed event info for a Get request*/ - if (sub_event) - sub_event->events = le16_to_cpu(cmd_sub_event->events); - return 0; } @@ -879,6 +907,13 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, case HostCmd_CMD_VERSION_EXT: ret = mwifiex_ret_ver_ext(priv, resp, data_buf); break; + case HostCmd_CMD_REMAIN_ON_CHAN: + ret = mwifiex_ret_remain_on_chan(priv, resp, data_buf); + break; + case HostCmd_CMD_P2P_MODE_CFG: + ret = mwifiex_ret_p2p_mode_cfg(priv, resp, data_buf); + break; + case HostCmd_CMD_MGMT_FRAME_REG: case HostCmd_CMD_FUNC_INIT: case HostCmd_CMD_FUNC_SHUTDOWN: break; @@ -913,7 +948,6 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, le16_to_cpu(resp->params.tx_buf.mp_end_port)); break; case HostCmd_CMD_AMSDU_AGGR_CTRL: - ret = mwifiex_ret_amsdu_aggr_ctrl(resp, data_buf); break; case HostCmd_CMD_WMM_GET_STATUS: ret = mwifiex_ret_wmm_get_status(priv, resp); @@ -932,12 +966,11 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, case HostCmd_CMD_SET_BSS_MODE: break; case HostCmd_CMD_11N_CFG: - ret = mwifiex_ret_11n_cfg(resp, data_buf); break; case HostCmd_CMD_PCIE_DESC_DETAILS: break; case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: - ret = mwifiex_ret_subsc_evt(priv, resp, data_buf); + ret = mwifiex_ret_subsc_evt(priv, resp); break; case HostCmd_CMD_UAP_SYS_CONFIG: break; diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index dff51d55271..aafde30e714 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -410,6 +410,18 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) dev_dbg(adapter->dev, "event: HOSTWAKE_STAIE %d\n", eventcause); break; + case EVENT_REMAIN_ON_CHAN_EXPIRED: + dev_dbg(adapter->dev, "event: Remain on channel expired\n"); + cfg80211_remain_on_channel_expired(priv->wdev, + priv->roc_cfg.cookie, + &priv->roc_cfg.chan, + priv->roc_cfg.chan_type, + GFP_ATOMIC); + + memset(&priv->roc_cfg, 0x00, sizeof(struct mwifiex_roc_cfg)); + + break; + default: dev_dbg(adapter->dev, "event: unknown event id: %#x\n", eventcause); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 3f025976f79..0c9f70b2cbe 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -26,6 +26,9 @@ #include "11n.h" #include "cfg80211.h" +static int disconnect_on_suspend = 1; +module_param(disconnect_on_suspend, int, 0644); + /* * Copies the multicast address list from device to driver. * @@ -192,6 +195,44 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, return ret; } +static int mwifiex_process_country_ie(struct mwifiex_private *priv, + struct cfg80211_bss *bss) +{ + u8 *country_ie, country_ie_len; + struct mwifiex_802_11d_domain_reg *domain_info = + &priv->adapter->domain_reg; + + country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + + if (!country_ie) + return 0; + + country_ie_len = country_ie[1]; + if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) + return 0; + + domain_info->country_code[0] = country_ie[2]; + domain_info->country_code[1] = country_ie[3]; + domain_info->country_code[2] = ' '; + + country_ie_len -= IEEE80211_COUNTRY_STRING_LEN; + + domain_info->no_of_triplet = + country_ie_len / sizeof(struct ieee80211_country_ie_triplet); + + memcpy((u8 *)domain_info->triplet, + &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); + + if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, + HostCmd_ACT_GEN_SET, 0, NULL)) { + wiphy_err(priv->adapter->wiphy, + "11D: setting domain info in FW\n"); + return -1; + } + + return 0; +} + /* * In Ad-Hoc mode, the IBSS is created if not found in scan list. * In both Ad-Hoc and infra mode, an deauthentication is performed @@ -207,6 +248,8 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, priv->scan_block = false; if (bss) { + mwifiex_process_country_ie(priv, bss); + /* Allocate and fill new bss descriptor */ bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL); @@ -408,6 +451,16 @@ EXPORT_SYMBOL_GPL(mwifiex_cancel_hs); int mwifiex_enable_hs(struct mwifiex_adapter *adapter) { struct mwifiex_ds_hs_cfg hscfg; + struct mwifiex_private *priv; + int i; + + if (disconnect_on_suspend) { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv) + mwifiex_deauthenticate(priv, NULL); + } + } if (adapter->hs_activated) { dev_dbg(adapter->dev, "cmd: HS Already actived\n"); @@ -990,6 +1043,65 @@ mwifiex_get_ver_ext(struct mwifiex_private *priv) return 0; } +int +mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, + struct ieee80211_channel *chan, + enum nl80211_channel_type *ct, + unsigned int duration) +{ + struct host_cmd_ds_remain_on_chan roc_cfg; + u8 sc; + + memset(&roc_cfg, 0, sizeof(roc_cfg)); + roc_cfg.action = cpu_to_le16(action); + if (action == HostCmd_ACT_GEN_SET) { + roc_cfg.band_cfg = chan->band; + sc = mwifiex_chan_type_to_sec_chan_offset(*ct); + roc_cfg.band_cfg |= (sc << 2); + + roc_cfg.channel = + ieee80211_frequency_to_channel(chan->center_freq); + roc_cfg.duration = cpu_to_le32(duration); + } + if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_REMAIN_ON_CHAN, + action, 0, &roc_cfg)) { + dev_err(priv->adapter->dev, "failed to remain on channel\n"); + return -1; + } + + return roc_cfg.status; +} + +int +mwifiex_set_bss_role(struct mwifiex_private *priv, u8 bss_role) +{ + if (GET_BSS_ROLE(priv) == bss_role) { + dev_dbg(priv->adapter->dev, + "info: already in the desired role.\n"); + return 0; + } + + mwifiex_free_priv(priv); + mwifiex_init_priv(priv); + + priv->bss_role = bss_role; + switch (bss_role) { + case MWIFIEX_BSS_ROLE_UAP: + priv->bss_mode = NL80211_IFTYPE_AP; + break; + case MWIFIEX_BSS_ROLE_STA: + case MWIFIEX_BSS_ROLE_ANY: + default: + priv->bss_mode = NL80211_IFTYPE_STATION; + break; + } + + mwifiex_send_cmd_sync(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL); + + return mwifiex_sta_init_cmd(priv, false); +} + /* * Sends IOCTL request to get statistics information. * diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c index d91d5c08c73..07d32b73783 100644 --- a/drivers/net/wireless/mwifiex/sta_rx.c +++ b/drivers/net/wireless/mwifiex/sta_rx.c @@ -174,6 +174,12 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *adapter, dev_err(adapter->dev, "Rx of A-MSDU failed"); } return 0; + } else if (rx_pkt_type == PKT_TYPE_MGMT) { + ret = mwifiex_process_mgmt_packet(adapter, skb); + if (ret) + dev_err(adapter->dev, "Rx of mgmt packet failed"); + dev_kfree_skb_any(skb); + return ret; } /* diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c index 0a046d3a0c1..7b581af24f5 100644 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ b/drivers/net/wireless/mwifiex/sta_tx.c @@ -48,6 +48,7 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, struct txpd *local_tx_pd; struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); u8 pad; + u16 pkt_type, pkt_offset; if (!skb->len) { dev_err(adapter->dev, "Tx: bad packet length: %d\n", skb->len); @@ -55,6 +56,8 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, return skb->data; } + pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; + /* If skb->data is not aligned; add padding */ pad = (4 - (((void *)skb->data - NULL) & 0x3)) % 4; @@ -93,7 +96,14 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, } /* Offset of actual data */ - local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd) + pad); + pkt_offset = sizeof(struct txpd) + pad; + if (pkt_type == PKT_TYPE_MGMT) { + /* Set the packet type and add header for management frame */ + local_tx_pd->tx_pkt_type = cpu_to_le16(pkt_type); + pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE; + } + + local_tx_pd->tx_pkt_offset = cpu_to_le16(pkt_offset); /* make space for INTF_HEADER_LEN */ skb_push(skb, INTF_HEADER_LEN); diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index 985073d0df1..2af263992e8 100644 --- a/drivers/net/wireless/mwifiex/txrx.c +++ b/drivers/net/wireless/mwifiex/txrx.c @@ -75,7 +75,11 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, u8 *head_ptr; struct txpd *local_tx_pd = NULL; - head_ptr = mwifiex_process_sta_txpd(priv, skb); + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) + head_ptr = mwifiex_process_uap_txpd(priv, skb); + else + head_ptr = mwifiex_process_sta_txpd(priv, skb); + if (head_ptr) { if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) local_tx_pd = diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index c10aac04be6..d95a2d558fc 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -177,6 +177,25 @@ mwifiex_set_ht_params(struct mwifiex_private *priv, return; } +/* This function finds supported rates IE from beacon parameter and sets + * these rates into bss_config structure. + */ +void +mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + struct ieee_types_header *rate_ie; + int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); + const u8 *var_pos = params->beacon.head + var_offset; + int len = params->beacon.head_len - var_offset; + + rate_ie = (void *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len); + if (rate_ie) + memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->len); + + return; +} + /* This function initializes some of mwifiex_uap_bss_param variables. * This helps FW in ignoring invalid values. These values may or may not * be get updated to valid ones at later stage. @@ -323,8 +342,11 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) struct host_cmd_tlv_retry_limit *retry_limit; struct host_cmd_tlv_encrypt_protocol *encrypt_protocol; struct host_cmd_tlv_auth_type *auth_type; + struct host_cmd_tlv_rates *tlv_rates; + struct host_cmd_tlv_ageout_timer *ao_timer, *ps_ao_timer; struct mwifiex_ie_types_htcap *htcap; struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; + int i; u16 cmd_size = *param_size; if (bss_cfg->ssid.ssid_len) { @@ -344,7 +366,23 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid); tlv += sizeof(struct host_cmd_tlv_bcast_ssid); } - if (bss_cfg->channel && bss_cfg->channel <= MAX_CHANNEL_BAND_BG) { + if (bss_cfg->rates[0]) { + tlv_rates = (struct host_cmd_tlv_rates *)tlv; + tlv_rates->tlv.type = cpu_to_le16(TLV_TYPE_UAP_RATES); + + for (i = 0; i < MWIFIEX_SUPPORTED_RATES && bss_cfg->rates[i]; + i++) + tlv_rates->rates[i] = bss_cfg->rates[i]; + + tlv_rates->tlv.len = cpu_to_le16(i); + cmd_size += sizeof(struct host_cmd_tlv_rates) + i; + tlv += sizeof(struct host_cmd_tlv_rates) + i; + } + if (bss_cfg->channel && + ((bss_cfg->band_cfg == BAND_CONFIG_BG && + bss_cfg->channel <= MAX_CHANNEL_BAND_BG) || + (bss_cfg->band_cfg == BAND_CONFIG_A && + bss_cfg->channel <= MAX_CHANNEL_BAND_A))) { chan_band = (struct host_cmd_tlv_channel_band *)tlv; chan_band->tlv.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); chan_band->tlv.len = @@ -460,6 +498,27 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) tlv += sizeof(struct mwifiex_ie_types_htcap); } + if (bss_cfg->sta_ao_timer) { + ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; + ao_timer->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER); + ao_timer->tlv.len = cpu_to_le16(sizeof(*ao_timer) - + sizeof(struct host_cmd_tlv)); + ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer); + cmd_size += sizeof(*ao_timer); + tlv += sizeof(*ao_timer); + } + + if (bss_cfg->ps_sta_ao_timer) { + ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; + ps_ao_timer->tlv.type = cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER); + ps_ao_timer->tlv.len = cpu_to_le16(sizeof(*ps_ao_timer) - + sizeof(struct host_cmd_tlv)); + ps_ao_timer->sta_ao_timer = + cpu_to_le32(bss_cfg->ps_sta_ao_timer); + cmd_size += sizeof(*ps_ao_timer); + tlv += sizeof(*ps_ao_timer); + } + *param_size = cmd_size; return 0; diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c index 6d814f0f07f..0966ac24b3b 100644 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/mwifiex/uap_txrx.c @@ -217,6 +217,12 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_adapter *adapter, } return 0; + } else if (rx_pkt_type == PKT_TYPE_MGMT) { + ret = mwifiex_process_mgmt_packet(adapter, skb); + if (ret) + dev_err(adapter->dev, "Rx of mgmt packet failed"); + dev_kfree_skb_any(skb); + return ret; } memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN); @@ -253,3 +259,82 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_adapter *adapter, return ret; } + +/* + * This function fills the TxPD for AP tx packets. + * + * The Tx buffer received by this function should already have the + * header space allocated for TxPD. + * + * This function inserts the TxPD in between interface header and actual + * data and adjusts the buffer pointers accordingly. + * + * The following TxPD fields are set by this function, as required - + * - BSS number + * - Tx packet length and offset + * - Priority + * - Packet delay + * - Priority specific Tx control + * - Flags + */ +void *mwifiex_process_uap_txpd(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct uap_txpd *txpd; + struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); + int pad, len; + u16 pkt_type; + + if (!skb->len) { + dev_err(adapter->dev, "Tx: bad packet length: %d\n", skb->len); + tx_info->status_code = -1; + return skb->data; + } + + pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; + + /* If skb->data is not aligned, add padding */ + pad = (4 - (((void *)skb->data - NULL) & 0x3)) % 4; + + len = sizeof(*txpd) + pad; + + BUG_ON(skb_headroom(skb) < len + INTF_HEADER_LEN); + + skb_push(skb, len); + + txpd = (struct uap_txpd *)skb->data; + memset(txpd, 0, sizeof(*txpd)); + txpd->bss_num = priv->bss_num; + txpd->bss_type = priv->bss_type; + txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - len)); + + txpd->priority = (u8)skb->priority; + txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb); + + if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl)) + /* + * Set the priority specific tx_control field, setting of 0 will + * cause the default value to be used later in this function. + */ + txpd->tx_control = + cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]); + + /* Offset of actual data */ + if (pkt_type == PKT_TYPE_MGMT) { + /* Set the packet type and add header for management frame */ + txpd->tx_pkt_type = cpu_to_le16(pkt_type); + len += MWIFIEX_MGMT_FRAME_HEADER_SIZE; + } + + txpd->tx_pkt_offset = cpu_to_le16(len); + + /* make space for INTF_HEADER_LEN */ + skb_push(skb, INTF_HEADER_LEN); + + if (!txpd->tx_control) + /* TxCtrl set by user or default */ + txpd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); + + return skb->data; +} diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index 2864c74bdb6..ae88f80cf86 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -142,6 +142,46 @@ int mwifiex_get_debug_info(struct mwifiex_private *priv, } /* + * This function processes the received management packet and send it + * to the kernel. + */ +int +mwifiex_process_mgmt_packet(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + struct rxpd *rx_pd; + struct mwifiex_private *priv; + u16 pkt_len; + + if (!skb) + return -1; + + rx_pd = (struct rxpd *)skb->data; + priv = mwifiex_get_priv_by_id(adapter, rx_pd->bss_num, rx_pd->bss_type); + if (!priv) + return -1; + + skb_pull(skb, le16_to_cpu(rx_pd->rx_pkt_offset)); + skb_pull(skb, sizeof(pkt_len)); + + pkt_len = le16_to_cpu(rx_pd->rx_pkt_length); + + /* Remove address4 */ + memmove(skb->data + sizeof(struct ieee80211_hdr_3addr), + skb->data + sizeof(struct ieee80211_hdr), + pkt_len - sizeof(struct ieee80211_hdr)); + + pkt_len -= ETH_ALEN + sizeof(pkt_len); + rx_pd->rx_pkt_length = cpu_to_le16(pkt_len); + + cfg80211_rx_mgmt(priv->wdev, priv->roc_cfg.chan.center_freq, + CAL_RSSI(rx_pd->snr, rx_pd->nf), + skb->data, pkt_len, GFP_ATOMIC); + + return 0; +} + +/* * This function processes the received packet before sending it to the * kernel. * diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 8ccd6999fa9..600d8194610 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -462,7 +462,7 @@ mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter) for (i = 0; i < adapter->priv_num; ++i) { priv = adapter->priv[i]; if (priv && atomic_read(&priv->wmm.tx_pkts_queued)) - return false; + return false; } return true; @@ -648,7 +648,7 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, u8 ra[ETH_ALEN], tid_down; unsigned long flags; - if (!priv->media_connected) { + if (!priv->media_connected && !mwifiex_is_skb_mgmt_frame(skb)) { dev_dbg(adapter->dev, "data: drop packet in disconnect\n"); mwifiex_write_data_complete(adapter, skb, -1); return; @@ -663,7 +663,8 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, /* In case of infra as we have already created the list during association we just don't have to call get_queue_raptr, we will have only 1 raptr for a tid in case of infra */ - if (!mwifiex_queuing_ra_based(priv)) { + if (!mwifiex_queuing_ra_based(priv) && + !mwifiex_is_skb_mgmt_frame(skb)) { if (!list_empty(&priv->wmm.tid_tbl_ptr[tid_down].ra_list)) ra_list = list_first_entry( &priv->wmm.tid_tbl_ptr[tid_down].ra_list, @@ -672,7 +673,7 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, ra_list = NULL; } else { memcpy(ra, skb->data, ETH_ALEN); - if (ra[0] & 0x01) + if (ra[0] & 0x01 || mwifiex_is_skb_mgmt_frame(skb)) memset(ra, 0xff, ETH_ALEN); ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, ra); } @@ -907,17 +908,16 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, if (adapter->bss_prio_tbl[j].bss_prio_cur == (struct mwifiex_bss_prio_node *) &adapter->bss_prio_tbl[j].bss_prio_head) { - bssprio_node = + adapter->bss_prio_tbl[j].bss_prio_cur = list_first_entry(&adapter->bss_prio_tbl[j] .bss_prio_head, struct mwifiex_bss_prio_node, list); - bssprio_head = bssprio_node; - } else { - bssprio_node = adapter->bss_prio_tbl[j].bss_prio_cur; - bssprio_head = bssprio_node; } + bssprio_node = adapter->bss_prio_tbl[j].bss_prio_cur; + bssprio_head = bssprio_node; + do { priv_tmp = bssprio_node->priv; hqp = &priv_tmp->wmm.highest_queued_prio; |