summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/mwifiex
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/mwifiex')
-rw-r--r--drivers/net/wireless/mwifiex/11n_rxreorder.c49
-rw-r--r--drivers/net/wireless/mwifiex/11n_rxreorder.h5
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c386
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c2
-rw-r--r--drivers/net/wireless/mwifiex/decl.h6
-rw-r--r--drivers/net/wireless/mwifiex/fw.h43
-rw-r--r--drivers/net/wireless/mwifiex/ie.c86
-rw-r--r--drivers/net/wireless/mwifiex/init.c14
-rw-r--r--drivers/net/wireless/mwifiex/ioctl.h2
-rw-r--r--drivers/net/wireless/mwifiex/main.c41
-rw-r--r--drivers/net/wireless/mwifiex/main.h36
-rw-r--r--drivers/net/wireless/mwifiex/scan.c5
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmd.c92
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmdresp.c39
-rw-r--r--drivers/net/wireless/mwifiex/sta_event.c12
-rw-r--r--drivers/net/wireless/mwifiex/sta_ioctl.c59
-rw-r--r--drivers/net/wireless/mwifiex/sta_rx.c6
-rw-r--r--drivers/net/wireless/mwifiex/sta_tx.c12
-rw-r--r--drivers/net/wireless/mwifiex/uap_cmd.c22
-rw-r--r--drivers/net/wireless/mwifiex/uap_txrx.c15
-rw-r--r--drivers/net/wireless/mwifiex/util.c40
-rw-r--r--drivers/net/wireless/mwifiex/wmm.c9
22 files changed, 872 insertions, 109 deletions
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c
index 24e2582b467c..9402b93b9a36 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 72848591691a..4064041ac852 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 c24824f8c8a1..2691620393ea 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),
@@ -77,8 +77,7 @@ static const struct ieee80211_regdomain mwifiex_world_regdom_custom = {
* 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:
@@ -139,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
@@ -493,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.
*/
@@ -525,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 */
@@ -550,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);
@@ -688,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, },
@@ -705,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, },
@@ -773,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,
@@ -782,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.
*
@@ -874,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;
}
@@ -962,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, &params->beacon))
return -1;
@@ -1032,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");
@@ -1497,8 +1802,9 @@ 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);
@@ -1521,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;
}
}
@@ -1706,6 +2016,41 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
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);
@@ -1813,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,
@@ -1849,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;
@@ -1870,17 +2223,20 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
- WIPHY_FLAG_CUSTOM_REGULATORY;
+ 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);
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
index 225c1a4feeba..8d465107f52b 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/mwifiex/cmdevt.c
@@ -1088,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 400d360ac91f..e9357d87d327 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 b8ce78e2ba47..dda588b35570 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
@@ -127,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)
@@ -141,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)
@@ -260,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
@@ -288,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
@@ -388,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
@@ -1331,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;
@@ -1347,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;
@@ -1444,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 fa3a80fb8c01..e38342f86c51 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;
@@ -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 b2ba262f8a13..b5d37a8caa09 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -144,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;
@@ -214,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;
@@ -648,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 8688535e95eb..4e31c6013ebe 100644
--- a/drivers/net/wireless/mwifiex/ioctl.h
+++ b/drivers/net/wireless/mwifiex/ioctl.h
@@ -105,6 +105,8 @@ struct mwifiex_uap_bss_param {
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 bfd6667be01e..eb22dd248d54 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -369,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);
@@ -469,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
@@ -516,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 90b64b015447..bfb3fa69805c 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -98,6 +98,8 @@ enum {
#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
@@ -368,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;
@@ -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,
@@ -960,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);
@@ -992,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);
@@ -1022,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,
@@ -1032,6 +1066,8 @@ 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,
const char *name,
enum nl80211_iftype type,
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index 9e077e5fc64a..e36a75988f87 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
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c
index 3a4161cfeed7..5d87195390f8 100644
--- a/drivers/net/wireless/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/mwifiex/sta_cmd.c
@@ -1167,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;
@@ -1236,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;
@@ -1285,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)
@@ -1322,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)
@@ -1346,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;
@@ -1364,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;
}
@@ -1375,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 31f80e10e29e..e380171c4c5d 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c
@@ -654,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
@@ -875,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;
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c
index dff51d55271c..aafde30e714a 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 731562f026f5..0c9f70b2cbe6 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/mwifiex/sta_ioctl.c
@@ -1043,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 d91d5c08c73a..07d32b73783e 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 0a046d3a0c16..7b581af24f5f 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/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c
index 8a627d856d18..d95a2d558fcf 100644
--- a/drivers/net/wireless/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/mwifiex/uap_cmd.c
@@ -343,6 +343,7 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
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;
@@ -497,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 df17d08715fe..0966ac24b3b4 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);
@@ -278,6 +284,7 @@ void *mwifiex_process_uap_txpd(struct mwifiex_private *priv,
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);
@@ -285,6 +292,8 @@ void *mwifiex_process_uap_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;
@@ -312,6 +321,12 @@ void *mwifiex_process_uap_txpd(struct mwifiex_private *priv,
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 */
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c
index 2864c74bdb6f..ae88f80cf86b 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 766d45294c86..600d8194610e 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);
}