From 664834dee63c55188093bb5f295283c7693003d6 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 15 Jan 2014 00:01:44 +0200 Subject: cfg80211: Clean up connect params and channel fetching Addition of the frequency hints showed up couple of places in cfg80211 where pointers could be marked const and a shared function could be used to fetch a valid channel. Signed-off-by: Jouni Malinen [fix mwifiex] Signed-off-by: Johannes Berg --- drivers/net/wireless/mwifiex/cfg80211.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 8bfc07cd330e..f4cf9c9d40ec 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1583,8 +1583,9 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) * the function notifies the CFG802.11 subsystem of the new BSS connection. */ static int -mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, - u8 *bssid, int mode, struct ieee80211_channel *channel, +mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, + const u8 *ssid, const u8 *bssid, int mode, + struct ieee80211_channel *channel, struct cfg80211_connect_params *sme, bool privacy) { struct cfg80211_ssid req_ssid; -- cgit From 4b5800fec6173765207abded99df3d692ed55691 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Jan 2014 14:55:59 +0100 Subject: cfg80211: make connect ie param const This required liberally sprinkling 'const' over brcmfmac and mwifiex but seems like a useful thing to do since the pointer can't really be written. Signed-off-by: Johannes Berg --- drivers/net/wireless/mwifiex/main.h | 2 +- drivers/net/wireless/mwifiex/sta_ioctl.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index d8ad554ce39f..29d27d9b5ebe 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -1078,7 +1078,7 @@ int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, const u8 *key, int key_len, u8 key_index, const u8 *mac_addr, int disable); -int mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len); +int mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len); int mwifiex_get_ver_ext(struct mwifiex_private *priv); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index c5cb2ed19ec2..0bec94351f36 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -1391,7 +1391,7 @@ static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv, * with requisite parameters and calls the IOCTL handler. */ int -mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len) +mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len) { struct mwifiex_ds_misc_gen_ie gen_ie; -- cgit From fe94f3a4ffaa20c7470038c69ffc8e545ef5f90a Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Wed, 29 Jan 2014 17:53:43 +0100 Subject: cfg80211: fix channel configuration in IBSS join When receiving an IBSS_JOINED event select the BSS object based on the {bssid, channel} couple rather than the bssid only. With the current approach if another cell having the same BSSID (but using a different channel) exists then cfg80211 picks up the wrong BSS object. The result is a mismatching channel configuration between cfg80211 and the driver, that can lead to any sort of problem. The issue can be triggered by having an IBSS sitting on given channel and then asking the driver to create a new cell using the same BSSID but with a different frequency. By passing the channel to cfg80211_get_bss() we can solve this ambiguity and retrieve/create the correct BSS object. All the users of cfg80211_ibss_joined() have been changed accordingly. Moreover WARN when cfg80211_ibss_joined() gets a NULL channel as argument and remove a bogus call of the same function in ath6kl (it does not make sense to call cfg80211_ibss_joined() with a zero BSSID on ibss-leave). Cc: Kalle Valo Cc: Arend van Spriel Cc: Bing Zhao Cc: Jussi Kivilinna Cc: libertas-dev@lists.infradead.org Acked-by: Kalle Valo Signed-off-by: Antonio Quartulli [minor code cleanup in ath6kl] Signed-off-by: Johannes Berg --- drivers/net/wireless/mwifiex/cfg80211.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index f4cf9c9d40ec..0948ebe8942e 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1882,7 +1882,8 @@ mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, params->privacy); done: if (!ret) { - cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL); + cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, + params->chandef.chan, GFP_KERNEL); dev_dbg(priv->adapter->dev, "info: joined/created adhoc network with bssid" " %pM successfully\n", priv->cfg_bssid); -- cgit From 7abf4129e6dfe231e81a07af5bde0488bba6f95b Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Fri, 7 Feb 2014 16:20:58 -0800 Subject: mwifiex: make use of IEEE80211_VHT_MCS_NOT_SUPPORTED Remove driver's macro and use ieee80211's definition instead Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11ac.c | 14 ++++++++------ drivers/net/wireless/mwifiex/fw.h | 2 -- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c index 5e0eec4d71c7..cc0458c87755 100644 --- a/drivers/net/wireless/mwifiex/11ac.c +++ b/drivers/net/wireless/mwifiex/11ac.c @@ -126,9 +126,10 @@ mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); - if ((mcs_user == NO_NSS_SUPPORT) || - (mcs_resp == NO_NSS_SUPPORT)) - SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT); + if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || + (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); else SET_VHTNSSMCS(mcs_map_result, nss, min(mcs_user, mcs_resp)); @@ -147,9 +148,10 @@ mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, for (nss = 1; nss <= 8; nss++) { mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); - if ((mcs_user == NO_NSS_SUPPORT) || - (mcs_resp == NO_NSS_SUPPORT)) - SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT); + if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || + (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); else SET_VHTNSSMCS(mcs_map_result, nss, min(mcs_user, mcs_resp)); diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 5fa932d5f905..059bc16adf81 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -232,8 +232,6 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define GET_VHTNSSMCS(mcs_mapset, nss) ((mcs_mapset >> (2 * (nss - 1))) & 0x3) #define SET_VHTNSSMCS(mcs_mapset, nss, value) (mcs_mapset |= (value & 0x3) << \ (2 * (nss - 1))) -#define NO_NSS_SUPPORT 0x3 - #define GET_DEVTXMCSMAP(dev_mcs_map) (dev_mcs_map >> 16) #define GET_DEVRXMCSMAP(dev_mcs_map) (dev_mcs_map & 0xFFFF) -- cgit From 89467d8ca21b4c62ab1acbadd09e725d2cd410be Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Fri, 7 Feb 2014 16:20:59 -0800 Subject: mwifiex: make 11ac mcs rate tables global and const Remove these local array variables and define them as static const array in global space. The duplicated mcs_rate table is removed automatically with this change. Reported-by: Paul Stewart Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11ac.c | 48 ++++----- drivers/net/wireless/mwifiex/cfp.c | 198 ++++++++++++++++-------------------- 2 files changed, 114 insertions(+), 132 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c index cc0458c87755..f07a5005455d 100644 --- a/drivers/net/wireless/mwifiex/11ac.c +++ b/drivers/net/wireless/mwifiex/11ac.c @@ -23,6 +23,31 @@ #include "main.h" #include "11ac.h" +/* Tables of the MCS map to the highest data rate (in Mbps) supported + * for long GI. + */ +static const u16 max_rate_lgi_80MHZ[8][3] = { + {0x124, 0x15F, 0x186}, /* NSS = 1 */ + {0x249, 0x2BE, 0x30C}, /* NSS = 2 */ + {0x36D, 0x41D, 0x492}, /* NSS = 3 */ + {0x492, 0x57C, 0x618}, /* NSS = 4 */ + {0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */ + {0x6DB, 0x83A, 0x0}, /* NSS = 6 */ + {0x7FF, 0x999, 0xAAA}, /* NSS = 7 */ + {0x924, 0xAF8, 0xC30} /* NSS = 8 */ +}; + +static const u16 max_rate_lgi_160MHZ[8][3] = { + {0x249, 0x2BE, 0x30C}, /* NSS = 1 */ + {0x492, 0x57C, 0x618}, /* NSS = 2 */ + {0x6DB, 0x83A, 0x0}, /* NSS = 3 */ + {0x924, 0xAF8, 0xC30}, /* NSS = 4 */ + {0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */ + {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */ + {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */ + {0x1248, 0x15F0, 0x1860} /* NSS = 8 */ +}; + /* This function converts the 2-bit MCS map to the highest long GI * VHT data rate. */ @@ -34,29 +59,6 @@ mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv, u16 max_rate = 0; u32 usr_vht_cap_info = 0; struct mwifiex_adapter *adapter = priv->adapter; - /* tables of the MCS map to the highest data rate (in Mbps) - * supported for long GI - */ - u16 max_rate_lgi_80MHZ[8][3] = { - {0x124, 0x15F, 0x186}, /* NSS = 1 */ - {0x249, 0x2BE, 0x30C}, /* NSS = 2 */ - {0x36D, 0x41D, 0x492}, /* NSS = 3 */ - {0x492, 0x57C, 0x618}, /* NSS = 4 */ - {0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */ - {0x6DB, 0x83A, 0x0}, /* NSS = 6 */ - {0x7FF, 0x999, 0xAAA}, /* NSS = 7 */ - {0x924, 0xAF8, 0xC30} /* NSS = 8 */ - }; - u16 max_rate_lgi_160MHZ[8][3] = { - {0x249, 0x2BE, 0x30C}, /* NSS = 1 */ - {0x492, 0x57C, 0x618}, /* NSS = 2 */ - {0x6DB, 0x83A, 0x0}, /* NSS = 3 */ - {0x924, 0xAF8, 0xC30}, /* NSS = 4 */ - {0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */ - {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */ - {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */ - {0x1248, 0x15F0, 0x1860} /* NSS = 8 */ - }; if (bands & BAND_AAC) usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c index 9eefacbc844b..dfb068ba2dbd 100644 --- a/drivers/net/wireless/mwifiex/cfp.c +++ b/drivers/net/wireless/mwifiex/cfp.c @@ -71,6 +71,95 @@ u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30, static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 }; +/* For every mcs_rate line, the first 8 bytes are for stream 1x1, + * and all 16 bytes are for stream 2x2. + */ +static const u16 mcs_rate[4][16] = { + /* LGI 40M */ + { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, + 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c }, + + /* SGI 40M */ + { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, + 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 }, + + /* LGI 20M */ + { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, + 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 }, + + /* SGI 20M */ + { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, + 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 } +}; + +/* AC rates */ +static const u16 ac_mcs_rate_nss1[8][10] = { + /* LG 160M */ + { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, + 0x492, 0x57C, 0x618 }, + + /* SG 160M */ + { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, + 0x514, 0x618, 0x6C6 }, + + /* LG 80M */ + { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F, + 0x249, 0x2BE, 0x30C }, + + /* SG 80M */ + { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249, + 0x28A, 0x30C, 0x363 }, + + /* LG 40M */ + { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, + 0x10E, 0x144, 0x168 }, + + /* SG 40M */ + { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E, + 0x12C, 0x168, 0x190 }, + + /* LG 20M */ + { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 }, + + /* SG 20M */ + { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 }, +}; + +/* NSS2 note: the value in the table is 2 multiplier of the actual rate */ +static const u16 ac_mcs_rate_nss2[8][10] = { + /* LG 160M */ + { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A, + 0x924, 0xAF8, 0xC30 }, + + /* SG 160M */ + { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924, + 0xA28, 0xC30, 0xD8B }, + + /* LG 80M */ + { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, + 0x492, 0x57C, 0x618 }, + + /* SG 80M */ + { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, + 0x514, 0x618, 0x6C6 }, + + /* LG 40M */ + { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6, + 0x21C, 0x288, 0x2D0 }, + + /* SG 40M */ + { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C, + 0x258, 0x2D0, 0x320 }, + + /* LG 20M */ + { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104, + 0x138, 0x00 }, + + /* SG 20M */ + { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121, + 0x15B, 0x00 }, +}; + struct region_code_mapping { u8 code; u8 region[IEEE80211_COUNTRY_STRING_LEN]; @@ -109,95 +198,6 @@ u8 *mwifiex_11d_code_2_region(u8 code) u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv, u8 index, u8 ht_info) { - /* - * For every mcs_rate line, the first 8 bytes are for stream 1x1, - * and all 16 bytes are for stream 2x2. - */ - u16 mcs_rate[4][16] = { - /* LGI 40M */ - { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, - 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c }, - - /* SGI 40M */ - { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, - 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 }, - - /* LGI 20M */ - { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, - 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 }, - - /* SGI 20M */ - { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, - 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 } - }; - /* AC rates */ - u16 ac_mcs_rate_nss1[8][10] = { - /* LG 160M */ - { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, - 0x492, 0x57C, 0x618 }, - - /* SG 160M */ - { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, - 0x514, 0x618, 0x6C6 }, - - /* LG 80M */ - { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F, - 0x249, 0x2BE, 0x30C }, - - /* SG 80M */ - { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249, - 0x28A, 0x30C, 0x363 }, - - /* LG 40M */ - { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, - 0x10E, 0x144, 0x168 }, - - /* SG 40M */ - { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E, - 0x12C, 0x168, 0x190 }, - - /* LG 20M */ - { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 }, - - /* SG 20M */ - { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 }, - }; - /* NSS2 note: the value in the table is 2 multiplier of the actual - * rate - */ - u16 ac_mcs_rate_nss2[8][10] = { - /* LG 160M */ - { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A, - 0x924, 0xAF8, 0xC30 }, - - /* SG 160M */ - { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924, - 0xA28, 0xC30, 0xD8B }, - - /* LG 80M */ - { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, - 0x492, 0x57C, 0x618 }, - - /* SG 80M */ - { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, - 0x514, 0x618, 0x6C6 }, - - /* LG 40M */ - { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6, - 0x21C, 0x288, 0x2D0 }, - - /* SG 40M */ - { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C, - 0x258, 0x2D0, 0x320 }, - - /* LG 20M */ - { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104, - 0x138, 0x00 }, - - /* SG 20M */ - { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121, - 0x15B, 0x00 }, - }; u32 rate = 0; u8 mcs_index = 0; u8 bw = 0; @@ -252,26 +252,6 @@ u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv, u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, u8 index, u8 ht_info) { - /* For every mcs_rate line, the first 8 bytes are for stream 1x1, - * and all 16 bytes are for stream 2x2. - */ - u16 mcs_rate[4][16] = { - /* LGI 40M */ - { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, - 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c }, - - /* SGI 40M */ - { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, - 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 }, - - /* LGI 20M */ - { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, - 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 }, - - /* SGI 20M */ - { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, - 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 } - }; u32 mcs_num_supp = (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8; u32 rate; -- cgit From 406d702b47a23506b944d8377647352e25f68ea1 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Fri, 7 Feb 2014 16:21:00 -0800 Subject: mwifiex: improve readability in 11ac mcsmap to maxrate conversion 1) rename max_mcs to mcs; 2) initialize 'i' and 'nss' as 1 instead of 0 in nss lookup; 3) use GET_VHTNSSMCS(mcs_map, nss) macro; 4) use IEEE80211_VHT_MCS_* definitions instead of hard coding Reported-by: Paul Stewart Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11ac.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c index f07a5005455d..47383920eb12 100644 --- a/drivers/net/wireless/mwifiex/11ac.c +++ b/drivers/net/wireless/mwifiex/11ac.c @@ -55,7 +55,7 @@ static u16 mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv, u8 bands, u16 mcs_map) { - u8 i, nss, max_mcs; + u8 i, nss, mcs; u16 max_rate = 0; u32 usr_vht_cap_info = 0; struct mwifiex_adapter *adapter = priv->adapter; @@ -66,29 +66,29 @@ mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv, usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; /* find the max NSS supported */ - nss = 0; - for (i = 0; i < 8; i++) { - max_mcs = (mcs_map >> (2 * i)) & 0x3; - if (max_mcs < 3) + nss = 1; + for (i = 1; i <= 8; i++) { + mcs = GET_VHTNSSMCS(mcs_map, i); + if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED) nss = i; } - max_mcs = (mcs_map >> (2 * nss)) & 0x3; + mcs = GET_VHTNSSMCS(mcs_map, nss); - /* if max_mcs is 3, nss must be 0 (SS = 1). Thus, max mcs is MCS 9 */ - if (max_mcs >= 3) - max_mcs = 2; + /* if mcs is 3, nss must be 1 (NSS = 1). Default mcs to MCS 0~9 */ + if (mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED) + mcs = IEEE80211_VHT_MCS_SUPPORT_0_9; if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) { /* support 160 MHz */ - max_rate = max_rate_lgi_160MHZ[nss][max_mcs]; + max_rate = max_rate_lgi_160MHZ[nss - 1][mcs]; if (!max_rate) /* MCS9 is not supported in NSS6 */ - max_rate = max_rate_lgi_160MHZ[nss][max_mcs - 1]; + max_rate = max_rate_lgi_160MHZ[nss - 1][mcs - 1]; } else { - max_rate = max_rate_lgi_80MHZ[nss][max_mcs]; + max_rate = max_rate_lgi_80MHZ[nss - 1][mcs]; if (!max_rate) /* MCS9 is not supported in NSS3 */ - max_rate = max_rate_lgi_80MHZ[nss][max_mcs - 1]; + max_rate = max_rate_lgi_80MHZ[nss - 1][mcs - 1]; } return max_rate; -- cgit From f25b14315e8084efbcc5c672cec8ee1f691dc2fd Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Fri, 7 Feb 2014 16:21:01 -0800 Subject: mwifiex: remove unsupported code in 11ac bit12 in fw_cap_info is for testing only. Remove all related code. Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 3 --- drivers/net/wireless/mwifiex/cfp.c | 5 ----- drivers/net/wireless/mwifiex/fw.h | 20 +++++++++++--------- drivers/net/wireless/mwifiex/ioctl.h | 3 +-- drivers/net/wireless/mwifiex/join.c | 12 ++++-------- drivers/net/wireless/mwifiex/sta_ioctl.c | 2 +- 6 files changed, 17 insertions(+), 28 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 0948ebe8942e..c6606288c61e 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1416,9 +1416,6 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, if (params->chandef.width > NL80211_CHAN_WIDTH_20_NOHT) config_bands |= BAND_GN; - - if (params->chandef.width > NL80211_CHAN_WIDTH_40) - config_bands |= BAND_GAC; } else { bss_cfg->band_cfg = BAND_CONFIG_A; config_bands = BAND_A; diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c index dfb068ba2dbd..2c3226bf86f8 100644 --- a/drivers/net/wireless/mwifiex/cfp.c +++ b/drivers/net/wireless/mwifiex/cfp.c @@ -438,7 +438,6 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) break; case BAND_G: case BAND_G | BAND_GN: - case BAND_G | BAND_GN | BAND_GAC: dev_dbg(adapter->dev, "info: infra band=%d " "supported_rates_g\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_g, @@ -449,10 +448,7 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) case BAND_A | BAND_B: case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN: case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC: - case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | - BAND_AAC | BAND_GAC: case BAND_B | BAND_G | BAND_GN: - case BAND_B | BAND_G | BAND_GN | BAND_GAC: dev_dbg(adapter->dev, "info: infra band=%d " "supported_rates_bg\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_bg, @@ -476,7 +472,6 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) sizeof(supported_rates_a)); break; case BAND_GN: - case BAND_GN | BAND_GAC: dev_dbg(adapter->dev, "info: infra band=%d " "supported_rates_n\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_n, diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 059bc16adf81..d8014c87d470 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -50,21 +50,23 @@ struct tx_packet_hdr { #define HOSTCMD_SUPPORTED_RATES 14 #define N_SUPPORTED_RATES 3 #define ALL_802_11_BANDS (BAND_A | BAND_B | BAND_G | BAND_GN | \ - BAND_AN | BAND_GAC | BAND_AAC) + BAND_AN | BAND_AAC) #define FW_MULTI_BANDS_SUPPORT (BIT(8) | BIT(9) | BIT(10) | BIT(11) | \ - BIT(12) | BIT(13)) + BIT(13)) #define IS_SUPPORT_MULTI_BANDS(adapter) \ (adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT) -/* shift bit 12 and bit 13 in fw_cap_info from the firmware to bit 13 and 14 - * for 11ac so that bit 11 is for GN, bit 12 for AN, bit 13 for GAC, and bit - * bit 14 for AAC, in order to be compatible with the band capability - * defined in the driver after right shift of 8 bits. +/* bit 13: 11ac BAND_AAC + * bit 12: reserved for lab testing, will be reused for BAND_AN + * bit 11: 11n BAND_GN + * bit 10: 11a BAND_A + * bit 9: 11g BAND_G + * bit 8: 11b BAND_B + * Map these bits to band capability by right shifting 8 bits. */ #define GET_FW_DEFAULT_BANDS(adapter) \ - (((((adapter->fw_cap_info & 0x3000) << 1) | \ - (adapter->fw_cap_info & ~0xF000)) >> 8) & \ + (((adapter->fw_cap_info & 0x2f00) >> 8) & \ ALL_802_11_BANDS) #define HostCmd_WEP_KEY_INDEX_MASK 0x3fff @@ -226,7 +228,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { /* HW_SPEC fw_cap_info */ -#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & (BIT(12)|BIT(13))) +#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & BIT(13)) #define GET_VHTCAP_CHWDSET(vht_cap_info) ((vht_cap_info >> 2) & 0x3) #define GET_VHTNSSMCS(mcs_mapset, nss) ((mcs_mapset >> (2 * (nss - 1))) & 0x3) diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index 00a95f4c6a6c..48f15906515d 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -60,8 +60,7 @@ enum { BAND_A = 4, BAND_GN = 8, BAND_AN = 16, - BAND_GAC = 32, - BAND_AAC = 64, + BAND_AAC = 32, }; #define MWIFIEX_WPA_PASSHPHRASE_LEN 64 diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 4e4686e6ac09..d3934c6dbd8a 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -515,8 +515,7 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && !bss_desc->disable_11n && !bss_desc->disable_11ac && - (priv->adapter->config_bands & BAND_GAC || - priv->adapter->config_bands & BAND_AAC)) + priv->adapter->config_bands & BAND_AAC) mwifiex_cmd_append_11ac_tlv(priv, bss_desc, &pos); /* Append vendor specific IE TLV */ @@ -1300,8 +1299,7 @@ int mwifiex_associate(struct mwifiex_private *priv, if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && !bss_desc->disable_11n && !bss_desc->disable_11ac && - (priv->adapter->config_bands & BAND_GAC || - priv->adapter->config_bands & BAND_AAC)) + priv->adapter->config_bands & BAND_AAC) mwifiex_set_11ac_ba_params(priv); else mwifiex_set_ba_params(priv); @@ -1335,8 +1333,7 @@ mwifiex_adhoc_start(struct mwifiex_private *priv, priv->curr_bss_params.band); if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && - (priv->adapter->config_bands & BAND_GAC || - priv->adapter->config_bands & BAND_AAC)) + priv->adapter->config_bands & BAND_AAC) mwifiex_set_11ac_ba_params(priv); else mwifiex_set_ba_params(priv); @@ -1376,8 +1373,7 @@ int mwifiex_adhoc_join(struct mwifiex_private *priv, if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && !bss_desc->disable_11n && !bss_desc->disable_11ac && - (priv->adapter->config_bands & BAND_GAC || - priv->adapter->config_bands & BAND_AAC)) + priv->adapter->config_bands & BAND_AAC) mwifiex_set_11ac_ba_params(priv); else mwifiex_set_ba_params(priv); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 0bec94351f36..c3d3ea55d605 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -290,7 +290,7 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, if (mwifiex_band_to_radio_type(bss_desc->bss_band) == HostCmd_SCAN_RADIO_TYPE_BG) - config_bands = BAND_B | BAND_G | BAND_GN | BAND_GAC; + config_bands = BAND_B | BAND_G | BAND_GN; else config_bands = BAND_A | BAND_AN | BAND_AAC; -- cgit From b8b3ecec91f106e2f26ac2e24dcda21f63336286 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 7 Feb 2014 16:23:34 -0800 Subject: mwifiex: change beacon parameter structure 'mwifiex_bcn_param' structure contains five parameters which are present in beacon buffer in case of legacy scan. 'rssi' field won't be there in this buffer for extended scan. Hence 'bssid' and 'rssi' are removed from the structure and it is renamed as 'mwifiex_fixed_bcn_param' so that we can have common parsing logic later for both. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/fw.h | 4 +--- drivers/net/wireless/mwifiex/scan.c | 25 +++++++++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index d8014c87d470..9b267901832a 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -1047,9 +1047,7 @@ struct host_cmd_ds_rf_ant_siso { __le16 ant_mode; }; -struct mwifiex_bcn_param { - u8 bssid[ETH_ALEN]; - u8 rssi; +struct mwifiex_fixed_bcn_param { __le64 timestamp; __le16 beacon_period; __le16 cap_info_bitmap; diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 0a8a26e10f01..b0be830aa342 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1689,7 +1689,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, u16 cap_info_bitmap; u8 *current_ptr; u64 timestamp; - struct mwifiex_bcn_param *bcn_param; + struct mwifiex_fixed_bcn_param *bcn_param; struct mwifiex_bss_priv *bss_priv; if (bytes_left >= sizeof(beacon_size)) { @@ -1716,25 +1716,30 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, curr_bcn_bytes = beacon_size; - /* - * First 5 fields are bssid, RSSI, time stamp, beacon interval, - * and capability information + /* First 5 fields are bssid, RSSI(for legacy scan only), + * time stamp, beacon interval, and capability information */ - if (curr_bcn_bytes < sizeof(struct mwifiex_bcn_param)) { + if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) + + sizeof(struct mwifiex_fixed_bcn_param)) { dev_err(adapter->dev, "InterpretIE: not enough bytes left\n"); continue; } - bcn_param = (struct mwifiex_bcn_param *)current_ptr; - current_ptr += sizeof(*bcn_param); - curr_bcn_bytes -= sizeof(*bcn_param); - memcpy(bssid, bcn_param->bssid, ETH_ALEN); + memcpy(bssid, current_ptr, ETH_ALEN); + current_ptr += ETH_ALEN; + curr_bcn_bytes -= ETH_ALEN; - rssi = (s32) bcn_param->rssi; + rssi = (s32) *(u8 *)current_ptr; rssi = (-rssi) * 100; /* Convert dBm to mBm */ + current_ptr += sizeof(u8); + curr_bcn_bytes -= sizeof(u8); dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%d\n", rssi); + bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr; + current_ptr += sizeof(*bcn_param); + curr_bcn_bytes -= sizeof(*bcn_param); + timestamp = le64_to_cpu(bcn_param->timestamp); beacon_period = le16_to_cpu(bcn_param->beacon_period); -- cgit From 3b4d5c644204476265083a554dad56868b93b9dd Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 7 Feb 2014 16:23:35 -0800 Subject: mwifiex: separate out response buffer parsing code This new function will be useful later for extended scan feature. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/scan.c | 309 +++++++++++++++++++----------------- 1 file changed, 163 insertions(+), 146 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index b0be830aa342..28f0a38ff2d9 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1576,6 +1576,156 @@ done: return 0; } +static int +mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, + u32 *bytes_left, u64 fw_tsf, u8 *radio_type, + bool ext_scan) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_chan_freq_power *cfp; + struct cfg80211_bss *bss; + u8 bssid[ETH_ALEN]; + s32 rssi; + const u8 *ie_buf; + size_t ie_len; + u16 channel = 0; + u16 beacon_size = 0; + u32 curr_bcn_bytes; + u32 freq; + u16 beacon_period; + u16 cap_info_bitmap; + u8 *current_ptr; + u64 timestamp; + struct mwifiex_fixed_bcn_param *bcn_param; + struct mwifiex_bss_priv *bss_priv; + + if (*bytes_left >= sizeof(beacon_size)) { + /* Extract & convert beacon size from command buffer */ + memcpy(&beacon_size, *bss_info, sizeof(beacon_size)); + *bytes_left -= sizeof(beacon_size); + *bss_info += sizeof(beacon_size); + } + + if (!beacon_size || beacon_size > *bytes_left) { + *bss_info += *bytes_left; + *bytes_left = 0; + return -EFAULT; + } + + /* Initialize the current working beacon pointer for this BSS + * iteration + */ + current_ptr = *bss_info; + + /* Advance the return beacon pointer past the current beacon */ + *bss_info += beacon_size; + *bytes_left -= beacon_size; + + curr_bcn_bytes = beacon_size; + + /* First 5 fields are bssid, RSSI(for legacy scan only), + * time stamp, beacon interval, and capability information + */ + if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) + + sizeof(struct mwifiex_fixed_bcn_param)) { + dev_err(adapter->dev, "InterpretIE: not enough bytes left\n"); + return -EFAULT; + } + + memcpy(bssid, current_ptr, ETH_ALEN); + current_ptr += ETH_ALEN; + curr_bcn_bytes -= ETH_ALEN; + + if (!ext_scan) { + rssi = (s32) *(u8 *)current_ptr; + rssi = (-rssi) * 100; /* Convert dBm to mBm */ + current_ptr += sizeof(u8); + curr_bcn_bytes -= sizeof(u8); + dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%d\n", rssi); + } + + bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr; + current_ptr += sizeof(*bcn_param); + curr_bcn_bytes -= sizeof(*bcn_param); + + timestamp = le64_to_cpu(bcn_param->timestamp); + beacon_period = le16_to_cpu(bcn_param->beacon_period); + + cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap); + dev_dbg(adapter->dev, "info: InterpretIE: capabilities=0x%X\n", + cap_info_bitmap); + + /* Rest of the current buffer are IE's */ + ie_buf = current_ptr; + ie_len = curr_bcn_bytes; + dev_dbg(adapter->dev, "info: InterpretIE: IELength for this AP = %d\n", + curr_bcn_bytes); + + while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) { + u8 element_id, element_len; + + element_id = *current_ptr; + element_len = *(current_ptr + 1); + if (curr_bcn_bytes < element_len + + sizeof(struct ieee_types_header)) { + dev_err(adapter->dev, + "%s: bytes left < IE length\n", __func__); + return -EFAULT; + } + if (element_id == WLAN_EID_DS_PARAMS) { + channel = *(current_ptr + + sizeof(struct ieee_types_header)); + break; + } + + current_ptr += element_len + sizeof(struct ieee_types_header); + curr_bcn_bytes -= element_len + + sizeof(struct ieee_types_header); + } + + if (channel) { + struct ieee80211_channel *chan; + u8 band; + + /* Skip entry if on csa closed channel */ + if (channel == priv->csa_chan) { + dev_dbg(adapter->dev, + "Dropping entry on csa closed channel\n"); + return 0; + } + + band = BAND_G; + if (radio_type) + band = mwifiex_radio_type_to_band(*radio_type & + (BIT(0) | BIT(1))); + + cfp = mwifiex_get_cfp(priv, band, channel, 0); + + freq = cfp ? cfp->freq : 0; + + chan = ieee80211_get_channel(priv->wdev->wiphy, freq); + + if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { + bss = cfg80211_inform_bss(priv->wdev->wiphy, + chan, bssid, timestamp, + cap_info_bitmap, beacon_period, + ie_buf, ie_len, rssi, GFP_KERNEL); + bss_priv = (struct mwifiex_bss_priv *)bss->priv; + bss_priv->band = band; + bss_priv->fw_tsf = fw_tsf; + if (priv->media_connected && + !memcmp(bssid, priv->curr_bss_params.bss_descriptor + .mac_address, ETH_ALEN)) + mwifiex_update_curr_bss_params(priv, bss); + cfg80211_put_bss(priv->wdev->wiphy, bss); + } + } else { + dev_dbg(adapter->dev, "missing BSS channel IE\n"); + } + + return 0; +} + /* * This function handles the command response of scan. * @@ -1609,12 +1759,12 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, u32 bytes_left; u32 idx; u32 tlv_buf_size; - struct mwifiex_chan_freq_power *cfp; struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv; struct chan_band_param_set *chan_band; u8 is_bgscan_resp; unsigned long flags; - struct cfg80211_bss *bss; + __le64 fw_tsf = 0; + u8 *radio_type; is_bgscan_resp = (le16_to_cpu(resp->command) == HostCmd_CMD_802_11_BG_SCAN_QUERY); @@ -1676,107 +1826,6 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, &chan_band_tlv); for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) { - u8 bssid[ETH_ALEN]; - s32 rssi; - const u8 *ie_buf; - size_t ie_len; - u16 channel = 0; - __le64 fw_tsf = 0; - u16 beacon_size = 0; - u32 curr_bcn_bytes; - u32 freq; - u16 beacon_period; - u16 cap_info_bitmap; - u8 *current_ptr; - u64 timestamp; - struct mwifiex_fixed_bcn_param *bcn_param; - struct mwifiex_bss_priv *bss_priv; - - if (bytes_left >= sizeof(beacon_size)) { - /* Extract & convert beacon size from command buffer */ - memcpy(&beacon_size, bss_info, sizeof(beacon_size)); - bytes_left -= sizeof(beacon_size); - bss_info += sizeof(beacon_size); - } - - if (!beacon_size || beacon_size > bytes_left) { - bss_info += bytes_left; - bytes_left = 0; - ret = -1; - goto check_next_scan; - } - - /* Initialize the current working beacon pointer for this BSS - * iteration */ - current_ptr = bss_info; - - /* Advance the return beacon pointer past the current beacon */ - bss_info += beacon_size; - bytes_left -= beacon_size; - - curr_bcn_bytes = beacon_size; - - /* First 5 fields are bssid, RSSI(for legacy scan only), - * time stamp, beacon interval, and capability information - */ - if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) + - sizeof(struct mwifiex_fixed_bcn_param)) { - dev_err(adapter->dev, - "InterpretIE: not enough bytes left\n"); - continue; - } - - memcpy(bssid, current_ptr, ETH_ALEN); - current_ptr += ETH_ALEN; - curr_bcn_bytes -= ETH_ALEN; - - rssi = (s32) *(u8 *)current_ptr; - rssi = (-rssi) * 100; /* Convert dBm to mBm */ - current_ptr += sizeof(u8); - curr_bcn_bytes -= sizeof(u8); - dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%d\n", rssi); - - bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr; - current_ptr += sizeof(*bcn_param); - curr_bcn_bytes -= sizeof(*bcn_param); - - timestamp = le64_to_cpu(bcn_param->timestamp); - beacon_period = le16_to_cpu(bcn_param->beacon_period); - - cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap); - dev_dbg(adapter->dev, "info: InterpretIE: capabilities=0x%X\n", - cap_info_bitmap); - - /* Rest of the current buffer are IE's */ - ie_buf = current_ptr; - ie_len = curr_bcn_bytes; - dev_dbg(adapter->dev, - "info: InterpretIE: IELength for this AP = %d\n", - curr_bcn_bytes); - - while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) { - u8 element_id, element_len; - - element_id = *current_ptr; - element_len = *(current_ptr + 1); - if (curr_bcn_bytes < element_len + - sizeof(struct ieee_types_header)) { - dev_err(priv->adapter->dev, - "%s: bytes left < IE length\n", - __func__); - goto check_next_scan; - } - if (element_id == WLAN_EID_DS_PARAMS) { - channel = *(current_ptr + sizeof(struct ieee_types_header)); - break; - } - - current_ptr += element_len + - sizeof(struct ieee_types_header); - curr_bcn_bytes -= element_len + - sizeof(struct ieee_types_header); - } - /* * If the TSF TLV was appended to the scan results, save this * entry's TSF value in the fw_tsf field. It is the firmware's @@ -1787,51 +1836,19 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE], sizeof(fw_tsf)); - if (channel) { - struct ieee80211_channel *chan; - u8 band; - - /* Skip entry if on csa closed channel */ - if (channel == priv->csa_chan) { - dev_dbg(adapter->dev, - "Dropping entry on csa closed channel\n"); - continue; - } - - band = BAND_G; - if (chan_band_tlv) { - chan_band = - &chan_band_tlv->chan_band_param[idx]; - band = mwifiex_radio_type_to_band( - chan_band->radio_type - & (BIT(0) | BIT(1))); - } - - cfp = mwifiex_get_cfp(priv, band, channel, 0); - - freq = cfp ? cfp->freq : 0; - - chan = ieee80211_get_channel(priv->wdev->wiphy, freq); - - if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { - bss = cfg80211_inform_bss(priv->wdev->wiphy, - chan, bssid, timestamp, - cap_info_bitmap, beacon_period, - ie_buf, ie_len, rssi, GFP_KERNEL); - bss_priv = (struct mwifiex_bss_priv *)bss->priv; - bss_priv->band = band; - bss_priv->fw_tsf = le64_to_cpu(fw_tsf); - if (priv->media_connected && - !memcmp(bssid, - priv->curr_bss_params.bss_descriptor - .mac_address, ETH_ALEN)) - mwifiex_update_curr_bss_params(priv, - bss); - cfg80211_put_bss(priv->wdev->wiphy, bss); - } + if (chan_band_tlv) { + chan_band = &chan_band_tlv->chan_band_param[idx]; + radio_type = &chan_band->radio_type; } else { - dev_dbg(adapter->dev, "missing BSS channel IE\n"); + radio_type = NULL; } + + ret = mwifiex_parse_single_response_buf(priv, &bss_info, + &bytes_left, + le64_to_cpu(fw_tsf), + radio_type, false); + if (ret) + goto check_next_scan; } check_next_scan: -- cgit From d44b5c2f2ec54569006dc85c7dbe25ccd41cfb73 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 7 Feb 2014 16:23:36 -0800 Subject: mwifiex: separate out next scan command queueing logic This new function will be useful later for extended scan feature. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/scan.c | 133 +++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 62 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 28f0a38ff2d9..3633347f50f6 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1726,6 +1726,76 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, return 0; } +static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node; + unsigned long flags; + + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + if (list_empty(&adapter->scan_pending_q)) { + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + /* Need to indicate IOCTL complete */ + if (adapter->curr_cmd->wait_q_enabled) { + adapter->cmd_wait_q.status = 0; + if (!priv->scan_request) { + dev_dbg(adapter->dev, + "complete internal scan\n"); + mwifiex_complete_cmd(adapter, + adapter->curr_cmd); + } + } + if (priv->report_scan_result) + priv->report_scan_result = false; + + if (priv->scan_request) { + dev_dbg(adapter->dev, "info: notifying scan done\n"); + cfg80211_scan_done(priv->scan_request, 0); + priv->scan_request = NULL; + } else { + priv->scan_aborting = false; + dev_dbg(adapter->dev, "info: scan already aborted\n"); + } + } else { + if ((priv->scan_aborting && !priv->scan_request) || + priv->scan_block) { + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT; + mod_timer(&priv->scan_delay_timer, jiffies); + dev_dbg(priv->adapter->dev, + "info: %s: triggerring scan abort\n", __func__); + } else if (!mwifiex_wmm_lists_empty(adapter) && + (priv->scan_request && (priv->scan_request->flags & + NL80211_SCAN_FLAG_LOW_PRIORITY))) { + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + adapter->scan_delay_cnt = 1; + mod_timer(&priv->scan_delay_timer, jiffies + + msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC)); + dev_dbg(priv->adapter->dev, + "info: %s: deferring scan\n", __func__); + } else { + /* Get scan command from scan_pending_q and put to + * cmd_pending_q + */ + cmd_node = list_first_entry(&adapter->scan_pending_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, + true); + } + } + + return; +} + /* * This function handles the command response of scan. * @@ -1750,7 +1820,6 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, { int ret = 0; struct mwifiex_adapter *adapter = priv->adapter; - struct cmd_ctrl_node *cmd_node; struct host_cmd_ds_802_11_scan_rsp *scan_rsp; struct mwifiex_ie_types_data *tlv_data; struct mwifiex_ie_types_tsf_timestamp *tsf_tlv; @@ -1762,7 +1831,6 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv; struct chan_band_param_set *chan_band; u8 is_bgscan_resp; - unsigned long flags; __le64 fw_tsf = 0; u8 *radio_type; @@ -1852,66 +1920,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, } check_next_scan: - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - if (list_empty(&adapter->scan_pending_q)) { - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - - /* Need to indicate IOCTL complete */ - if (adapter->curr_cmd->wait_q_enabled) { - adapter->cmd_wait_q.status = 0; - if (!priv->scan_request) { - dev_dbg(adapter->dev, - "complete internal scan\n"); - mwifiex_complete_cmd(adapter, - adapter->curr_cmd); - } - } - if (priv->report_scan_result) - priv->report_scan_result = false; - - if (priv->scan_request) { - dev_dbg(adapter->dev, "info: notifying scan done\n"); - cfg80211_scan_done(priv->scan_request, 0); - priv->scan_request = NULL; - } else { - priv->scan_aborting = false; - dev_dbg(adapter->dev, "info: scan already aborted\n"); - } - } else { - if ((priv->scan_aborting && !priv->scan_request) || - priv->scan_block) { - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT; - mod_timer(&priv->scan_delay_timer, jiffies); - dev_dbg(priv->adapter->dev, - "info: %s: triggerring scan abort\n", __func__); - } else if (!mwifiex_wmm_lists_empty(adapter) && - (priv->scan_request && (priv->scan_request->flags & - NL80211_SCAN_FLAG_LOW_PRIORITY))) { - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - adapter->scan_delay_cnt = 1; - mod_timer(&priv->scan_delay_timer, jiffies + - msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC)); - dev_dbg(priv->adapter->dev, - "info: %s: deferring scan\n", __func__); - } else { - /* Get scan command from scan_pending_q and put to - cmd_pending_q */ - cmd_node = list_first_entry(&adapter->scan_pending_q, - struct cmd_ctrl_node, list); - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, - true); - } - } - + mwifiex_check_next_scan_command(priv); return ret; } -- cgit From 21f58d200388480547df909b5464b5aafebf299d Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Tue, 11 Feb 2014 18:39:56 -0800 Subject: mwifiex: implement extended scan feature In extended scan, host gets scan results through one or multiple events instead of scan command response. Host will send next scan command when all the events are received. Legacy scan sometimes truncates scan results in a noisy environment due to buffer length limitation. This issue is addressed in extended scan. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cmdevt.c | 3 +- drivers/net/wireless/mwifiex/fw.h | 43 +++++++ drivers/net/wireless/mwifiex/init.c | 1 + drivers/net/wireless/mwifiex/main.h | 9 +- drivers/net/wireless/mwifiex/scan.c | 191 ++++++++++++++++++++++++++++- drivers/net/wireless/mwifiex/sta_cmd.c | 3 + drivers/net/wireless/mwifiex/sta_cmdresp.c | 5 + drivers/net/wireless/mwifiex/sta_event.c | 8 ++ 8 files changed, 255 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 1ddc8b2e3722..556cb2c74e30 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -595,7 +595,8 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, } /* Send command */ - if (cmd_no == HostCmd_CMD_802_11_SCAN) { + if (cmd_no == HostCmd_CMD_802_11_SCAN || + cmd_no == HostCmd_CMD_802_11_SCAN_EXT) { mwifiex_queue_scan_cmd(priv, cmd_node); } else { mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 9b267901832a..ed41af17b213 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -132,6 +132,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) #define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) #define TLV_TYPE_STA_MAC_ADDR (PROPRIETARY_TLV_BASE_ID + 32) +#define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 35) #define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42) #define TLV_TYPE_UAP_BEACON_PERIOD (PROPRIETARY_TLV_BASE_ID + 44) #define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45) @@ -146,6 +147,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82) #define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83) #define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84) +#define TLV_TYPE_BSS_SCAN_RSP (PROPRIETARY_TLV_BASE_ID + 86) +#define TLV_TYPE_BSS_SCAN_INFO (PROPRIETARY_TLV_BASE_ID + 87) #define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 93) #define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 94) #define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 104) @@ -295,6 +298,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_CAU_REG_ACCESS 0x00ed #define HostCmd_CMD_SET_BSS_MODE 0x00f7 #define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa +#define HostCmd_CMD_802_11_SCAN_EXT 0x0107 #define HostCmd_CMD_COALESCE_CFG 0x010a #define HostCmd_CMD_MGMT_FRAME_REG 0x010c #define HostCmd_CMD_REMAIN_ON_CHAN 0x010d @@ -440,6 +444,7 @@ enum P2P_MODES { #define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c #define EVENT_HOSTWAKE_STAIE 0x0000004d #define EVENT_CHANNEL_SWITCH_ANN 0x00000050 +#define EVENT_EXT_SCAN_REPORT 0x00000058 #define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f #define EVENT_ID_MASK 0xffff @@ -1053,6 +1058,16 @@ struct mwifiex_fixed_bcn_param { __le16 cap_info_bitmap; } __packed; +struct mwifiex_event_scan_result { + __le16 event_id; + u8 bss_index; + u8 bss_type; + u8 more_event; + u8 reserved[3]; + __le16 buf_size; + u8 num_of_set; +} __packed; + #define MWIFIEX_USER_SCAN_CHAN_MAX 50 #define MWIFIEX_MAX_SSID_LIST_LENGTH 10 @@ -1122,6 +1137,28 @@ struct host_cmd_ds_802_11_scan_rsp { u8 bss_desc_and_tlv_buffer[1]; } __packed; +struct host_cmd_ds_802_11_scan_ext { + u32 reserved; + u8 tlv_buffer[1]; +} __packed; + +struct mwifiex_ie_types_bss_scan_rsp { + struct mwifiex_ie_types_header header; + u8 bssid[ETH_ALEN]; + u8 frame_body[1]; +} __packed; + +struct mwifiex_ie_types_bss_scan_info { + struct mwifiex_ie_types_header header; + __le16 rssi; + __le16 anpi; + u8 cca_busy_fraction; + u8 radio_type; + u8 channel; + u8 reserved; + __le64 tsf; +} __packed; + struct host_cmd_ds_802_11_bg_scan_query { u8 flush; } __packed; @@ -1439,6 +1476,11 @@ struct host_cmd_tlv_rates { u8 rates[0]; } __packed; +struct mwifiex_ie_types_bssid_list { + struct mwifiex_ie_types_header header; + u8 bssid[ETH_ALEN]; +} __packed; + struct host_cmd_tlv_bcast_ssid { struct mwifiex_ie_types_header header; u8 bcast_ctl; @@ -1632,6 +1674,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_802_11_ps_mode_enh psmode_enh; struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg; struct host_cmd_ds_802_11_scan scan; + struct host_cmd_ds_802_11_scan_ext ext_scan; struct host_cmd_ds_802_11_scan_rsp scan_resp; struct host_cmd_ds_802_11_bg_scan_query bg_scan_query; struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp; diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 1d0a817f2bf0..308c56fd32eb 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -281,6 +281,7 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) adapter->arp_filter_size = 0; adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX; adapter->empty_tx_q_cnt = 0; + adapter->ext_scan = true; } /* diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 29d27d9b5ebe..c473f54ba738 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -59,7 +59,7 @@ enum { #define MWIFIEX_UPLD_SIZE (2312) -#define MAX_EVENT_SIZE 1024 +#define MAX_EVENT_SIZE 2048 #define ARP_FILTER_MAX_BUF_SIZE 68 @@ -753,6 +753,7 @@ struct mwifiex_adapter { atomic_t is_tx_received; atomic_t pending_bridged_pkts; struct semaphore *card_sem; + bool ext_scan; }; int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); @@ -938,6 +939,12 @@ mwifiex_set_wmm_params(struct mwifiex_private *priv, struct cfg80211_ap_settings *params); void mwifiex_set_ba_params(struct mwifiex_private *priv); void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv); +int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf); +int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv); +int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, + void *buf); /* * This function checks if the queuing is RA based or not. diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 3633347f50f6..c548a7d4877d 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -595,7 +595,7 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, struct mwifiex_chan_scan_param_set *tmp_chan_list; struct mwifiex_chan_scan_param_set *start_chan; - u32 tlv_idx, rates_size; + u32 tlv_idx, rates_size, cmd_no; u32 total_scan_time; u32 done_early; u8 radio_type; @@ -733,9 +733,13 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, /* Send the scan command to the firmware with the specified cfg */ - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SCAN, - HostCmd_ACT_GEN_SET, 0, - scan_cfg_out); + if (priv->adapter->ext_scan) + cmd_no = HostCmd_CMD_802_11_SCAN_EXT; + else + cmd_no = HostCmd_CMD_802_11_SCAN; + + ret = mwifiex_send_cmd_async(priv, cmd_no, HostCmd_ACT_GEN_SET, + 0, scan_cfg_out); /* rate IE is updated per scan command but same starting * pointer is used each time so that rate IE from earlier @@ -786,6 +790,7 @@ mwifiex_config_scan(struct mwifiex_private *priv, struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_ie_types_num_probes *num_probes_tlv; struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; + struct mwifiex_ie_types_bssid_list *bssid_tlv; u8 *tlv_pos; u32 num_probes; u32 ssid_len; @@ -848,6 +853,17 @@ mwifiex_config_scan(struct mwifiex_private *priv, user_scan_in->specific_bssid, sizeof(scan_cfg_out->specific_bssid)); + if (adapter->ext_scan && + !is_zero_ether_addr(scan_cfg_out->specific_bssid)) { + bssid_tlv = + (struct mwifiex_ie_types_bssid_list *)tlv_pos; + bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID); + bssid_tlv->header.len = cpu_to_le16(ETH_ALEN); + memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid, + ETH_ALEN); + tlv_pos += sizeof(struct mwifiex_ie_types_bssid_list); + } + for (i = 0; i < user_scan_in->num_ssids; i++) { ssid_len = user_scan_in->ssid_list[i].ssid_len; @@ -1579,7 +1595,7 @@ done: static int mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, u32 *bytes_left, u64 fw_tsf, u8 *radio_type, - bool ext_scan) + bool ext_scan, s32 rssi_val) { struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_chan_freq_power *cfp; @@ -1642,6 +1658,8 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, current_ptr += sizeof(u8); curr_bcn_bytes -= sizeof(u8); dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%d\n", rssi); + } else { + rssi = rssi_val; } bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr; @@ -1914,7 +1932,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, ret = mwifiex_parse_single_response_buf(priv, &bss_info, &bytes_left, le64_to_cpu(fw_tsf), - radio_type, false); + radio_type, false, 0); if (ret) goto check_next_scan; } @@ -1924,6 +1942,167 @@ check_next_scan: return ret; } +/* + * This function prepares an extended scan command to be sent to the firmware + * + * This uses the scan command configuration sent to the command processing + * module in command preparation stage to configure a extended scan command + * structure to send to firmware. + */ +int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan; + struct mwifiex_scan_cmd_config *scan_cfg = data_buf; + + memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved) + + scan_cfg->tlv_buf_len + S_DS_GEN)); + + return 0; +} + +/* This function handles the command response of extended scan */ +int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv) +{ + dev_dbg(priv->adapter->dev, "info: EXT scan returns successfully\n"); + return 0; +} + +/* This function This function handles the event extended scan report. It + * parses extended scan results and informs to cfg80211 stack. + */ +int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, + void *buf) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + u8 *bss_info; + u32 bytes_left, bytes_left_for_tlv, idx; + u16 type, len; + struct mwifiex_ie_types_data *tlv; + struct mwifiex_ie_types_bss_scan_rsp *scan_rsp_tlv; + struct mwifiex_ie_types_bss_scan_info *scan_info_tlv; + u8 *radio_type; + u64 fw_tsf = 0; + s32 rssi = 0; + struct mwifiex_event_scan_result *event_scan = buf; + u8 num_of_set = event_scan->num_of_set; + u8 *scan_resp = buf + sizeof(struct mwifiex_event_scan_result); + u16 scan_resp_size = le16_to_cpu(event_scan->buf_size); + + if (num_of_set > MWIFIEX_MAX_AP) { + dev_err(adapter->dev, + "EXT_SCAN: Invalid number of AP returned (%d)!!\n", + num_of_set); + ret = -1; + goto check_next_scan; + } + + bytes_left = scan_resp_size; + dev_dbg(adapter->dev, + "EXT_SCAN: size %d, returned %d APs...", + scan_resp_size, num_of_set); + + tlv = (struct mwifiex_ie_types_data *)scan_resp; + + for (idx = 0; idx < num_of_set && bytes_left; idx++) { + type = le16_to_cpu(tlv->header.type); + len = le16_to_cpu(tlv->header.len); + if (bytes_left < sizeof(struct mwifiex_ie_types_header) + len) { + dev_err(adapter->dev, "EXT_SCAN: Error bytes left < TLV length\n"); + break; + } + scan_rsp_tlv = NULL; + scan_info_tlv = NULL; + bytes_left_for_tlv = bytes_left; + + /* BSS response TLV with beacon or probe response buffer + * at the initial position of each descriptor + */ + if (type != TLV_TYPE_BSS_SCAN_RSP) + break; + + bss_info = (u8 *)tlv; + scan_rsp_tlv = (struct mwifiex_ie_types_bss_scan_rsp *)tlv; + tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); + bytes_left_for_tlv -= + (len + sizeof(struct mwifiex_ie_types_header)); + + while (bytes_left_for_tlv >= + sizeof(struct mwifiex_ie_types_header) && + le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) { + type = le16_to_cpu(tlv->header.type); + len = le16_to_cpu(tlv->header.len); + if (bytes_left_for_tlv < + sizeof(struct mwifiex_ie_types_header) + len) { + dev_err(adapter->dev, + "EXT_SCAN: Error in processing TLV, bytes left < TLV length\n"); + scan_rsp_tlv = NULL; + bytes_left_for_tlv = 0; + continue; + } + switch (type) { + case TLV_TYPE_BSS_SCAN_INFO: + scan_info_tlv = + (struct mwifiex_ie_types_bss_scan_info *)tlv; + if (len != + sizeof(struct mwifiex_ie_types_bss_scan_info) - + sizeof(struct mwifiex_ie_types_header)) { + bytes_left_for_tlv = 0; + continue; + } + break; + default: + break; + } + tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); + bytes_left -= + (len + sizeof(struct mwifiex_ie_types_header)); + bytes_left_for_tlv -= + (len + sizeof(struct mwifiex_ie_types_header)); + } + + if (!scan_rsp_tlv) + break; + + /* Advance pointer to the beacon buffer length and + * update the bytes count so that the function + * wlan_interpret_bss_desc_with_ie() can handle the + * scan buffer withut any change + */ + bss_info += sizeof(u16); + bytes_left -= sizeof(u16); + + if (scan_info_tlv) { + rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi)); + rssi *= 100; /* Convert dBm to mBm */ + dev_dbg(adapter->dev, + "info: InterpretIE: RSSI=%d\n", rssi); + fw_tsf = le64_to_cpu(scan_info_tlv->tsf); + radio_type = &scan_info_tlv->radio_type; + } else { + radio_type = NULL; + } + ret = mwifiex_parse_single_response_buf(priv, &bss_info, + &bytes_left, fw_tsf, + radio_type, true, rssi); + if (ret) + goto check_next_scan; + } + +check_next_scan: + if (!event_scan->more_event) + mwifiex_check_next_scan_command(priv); + + return ret; +} + /* * This function prepares command for background scan query. * diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 9208a8816b80..1b2991261538 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -1472,6 +1472,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, ret = mwifiex_cmd_ibss_coalescing_status(cmd_ptr, cmd_action, data_buf); break; + case HostCmd_CMD_802_11_SCAN_EXT: + ret = mwifiex_cmd_802_11_scan_ext(priv, cmd_ptr, data_buf); + break; case HostCmd_CMD_MAC_REG_ACCESS: case HostCmd_CMD_BBP_REG_ACCESS: case HostCmd_CMD_RF_REG_ACCESS: diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 24523e4015cb..95e13589d2bd 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -69,6 +69,7 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv, break; case HostCmd_CMD_802_11_SCAN: + case HostCmd_CMD_802_11_SCAN_EXT: /* Cancel all pending scan command */ spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); list_for_each_entry_safe(cmd_node, tmp_node, @@ -871,6 +872,10 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, ret = mwifiex_ret_802_11_scan(priv, resp); adapter->curr_cmd->wait_q_enabled = false; break; + case HostCmd_CMD_802_11_SCAN_EXT: + ret = mwifiex_ret_802_11_scan_ext(priv); + adapter->curr_cmd->wait_q_enabled = false; + break; case HostCmd_CMD_802_11_BG_SCAN_QUERY: ret = mwifiex_ret_802_11_scan(priv, resp); dev_dbg(adapter->dev, diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index 8c351f71f72f..de4a6affe72e 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -331,6 +331,14 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) dev_dbg(adapter->dev, "event: PORT RELEASE\n"); break; + case EVENT_EXT_SCAN_REPORT: + dev_dbg(adapter->dev, "event: EXT_SCAN Report\n"); + if (adapter->ext_scan) + ret = mwifiex_handle_event_ext_scan_report(priv, + adapter->event_skb->data); + + break; + case EVENT_WMM_STATUS_CHANGE: dev_dbg(adapter->dev, "event: WMM status changed\n"); ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_WMM_GET_STATUS, -- cgit From dbccc92b5d543d1ead727f1416af9e113a3ccc4a Mon Sep 17 00:00:00 2001 From: Aaron Durbin Date: Fri, 7 Feb 2014 16:25:50 -0800 Subject: mwifiex: balance dma map/unmap sizes Depending on the underlying DMA implementation its not possible to partially unmap DMA buffers. Moreover its not possible to understand the intent of passing 0 as the size to dma unmap. The intent of this driver is unmap the entire skb buffer. The only way to ensure that the size matches on unmap is to store both the dma address and the size in the skb ca field. Introduce a mwifiex_dma_mapping structure which tracks the dma address and the size. Additionally, provide a mwifiex_unmap_pci_memory() that utilizes the new structure. This also provide symmetry within the internal API. Signed-off-by: Aaron Durbin Reviewed-by: Paul Stewart Reviewed-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/pcie.c | 126 ++++++++++++++++-------------------- drivers/net/wireless/mwifiex/util.h | 20 +++++- 2 files changed, 72 insertions(+), 74 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 03688aa14e8a..4e1c6b268f99 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -39,20 +39,31 @@ static struct semaphore add_remove_card_sem; static int mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, - int size, int flags) + size_t size, int flags) { struct pcie_service_card *card = adapter->card; - dma_addr_t buf_pa; + struct mwifiex_dma_mapping mapping; - buf_pa = pci_map_single(card->dev, skb->data, size, flags); - if (pci_dma_mapping_error(card->dev, buf_pa)) { + mapping.addr = pci_map_single(card->dev, skb->data, size, flags); + if (pci_dma_mapping_error(card->dev, mapping.addr)) { dev_err(adapter->dev, "failed to map pci memory!\n"); return -1; } - memcpy(skb->cb, &buf_pa, sizeof(dma_addr_t)); + mapping.len = size; + memcpy(skb->cb, &mapping, sizeof(mapping)); return 0; } +static void mwifiex_unmap_pci_memory(struct mwifiex_adapter *adapter, + struct sk_buff *skb, int flags) +{ + struct pcie_service_card *card = adapter->card; + struct mwifiex_dma_mapping mapping; + + MWIFIEX_SKB_PACB(skb, &mapping); + pci_unmap_single(card->dev, mapping.addr, mapping.len, flags); +} + /* * This function reads sleep cookie and checks if FW is ready */ @@ -456,7 +467,7 @@ static int mwifiex_init_rxq_ring(struct mwifiex_adapter *adapter) PCI_DMA_FROMDEVICE)) return -1; - MWIFIEX_SKB_PACB(skb, &buf_pa); + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); dev_dbg(adapter->dev, "info: RX ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", @@ -513,7 +524,7 @@ static int mwifiex_pcie_init_evt_ring(struct mwifiex_adapter *adapter) PCI_DMA_FROMDEVICE)) return -1; - MWIFIEX_SKB_PACB(skb, &buf_pa); + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); dev_dbg(adapter->dev, "info: EVT ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", @@ -549,8 +560,8 @@ static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter) desc2 = card->txbd_ring[i]; if (card->tx_buf_list[i]) { skb = card->tx_buf_list[i]; - pci_unmap_single(card->dev, desc2->paddr, - skb->len, PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); dev_kfree_skb_any(skb); } memset(desc2, 0, sizeof(*desc2)); @@ -558,8 +569,8 @@ static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter) desc = card->txbd_ring[i]; if (card->tx_buf_list[i]) { skb = card->tx_buf_list[i]; - pci_unmap_single(card->dev, desc->paddr, - skb->len, PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); dev_kfree_skb_any(skb); } memset(desc, 0, sizeof(*desc)); @@ -587,8 +598,8 @@ static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter) desc2 = card->rxbd_ring[i]; if (card->rx_buf_list[i]) { skb = card->rx_buf_list[i]; - pci_unmap_single(card->dev, desc2->paddr, - skb->len, PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_FROMDEVICE); dev_kfree_skb_any(skb); } memset(desc2, 0, sizeof(*desc2)); @@ -596,8 +607,8 @@ static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter) desc = card->rxbd_ring[i]; if (card->rx_buf_list[i]) { skb = card->rx_buf_list[i]; - pci_unmap_single(card->dev, desc->paddr, - skb->len, PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_FROMDEVICE); dev_kfree_skb_any(skb); } memset(desc, 0, sizeof(*desc)); @@ -622,8 +633,8 @@ static void mwifiex_cleanup_evt_ring(struct mwifiex_adapter *adapter) desc = card->evtbd_ring[i]; if (card->evt_buf_list[i]) { skb = card->evt_buf_list[i]; - pci_unmap_single(card->dev, desc->paddr, MAX_EVENT_SIZE, - PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_FROMDEVICE); dev_kfree_skb_any(skb); } card->evt_buf_list[i] = NULL; @@ -861,7 +872,6 @@ static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter) static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) { struct pcie_service_card *card; - dma_addr_t buf_pa; if (!adapter) return 0; @@ -869,16 +879,14 @@ static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) card = adapter->card; if (card && card->cmdrsp_buf) { - MWIFIEX_SKB_PACB(card->cmdrsp_buf, &buf_pa); - pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, - PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, card->cmdrsp_buf, + PCI_DMA_FROMDEVICE); dev_kfree_skb_any(card->cmdrsp_buf); } if (card && card->cmd_buf) { - MWIFIEX_SKB_PACB(card->cmd_buf, &buf_pa); - pci_unmap_single(card->dev, buf_pa, card->cmd_buf->len, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, card->cmd_buf, + PCI_DMA_TODEVICE); } return 0; } @@ -956,7 +964,6 @@ static int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter) static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) { struct sk_buff *skb; - dma_addr_t buf_pa; u32 wrdoneidx, rdptr, num_tx_buffs, unmap_count = 0; struct mwifiex_pcie_buf_desc *desc; struct mwifiex_pfu_buf_desc *desc2; @@ -986,13 +993,13 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) reg->tx_start_ptr; skb = card->tx_buf_list[wrdoneidx]; + if (skb) { dev_dbg(adapter->dev, "SEND COMP: Detach skb %p at txbd_rdidx=%d\n", skb, wrdoneidx); - MWIFIEX_SKB_PACB(skb, &buf_pa); - pci_unmap_single(card->dev, buf_pa, skb->len, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); unmap_count++; @@ -1082,12 +1089,12 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, tmp = (__le16 *)&payload[2]; *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA); - if (mwifiex_map_pci_memory(adapter, skb, skb->len , + if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE)) return -1; wrindx = (card->txbd_wrptr & reg->tx_mask) >> reg->tx_start_ptr; - MWIFIEX_SKB_PACB(skb, &buf_pa); + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); card->tx_buf_list[wrindx] = skb; if (reg->pfu_enabled) { @@ -1162,8 +1169,7 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, return -EINPROGRESS; done_unmap: - MWIFIEX_SKB_PACB(skb, &buf_pa); - pci_unmap_single(card->dev, buf_pa, skb->len, PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); card->tx_buf_list[wrindx] = NULL; if (reg->pfu_enabled) memset(desc2, 0, sizeof(*desc2)); @@ -1211,9 +1217,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) rd_index = card->rxbd_rdptr & reg->rx_mask; skb_data = card->rx_buf_list[rd_index]; - MWIFIEX_SKB_PACB(skb_data, &buf_pa); - pci_unmap_single(card->dev, buf_pa, MWIFIEX_RX_DATA_BUF_SIZE, - PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, skb_data, PCI_DMA_FROMDEVICE); card->rx_buf_list[rd_index] = NULL; /* Get data length from interface header - @@ -1240,7 +1244,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) PCI_DMA_FROMDEVICE)) return -1; - MWIFIEX_SKB_PACB(skb_tmp, &buf_pa); + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb_tmp); dev_dbg(adapter->dev, "RECV DATA: Attach new sk_buff %p at rxbd_rdidx=%d\n", @@ -1316,7 +1320,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE)) return -1; - MWIFIEX_SKB_PACB(skb, &buf_pa); + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); /* Write the lower 32bits of the physical address to low command * address scratch register @@ -1325,8 +1329,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) dev_err(adapter->dev, "%s: failed to write download command to boot code.\n", __func__); - pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); return -1; } @@ -1338,8 +1341,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) dev_err(adapter->dev, "%s: failed to write download command to boot code.\n", __func__); - pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); return -1; } @@ -1348,8 +1350,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) dev_err(adapter->dev, "%s: failed to write command len to cmd_size scratch reg\n", __func__); - pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); return -1; } @@ -1358,8 +1359,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) CPU_INTR_DOOR_BELL)) { dev_err(adapter->dev, "%s: failed to assert door-bell intr\n", __func__); - pci_unmap_single(card->dev, buf_pa, - MWIFIEX_UPLD_SIZE, PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); return -1; } @@ -1433,7 +1433,7 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) */ if (card->cmdrsp_buf) { - MWIFIEX_SKB_PACB(card->cmdrsp_buf, &cmdrsp_buf_pa); + cmdrsp_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmdrsp_buf); /* Write the lower 32bits of the cmdrsp buffer physical address */ if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, @@ -1454,7 +1454,7 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) } } - MWIFIEX_SKB_PACB(card->cmd_buf, &cmd_buf_pa); + cmd_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmd_buf); /* Write the lower 32bits of the physical address to reg->cmd_addr_lo */ if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, (u32)cmd_buf_pa)) { @@ -1508,13 +1508,10 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) int count = 0; u16 rx_len; __le16 pkt_len; - dma_addr_t buf_pa; dev_dbg(adapter->dev, "info: Rx CMD Response\n"); - MWIFIEX_SKB_PACB(skb, &buf_pa); - pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, - PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_FROMDEVICE); pkt_len = *((__le16 *)skb->data); rx_len = le16_to_cpu(pkt_len); @@ -1538,8 +1535,6 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, PCI_DMA_FROMDEVICE)) return -1; - - MWIFIEX_SKB_PACB(skb, &buf_pa); } else if (mwifiex_pcie_ok_to_access_hw(adapter)) { adapter->curr_cmd->resp_skb = skb; adapter->cmd_resp_received = true; @@ -1574,7 +1569,6 @@ static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter, struct sk_buff *skb) { struct pcie_service_card *card = adapter->card; - dma_addr_t buf_pa; struct sk_buff *skb_tmp; if (skb) { @@ -1587,9 +1581,7 @@ static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter, skb_tmp = card->cmd_buf; if (skb_tmp) { - MWIFIEX_SKB_PACB(skb_tmp, &buf_pa); - pci_unmap_single(card->dev, buf_pa, skb_tmp->len, - PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, skb_tmp, PCI_DMA_FROMDEVICE); card->cmd_buf = NULL; } @@ -1605,7 +1597,6 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter) const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; u32 wrptr, event; - dma_addr_t buf_pa; struct mwifiex_evt_buf_desc *desc; if (!mwifiex_pcie_ok_to_access_hw(adapter)) @@ -1641,9 +1632,7 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter) dev_dbg(adapter->dev, "info: Read Index: %d\n", rdptr); skb_cmd = card->evt_buf_list[rdptr]; - MWIFIEX_SKB_PACB(skb_cmd, &buf_pa); - pci_unmap_single(card->dev, buf_pa, MAX_EVENT_SIZE, - PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, skb_cmd, PCI_DMA_FROMDEVICE); /* Take the pointer and set it to event pointer in adapter and will return back after event handling callback */ @@ -1689,7 +1678,6 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, int ret = 0; u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; u32 wrptr; - dma_addr_t buf_pa; struct mwifiex_evt_buf_desc *desc; if (!skb) @@ -1714,11 +1702,9 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, MAX_EVENT_SIZE, PCI_DMA_FROMDEVICE)) return -1; - MWIFIEX_SKB_PACB(skb, &buf_pa); card->evt_buf_list[rdptr] = skb; - MWIFIEX_SKB_PACB(skb, &buf_pa); desc = card->evtbd_ring[rdptr]; - desc->paddr = buf_pa; + desc->paddr = MWIFIEX_SKB_DMA_ADDR(skb); desc->len = (u16)skb->len; desc->flags = 0; skb = NULL; @@ -1768,7 +1754,6 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, struct sk_buff *skb; u32 txlen, tx_blocks = 0, tries, len; u32 block_retry_cnt = 0; - dma_addr_t buf_pa; struct pcie_service_card *card = adapter->card; const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; @@ -1866,8 +1851,6 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, goto done; } - MWIFIEX_SKB_PACB(skb, &buf_pa); - /* Wait for the command done interrupt */ do { if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS, @@ -1875,16 +1858,15 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, dev_err(adapter->dev, "%s: Failed to read " "interrupt status during fw dnld.\n", __func__); - pci_unmap_single(card->dev, buf_pa, skb->len, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); ret = -1; goto done; } } while ((ireg_intr & CPU_INTR_DOOR_BELL) == CPU_INTR_DOOR_BELL); - pci_unmap_single(card->dev, buf_pa, skb->len, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); offset += txlen; } while (true); diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h index cb2d0582bd36..ddae57021397 100644 --- a/drivers/net/wireless/mwifiex/util.h +++ b/drivers/net/wireless/mwifiex/util.h @@ -30,8 +30,24 @@ static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb) return (struct mwifiex_txinfo *)(skb->cb + sizeof(dma_addr_t)); } -static inline void MWIFIEX_SKB_PACB(struct sk_buff *skb, dma_addr_t *buf_pa) +struct mwifiex_dma_mapping { + dma_addr_t addr; + size_t len; +}; + +static inline void MWIFIEX_SKB_PACB(struct sk_buff *skb, + struct mwifiex_dma_mapping *mapping) { - memcpy(buf_pa, skb->cb, sizeof(dma_addr_t)); + memcpy(mapping, skb->cb, sizeof(*mapping)); } + +static inline dma_addr_t MWIFIEX_SKB_DMA_ADDR(struct sk_buff *skb) +{ + struct mwifiex_dma_mapping mapping; + + MWIFIEX_SKB_PACB(skb, &mapping); + + return mapping.addr; +} + #endif /* !_MWIFIEX_UTIL_H_ */ -- cgit From 189b3299fe46c3d3f7555e1c80e8e8691e71faf1 Mon Sep 17 00:00:00 2001 From: Aaron Durbin Date: Fri, 7 Feb 2014 16:25:51 -0800 Subject: mwifiex: don't leak DMA command skbuffs The current mwifiex pcie driver assumed that it would get its cmdrsp_complete() callback called before another command was sent to unmap the command's skbuff. However, that is not true. The mwifiex_check_ps_cond() will send a sleep command to the card without having adapter->curr_cmd set. Within the workqueue's state machine the adapter's state would be set to allow commands (curr_cmd = NULL && cmd_sent = false) after having receieved the response from the sleep command. The card->cmd_buf would then be overridden with the new command but the first command's skbuff was not unmapped. This leaks mapped skbuffs when a bounce buffer is employed. To rectify this unmap the card->cmd_buf when the response is received from the card instead of waiting for the cmdrsp_complete() callback. Signed-off-by: Aaron Durbin Reviewed-by: Paul Stewart Reviewed-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/pcie.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 4e1c6b268f99..d11d4acf0890 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -1513,6 +1513,13 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_FROMDEVICE); + /* Unmap the command as a response has been received. */ + if (card->cmd_buf) { + mwifiex_unmap_pci_memory(adapter, card->cmd_buf, + PCI_DMA_TODEVICE); + card->cmd_buf = NULL; + } + pkt_len = *((__le16 *)skb->data); rx_len = le16_to_cpu(pkt_len); skb_trim(skb, rx_len); @@ -1569,7 +1576,6 @@ static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter, struct sk_buff *skb) { struct pcie_service_card *card = adapter->card; - struct sk_buff *skb_tmp; if (skb) { card->cmdrsp_buf = skb; @@ -1579,12 +1585,6 @@ static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter, return -1; } - skb_tmp = card->cmd_buf; - if (skb_tmp) { - mwifiex_unmap_pci_memory(adapter, skb_tmp, PCI_DMA_FROMDEVICE); - card->cmd_buf = NULL; - } - return 0; } -- cgit From 645097cea620cb94d620360226508c6074ec278b Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 7 Feb 2014 16:27:26 -0800 Subject: mwifiex: update beamforming capability field for HT This patch makes sure that beamforming capability field in ht capability info gets filled if hardware supports the feature. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11n.c | 4 ++++ drivers/net/wireless/mwifiex/fw.h | 3 +++ 2 files changed, 7 insertions(+) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index 6261f8c53d44..822e622fa323 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -64,6 +64,10 @@ mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, ht_cap->ht_cap.cap_info = cpu_to_le16(sband->ht_cap.cap); ht_cap->ht_cap.extended_ht_cap_info = cpu_to_le16(ht_ext_cap); + + if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap)) + ht_cap->ht_cap.tx_BF_cap_info = + cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP); } /* diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index ed41af17b213..cad9f86600c2 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -186,6 +186,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \ IEEE80211_HT_CAP_SM_PS) +#define MWIFIEX_DEF_11N_TX_BF_CAP 0x09E1E008 + #define MWIFIEX_DEF_AMPDU IEEE80211_HT_AMPDU_PARM_FACTOR /* dev_cap bitmap @@ -209,6 +211,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29)) #define ISENABLED_40MHZ_INTOLERANT(Dot11nDevCap) (Dot11nDevCap & BIT(8)) #define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22)) +#define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30)) /* httxcfg bitmap * 0 reserved -- cgit From 79d9a54cf0e7c77dd6eada44c1461ad2e7da83b8 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 7 Feb 2014 16:27:27 -0800 Subject: mwifiex: advertise correct beamforming information for VHT Currently MU/SU beamformer and MU beamformee features are not supported. Hence this patch modifies VHT capability information accordingly. Number of sounding dimensions should be zero in this case. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cmdevt.c | 6 ++++-- drivers/net/wireless/mwifiex/fw.h | 9 +++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 556cb2c74e30..7711b11a9812 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -1499,8 +1499,10 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, /* Copy 11AC cap */ adapter->hw_dot_11ac_dev_cap = le32_to_cpu(hw_spec->dot_11ac_dev_cap); - adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap; - adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap; + adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap + & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK; + adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap + & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK; /* Copy 11AC mcs */ adapter->hw_dot_11ac_mcs_support = diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index cad9f86600c2..4139c6f4a52b 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -243,6 +243,15 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define GET_DEVTXMCSMAP(dev_mcs_map) (dev_mcs_map >> 16) #define GET_DEVRXMCSMAP(dev_mcs_map) (dev_mcs_map & 0xFFFF) +/* Clear SU Beanformer, MU beanformer, MU beanformee and + * sounding dimensions bits + */ +#define MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK \ + (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | \ + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | \ + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | \ + IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK) + #define MOD_CLASS_HR_DSSS 0x03 #define MOD_CLASS_OFDM 0x07 #define MOD_CLASS_HT 0x08 -- cgit From eac4322729aebf01ae231d3b3f63aae73d469a57 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:27:28 -0800 Subject: mwifiex: handle AMPDU supported check for AP interface This patch fixes a bug where we were checking for AP's AMPDU param setting even when transmitting traffic to associated station. Patch adds provision to pass additional parameter ra_list pointer to function which checks if AMPDU is allowed. If current BSS type is AP, we check station's AMPDU params else we check AP's AMPDU params. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11n.h | 26 ++++++++++++++++++++------ drivers/net/wireless/mwifiex/wmm.c | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h index 375db01442bf..353032d55ff3 100644 --- a/drivers/net/wireless/mwifiex/11n.h +++ b/drivers/net/wireless/mwifiex/11n.h @@ -64,14 +64,28 @@ int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl); void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra); -/* - * This function checks whether AMPDU is allowed or not for a particular TID. - */ static inline u8 -mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, int tid) +mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, int tid) { - return ((priv->aggr_prio_tbl[tid].ampdu_ap != BA_STREAM_NOT_ALLOWED) - ? true : false); + struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ptr->ra); + + if (unlikely(!node)) + return false; + + return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false; +} + +/* This function checks whether AMPDU is allowed or not for a particular TID. */ +static inline u8 +mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, int tid) +{ + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) + return mwifiex_is_station_ampdu_allowed(priv, ptr, tid); + else + return (priv->aggr_prio_tbl[tid].ampdu_ap != + BA_STREAM_NOT_ALLOWED) ? true : false; } /* diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 13eaeed03898..ee447893fe57 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -1226,7 +1226,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) /* ra_list_spinlock has been freed in mwifiex_send_single_packet() */ } else { - if (mwifiex_is_ampdu_allowed(priv, tid) && + if (mwifiex_is_ampdu_allowed(priv, ptr, tid) && ptr->ba_pkt_count > ptr->ba_packet_thr) { if (mwifiex_space_avail_for_new_ba_stream(adapter)) { mwifiex_create_ba_tbl(priv, ptr->ra, tid, -- cgit From 41a24a29142dd0352de965c40b840a90d6e55f6c Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:27:29 -0800 Subject: mwifiex: make tos_to_tid_inv part of mwifiex_private structure tos_to_tid_inv values are needed even during TDLS restore operations. Currently tos_to_tid_inv is part of wmm.c and is declared static. Make it part of private structure so that it can be used in other files as well. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/init.c | 1 + drivers/net/wireless/mwifiex/main.h | 1 + drivers/net/wireless/mwifiex/wmm.c | 34 +++++++++++----------------------- drivers/net/wireless/mwifiex/wmm.h | 15 +++++++++++++++ 4 files changed, 28 insertions(+), 23 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 308c56fd32eb..dead65960d34 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -137,6 +137,7 @@ int mwifiex_init_priv(struct mwifiex_private *priv) priv->csa_expire_time = 0; priv->del_list_idx = 0; priv->hs2_enabled = false; + memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID); return mwifiex_add_bss_prio_tbl(priv); } diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index c473f54ba738..39f661a53df5 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -461,6 +461,7 @@ struct mwifiex_private { struct mwifiex_tx_aggr aggr_prio_tbl[MAX_NUM_TID]; struct mwifiex_add_ba_param add_ba_param; u16 rx_seq[MAX_NUM_TID]; + u8 tos_to_tid_inv[MAX_NUM_TID]; struct list_head rx_reorder_tbl_ptr; /* spin lock for rx_reorder_tbl_ptr queue */ spinlock_t rx_reorder_tbl_lock; diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index ee447893fe57..2999c3b1e112 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -64,21 +64,6 @@ static u8 tos_to_tid[] = { 0x07 /* 1 1 1 AC_VO */ }; -/* - * This table inverses the tos_to_tid operation to get a priority - * which is in sequential order, and can be compared. - * Use this to compare the priority of two different TIDs. - */ -static u8 tos_to_tid_inv[] = { - 0x02, /* from tos_to_tid[2] = 0 */ - 0x00, /* from tos_to_tid[0] = 1 */ - 0x01, /* from tos_to_tid[1] = 2 */ - 0x03, - 0x04, - 0x05, - 0x06, - 0x07}; - static u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} }; /* @@ -213,8 +198,9 @@ static void mwifiex_wmm_default_queue_priorities(struct mwifiex_private *priv) * This function map ACs to TIDs. */ static void -mwifiex_wmm_queue_priorities_tid(struct mwifiex_wmm_desc *wmm) +mwifiex_wmm_queue_priorities_tid(struct mwifiex_private *priv) { + struct mwifiex_wmm_desc *wmm = &priv->wmm; u8 *queue_priority = wmm->queue_priority; int i; @@ -224,7 +210,7 @@ mwifiex_wmm_queue_priorities_tid(struct mwifiex_wmm_desc *wmm) } for (i = 0; i < MAX_NUM_TID; ++i) - tos_to_tid_inv[tos_to_tid[i]] = (u8)i; + priv->tos_to_tid_inv[tos_to_tid[i]] = (u8)i; atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID); } @@ -285,7 +271,7 @@ mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, } } - mwifiex_wmm_queue_priorities_tid(&priv->wmm); + mwifiex_wmm_queue_priorities_tid(priv); } /* @@ -421,9 +407,11 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter) continue; for (i = 0; i < MAX_NUM_TID; ++i) { - priv->aggr_prio_tbl[i].amsdu = tos_to_tid_inv[i]; - priv->aggr_prio_tbl[i].ampdu_ap = tos_to_tid_inv[i]; - priv->aggr_prio_tbl[i].ampdu_user = tos_to_tid_inv[i]; + priv->aggr_prio_tbl[i].amsdu = priv->tos_to_tid_inv[i]; + priv->aggr_prio_tbl[i].ampdu_ap = + priv->tos_to_tid_inv[i]; + priv->aggr_prio_tbl[i].ampdu_user = + priv->tos_to_tid_inv[i]; } priv->aggr_prio_tbl[6].amsdu @@ -683,9 +671,9 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, ra_list->total_pkt_count++; if (atomic_read(&priv->wmm.highest_queued_prio) < - tos_to_tid_inv[tid_down]) + priv->tos_to_tid_inv[tid_down]) atomic_set(&priv->wmm.highest_queued_prio, - tos_to_tid_inv[tid_down]); + priv->tos_to_tid_inv[tid_down]); atomic_inc(&priv->wmm.tx_pkts_queued); diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h index 0f129d498fb1..d4e607395da7 100644 --- a/drivers/net/wireless/mwifiex/wmm.h +++ b/drivers/net/wireless/mwifiex/wmm.h @@ -33,6 +33,21 @@ enum ieee_types_wmm_ecw_bitmasks { static const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; +/* + * This table inverses the tos_to_tid operation to get a priority + * which is in sequential order, and can be compared. + * Use this to compare the priority of two different TIDs. + */ +static const u8 tos_to_tid_inv[] = { + 0x02, /* from tos_to_tid[2] = 0 */ + 0x00, /* from tos_to_tid[0] = 1 */ + 0x01, /* from tos_to_tid[1] = 2 */ + 0x03, + 0x04, + 0x05, + 0x06, + 0x07}; + /* * This function retrieves the TID of the given RA list. */ -- cgit From 4bcf93d3a4d6f145e9d871afc4797018d33c4bb1 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:27:30 -0800 Subject: mwifiex: move station list functions to common code These functions are now needed by TDLS while managing station list. Move them from AP related file to utility file. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/main.h | 10 +++ drivers/net/wireless/mwifiex/uap_event.c | 118 ------------------------------- drivers/net/wireless/mwifiex/util.c | 114 +++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 118 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 39f661a53df5..2114475f03c3 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -1167,6 +1167,16 @@ void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv); extern const struct ethtool_ops mwifiex_ethtool_ops; +void mwifiex_del_all_sta_list(struct mwifiex_private *priv); +void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac); +void +mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, + int ies_len, struct mwifiex_sta_node *node); +struct mwifiex_sta_node * +mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac); +struct mwifiex_sta_node * +mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac); + #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); void mwifiex_debugfs_remove(void); diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c index 718066577c6c..2d47ba70225c 100644 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ b/drivers/net/wireless/mwifiex/uap_event.c @@ -21,126 +21,8 @@ #include "main.h" #include "11n.h" -/* - * This function will return the pointer to station entry in station list - * table which matches specified mac address. - * This function should be called after acquiring RA list spinlock. - * NULL is returned if station entry is not found in associated STA list. - */ -struct mwifiex_sta_node * -mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac) -{ - struct mwifiex_sta_node *node; - - if (!mac) - return NULL; - - list_for_each_entry(node, &priv->sta_list, list) { - if (!memcmp(node->mac_addr, mac, ETH_ALEN)) - return node; - } - return NULL; -} - -/* - * This function will add a sta_node entry to associated station list - * table with the given mac address. - * If entry exist already, existing entry is returned. - * If received mac address is NULL, NULL is returned. - */ -static struct mwifiex_sta_node * -mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac) -{ - struct mwifiex_sta_node *node; - unsigned long flags; - if (!mac) - return NULL; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - node = mwifiex_get_sta_entry(priv, mac); - if (node) - goto done; - - node = kzalloc(sizeof(struct mwifiex_sta_node), GFP_ATOMIC); - if (!node) - goto done; - - memcpy(node->mac_addr, mac, ETH_ALEN); - list_add_tail(&node->list, &priv->sta_list); - -done: - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - return node; -} - -/* - * This function will search for HT IE in association request IEs - * and set station HT parameters accordingly. - */ -static void -mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, - int ies_len, struct mwifiex_sta_node *node) -{ - const struct ieee80211_ht_cap *ht_cap; - - if (!ies) - return; - - ht_cap = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len); - if (ht_cap) { - node->is_11n_enabled = 1; - node->max_amsdu = le16_to_cpu(ht_cap->cap_info) & - IEEE80211_HT_CAP_MAX_AMSDU ? - MWIFIEX_TX_DATA_BUF_SIZE_8K : - MWIFIEX_TX_DATA_BUF_SIZE_4K; - } else { - node->is_11n_enabled = 0; - } - - return; -} - -/* - * This function will delete a station entry from station list - */ -static void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac) -{ - struct mwifiex_sta_node *node; - unsigned long flags; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - - node = mwifiex_get_sta_entry(priv, mac); - if (node) { - list_del(&node->list); - kfree(node); - } - - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - return; -} - -/* - * This function will delete all stations from associated station list. - */ -static void mwifiex_del_all_sta_list(struct mwifiex_private *priv) -{ - struct mwifiex_sta_node *node, *tmp; - unsigned long flags; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - - list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { - list_del(&node->list); - kfree(node); - } - - INIT_LIST_HEAD(&priv->sta_list); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - return; -} /* * This function handles AP interface specific events generated by firmware. diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index 9b82e225880c..8d37bfc578bd 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -252,3 +252,117 @@ int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, return 0; } + +/* This function will return the pointer to station entry in station list + * table which matches specified mac address. + * This function should be called after acquiring RA list spinlock. + * NULL is returned if station entry is not found in associated STA list. + */ +struct mwifiex_sta_node * +mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac) +{ + struct mwifiex_sta_node *node; + + if (!mac) + return NULL; + + list_for_each_entry(node, &priv->sta_list, list) { + if (!memcmp(node->mac_addr, mac, ETH_ALEN)) + return node; + } + + return NULL; +} + +/* This function will add a sta_node entry to associated station list + * table with the given mac address. + * If entry exist already, existing entry is returned. + * If received mac address is NULL, NULL is returned. + */ +struct mwifiex_sta_node * +mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac) +{ + struct mwifiex_sta_node *node; + unsigned long flags; + + if (!mac) + return NULL; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + node = mwifiex_get_sta_entry(priv, mac); + if (node) + goto done; + + node = kzalloc(sizeof(*node), GFP_ATOMIC); + if (!node) + goto done; + + memcpy(node->mac_addr, mac, ETH_ALEN); + list_add_tail(&node->list, &priv->sta_list); + +done: + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + return node; +} + +/* This function will search for HT IE in association request IEs + * and set station HT parameters accordingly. + */ +void +mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, + int ies_len, struct mwifiex_sta_node *node) +{ + const struct ieee80211_ht_cap *ht_cap; + + if (!ies) + return; + + ht_cap = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len); + if (ht_cap) { + node->is_11n_enabled = 1; + node->max_amsdu = le16_to_cpu(ht_cap->cap_info) & + IEEE80211_HT_CAP_MAX_AMSDU ? + MWIFIEX_TX_DATA_BUF_SIZE_8K : + MWIFIEX_TX_DATA_BUF_SIZE_4K; + } else { + node->is_11n_enabled = 0; + } + + return; +} + +/* This function will delete a station entry from station list */ +void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac) +{ + struct mwifiex_sta_node *node; + unsigned long flags; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + + node = mwifiex_get_sta_entry(priv, mac); + if (node) { + list_del(&node->list); + kfree(node); + } + + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + return; +} + +/* This function will delete all stations from associated station list. */ +void mwifiex_del_all_sta_list(struct mwifiex_private *priv) +{ + struct mwifiex_sta_node *node, *tmp; + unsigned long flags; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + + list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { + list_del(&node->list); + kfree(node); + } + + INIT_LIST_HEAD(&priv->sta_list); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + return; +} -- cgit From 341b88007275121e9d85e3e38fc6b7546a4e7e9d Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 7 Feb 2014 16:27:31 -0800 Subject: mwifiex: cleanup in mwifiex_fill_cap_info() Pass 'struct ieee80211_ht_cap' pointer to mwifiex_fill_cap_info() instead of 'struct mwifiex_ie_types_htcap' pointer, because the routine internally uses the later one. This patch also adds WARN_ON_ONCE check for NULL band. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11n.c | 29 +++++++++++++++++------------ drivers/net/wireless/mwifiex/11n.h | 4 ++-- drivers/net/wireless/mwifiex/join.c | 2 +- drivers/net/wireless/mwifiex/scan.c | 2 +- 4 files changed, 21 insertions(+), 16 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index 822e622fa323..616b1d6221b9 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -34,22 +34,26 @@ * * RD responder bit to set to clear in the extended capability header. */ -void -mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, - struct mwifiex_ie_types_htcap *ht_cap) +int mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, + struct ieee80211_ht_cap *ht_cap) { - uint16_t ht_ext_cap = le16_to_cpu(ht_cap->ht_cap.extended_ht_cap_info); + uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info); struct ieee80211_supported_band *sband = priv->wdev->wiphy->bands[radio_type]; - ht_cap->ht_cap.ampdu_params_info = + if (WARN_ON_ONCE(!sband)) { + dev_err(priv->adapter->dev, "Invalid radio type!\n"); + return -EINVAL; + } + + ht_cap->ampdu_params_info = (sband->ht_cap.ampdu_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) | ((sband->ht_cap.ampdu_density << IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) & IEEE80211_HT_AMPDU_PARM_DENSITY); - memcpy((u8 *) &ht_cap->ht_cap.mcs, &sband->ht_cap.mcs, + memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); if (priv->bss_mode == NL80211_IFTYPE_STATION || @@ -57,17 +61,18 @@ mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, (priv->adapter->sec_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_NONE))) /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ - SETHT_MCS32(ht_cap->ht_cap.mcs.rx_mask); + SETHT_MCS32(ht_cap->mcs.rx_mask); /* Clear RD responder bit */ ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER; - ht_cap->ht_cap.cap_info = cpu_to_le16(sband->ht_cap.cap); - ht_cap->ht_cap.extended_ht_cap_info = cpu_to_le16(ht_ext_cap); + ht_cap->cap_info = cpu_to_le16(sband->ht_cap.cap); + ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap); if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap)) - ht_cap->ht_cap.tx_BF_cap_info = - cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP); + ht_cap->tx_BF_cap_info = cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP); + + return 0; } /* @@ -316,7 +321,7 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, sizeof(struct ieee_types_header), le16_to_cpu(ht_cap->header.len)); - mwifiex_fill_cap_info(priv, radio_type, ht_cap); + mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); *buffer += sizeof(struct mwifiex_ie_types_htcap); ret_len += sizeof(struct mwifiex_ie_types_htcap); diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h index 353032d55ff3..fde39fedf0c0 100644 --- a/drivers/net/wireless/mwifiex/11n.h +++ b/drivers/net/wireless/mwifiex/11n.h @@ -34,8 +34,8 @@ int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv, int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc, u8 **buffer); -void mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type, - struct mwifiex_ie_types_htcap *); +int mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type, + struct ieee80211_ht_cap *); int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv, u16 action, int *htcap_cfg); void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index d3934c6dbd8a..34472ea53841 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -982,7 +982,7 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, cpu_to_le16(sizeof(struct ieee80211_ht_cap)); radio_type = mwifiex_band_to_radio_type( priv->adapter->config_bands); - mwifiex_fill_cap_info(priv, radio_type, ht_cap); + mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); if (adapter->sec_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_NONE) { diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index c548a7d4877d..92adbb1ebabc 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -957,7 +957,7 @@ mwifiex_config_scan(struct mwifiex_private *priv, cpu_to_le16(sizeof(struct ieee80211_ht_cap)); radio_type = mwifiex_band_to_radio_type(priv->adapter->config_bands); - mwifiex_fill_cap_info(priv, radio_type, ht_cap); + mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); tlv_pos += sizeof(struct mwifiex_ie_types_htcap); } -- cgit From b23bce296568011b76c27103032dea5a90291d8a Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:27:32 -0800 Subject: mwifiex: add tdls_mgmt handler support This patch adds support for TDLS management frames transmit handler. mwifiex driver supports TDLS with external support, i.e. expects user space application to form TDLS frames. Same is advertised to cfg80211 during registration. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: Amitkumar Karwar Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/Makefile | 1 + drivers/net/wireless/mwifiex/cfg80211.c | 81 ++++++ drivers/net/wireless/mwifiex/decl.h | 1 + drivers/net/wireless/mwifiex/fw.h | 2 + drivers/net/wireless/mwifiex/ioctl.h | 4 + drivers/net/wireless/mwifiex/main.h | 18 ++ drivers/net/wireless/mwifiex/sta_tx.c | 3 + drivers/net/wireless/mwifiex/tdls.c | 423 ++++++++++++++++++++++++++++++++ 8 files changed, 533 insertions(+) create mode 100644 drivers/net/wireless/mwifiex/tdls.c (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile index a42a506fd32b..2aa208ffbe23 100644 --- a/drivers/net/wireless/mwifiex/Makefile +++ b/drivers/net/wireless/mwifiex/Makefile @@ -41,6 +41,7 @@ mwifiex-y += uap_txrx.o mwifiex-y += cfg80211.o mwifiex-y += ethtool.o mwifiex-y += 11h.o +mwifiex-y += tdls.o mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_MWIFIEX) += mwifiex.o diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index c6606288c61e..2968af273ef9 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2594,6 +2594,81 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy, HostCmd_ACT_GEN_SET, 0, &coalesce_cfg); } +/* cfg80211 ops handler for tdls_mgmt. + * Function prepares TDLS action frame packets and forwards them to FW + */ +static int +mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + int ret; + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return -ENOTSUPP; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + dev_dbg(priv->adapter->dev, + "Send TDLS Setup Request to %pM status_code=%d\n", peer, + status_code); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_SETUP_RESPONSE: + dev_dbg(priv->adapter->dev, + "Send TDLS Setup Response to %pM status_code=%d\n", + peer, status_code); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_SETUP_CONFIRM: + dev_dbg(priv->adapter->dev, + "Send TDLS Confirm to %pM status_code=%d\n", peer, + status_code); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_TEARDOWN: + dev_dbg(priv->adapter->dev, "Send TDLS Tear down to %pM\n", + peer); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_DISCOVERY_REQUEST: + dev_dbg(priv->adapter->dev, + "Send TDLS Discovery Request to %pM\n", peer); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + dev_dbg(priv->adapter->dev, + "Send TDLS Discovery Response to %pM\n", peer); + ret = mwifiex_send_tdls_action_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + default: + dev_warn(priv->adapter->dev, + "Unknown TDLS mgmt/action frame %pM\n", peer); + ret = -EINVAL; + break; + } + + return ret; +} + /* station cfg80211 operations */ static struct cfg80211_ops mwifiex_cfg80211_ops = { .add_virtual_intf = mwifiex_add_virtual_intf, @@ -2629,6 +2704,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { .set_wakeup = mwifiex_cfg80211_set_wakeup, #endif .set_coalesce = mwifiex_cfg80211_set_coalesce, + .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt, }; #ifdef CONFIG_PM @@ -2714,6 +2790,11 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + + if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | + WIPHY_FLAG_TDLS_EXTERNAL_SETUP; + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | REGULATORY_STRICT_REG; diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index 3a21bd03d6db..c709f1c25b97 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -75,6 +75,7 @@ #define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0) #define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1) +#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2) #define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024 #define MWIFIEX_BRIDGED_PKTS_THR_LOW 128 diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 4139c6f4a52b..416518a939b2 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -181,6 +181,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192 #define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11)) +#define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14)) #define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \ @@ -497,6 +498,7 @@ struct mwifiex_ie_types_data { #define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01 #define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08 +#define MWIFIEX_TXPD_FLAGS_TDLS_PACKET 0x10 struct txpd { u8 bss_type; diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index 48f15906515d..fe122742b010 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -85,6 +85,10 @@ struct wep_key { #define BAND_CONFIG_A 0x01 #define MWIFIEX_SUPPORTED_RATES 14 #define MWIFIEX_SUPPORTED_RATES_EXT 32 +#define MWIFIEX_TDLS_SUPPORTED_RATES 8 +#define MWIFIEX_TDLS_DEF_QOS_CAPAB 0xf +#define MWIFIEX_PRIO_BK 2 +#define MWIFIEX_PRIO_VI 5 struct mwifiex_uap_bss_param { u8 channel; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 2114475f03c3..54197847d494 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -262,6 +262,16 @@ struct ieee_types_generic { u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)]; } __packed; +struct ieee_types_bss_co_2040 { + struct ieee_types_header ieee_hdr; + u8 bss_2040co; +} __packed; + +struct ieee_types_extcap { + struct ieee_types_header ieee_hdr; + u8 ext_capab[8]; +} __packed; + struct mwifiex_bssdescriptor { u8 mac_address[ETH_ALEN]; struct cfg80211_ssid ssid; @@ -1176,6 +1186,14 @@ struct mwifiex_sta_node * mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac); struct mwifiex_sta_node * mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac); +int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len); +int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len); #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c index 354d64c9606f..1236a5de7bca 100644 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ b/drivers/net/wireless/mwifiex/sta_tx.c @@ -95,6 +95,9 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, } } + if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) + local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; + /* Offset of actual data */ pkt_offset = sizeof(struct txpd) + pad; if (pkt_type == PKT_TYPE_MGMT) { diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c new file mode 100644 index 000000000000..73cd444a6d43 --- /dev/null +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -0,0 +1,423 @@ +/* Marvell Wireless LAN device driver: TDLS handling + * + * Copyright (C) 2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available on the worldwide web at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "main.h" + +/* This function appends rate TLV to scan config command. */ +static int +mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + u8 rates[MWIFIEX_SUPPORTED_RATES], *pos; + u16 rates_size, supp_rates_size, ext_rates_size; + + memset(rates, 0, sizeof(rates)); + rates_size = mwifiex_get_supported_rates(priv, rates); + + supp_rates_size = min_t(u16, rates_size, MWIFIEX_TDLS_SUPPORTED_RATES); + + if (skb_tailroom(skb) < rates_size + 4) { + dev_err(priv->adapter->dev, + "Insuffient space while adding rates\n"); + return -ENOMEM; + } + + pos = skb_put(skb, supp_rates_size + 2); + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = supp_rates_size; + memcpy(pos, rates, supp_rates_size); + + if (rates_size > MWIFIEX_TDLS_SUPPORTED_RATES) { + ext_rates_size = rates_size - MWIFIEX_TDLS_SUPPORTED_RATES; + pos = skb_put(skb, ext_rates_size + 2); + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = ext_rates_size; + memcpy(pos, rates + MWIFIEX_TDLS_SUPPORTED_RATES, + ext_rates_size); + } + + return 0; +} + +static void mwifiex_tdls_add_ext_capab(struct sk_buff *skb) +{ + struct ieee_types_extcap *extcap; + + extcap = (void *)skb_put(skb, sizeof(struct ieee_types_extcap)); + extcap->ieee_hdr.element_id = WLAN_EID_EXT_CAPABILITY; + extcap->ieee_hdr.len = 8; + memset(extcap->ext_capab, 0, 8); + extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED; +} + +static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb) +{ + u8 *pos = (void *)skb_put(skb, 3); + + *pos++ = WLAN_EID_QOS_CAPA; + *pos++ = 1; + *pos++ = MWIFIEX_TDLS_DEF_QOS_CAPAB; +} + +static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_tdls_data *tf; + int ret; + u16 capab; + struct ieee80211_ht_cap *ht_cap; + u8 radio, *pos; + + capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; + + tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); + memcpy(tf->da, peer, ETH_ALEN); + memcpy(tf->sa, priv->curr_addr, ETH_ALEN); + tf->ether_type = cpu_to_be16(ETH_P_TDLS); + tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_REQUEST; + skb_put(skb, sizeof(tf->u.setup_req)); + tf->u.setup_req.dialog_token = dialog_token; + tf->u.setup_req.capability = cpu_to_le16(capab); + ret = mwifiex_tdls_append_rates_ie(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + ht_cap = (void *)pos; + radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + ret = mwifiex_fill_cap_info(priv, radio, ht_cap); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + mwifiex_tdls_add_ext_capab(skb); + mwifiex_tdls_add_qos_capab(skb); + break; + + case WLAN_TDLS_SETUP_RESPONSE: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_RESPONSE; + skb_put(skb, sizeof(tf->u.setup_resp)); + tf->u.setup_resp.status_code = cpu_to_le16(status_code); + tf->u.setup_resp.dialog_token = dialog_token; + tf->u.setup_resp.capability = cpu_to_le16(capab); + ret = mwifiex_tdls_append_rates_ie(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + ht_cap = (void *)pos; + radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + ret = mwifiex_fill_cap_info(priv, radio, ht_cap); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + mwifiex_tdls_add_ext_capab(skb); + mwifiex_tdls_add_qos_capab(skb); + break; + + case WLAN_TDLS_SETUP_CONFIRM: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_CONFIRM; + skb_put(skb, sizeof(tf->u.setup_cfm)); + tf->u.setup_cfm.status_code = cpu_to_le16(status_code); + tf->u.setup_cfm.dialog_token = dialog_token; + break; + + case WLAN_TDLS_TEARDOWN: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_TEARDOWN; + skb_put(skb, sizeof(tf->u.teardown)); + tf->u.teardown.reason_code = cpu_to_le16(status_code); + break; + + case WLAN_TDLS_DISCOVERY_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; + skb_put(skb, sizeof(tf->u.discover_req)); + tf->u.discover_req.dialog_token = dialog_token; + break; + default: + dev_err(priv->adapter->dev, "Unknown TDLS frame type.\n"); + return -EINVAL; + } + + return 0; +} + +static void +mwifiex_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid) +{ + struct ieee80211_tdls_lnkie *lnkid; + + lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); + lnkid->ie_type = WLAN_EID_LINK_ID; + lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - + sizeof(struct ieee_types_header); + + memcpy(lnkid->bssid, bssid, ETH_ALEN); + memcpy(lnkid->init_sta, src_addr, ETH_ALEN); + memcpy(lnkid->resp_sta, peer, ETH_ALEN); +} + +int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct sk_buff *skb; + struct mwifiex_txinfo *tx_info; + struct timeval tv; + int ret; + u16 skb_len; + + skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + + MWIFIEX_SUPPORTED_RATES + + 3 + /* Qos Info */ + sizeof(struct ieee_types_extcap) + + sizeof(struct ieee80211_ht_cap) + + sizeof(struct ieee_types_bss_co_2040) + + sizeof(struct ieee80211_ht_operation) + + sizeof(struct ieee80211_tdls_lnkie) + + extra_ies_len; + + skb = dev_alloc_skb(skb_len); + if (!skb) { + dev_err(priv->adapter->dev, + "allocate skb failed for management frame\n"); + return -ENOMEM; + } + skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, + dialog_token, status_code, + skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, + extra_ies_len); + mwifiex_tdls_add_link_ie(skb, priv->curr_addr, peer, + priv->cfg_bssid); + break; + case WLAN_TDLS_SETUP_RESPONSE: + ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, + dialog_token, status_code, + skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, + extra_ies_len); + mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, + priv->cfg_bssid); + break; + } + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + skb->priority = MWIFIEX_PRIO_BK; + break; + default: + skb->priority = MWIFIEX_PRIO_VI; + break; + } + + tx_info = MWIFIEX_SKB_TXCB(skb); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + + do_gettimeofday(&tv); + skb->tstamp = timeval_to_ktime(tv); + mwifiex_queue_tx_pkt(priv, skb); + + return 0; +} + +static int +mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt; + u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + int ret; + u16 capab; + struct ieee80211_ht_cap *ht_cap; + u8 radio, *pos; + + capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; + + mgmt = (void *)skb_put(skb, offsetof(struct ieee80211_mgmt, u)); + + memset(mgmt, 0, 24); + memcpy(mgmt->da, peer, ETH_ALEN); + memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN); + memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + /* add address 4 */ + pos = skb_put(skb, ETH_ALEN); + + switch (action_code) { + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + skb_put(skb, sizeof(mgmt->u.action.u.tdls_discover_resp) + 1); + mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; + mgmt->u.action.u.tdls_discover_resp.action_code = + WLAN_PUB_ACTION_TDLS_DISCOVER_RES; + mgmt->u.action.u.tdls_discover_resp.dialog_token = + dialog_token; + mgmt->u.action.u.tdls_discover_resp.capability = + cpu_to_le16(capab); + /* move back for addr4 */ + memmove(pos + ETH_ALEN, &mgmt->u.action.category, + sizeof(mgmt->u.action.u.tdls_discover_resp)); + /* init address 4 */ + memcpy(pos, bc_addr, ETH_ALEN); + + ret = mwifiex_tdls_append_rates_ie(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + ht_cap = (void *)pos; + radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + ret = mwifiex_fill_cap_info(priv, radio, ht_cap); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + mwifiex_tdls_add_ext_capab(skb); + mwifiex_tdls_add_qos_capab(skb); + break; + default: + dev_err(priv->adapter->dev, "Unknown TDLS action frame type\n"); + return -EINVAL; + } + + return 0; +} + +int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct sk_buff *skb; + struct mwifiex_txinfo *tx_info; + struct timeval tv; + u8 *pos; + u32 pkt_type, tx_control; + u16 pkt_len, skb_len; + + skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + + MWIFIEX_SUPPORTED_RATES + + sizeof(struct ieee_types_extcap) + + sizeof(struct ieee80211_ht_cap) + + sizeof(struct ieee_types_bss_co_2040) + + sizeof(struct ieee80211_ht_operation) + + sizeof(struct ieee80211_tdls_lnkie) + + extra_ies_len + + 3 + /* Qos Info */ + ETH_ALEN; /* Address4 */ + + skb = dev_alloc_skb(skb_len); + if (!skb) { + dev_err(priv->adapter->dev, + "allocate skb failed for management frame\n"); + return -ENOMEM; + } + + skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); + + pkt_type = PKT_TYPE_MGMT; + tx_control = 0; + pos = skb_put(skb, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); + memset(pos, 0, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); + memcpy(pos, &pkt_type, sizeof(pkt_type)); + memcpy(pos + sizeof(pkt_type), &tx_control, sizeof(tx_control)); + + if (mwifiex_construct_tdls_action_frame(priv, peer, action_code, + dialog_token, status_code, + skb)) { + dev_kfree_skb_any(skb); + return -EINVAL; + } + + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + + /* the TDLS link IE is always added last we are the responder */ + + mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, + priv->cfg_bssid); + + skb->priority = MWIFIEX_PRIO_VI; + + tx_info = MWIFIEX_SKB_TXCB(skb); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + + pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len); + memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len, + sizeof(pkt_len)); + do_gettimeofday(&tv); + skb->tstamp = timeval_to_ktime(tv); + mwifiex_queue_tx_pkt(priv, skb); + + return 0; +} -- cgit From 5f2caaf32bc64c200007611505ce2453f4862276 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:27:33 -0800 Subject: mwifiex: parse TDLS action frames during RX This patch adds support for parsing TDLS action frames during station receive handler. Peer station capabilities are stored into station node. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/decl.h | 8 +++ drivers/net/wireless/mwifiex/main.h | 19 +++++- drivers/net/wireless/mwifiex/sta_rx.c | 13 +++- drivers/net/wireless/mwifiex/tdls.c | 119 ++++++++++++++++++++++++++++++++++ drivers/net/wireless/mwifiex/wmm.c | 5 +- drivers/net/wireless/mwifiex/wmm.h | 3 + 6 files changed, 160 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index c709f1c25b97..efcd1b8b9a26 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -93,6 +93,14 @@ enum mwifiex_bss_role { MWIFIEX_BSS_ROLE_ANY = 0xff, }; +enum mwifiex_tdls_status { + TDLS_NOT_SETUP = 0, + TDLS_SETUP_INPROGRESS, + TDLS_SETUP_COMPLETE, + TDLS_SETUP_FAILURE, + TDLS_LINK_TEARDOWN, +}; + #define BSS_ROLE_BIT_MASK BIT(0) #define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 54197847d494..3b0be45f833c 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -594,8 +594,20 @@ struct mwifiex_bss_priv { u64 fw_tsf; }; -/* This is AP specific structure which stores information - * about associated STA +struct mwifiex_tdls_capab { + __le16 capab; + u8 rates[32]; + u8 rates_len; + u8 qos_info; + u8 coex_2040; + struct ieee80211_ht_cap ht_capb; + struct ieee80211_ht_operation ht_oper; + struct ieee_types_extcap extcap; + struct ieee_types_generic rsn_ie; +}; + +/* This is AP/TDLS specific structure which stores information + * about associated/peer STA */ struct mwifiex_sta_node { struct list_head list; @@ -605,6 +617,7 @@ struct mwifiex_sta_node { u8 ampdu_sta[MAX_NUM_TID]; u16 rx_seq[MAX_NUM_TID]; u16 max_amsdu; + struct mwifiex_tdls_capab tdls_cap; }; struct mwifiex_if_ops { @@ -1194,6 +1207,8 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, const u8 *extra_ies, size_t extra_ies_len); +void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, + u8 *buf, int len); #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c index 4651d676df38..b6aa958bd6e4 100644 --- a/drivers/net/wireless/mwifiex/sta_rx.c +++ b/drivers/net/wireless/mwifiex/sta_rx.c @@ -88,11 +88,14 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv, struct rxpd *local_rx_pd; int hdr_chop; struct ethhdr *eth; + u16 rx_pkt_off, rx_pkt_len; + u8 *offset; local_rx_pd = (struct rxpd *) (skb->data); - rx_pkt_hdr = (void *)local_rx_pd + - le16_to_cpu(local_rx_pd->rx_pkt_offset); + rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset); + rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length); + rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off; if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, sizeof(bridge_tunnel_header))) || @@ -142,6 +145,12 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv, return 0; } + if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + ntohs(rx_pkt_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) { + offset = (u8 *)local_rx_pd + rx_pkt_off; + mwifiex_process_tdls_action_frame(priv, offset, rx_pkt_len); + } + priv->rxpd_rate = local_rx_pd->rx_rate; priv->rxpd_htinfo = local_rx_pd->ht_info; diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index 73cd444a6d43..ba54037a324c 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -16,6 +16,11 @@ */ #include "main.h" +#include "wmm.h" + +#define TDLS_REQ_FIX_LEN 6 +#define TDLS_RESP_FIX_LEN 8 +#define TDLS_CONFIRM_FIX_LEN 6 /* This function appends rate TLV to scan config command. */ static int @@ -421,3 +426,117 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, return 0; } + +/* This function process tdls action frame from peer. + * Peer capabilities are stored into station node structure. + */ +void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, + u8 *buf, int len) +{ + struct mwifiex_sta_node *sta_ptr; + u8 *peer, *pos, *end; + u8 i, action, basic; + int ie_len = 0; + + if (len < (sizeof(struct ethhdr) + 3)) + return; + if (*(u8 *)(buf + sizeof(struct ethhdr)) != WLAN_TDLS_SNAP_RFTYPE) + return; + if (*(u8 *)(buf + sizeof(struct ethhdr) + 1) != WLAN_CATEGORY_TDLS) + return; + + peer = buf + ETH_ALEN; + action = *(u8 *)(buf + sizeof(struct ethhdr) + 2); + + /* just handle TDLS setup request/response/confirm */ + if (action > WLAN_TDLS_SETUP_CONFIRM) + return; + + dev_dbg(priv->adapter->dev, + "rx:tdls action: peer=%pM, action=%d\n", peer, action); + + sta_ptr = mwifiex_add_sta_entry(priv, peer); + if (!sta_ptr) + return; + + switch (action) { + case WLAN_TDLS_SETUP_REQUEST: + if (len < (sizeof(struct ethhdr) + TDLS_REQ_FIX_LEN)) + return; + + pos = buf + sizeof(struct ethhdr) + 4; + /* payload 1+ category 1 + action 1 + dialog 1 */ + sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos); + ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN; + pos += 2; + break; + + case WLAN_TDLS_SETUP_RESPONSE: + if (len < (sizeof(struct ethhdr) + TDLS_RESP_FIX_LEN)) + return; + /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/ + pos = buf + sizeof(struct ethhdr) + 6; + sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos); + ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN; + pos += 2; + break; + + case WLAN_TDLS_SETUP_CONFIRM: + if (len < (sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN)) + return; + pos = buf + sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN; + ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN; + break; + default: + dev_warn(priv->adapter->dev, "Unknown TDLS frame type.\n"); + return; + } + + for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos + 2 + pos[1] > end) + break; + + switch (*pos) { + case WLAN_EID_SUPP_RATES: + sta_ptr->tdls_cap.rates_len = pos[1]; + for (i = 0; i < pos[1]; i++) + sta_ptr->tdls_cap.rates[i] = pos[i + 2]; + break; + + case WLAN_EID_EXT_SUPP_RATES: + basic = sta_ptr->tdls_cap.rates_len; + for (i = 0; i < pos[1]; i++) + sta_ptr->tdls_cap.rates[basic + i] = pos[i + 2]; + sta_ptr->tdls_cap.rates_len += pos[1]; + break; + case WLAN_EID_HT_CAPABILITY: + memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos, + sizeof(struct ieee80211_ht_cap)); + sta_ptr->is_11n_enabled = 1; + break; + case WLAN_EID_HT_OPERATION: + memcpy(&sta_ptr->tdls_cap.ht_oper, pos, + sizeof(struct ieee80211_ht_operation)); + break; + case WLAN_EID_BSS_COEX_2040: + sta_ptr->tdls_cap.coex_2040 = pos[2]; + break; + case WLAN_EID_EXT_CAPABILITY: + memcpy((u8 *)&sta_ptr->tdls_cap.extcap, pos, + sizeof(struct ieee_types_header) + + min_t(u8, pos[1], 8)); + break; + case WLAN_EID_RSN: + memcpy((u8 *)&sta_ptr->tdls_cap.rsn_ie, pos, + sizeof(struct ieee_types_header) + pos[1]); + break; + case WLAN_EID_QOS_CAPA: + sta_ptr->tdls_cap.qos_info = pos[2]; + break; + default: + break; + } + } + + return; +} diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 2999c3b1e112..557d36318d1d 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -374,8 +374,7 @@ mwifiex_wmm_convert_tos_to_ac(struct mwifiex_adapter *adapter, u32 tos) * AP is disabled (due to call admission control (ACM bit). Mapping * of TID to AC is taken care of internally. */ -static u8 -mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid) +u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid) { enum mwifiex_wmm_ac_e ac, ac_down; u8 new_tid; @@ -578,7 +577,7 @@ mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, * If no such node is found, a new node is added first and then * retrieved. */ -static struct mwifiex_ra_list_tbl * +struct mwifiex_ra_list_tbl * mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr) { struct mwifiex_ra_list_tbl *ra_list; diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h index d4e607395da7..83e42083ebff 100644 --- a/drivers/net/wireless/mwifiex/wmm.h +++ b/drivers/net/wireless/mwifiex/wmm.h @@ -122,5 +122,8 @@ void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv); int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, const struct host_cmd_ds_command *resp); +struct mwifiex_ra_list_tbl * +mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr); +u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid); #endif /* !_MWIFIEX_WMM_H_ */ -- cgit From 429d90d2212b561859767a74e3bb855f32b4600d Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:27:34 -0800 Subject: mwifiex: add cfg80211 tdls_oper handler support This patch adds cfg80211 handler tdls_oper handler support to mwifiex. Upon enable link, driver sets status as TDLS status as setup complete and also sets AMSDU size, AMPDU params for direct link. Upon disable link, driver issues command to FW to delete this link in FW. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 49 +++++++++++++++ drivers/net/wireless/mwifiex/decl.h | 14 +++++ drivers/net/wireless/mwifiex/fw.h | 12 ++++ drivers/net/wireless/mwifiex/ioctl.h | 12 ++++ drivers/net/wireless/mwifiex/main.h | 2 + drivers/net/wireless/mwifiex/sta_cmd.c | 32 ++++++++++ drivers/net/wireless/mwifiex/sta_cmdresp.c | 28 +++++++++ drivers/net/wireless/mwifiex/tdls.c | 97 ++++++++++++++++++++++++++++++ 8 files changed, 246 insertions(+) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 2968af273ef9..88cff9ca5577 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2669,6 +2669,54 @@ mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, return ret; } +static int +mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, enum nl80211_tdls_operation action) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) || + !(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return -ENOTSUPP; + + dev_dbg(priv->adapter->dev, + "TDLS peer=%pM, oper=%d\n", peer, action); + + switch (action) { + case NL80211_TDLS_ENABLE_LINK: + action = MWIFIEX_TDLS_ENABLE_LINK; + break; + case NL80211_TDLS_DISABLE_LINK: + action = MWIFIEX_TDLS_DISABLE_LINK; + break; + case NL80211_TDLS_TEARDOWN: + /* shouldn't happen!*/ + dev_warn(priv->adapter->dev, + "tdls_oper: teardown from driver not supported\n"); + return -EINVAL; + case NL80211_TDLS_SETUP: + /* shouldn't happen!*/ + dev_warn(priv->adapter->dev, + "tdls_oper: setup from driver not supported\n"); + return -EINVAL; + case NL80211_TDLS_DISCOVERY_REQ: + /* shouldn't happen!*/ + dev_warn(priv->adapter->dev, + "tdls_oper: discovery from driver not supported\n"); + return -EINVAL; + default: + dev_err(priv->adapter->dev, + "tdls_oper: operation not supported\n"); + return -ENOTSUPP; + } + + return mwifiex_tdls_oper(priv, peer, action); +} + /* station cfg80211 operations */ static struct cfg80211_ops mwifiex_cfg80211_ops = { .add_virtual_intf = mwifiex_add_virtual_intf, @@ -2705,6 +2753,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { #endif .set_coalesce = mwifiex_cfg80211_set_coalesce, .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt, + .tdls_oper = mwifiex_cfg80211_tdls_oper, }; #ifdef CONFIG_PM diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index efcd1b8b9a26..e7b3e16e5d34 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -80,6 +80,11 @@ #define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024 #define MWIFIEX_BRIDGED_PKTS_THR_LOW 128 +#define MWIFIEX_TDLS_DISABLE_LINK 0x00 +#define MWIFIEX_TDLS_ENABLE_LINK 0x01 +#define MWIFIEX_TDLS_CREATE_LINK 0x02 +#define MWIFIEX_TDLS_CONFIG_LINK 0x03 + enum mwifiex_bss_type { MWIFIEX_BSS_TYPE_STA = 0, MWIFIEX_BSS_TYPE_UAP = 1, @@ -101,6 +106,15 @@ enum mwifiex_tdls_status { TDLS_LINK_TEARDOWN, }; +enum mwifiex_tdls_error_code { + TDLS_ERR_NO_ERROR = 0, + TDLS_ERR_INTERNAL_ERROR, + TDLS_ERR_MAX_LINKS_EST, + TDLS_ERR_LINK_EXISTS, + TDLS_ERR_LINK_NONEXISTENT, + TDLS_ERR_PEER_STA_UNREACHABLE = 25, +}; + #define BSS_ROLE_BIT_MASK BIT(0) #define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 416518a939b2..3180fc6b7c6d 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -316,6 +316,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_MGMT_FRAME_REG 0x010c #define HostCmd_CMD_REMAIN_ON_CHAN 0x010d #define HostCmd_CMD_11AC_CFG 0x0112 +#define HostCmd_CMD_TDLS_OPER 0x0122 #define PROTOCOL_NO_SECURITY 0x01 #define PROTOCOL_STATIC_WEP 0x02 @@ -486,6 +487,10 @@ enum P2P_MODES { #define MWIFIEX_CRITERIA_UNICAST BIT(1) #define MWIFIEX_CRITERIA_MULTICAST BIT(3) +#define ACT_TDLS_DELETE 0x00 +#define ACT_TDLS_CREATE 0x01 +#define ACT_TDLS_CONFIG 0x02 + struct mwifiex_ie_types_header { __le16 type; __le16 len; @@ -1066,6 +1071,12 @@ struct host_cmd_ds_rf_ant_siso { __le16 ant_mode; }; +struct host_cmd_ds_tdls_oper { + __le16 tdls_action; + __le16 reason; + u8 peer_mac[ETH_ALEN]; +} __packed; + struct mwifiex_fixed_bcn_param { __le64 timestamp; __le16 beacon_period; @@ -1726,6 +1737,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_sta_deauth sta_deauth; struct host_cmd_11ac_vht_cfg vht_cfg; struct host_cmd_ds_coalesce_cfg coalesce_cfg; + struct host_cmd_ds_tdls_oper tdls_oper; } params; } __packed; diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index fe122742b010..6ed1e13d7b40 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -435,4 +435,16 @@ struct mwifiex_ds_coalesce_cfg { struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES]; }; +struct mwifiex_ds_tdls_oper { + u16 tdls_action; + u8 peer_mac[ETH_ALEN]; + u16 capability; + u8 qos_info; + u8 *ext_capab; + u8 ext_capab_len; + u8 *supp_rates; + u8 supp_rates_len; + u8 *ht_capab; +}; + #endif /* !_MWIFIEX_IOCTL_H_ */ diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 3b0be45f833c..d35c9950efba 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -617,6 +617,7 @@ struct mwifiex_sta_node { u8 ampdu_sta[MAX_NUM_TID]; u16 rx_seq[MAX_NUM_TID]; u16 max_amsdu; + u8 tdls_status; struct mwifiex_tdls_capab tdls_cap; }; @@ -1209,6 +1210,7 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, size_t extra_ies_len); void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, u8 *buf, int len); +int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action); #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 1b2991261538..4559f84e64b5 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -1280,6 +1280,35 @@ mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv, return 0; } +static int +mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_tdls_oper *tdls_oper = &cmd->params.tdls_oper; + struct mwifiex_ds_tdls_oper *oper = data_buf; + struct mwifiex_sta_node *sta_ptr; + + cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_OPER); + cmd->size = cpu_to_le16(S_DS_GEN); + + tdls_oper->reason = 0; + memcpy(tdls_oper->peer_mac, oper->peer_mac, ETH_ALEN); + sta_ptr = mwifiex_get_sta_entry(priv, oper->peer_mac); + + switch (oper->tdls_action) { + case MWIFIEX_TDLS_DISABLE_LINK: + tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_DELETE); + break; + default: + dev_err(priv->adapter->dev, "Unknown TDLS operation\n"); + return -ENOTSUPP; + } + + le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_tdls_oper)); + + return 0; +} /* * This function prepares the commands before sending them to the firmware. * @@ -1510,6 +1539,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action, data_buf); break; + case HostCmd_CMD_TDLS_OPER: + ret = mwifiex_cmd_tdls_oper(priv, cmd_ptr, data_buf); + break; default: dev_err(priv->adapter->dev, "PREP_CMD: unknown cmd- %#x\n", cmd_no); diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 95e13589d2bd..cb17f490bc6d 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -801,7 +801,32 @@ static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv, return 0; } +static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_tdls_oper *cmd_tdls_oper = &resp->params.tdls_oper; + u16 reason = le16_to_cpu(cmd_tdls_oper->reason); + u16 action = le16_to_cpu(cmd_tdls_oper->tdls_action); + switch (action) { + case ACT_TDLS_DELETE: + if (reason) + dev_err(priv->adapter->dev, + "TDLS link delete for %pM failed: reason %d\n", + cmd_tdls_oper->peer_mac, reason); + else + dev_dbg(priv->adapter->dev, + "TDLS link config for %pM successful\n", + cmd_tdls_oper->peer_mac); + break; + default: + dev_err(priv->adapter->dev, + "Unknown TDLS command action respnse %d", action); + return -1; + } + + return 0; +} /* * This function handles the command response for subscribe event command. */ @@ -1004,6 +1029,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, break; case HostCmd_CMD_COALESCE_CFG: break; + case HostCmd_CMD_TDLS_OPER: + ret = mwifiex_ret_tdls_oper(priv, resp); + break; default: dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n", resp->command); diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index ba54037a324c..7fead7bf768f 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -17,6 +17,8 @@ #include "main.h" #include "wmm.h" +#include "11n.h" +#include "11n_rxreorder.h" #define TDLS_REQ_FIX_LEN 6 #define TDLS_RESP_FIX_LEN 8 @@ -540,3 +542,98 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, return; } + +static int +mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + unsigned long flags; + + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (sta_ptr) { + if (sta_ptr->is_11n_enabled) { + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + } + mwifiex_del_sta_entry(priv, peer); + } + + memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; + return mwifiex_send_cmd_sync(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper); +} + +static int +mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct ieee80211_mcs_info mcs; + unsigned long flags; + int i; + + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (sta_ptr && (sta_ptr->tdls_status != TDLS_SETUP_FAILURE)) { + dev_dbg(priv->adapter->dev, + "tdls: enable link %pM success\n", peer); + + sta_ptr->tdls_status = TDLS_SETUP_COMPLETE; + + mcs = sta_ptr->tdls_cap.ht_capb.mcs; + if (mcs.rx_mask[0] != 0xff) + sta_ptr->is_11n_enabled = true; + if (sta_ptr->is_11n_enabled) { + if (le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info) & + IEEE80211_HT_CAP_MAX_AMSDU) + sta_ptr->max_amsdu = + MWIFIEX_TX_DATA_BUF_SIZE_8K; + else + sta_ptr->max_amsdu = + MWIFIEX_TX_DATA_BUF_SIZE_4K; + + for (i = 0; i < MAX_NUM_TID; i++) + sta_ptr->ampdu_sta[i] = + priv->aggr_prio_tbl[i].ampdu_user; + } else { + for (i = 0; i < MAX_NUM_TID; i++) + sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; + } + + memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq)); + } else { + dev_dbg(priv->adapter->dev, + "tdls: enable link %pM failed\n", peer); + if (sta_ptr) { + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_del_sta_entry(priv, peer); + } + + return -1; + } + + return 0; +} + +int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action) +{ + switch (action) { + case MWIFIEX_TDLS_ENABLE_LINK: + return mwifiex_tdls_process_enable_link(priv, peer); + case MWIFIEX_TDLS_DISABLE_LINK: + return mwifiex_tdls_process_disable_link(priv, peer); + } + return 0; +} -- cgit From e48e0de0053f077dc8a98e1e06019024e93bb866 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:30:33 -0800 Subject: mwifiex: add cfg80211 add_station handler support This patch adds cfg80211 add_station handler support for mwifiex which is needed for TDLS setup. Driver issues create TDLS link command to FW upon receiving add_station from cfg80211. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 18 ++++++++++++++++++ drivers/net/wireless/mwifiex/sta_cmd.c | 3 +++ drivers/net/wireless/mwifiex/sta_cmdresp.c | 15 +++++++++++++++ drivers/net/wireless/mwifiex/tdls.c | 28 ++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 88cff9ca5577..cac8aea69ce5 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2717,6 +2717,23 @@ mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, return mwifiex_tdls_oper(priv, peer, action); } +static int +mwifiex_cfg80211_add_station(struct wiphy *wiphy, + struct net_device *dev, + u8 *mac, struct station_parameters *params) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) + return -ENOTSUPP; + + return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK); +} + /* station cfg80211 operations */ static struct cfg80211_ops mwifiex_cfg80211_ops = { .add_virtual_intf = mwifiex_add_virtual_intf, @@ -2754,6 +2771,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { .set_coalesce = mwifiex_cfg80211_set_coalesce, .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt, .tdls_oper = mwifiex_cfg80211_tdls_oper, + .add_station = mwifiex_cfg80211_add_station, }; #ifdef CONFIG_PM diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 4559f84e64b5..1e36fa7a6463 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -1300,6 +1300,9 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, case MWIFIEX_TDLS_DISABLE_LINK: tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_DELETE); break; + case MWIFIEX_TDLS_CREATE_LINK: + tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CREATE); + break; default: dev_err(priv->adapter->dev, "Unknown TDLS operation\n"); return -ENOTSUPP; diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index cb17f490bc6d..396b93682bd8 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -807,6 +807,8 @@ static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv, struct host_cmd_ds_tdls_oper *cmd_tdls_oper = &resp->params.tdls_oper; u16 reason = le16_to_cpu(cmd_tdls_oper->reason); u16 action = le16_to_cpu(cmd_tdls_oper->tdls_action); + struct mwifiex_sta_node *node = + mwifiex_get_sta_entry(priv, cmd_tdls_oper->peer_mac); switch (action) { case ACT_TDLS_DELETE: @@ -819,6 +821,19 @@ static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv, "TDLS link config for %pM successful\n", cmd_tdls_oper->peer_mac); break; + case ACT_TDLS_CREATE: + if (reason) { + dev_err(priv->adapter->dev, + "TDLS link creation for %pM failed: reason %d", + cmd_tdls_oper->peer_mac, reason); + if (node && reason != TDLS_ERR_LINK_EXISTS) + node->tdls_status = TDLS_SETUP_FAILURE; + } else { + dev_dbg(priv->adapter->dev, + "TDLS link creation for %pM successful", + cmd_tdls_oper->peer_mac); + } + break; default: dev_err(priv->adapter->dev, "Unknown TDLS command action respnse %d", action); diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index 7fead7bf768f..1d5ed70432d6 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -543,6 +543,32 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, return; } +static int +mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (sta_ptr && sta_ptr->tdls_status == TDLS_SETUP_INPROGRESS) { + dev_dbg(priv->adapter->dev, + "Setup already in progress for peer %pM\n", peer); + return 0; + } + + sta_ptr = mwifiex_add_sta_entry(priv, peer); + if (!sta_ptr) + return -ENOMEM; + + sta_ptr->tdls_status = TDLS_SETUP_INPROGRESS; + memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_CREATE_LINK; + return mwifiex_send_cmd_sync(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper); +} + static int mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer) { @@ -634,6 +660,8 @@ int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action) return mwifiex_tdls_process_enable_link(priv, peer); case MWIFIEX_TDLS_DISABLE_LINK: return mwifiex_tdls_process_disable_link(priv, peer); + case MWIFIEX_TDLS_CREATE_LINK: + return mwifiex_tdls_process_create_link(priv, peer); } return 0; } -- cgit From 1f4dfd8a1e911cd9e12994cd7cc1180e94ee1bc5 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:30:34 -0800 Subject: mwifiex: add cfg80211 change_station handler support This patch adds cfg80211 change_station handler support for mwifiex which is needed for TDLS link setup. Driver creates a command to modify peer link capabilities and issues command to FW. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 25 +++++++++++ drivers/net/wireless/mwifiex/fw.h | 5 +++ drivers/net/wireless/mwifiex/main.h | 1 + drivers/net/wireless/mwifiex/sta_cmd.c | 71 +++++++++++++++++++++++++++++- drivers/net/wireless/mwifiex/sta_cmdresp.c | 13 ++++++ drivers/net/wireless/mwifiex/tdls.c | 23 ++++++++++ 6 files changed, 137 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index cac8aea69ce5..436ba437a4ba 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2734,6 +2734,30 @@ mwifiex_cfg80211_add_station(struct wiphy *wiphy, return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK); } +static int +mwifiex_cfg80211_change_station(struct wiphy *wiphy, + struct net_device *dev, + u8 *mac, struct station_parameters *params) +{ + int ret; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + /* we support change_station handler only for TDLS peers*/ + if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) + return -ENOTSUPP; + + priv->sta_params = params; + + ret = mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CONFIG_LINK); + priv->sta_params = NULL; + + return ret; +} + /* station cfg80211 operations */ static struct cfg80211_ops mwifiex_cfg80211_ops = { .add_virtual_intf = mwifiex_add_virtual_intf, @@ -2772,6 +2796,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt, .tdls_oper = mwifiex_cfg80211_tdls_oper, .add_station = mwifiex_cfg80211_add_station, + .change_station = mwifiex_cfg80211_change_station, }; #ifdef CONFIG_PM diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 3180fc6b7c6d..8c119bc93899 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -1391,6 +1391,11 @@ struct mwifiex_ie_types_extcap { u8 ext_capab[0]; } __packed; +struct mwifiex_ie_types_qos_info { + struct mwifiex_ie_types_header header; + u8 qos_info; +} __packed; + struct host_cmd_ds_mac_reg_access { __le16 action; __le16 offset; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index d35c9950efba..c8c30a4c9a73 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -529,6 +529,7 @@ struct mwifiex_private { unsigned long csa_expire_time; u8 del_list_idx; bool hs2_enabled; + struct station_parameters *sta_params; }; enum mwifiex_ba_status { diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 1e36fa7a6463..8f1bcc3255dd 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -1288,27 +1288,96 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, struct host_cmd_ds_tdls_oper *tdls_oper = &cmd->params.tdls_oper; struct mwifiex_ds_tdls_oper *oper = data_buf; struct mwifiex_sta_node *sta_ptr; + struct host_cmd_tlv_rates *tlv_rates; + struct mwifiex_ie_types_htcap *ht_capab; + struct mwifiex_ie_types_qos_info *wmm_qos_info; + struct mwifiex_ie_types_extcap *extcap; + u8 *pos, qos_info; + u16 config_len = 0; + struct station_parameters *params = priv->sta_params; cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_OPER); cmd->size = cpu_to_le16(S_DS_GEN); + le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_tdls_oper)); tdls_oper->reason = 0; memcpy(tdls_oper->peer_mac, oper->peer_mac, ETH_ALEN); sta_ptr = mwifiex_get_sta_entry(priv, oper->peer_mac); + pos = (u8 *)tdls_oper + sizeof(struct host_cmd_ds_tdls_oper); + switch (oper->tdls_action) { case MWIFIEX_TDLS_DISABLE_LINK: tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_DELETE); break; case MWIFIEX_TDLS_CREATE_LINK: tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CREATE); + break; + case MWIFIEX_TDLS_CONFIG_LINK: + tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CONFIG); + + if (!params) { + dev_err(priv->adapter->dev, + "TDLS config params not available for %pM\n", + oper->peer_mac); + return -ENODATA; + } + + *(__le16 *)pos = cpu_to_le16(params->capability); + config_len += sizeof(params->capability); + + qos_info = params->uapsd_queues | (params->max_sp << 5); + wmm_qos_info = (struct mwifiex_ie_types_qos_info *)(pos + + config_len); + wmm_qos_info->header.type = cpu_to_le16(WLAN_EID_QOS_CAPA); + wmm_qos_info->header.len = cpu_to_le16(sizeof(qos_info)); + wmm_qos_info->qos_info = qos_info; + config_len += sizeof(struct mwifiex_ie_types_qos_info); + + if (params->ht_capa) { + ht_capab = (struct mwifiex_ie_types_htcap *)(pos + + config_len); + ht_capab->header.type = + cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_capab->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + memcpy(&ht_capab->ht_cap, params->ht_capa, + sizeof(struct ieee80211_ht_cap)); + config_len += sizeof(struct mwifiex_ie_types_htcap); + } + + if (params->supported_rates && params->supported_rates_len) { + tlv_rates = (struct host_cmd_tlv_rates *)(pos + + config_len); + tlv_rates->header.type = + cpu_to_le16(WLAN_EID_SUPP_RATES); + tlv_rates->header.len = + cpu_to_le16(params->supported_rates_len); + memcpy(tlv_rates->rates, params->supported_rates, + params->supported_rates_len); + config_len += sizeof(struct host_cmd_tlv_rates) + + params->supported_rates_len; + } + + if (params->ext_capab && params->ext_capab_len) { + extcap = (struct mwifiex_ie_types_extcap *)(pos + + config_len); + extcap->header.type = + cpu_to_le16(WLAN_EID_EXT_CAPABILITY); + extcap->header.len = cpu_to_le16(params->ext_capab_len); + memcpy(extcap->ext_capab, params->ext_capab, + params->ext_capab_len); + config_len += sizeof(struct mwifiex_ie_types_extcap) + + params->ext_capab_len; + } + break; default: dev_err(priv->adapter->dev, "Unknown TDLS operation\n"); return -ENOTSUPP; } - le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_tdls_oper)); + le16_add_cpu(&cmd->size, config_len); return 0; } diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 396b93682bd8..48abab6bfce9 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -834,6 +834,19 @@ static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv, cmd_tdls_oper->peer_mac); } break; + case ACT_TDLS_CONFIG: + if (reason) { + dev_err(priv->adapter->dev, + "TDLS link config for %pM failed, reason %d\n", + cmd_tdls_oper->peer_mac, reason); + if (node) + node->tdls_status = TDLS_SETUP_FAILURE; + } else { + dev_dbg(priv->adapter->dev, + "TDLS link config for %pM successful\n", + cmd_tdls_oper->peer_mac); + } + break; default: dev_err(priv->adapter->dev, "Unknown TDLS command action respnse %d", action); diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index 1d5ed70432d6..f37862b5fabb 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -543,6 +543,27 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, return; } +static int +mwifiex_tdls_process_config_link(struct mwifiex_private *priv, u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (!sta_ptr || sta_ptr->tdls_status == TDLS_SETUP_FAILURE) { + dev_err(priv->adapter->dev, + "link absent for peer %pM; cannot config\n", peer); + return -EINVAL; + } + + memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_CONFIG_LINK; + return mwifiex_send_cmd_sync(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper); +} + static int mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer) { @@ -662,6 +683,8 @@ int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action) return mwifiex_tdls_process_disable_link(priv, peer); case MWIFIEX_TDLS_CREATE_LINK: return mwifiex_tdls_process_create_link(priv, peer); + case MWIFIEX_TDLS_CONFIG_LINK: + return mwifiex_tdls_process_config_link(priv, peer); } return 0; } -- cgit From 56bd24a18e1a7306a21f6b7d7716cced7e593057 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:30:35 -0800 Subject: mwifiex: provision for holding and restoring packets during TDLS setup While TDLS link is being setup, few packets from this station to peer station may be buffered at AP. It may happen that once TDLS link is setup, packets sent from station to peer on direct link get delivered before traffic from AP arrives at peer station. This results into packet reordering issue at peer station. To avoid this, we hold data packets destined to TDLS peer during TDLS setup. These packets are moved to temperory TDLS TX queue. Upon successful TDLS setup, they are moved to RA list created for this peer. Upon failure, packets are moved back to AP's RA list for that particular TID. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/init.c | 1 + drivers/net/wireless/mwifiex/main.h | 1 + drivers/net/wireless/mwifiex/tdls.c | 94 +++++++++++++++++++++++++++++++++++++ drivers/net/wireless/mwifiex/wmm.c | 4 ++ 4 files changed, 100 insertions(+) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index dead65960d34..9dc8059778a0 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -452,6 +452,7 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter) INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); INIT_LIST_HEAD(&priv->sta_list); + skb_queue_head_init(&priv->tdls_txq); spin_lock_init(&priv->tx_ba_stream_tbl_lock); spin_lock_init(&priv->rx_reorder_tbl_lock); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index c8c30a4c9a73..bce65f5f0046 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -530,6 +530,7 @@ struct mwifiex_private { u8 del_list_idx; bool hs2_enabled; struct station_parameters *sta_params; + struct sk_buff_head tdls_txq; }; enum mwifiex_ba_status { diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index f37862b5fabb..3198739c133e 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -24,6 +24,96 @@ #define TDLS_RESP_FIX_LEN 8 #define TDLS_CONFIRM_FIX_LEN 6 +static void +mwifiex_restore_tdls_packets(struct mwifiex_private *priv, u8 *mac, u8 status) +{ + struct mwifiex_ra_list_tbl *ra_list; + struct list_head *tid_list; + struct sk_buff *skb, *tmp; + struct mwifiex_txinfo *tx_info; + unsigned long flags; + u32 tid; + u8 tid_down; + + dev_dbg(priv->adapter->dev, "%s: %pM\n", __func__, mac); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) { + if (!ether_addr_equal(mac, skb->data)) + continue; + + __skb_unlink(skb, &priv->tdls_txq); + tx_info = MWIFIEX_SKB_TXCB(skb); + tid = skb->priority; + tid_down = mwifiex_wmm_downgrade_tid(priv, tid); + + if (status == TDLS_SETUP_COMPLETE) { + ra_list = mwifiex_wmm_get_queue_raptr(priv, tid, mac); + tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + } else { + tid_list = &priv->wmm.tid_tbl_ptr[tid_down].ra_list; + if (!list_empty(tid_list)) + ra_list = list_first_entry(tid_list, + struct mwifiex_ra_list_tbl, list); + else + ra_list = NULL; + tx_info->flags &= ~MWIFIEX_BUF_FLAG_TDLS_PKT; + } + + if (!ra_list) { + mwifiex_write_data_complete(priv->adapter, skb, 0, -1); + continue; + } + + skb_queue_tail(&ra_list->skb_head, skb); + + ra_list->ba_pkt_count++; + ra_list->total_pkt_count++; + + if (atomic_read(&priv->wmm.highest_queued_prio) < + tos_to_tid_inv[tid_down]) + atomic_set(&priv->wmm.highest_queued_prio, + tos_to_tid_inv[tid_down]); + + atomic_inc(&priv->wmm.tx_pkts_queued); + } + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + return; +} + +static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, u8 *mac) +{ + struct mwifiex_ra_list_tbl *ra_list; + struct list_head *ra_list_head; + struct sk_buff *skb, *tmp; + unsigned long flags; + int i; + + dev_dbg(priv->adapter->dev, "%s: %pM\n", __func__, mac); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + for (i = 0; i < MAX_NUM_TID; i++) { + if (!list_empty(&priv->wmm.tid_tbl_ptr[i].ra_list)) { + ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list; + list_for_each_entry(ra_list, ra_list_head, list) { + skb_queue_walk_safe(&ra_list->skb_head, skb, + tmp) { + if (!ether_addr_equal(mac, skb->data)) + continue; + __skb_unlink(skb, &ra_list->skb_head); + atomic_dec(&priv->wmm.tx_pkts_queued); + ra_list->total_pkt_count--; + skb_queue_tail(&priv->tdls_txq, skb); + } + } + } + } + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + return; +} + /* This function appends rate TLV to scan config command. */ static int mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv, @@ -584,6 +674,7 @@ mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer) return -ENOMEM; sta_ptr->tdls_status = TDLS_SETUP_INPROGRESS; + mwifiex_hold_tdls_packets(priv, peer); memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); tdls_oper.tdls_action = MWIFIEX_TDLS_CREATE_LINK; return mwifiex_send_cmd_sync(priv, HostCmd_CMD_TDLS_OPER, @@ -612,6 +703,7 @@ mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer) mwifiex_del_sta_entry(priv, peer); } + mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; return mwifiex_send_cmd_sync(priv, HostCmd_CMD_TDLS_OPER, @@ -655,6 +747,7 @@ mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer) } memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq)); + mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE); } else { dev_dbg(priv->adapter->dev, "tdls: enable link %pM failed\n", peer); @@ -667,6 +760,7 @@ mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer) flags); mwifiex_del_sta_entry(priv, peer); } + mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); return -1; } diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 557d36318d1d..e9f7628684ce 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -533,6 +533,7 @@ void mwifiex_clean_txrx(struct mwifiex_private *priv) { unsigned long flags; + struct sk_buff *skb, *tmp; mwifiex_11n_cleanup_reorder_tbl(priv); spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); @@ -549,6 +550,9 @@ mwifiex_clean_txrx(struct mwifiex_private *priv) if (priv->adapter->if_ops.clean_pcie_ring) priv->adapter->if_ops.clean_pcie_ring(priv->adapter); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + + skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) + mwifiex_write_data_complete(priv->adapter, skb, 0, -1); } /* -- cgit From d63bf5e5e00dc025c71532e9244a96966ac8e252 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:30:36 -0800 Subject: mwifiex: tdls related handling for data packets addressed to TDLS peer 1. If data packet is addressed to TDLS peer for which link is established, mark these packets with TDLS flag so that FW can send them on direct link instead of sending via AP. 2. If data packet is addressed to TDLS peer and TDLS setup is underway, move these packets to TDLS queue. 3. If this packet is TDLS setup packet, do not block it. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/main.h | 1 + drivers/net/wireless/mwifiex/tdls.c | 11 ++++++++++ drivers/net/wireless/mwifiex/wmm.c | 42 +++++++++++++++++++++++++++++++------ 3 files changed, 48 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index bce65f5f0046..3001332191a3 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -1213,6 +1213,7 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, u8 *buf, int len); int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action); +int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac); #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index 3198739c133e..7ef3593e2c44 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -782,3 +782,14 @@ int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action) } return 0; } + +int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac) +{ + struct mwifiex_sta_node *sta_ptr; + + sta_ptr = mwifiex_get_sta_entry(priv, mac); + if (sta_ptr) + return sta_ptr->tdls_status; + + return TDLS_NOT_SETUP; +} diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index e9f7628684ce..63496ed10272 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -631,6 +631,21 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ra_list; u8 ra[ETH_ALEN], tid_down; unsigned long flags; + struct list_head list_head; + int tdls_status = TDLS_NOT_SETUP; + struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; + struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); + + memcpy(ra, eth_hdr->h_dest, ETH_ALEN); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && + ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) { + if (ntohs(eth_hdr->h_proto) == ETH_P_TDLS) + dev_dbg(adapter->dev, + "TDLS setup packet for %pM. Don't block\n", ra); + else + tdls_status = mwifiex_get_tdls_link_status(priv, ra); + } if (!priv->media_connected && !mwifiex_is_skb_mgmt_frame(skb)) { dev_dbg(adapter->dev, "data: drop packet in disconnect\n"); @@ -649,12 +664,27 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, have only 1 raptr for a tid in case of infra */ 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, - struct mwifiex_ra_list_tbl, list); - else - ra_list = NULL; + switch (tdls_status) { + case TDLS_SETUP_COMPLETE: + ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, + ra); + tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + break; + case TDLS_SETUP_INPROGRESS: + skb_queue_tail(&priv->tdls_txq, skb); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + return; + default: + list_head = priv->wmm.tid_tbl_ptr[tid_down].ra_list; + if (!list_empty(&list_head)) + ra_list = list_first_entry( + &list_head, struct mwifiex_ra_list_tbl, + list); + else + ra_list = NULL; + break; + } } else { memcpy(ra, skb->data, ETH_ALEN); if (ra[0] & 0x01 || mwifiex_is_skb_mgmt_frame(skb)) -- cgit From daeb5bb48256d7488246d48453b2315281ce9c75 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:30:37 -0800 Subject: mwifiex: AMPDU support for TDLS link This patch adds AMPDU support for TDLS link. We have set 11n capabilities including AMPDU parameters during ENABLE_LINK. We set a variable in RA list to indicate this as TDLS link. This patch uses these capabilities to know if AMPDU is allowed on TDLS link and enables AMPDU aggregation for TX and RX reording support for RA list for this peer. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11n.h | 18 ++++++++++++++++-- drivers/net/wireless/mwifiex/11n_rxreorder.c | 6 +++++- drivers/net/wireless/mwifiex/main.h | 1 + drivers/net/wireless/mwifiex/tdls.c | 1 + drivers/net/wireless/mwifiex/wmm.c | 9 ++++++++- 5 files changed, 31 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h index fde39fedf0c0..12bb6acbdd58 100644 --- a/drivers/net/wireless/mwifiex/11n.h +++ b/drivers/net/wireless/mwifiex/11n.h @@ -81,11 +81,15 @@ static inline u8 mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ptr, int tid) { - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { return mwifiex_is_station_ampdu_allowed(priv, ptr, tid); - else + } else { + if (ptr->tdls_link) + return mwifiex_is_station_ampdu_allowed(priv, ptr, tid); + return (priv->aggr_prio_tbl[tid].ampdu_ap != BA_STREAM_NOT_ALLOWED) ? true : false; + } } /* @@ -179,4 +183,14 @@ static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv, return node->is_11n_enabled; } + +static inline u8 +mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, u8 *ra) +{ + struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ra); + if (node) + return node->is_11n_enabled; + + return false; +} #endif /* !_MWIFIEX_11N_H_ */ diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index ada809f576fe..3767399576b3 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c @@ -290,7 +290,11 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, last_seq = node->rx_seq[tid]; } } else { - last_seq = priv->rx_seq[tid]; + node = mwifiex_get_sta_entry(priv, ta); + if (node) + last_seq = node->rx_seq[tid]; + else + last_seq = priv->rx_seq[tid]; } if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM && diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 3001332191a3..6d49d99045c0 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -210,6 +210,7 @@ struct mwifiex_ra_list_tbl { u16 ba_pkt_count; u8 ba_packet_thr; u16 total_pkt_count; + bool tdls_link; }; struct mwifiex_tid_tbl { diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index 7ef3593e2c44..243beaba9160 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -49,6 +49,7 @@ mwifiex_restore_tdls_packets(struct mwifiex_private *priv, u8 *mac, u8 status) if (status == TDLS_SETUP_COMPLETE) { ra_list = mwifiex_wmm_get_queue_raptr(priv, tid, mac); + ra_list->tdls_link = true; tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; } else { tid_list = &priv->wmm.tid_tbl_ptr[tid_down].ra_list; diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 63496ed10272..e0ba0115e5ae 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -160,8 +160,15 @@ mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra) break; ra_list->is_11n_enabled = 0; + ra_list->tdls_link = false; if (!mwifiex_queuing_ra_based(priv)) { - ra_list->is_11n_enabled = IS_11N_ENABLED(priv); + if (mwifiex_get_tdls_link_status(priv, ra) == + TDLS_SETUP_COMPLETE) { + ra_list->is_11n_enabled = + mwifiex_tdls_peer_11n_enabled(priv, ra); + } else { + ra_list->is_11n_enabled = IS_11N_ENABLED(priv); + } } else { ra_list->is_11n_enabled = mwifiex_is_sta_11n_enabled(priv, node); -- cgit From 9ed230bcbab74a84b4f7d8eade107cd4c20630df Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:30:38 -0800 Subject: mwifiex: pass ieee80211_vht_cap to mwifiex_fill_vht_cap_tlv This patch changes mwifiex_fill_vht_cap_tlv function to pass struct ieee80211_vht_cap instead of mwifiex_ie_types_vhtcap so that it can be used generically. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11ac.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c index 47383920eb12..5895bc8978a5 100644 --- a/drivers/net/wireless/mwifiex/11ac.c +++ b/drivers/net/wireless/mwifiex/11ac.c @@ -96,21 +96,21 @@ mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv, static void mwifiex_fill_vht_cap_info(struct mwifiex_private *priv, - struct mwifiex_ie_types_vhtcap *vht_cap, u8 bands) + struct ieee80211_vht_cap *vht_cap, u8 bands) { struct mwifiex_adapter *adapter = priv->adapter; if (bands & BAND_A) - vht_cap->vht_cap.vht_cap_info = + vht_cap->vht_cap_info = cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a); else - vht_cap->vht_cap.vht_cap_info = + vht_cap->vht_cap_info = cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg); } static void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, - struct mwifiex_ie_types_vhtcap *vht_cap, u8 bands) + struct ieee80211_vht_cap *vht_cap, u8 bands) { struct mwifiex_adapter *adapter = priv->adapter; u16 mcs_map_user, mcs_map_resp, mcs_map_result; @@ -121,7 +121,7 @@ mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, /* rx MCS Set: find the minimum of the user rx mcs and ap rx mcs */ mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); - mcs_map_resp = le16_to_cpu(vht_cap->vht_cap.supp_mcs.rx_mcs_map); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map); mcs_map_result = 0; for (nss = 1; nss <= 8; nss++) { @@ -137,14 +137,14 @@ mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, min(mcs_user, mcs_resp)); } - vht_cap->vht_cap.supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result); + vht_cap->supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result); tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); - vht_cap->vht_cap.supp_mcs.rx_highest = cpu_to_le16(tmp); + vht_cap->supp_mcs.rx_highest = cpu_to_le16(tmp); /* tx MCS Set: find the minimum of the user tx mcs and ap tx mcs */ mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support); - mcs_map_resp = le16_to_cpu(vht_cap->vht_cap.supp_mcs.tx_mcs_map); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map); mcs_map_result = 0; for (nss = 1; nss <= 8; nss++) { @@ -159,10 +159,10 @@ mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, min(mcs_user, mcs_resp)); } - vht_cap->vht_cap.supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result); + vht_cap->supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result); tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); - vht_cap->vht_cap.supp_mcs.tx_highest = cpu_to_le16(tmp); + vht_cap->supp_mcs.tx_highest = cpu_to_le16(tmp); return; } @@ -197,7 +197,8 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, sizeof(struct ieee_types_header), le16_to_cpu(vht_cap->header.len)); - mwifiex_fill_vht_cap_tlv(priv, vht_cap, bss_desc->bss_band); + mwifiex_fill_vht_cap_tlv(priv, &vht_cap->vht_cap, + bss_desc->bss_band); *buffer += sizeof(*vht_cap); ret_len += sizeof(*vht_cap); } -- cgit From 5f6d5983394fd7b918385dbee7e4d983a7b990d9 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:30:39 -0800 Subject: mwifiex: add VHT support for TDLS During TDLS setup request/response, if HW is 11ac capable, we add VHT Capability IEs in outgoing data frame. Also while processing received setup request/response, we preserve peer's 11ac capability retrieved from IEs. Patch also gets VHT parameters from config_station handlers and sets it to FW using TDLS config command. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: Amitkumar Karwar Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11ac.c | 83 ++++++++++++- drivers/net/wireless/mwifiex/11ac.h | 2 + drivers/net/wireless/mwifiex/fw.h | 5 + drivers/net/wireless/mwifiex/main.h | 22 ++++ drivers/net/wireless/mwifiex/sta_cmd.c | 20 +++ drivers/net/wireless/mwifiex/tdls.c | 221 ++++++++++++++++++++++++++++++++- 6 files changed, 346 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c index 5895bc8978a5..bb43251c18f2 100644 --- a/drivers/net/wireless/mwifiex/11ac.c +++ b/drivers/net/wireless/mwifiex/11ac.c @@ -108,9 +108,8 @@ mwifiex_fill_vht_cap_info(struct mwifiex_private *priv, cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg); } -static void -mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, - struct ieee80211_vht_cap *vht_cap, u8 bands) +void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, + struct ieee80211_vht_cap *vht_cap, u8 bands) { struct mwifiex_adapter *adapter = priv->adapter; u16 mcs_map_user, mcs_map_resp, mcs_map_result; @@ -305,3 +304,81 @@ void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv) return; } + +bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv) +{ + struct mwifiex_bssdescriptor *bss_desc; + struct ieee80211_vht_operation *vht_oper; + + bss_desc = &priv->curr_bss_params.bss_descriptor; + vht_oper = bss_desc->bcn_vht_oper; + + if (!bss_desc->bcn_vht_cap || !vht_oper) + return false; + + if (vht_oper->chan_width == IEEE80211_VHT_CHANWIDTH_USE_HT) + return false; + + return true; +} + +u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, + u32 pri_chan, u8 chan_bw) +{ + u8 center_freq_idx = 0; + + if (band & BAND_AAC) { + switch (pri_chan) { + case 36: + case 40: + case 44: + case 48: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 42; + break; + case 52: + case 56: + case 60: + case 64: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 58; + else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ) + center_freq_idx = 50; + break; + case 100: + case 104: + case 108: + case 112: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 106; + break; + case 116: + case 120: + case 124: + case 128: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 122; + else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ) + center_freq_idx = 114; + break; + case 132: + case 136: + case 140: + case 144: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 138; + break; + case 149: + case 153: + case 157: + case 161: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 155; + break; + default: + center_freq_idx = 42; + } + } + + return center_freq_idx; +} diff --git a/drivers/net/wireless/mwifiex/11ac.h b/drivers/net/wireless/mwifiex/11ac.h index 7c2c69b5b3eb..0b02cb6cfcb4 100644 --- a/drivers/net/wireless/mwifiex/11ac.h +++ b/drivers/net/wireless/mwifiex/11ac.h @@ -40,4 +40,6 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action, struct mwifiex_11ac_vht_cfg *cfg); +void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, + struct ieee80211_vht_cap *vht_cap, u8 bands); #endif /* _MWIFIEX_11AC_H_ */ diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 8c119bc93899..2344abdeaf6d 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -1356,6 +1356,11 @@ struct mwifiex_ie_types_vhtcap { struct ieee80211_vht_cap vht_cap; } __packed; +struct mwifiex_ie_types_aid { + struct mwifiex_ie_types_header header; + __le16 aid; +} __packed; + struct mwifiex_ie_types_oper_mode_ntf { struct mwifiex_ie_types_header header; u8 oper_mode; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 6d49d99045c0..91df7ee4c612 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -273,6 +273,21 @@ struct ieee_types_extcap { u8 ext_capab[8]; } __packed; +struct ieee_types_vht_cap { + struct ieee_types_header ieee_hdr; + struct ieee80211_vht_cap vhtcap; +} __packed; + +struct ieee_types_vht_oper { + struct ieee_types_header ieee_hdr; + struct ieee80211_vht_operation vhtoper; +} __packed; + +struct ieee_types_aid { + struct ieee_types_header ieee_hdr; + u16 aid; +} __packed; + struct mwifiex_bssdescriptor { u8 mac_address[ETH_ALEN]; struct cfg80211_ssid ssid; @@ -603,10 +618,13 @@ struct mwifiex_tdls_capab { u8 rates_len; u8 qos_info; u8 coex_2040; + u16 aid; struct ieee80211_ht_cap ht_capb; struct ieee80211_ht_operation ht_oper; struct ieee_types_extcap extcap; struct ieee_types_generic rsn_ie; + struct ieee80211_vht_cap vhtcap; + struct ieee80211_vht_operation vhtoper; }; /* This is AP/TDLS specific structure which stores information @@ -617,6 +635,7 @@ struct mwifiex_sta_node { u8 mac_addr[ETH_ALEN]; u8 is_wmm_enabled; u8 is_11n_enabled; + u8 is_11ac_enabled; u8 ampdu_sta[MAX_NUM_TID]; u16 rx_seq[MAX_NUM_TID]; u16 max_amsdu; @@ -1215,6 +1234,9 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, u8 *buf, int len); int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action); int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac); +bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv); +u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, + u32 pri_chan, u8 chan_bw); #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 8f1bcc3255dd..b10425c80555 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -1292,6 +1292,8 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, struct mwifiex_ie_types_htcap *ht_capab; struct mwifiex_ie_types_qos_info *wmm_qos_info; struct mwifiex_ie_types_extcap *extcap; + struct mwifiex_ie_types_vhtcap *vht_capab; + struct mwifiex_ie_types_aid *aid; u8 *pos, qos_info; u16 config_len = 0; struct station_parameters *params = priv->sta_params; @@ -1370,6 +1372,24 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, config_len += sizeof(struct mwifiex_ie_types_extcap) + params->ext_capab_len; } + if (params->vht_capa) { + vht_capab = (struct mwifiex_ie_types_vhtcap *)(pos + + config_len); + vht_capab->header.type = + cpu_to_le16(WLAN_EID_VHT_CAPABILITY); + vht_capab->header.len = + cpu_to_le16(sizeof(struct ieee80211_vht_cap)); + memcpy(&vht_capab->vht_cap, params->vht_capa, + sizeof(struct ieee80211_vht_cap)); + config_len += sizeof(struct mwifiex_ie_types_vhtcap); + } + if (params->aid) { + aid = (struct mwifiex_ie_types_aid *)(pos + config_len); + aid->header.type = cpu_to_le16(WLAN_EID_AID); + aid->header.len = cpu_to_le16(sizeof(params->aid)); + aid->aid = cpu_to_le16(params->aid); + config_len += sizeof(struct mwifiex_ie_types_aid); + } break; default: diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index 243beaba9160..770dcd727b5c 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -19,6 +19,7 @@ #include "wmm.h" #include "11n.h" #include "11n_rxreorder.h" +#include "11ac.h" #define TDLS_REQ_FIX_LEN 6 #define TDLS_RESP_FIX_LEN 8 @@ -151,7 +152,156 @@ mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv, return 0; } -static void mwifiex_tdls_add_ext_capab(struct sk_buff *skb) +static void mwifiex_tdls_add_aid(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct ieee_types_assoc_rsp *assoc_rsp; + u8 *pos; + + assoc_rsp = (struct ieee_types_assoc_rsp *)&priv->assoc_rsp_buf; + pos = (void *)skb_put(skb, 4); + *pos++ = WLAN_EID_AID; + *pos++ = 2; + *pos++ = le16_to_cpu(assoc_rsp->a_id); + + return; +} + +static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct ieee80211_vht_cap vht_cap; + u8 *pos; + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); + *pos++ = WLAN_EID_VHT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_vht_cap); + + memset(&vht_cap, 0, sizeof(struct ieee80211_vht_cap)); + + mwifiex_fill_vht_cap_tlv(priv, &vht_cap, priv->curr_bss_params.band); + memcpy(pos, &vht_cap, sizeof(struct ieee80211_ht_cap)); + + return 0; +} + +static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, + u8 *mac, struct sk_buff *skb) +{ + struct mwifiex_bssdescriptor *bss_desc; + struct ieee80211_vht_operation *vht_oper; + struct ieee80211_vht_cap *vht_cap, *ap_vht_cap = NULL; + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_adapter *adapter = priv->adapter; + u8 supp_chwd_set, peer_supp_chwd_set; + u8 *pos, ap_supp_chwd_set, chan_bw; + u16 mcs_map_user, mcs_map_resp, mcs_map_result; + u16 mcs_user, mcs_resp, nss; + u32 usr_vht_cap_info; + + bss_desc = &priv->curr_bss_params.bss_descriptor; + + sta_ptr = mwifiex_get_sta_entry(priv, mac); + if (unlikely(!sta_ptr)) { + dev_warn(adapter->dev, "TDLS peer station not found in list\n"); + return -1; + } + + if (!mwifiex_is_bss_in_11ac_mode(priv)) { + if (sta_ptr->tdls_cap.extcap.ext_capab[7] & + WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { + dev_dbg(adapter->dev, + "TDLS peer doesn't support wider bandwitdh\n"); + return 0; + } + } else { + ap_vht_cap = bss_desc->bcn_vht_cap; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_operation) + 2); + *pos++ = WLAN_EID_VHT_OPERATION; + *pos++ = sizeof(struct ieee80211_vht_operation); + vht_oper = (struct ieee80211_vht_operation *)pos; + + if (bss_desc->bss_band & BAND_A) + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; + + /* find the minmum bandwith between AP/TDLS peers */ + vht_cap = &sta_ptr->tdls_cap.vhtcap; + supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info); + peer_supp_chwd_set = + GET_VHTCAP_CHWDSET(le32_to_cpu(vht_cap->vht_cap_info)); + supp_chwd_set = min_t(u8, supp_chwd_set, peer_supp_chwd_set); + + /* We need check AP's bandwidth when TDLS_WIDER_BANDWIDTH is off */ + + if (ap_vht_cap && sta_ptr->tdls_cap.extcap.ext_capab[7] & + WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { + ap_supp_chwd_set = + GET_VHTCAP_CHWDSET(le32_to_cpu(ap_vht_cap->vht_cap_info)); + supp_chwd_set = min_t(u8, supp_chwd_set, ap_supp_chwd_set); + } + + switch (supp_chwd_set) { + case IEEE80211_VHT_CHANWIDTH_80MHZ: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; + break; + default: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + + mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map); + mcs_map_result = 0; + + for (nss = 1; nss <= 8; nss++) { + mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); + mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); + + if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || + (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); + else + SET_VHTNSSMCS(mcs_map_result, nss, + min_t(u16, mcs_user, mcs_resp)); + } + + vht_oper->basic_mcs_set = cpu_to_le16(mcs_map_result); + + switch (vht_oper->chan_width) { + case IEEE80211_VHT_CHANWIDTH_80MHZ: + chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + chan_bw = IEEE80211_VHT_CHANWIDTH_160MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + default: + chan_bw = IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + vht_oper->center_freq_seg1_idx = + mwifiex_get_center_freq_index(priv, BAND_AAC, + bss_desc->channel, + chan_bw); + + return 0; +} + +static void mwifiex_tdls_add_ext_capab(struct mwifiex_private *priv, + struct sk_buff *skb) { struct ieee_types_extcap *extcap; @@ -160,6 +310,9 @@ static void mwifiex_tdls_add_ext_capab(struct sk_buff *skb) extcap->ieee_hdr.len = 8; memset(extcap->ext_capab, 0, 8); extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED; + + if (priv->adapter->is_hw_11ac_capable) + extcap->ext_capab[7] |= WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED; } static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb) @@ -213,7 +366,16 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, return ret; } - mwifiex_tdls_add_ext_capab(skb); + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_capab(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + mwifiex_tdls_add_aid(priv, skb); + } + + mwifiex_tdls_add_ext_capab(priv, skb); mwifiex_tdls_add_qos_capab(skb); break; @@ -241,7 +403,16 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, return ret; } - mwifiex_tdls_add_ext_capab(skb); + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_capab(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + mwifiex_tdls_add_aid(priv, skb); + } + + mwifiex_tdls_add_ext_capab(priv, skb); mwifiex_tdls_add_qos_capab(skb); break; @@ -251,6 +422,13 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, skb_put(skb, sizeof(tf->u.setup_cfm)); tf->u.setup_cfm.status_code = cpu_to_le16(status_code); tf->u.setup_cfm.dialog_token = dialog_token; + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_oper(priv, peer, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + } break; case WLAN_TDLS_TEARDOWN: @@ -313,6 +491,11 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, sizeof(struct ieee80211_tdls_lnkie) + extra_ies_len; + if (priv->adapter->is_hw_11ac_capable) + skb_len += sizeof(struct ieee_types_vht_cap) + + sizeof(struct ieee_types_vht_oper) + + sizeof(struct ieee_types_aid); + skb = dev_alloc_skb(skb_len); if (!skb) { dev_err(priv->adapter->dev, @@ -435,7 +618,16 @@ mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer, return ret; } - mwifiex_tdls_add_ext_capab(skb); + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_capab(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + mwifiex_tdls_add_aid(priv, skb); + } + + mwifiex_tdls_add_ext_capab(priv, skb); mwifiex_tdls_add_qos_capab(skb); break; default: @@ -472,6 +664,11 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, 3 + /* Qos Info */ ETH_ALEN; /* Address4 */ + if (priv->adapter->is_hw_11ac_capable) + skb_len += sizeof(struct ieee_types_vht_cap) + + sizeof(struct ieee_types_vht_oper) + + sizeof(struct ieee_types_aid); + skb = dev_alloc_skb(skb_len); if (!skb) { dev_err(priv->adapter->dev, @@ -626,6 +823,22 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, case WLAN_EID_QOS_CAPA: sta_ptr->tdls_cap.qos_info = pos[2]; break; + case WLAN_EID_VHT_OPERATION: + if (priv->adapter->is_hw_11ac_capable) + memcpy(&sta_ptr->tdls_cap.vhtoper, pos, + sizeof(struct ieee80211_vht_operation)); + break; + case WLAN_EID_VHT_CAPABILITY: + if (priv->adapter->is_hw_11ac_capable) { + memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos, + sizeof(struct ieee80211_vht_cap)); + sta_ptr->is_11ac_enabled = 1; + } + break; + case WLAN_EID_AID: + if (priv->adapter->is_hw_11ac_capable) + sta_ptr->tdls_cap.aid = + le16_to_cpu(*(__le16 *)(pos + 2)); default: break; } -- cgit From b06c5321141382e72790077ef0caa1e42b69ff2d Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:30:40 -0800 Subject: mwifiex: separate BA params for TDLS link if 11ac is supported If TDLS link is 11ac enabled i.e. we as well as peer station supports VHT, configure seprate TX & RX window sizes during BA setup. So even if BSS does not support 11ac, we can use VHT capabilities and higher window sizes on direct link. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11n.c | 22 +++++++++++++++++++--- drivers/net/wireless/mwifiex/11n_rxreorder.c | 21 +++++++++++++++++++-- 2 files changed, 38 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index 616b1d6221b9..37677af8d2fc 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -537,16 +537,32 @@ void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) { struct host_cmd_ds_11n_addba_req add_ba_req; + struct mwifiex_sta_node *sta_ptr; + u32 tx_win_size = priv->add_ba_param.tx_win_size; static u8 dialog_tok; int ret; dev_dbg(priv->adapter->dev, "cmd: %s: tid %d\n", __func__, tid); + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->adapter->is_hw_11ac_capable && + memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) { + sta_ptr = mwifiex_get_sta_entry(priv, peer_mac); + if (!sta_ptr) { + dev_warn(priv->adapter->dev, + "BA setup with unknown TDLS peer %pM!\n", + peer_mac); + return -1; + } + if (sta_ptr->is_11ac_enabled) + tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE; + } + add_ba_req.block_ack_param_set = cpu_to_le16( (u16) ((tid << BLOCKACKPARAM_TID_POS) | - (priv->add_ba_param. - tx_win_size << BLOCKACKPARAM_WINSIZE_POS) | - IMMEDIATE_BLOCK_ACK)); + tx_win_size << BLOCKACKPARAM_WINSIZE_POS | + IMMEDIATE_BLOCK_ACK)); add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout); ++dialog_tok; diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index 3767399576b3..b361257fb65e 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c @@ -362,10 +362,28 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, *cmd_addba_req) { struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp; + struct mwifiex_sta_node *sta_ptr; + u32 rx_win_size = priv->add_ba_param.rx_win_size; u8 tid; int win_size; uint16_t block_ack_param_set; + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->adapter->is_hw_11ac_capable && + memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) { + sta_ptr = mwifiex_get_sta_entry(priv, + cmd_addba_req->peer_mac_addr); + if (!sta_ptr) { + dev_warn(priv->adapter->dev, + "BA setup with unknown TDLS peer %pM!\n", + cmd_addba_req->peer_mac_addr); + return -1; + } + if (sta_ptr->is_11ac_enabled) + rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; + } + cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN); @@ -382,8 +400,7 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK; /* We donot support AMSDU inside AMPDU, hence reset the bit */ block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; - block_ack_param_set |= (priv->add_ba_param.rx_win_size << - BLOCKACKPARAM_WINSIZE_POS); + block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS; add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set); win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set) & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) -- cgit From be104b916caf36af7e664b61149389b96c1c0ff6 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:30:41 -0800 Subject: mwifiex: disable all TDLS link during disconnection During deauthenticate/link lost event, disable all TDLS links as TDLS would not work when infra connection is not active. Also this will avoid an issue where ping to peer station doesn't work after reassociation to AP where we had created TDLS link in earlier association. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/main.h | 1 + drivers/net/wireless/mwifiex/sta_event.c | 4 ++++ drivers/net/wireless/mwifiex/tdls.c | 35 ++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 91df7ee4c612..82754ed94a8a 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -1234,6 +1234,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, u8 *buf, int len); int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action); int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac); +void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv); bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv); u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, u32 pri_chan, u8 chan_bw); diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index de4a6affe72e..92ff7b324b00 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -54,6 +54,10 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) priv->scan_block = false; + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + mwifiex_disable_all_tdls_links(priv); + /* Free Tx and Rx packets, report disconnect to upper layer */ mwifiex_clean_txrx(priv); diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index 770dcd727b5c..5efd456af571 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -1007,3 +1007,38 @@ int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac) return TDLS_NOT_SETUP; } + +void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + unsigned long flags; + + if (list_empty(&priv->sta_list)) + return; + + list_for_each_entry(sta_ptr, &priv->sta_list, list) { + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + + if (sta_ptr->is_11n_enabled) { + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + } + + mwifiex_restore_tdls_packets(priv, sta_ptr->mac_addr, + TDLS_LINK_TEARDOWN); + memcpy(&tdls_oper.peer_mac, sta_ptr->mac_addr, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; + if (mwifiex_send_cmd_async(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper)) + dev_warn(priv->adapter->dev, + "Disable link failed for TDLS peer %pM", + sta_ptr->mac_addr); + } + + mwifiex_del_all_sta_list(priv); +} -- cgit From 7f445d0435017ba0daeb83574028fc16c68df1f5 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:30:42 -0800 Subject: mwifiex: parse API version from FW This patch adds support to parse FW API version TLVs. Currently only API version for key_material is supported. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cmdevt.c | 45 ++++++++++++++++++++++++++++++++++- drivers/net/wireless/mwifiex/fw.h | 13 ++++++++++ drivers/net/wireless/mwifiex/init.c | 2 ++ drivers/net/wireless/mwifiex/main.h | 1 + 4 files changed, 60 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 7711b11a9812..21544602043c 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -1455,7 +1455,10 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, { struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec; struct mwifiex_adapter *adapter = priv->adapter; - int i; + struct mwifiex_ie_types_header *tlv; + struct hw_spec_fw_api_rev *api_rev; + u16 resp_size, api_id; + int i, left_len, parsed_len = 0; adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info); @@ -1513,6 +1516,46 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, adapter->is_hw_11ac_capable = false; } + resp_size = le16_to_cpu(resp->size) - S_DS_GEN; + if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) { + /* we have variable HW SPEC information */ + left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec); + while (left_len > sizeof(struct mwifiex_ie_types_header)) { + tlv = (void *)&hw_spec->tlvs + parsed_len; + switch (le16_to_cpu(tlv->type)) { + case TLV_TYPE_FW_API_REV: + api_rev = (struct hw_spec_fw_api_rev *)tlv; + api_id = le16_to_cpu(api_rev->api_id); + switch (api_id) { + case KEY_API_VER_ID: + adapter->fw_key_api_major_ver = + api_rev->major_ver; + adapter->fw_key_api_minor_ver = + api_rev->minor_ver; + dev_dbg(adapter->dev, + "fw_key_api v%d.%d\n", + adapter->fw_key_api_major_ver, + adapter->fw_key_api_minor_ver); + break; + default: + dev_warn(adapter->dev, + "Unknown FW api_id: %d\n", + api_id); + break; + } + break; + default: + dev_warn(adapter->dev, + "Unknown GET_HW_SPEC TLV type: %#x\n", + le16_to_cpu(tlv->type)); + break; + } + parsed_len += le16_to_cpu(tlv->len) + + sizeof(struct mwifiex_ie_types_header); + left_len -= parsed_len; + } + } + dev_dbg(adapter->dev, "info: GET_HW_SPEC: fw_release_number- %#x\n", adapter->fw_release_number); dev_dbg(adapter->dev, "info: GET_HW_SPEC: permanent addr: %pM\n", diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 2344abdeaf6d..5808f238f1cd 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -159,6 +159,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145) #define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146) #define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) +#define TLV_TYPE_FW_API_REV (PROPRIETARY_TLV_BASE_ID + 199) #define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 @@ -751,6 +752,17 @@ struct host_cmd_ds_802_11_ps_mode_enh { } params; } __packed; +enum FW_API_VER_ID { + KEY_API_VER_ID = 1, +}; + +struct hw_spec_fw_api_rev { + struct mwifiex_ie_types_header header; + __le16 api_id; + u8 major_ver; + u8 minor_ver; +} __packed; + struct host_cmd_ds_get_hw_spec { __le16 hw_if_version; __le16 version; @@ -772,6 +784,7 @@ struct host_cmd_ds_get_hw_spec { __le32 reserved_6; __le32 dot_11ac_dev_cap; __le32 dot_11ac_mcs_support; + u8 tlvs[0]; } __packed; struct host_cmd_ds_802_11_rssi_info { diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 9dc8059778a0..a4cd2cb066ed 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -283,6 +283,8 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX; adapter->empty_tx_q_cnt = 0; adapter->ext_scan = true; + adapter->fw_key_api_major_ver = 0; + adapter->fw_key_api_minor_ver = 0; } /* diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 82754ed94a8a..de115fa745cb 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -801,6 +801,7 @@ struct mwifiex_adapter { atomic_t pending_bridged_pkts; struct semaphore *card_sem; bool ext_scan; + u8 fw_key_api_major_ver, fw_key_api_minor_ver; }; int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); -- cgit From e57f1734d87aa0e9a00905ed08888f0c62f56227 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 7 Feb 2014 16:32:35 -0800 Subject: mwifiex: add key material v2 support This patch adds key material V2 support to mwifiex. Newer FW supports this feature and FW KEY API version is used to determine which command structure needs to be used. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/fw.h | 63 +++++++- drivers/net/wireless/mwifiex/ioctl.h | 3 + drivers/net/wireless/mwifiex/main.h | 1 + drivers/net/wireless/mwifiex/sta_cmd.c | 246 ++++++++++++++++++++++++++++- drivers/net/wireless/mwifiex/sta_cmdresp.c | 51 +++++- drivers/net/wireless/mwifiex/sta_ioctl.c | 36 ++++- 6 files changed, 388 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless/mwifiex') diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 5808f238f1cd..aa8abef58349 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -79,12 +79,21 @@ enum KEY_TYPE_ID { KEY_TYPE_ID_WAPI, KEY_TYPE_ID_AES_CMAC, }; + +#define WPA_PN_SIZE 8 +#define KEY_PARAMS_FIXED_LEN 10 +#define KEY_INDEX_MASK 0xf +#define FW_KEY_API_VER_MAJOR_V2 2 + #define KEY_MCAST BIT(0) #define KEY_UNICAST BIT(1) #define KEY_ENABLED BIT(2) +#define KEY_DEFAULT BIT(3) +#define KEY_TX_KEY BIT(4) +#define KEY_RX_KEY BIT(5) #define KEY_IGTK BIT(10) -#define WAPI_KEY_LEN 50 +#define WAPI_KEY_LEN (WLAN_KEY_LEN_SMS4 + PN_LEN + 2) #define MAX_POLL_TRIES 100 #define MAX_FIRMWARE_POLL_TRIES 100 @@ -159,6 +168,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145) #define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146) #define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) +#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156) #define TLV_TYPE_FW_API_REV (PROPRIETARY_TLV_BASE_ID + 199) #define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 @@ -701,6 +711,56 @@ struct mwifiex_cmac_param { u8 key[WLAN_KEY_LEN_AES_CMAC]; } __packed; +struct mwifiex_wep_param { + __le16 key_len; + u8 key[WLAN_KEY_LEN_WEP104]; +} __packed; + +struct mwifiex_tkip_param { + u8 pn[WPA_PN_SIZE]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_TKIP]; +} __packed; + +struct mwifiex_aes_param { + u8 pn[WPA_PN_SIZE]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_CCMP]; +} __packed; + +struct mwifiex_wapi_param { + u8 pn[PN_LEN]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_SMS4]; +} __packed; + +struct mwifiex_cmac_aes_param { + u8 ipn[IGTK_PN_LEN]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_AES_CMAC]; +} __packed; + +struct mwifiex_ie_type_key_param_set_v2 { + __le16 type; + __le16 len; + u8 mac_addr[ETH_ALEN]; + u8 key_idx; + u8 key_type; + __le16 key_info; + union { + struct mwifiex_wep_param wep; + struct mwifiex_tkip_param tkip; + struct mwifiex_aes_param aes; + struct mwifiex_wapi_param wapi; + struct mwifiex_cmac_aes_param cmac_aes; + } key_params; +} __packed; + +struct host_cmd_ds_802_11_key_material_v2 { + __le16 action; + struct mwifiex_ie_type_key_param_set_v2 key_param_set; +} __packed; + struct host_cmd_ds_802_11_key_material { __le16 action; struct mwifiex_ie_type_key_param_set key_param_set; @@ -1742,6 +1802,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_11n_cfg htcfg; struct host_cmd_ds_wmm_get_status get_wmm_status; struct host_cmd_ds_802_11_key_material key_material; + struct host_cmd_ds_802_11_key_material_v2 key_material_v2; 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; diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index 6ed1e13d7b40..5974642f38b1 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -236,7 +236,10 @@ struct mwifiex_ds_encrypt_key { u8 mac_addr[ETH_ALEN]; u32 is_wapi_key; u8 pn[PN_LEN]; /* packet number */ + u8 pn_len; u8 is_igtk_key; + u8 is_current_wep_key; + u8 is_rx_seq_valid; }; struct mwifiex_power_cfg { diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index de115fa745cb..407f8eada720 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -469,6 +469,7 @@ struct mwifiex_private { u8 wpa_ie_len; u8 wpa_is_gtk_set; struct host_cmd_ds_802_11_key_material aes_key; + struct host_cmd_ds_802_11_key_material_v2 aes_key_v2; u8 wapi_ie[256]; u8 wapi_ie_len; u8 *wps_ie; diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index b10425c80555..5aa3d39e48bc 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -532,8 +532,228 @@ mwifiex_set_keyparamset_wep(struct mwifiex_private *priv, return 0; } +/* This function populates key material v2 command + * to set network key for AES & CMAC AES. + */ +static int mwifiex_set_aes_key_v2(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_ds_encrypt_key *enc_key, + struct host_cmd_ds_802_11_key_material_v2 *km) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u16 size, len = KEY_PARAMS_FIXED_LEN; + + if (enc_key->is_igtk_key) { + dev_dbg(adapter->dev, "%s: Set CMAC AES Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.cmac_aes.ipn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_info &= cpu_to_le16(~KEY_MCAST); + km->key_param_set.key_info |= cpu_to_le16(KEY_IGTK); + km->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC; + km->key_param_set.key_params.cmac_aes.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.cmac_aes.key, + enc_key->key_material, enc_key->key_len); + len += sizeof(struct mwifiex_cmac_aes_param); + } else { + dev_dbg(adapter->dev, "%s: Set AES Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.aes.pn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_type = KEY_TYPE_ID_AES; + km->key_param_set.key_params.aes.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.aes.key, + enc_key->key_material, enc_key->key_len); + len += sizeof(struct mwifiex_aes_param); + } + + km->key_param_set.len = cpu_to_le16(len); + size = len + sizeof(struct mwifiex_ie_types_header) + + sizeof(km->action) + S_DS_GEN; + cmd->size = cpu_to_le16(size); + + return 0; +} + +/* This function prepares command to set/get/reset network key(s). + * This function prepares key material command for V2 format. + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting WEP keys, WAPI keys or WPA keys along with required + * encryption (TKIP, AES) (as required) + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_802_11_key_material_v2(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + struct mwifiex_ds_encrypt_key *enc_key) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u8 *mac = enc_key->mac_addr; + u16 key_info, len = KEY_PARAMS_FIXED_LEN; + struct host_cmd_ds_802_11_key_material_v2 *km = + &cmd->params.key_material_v2; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); + km->action = cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_GET) { + dev_dbg(adapter->dev, "%s: Get key\n", __func__); + km->key_param_set.key_idx = + enc_key->key_index & KEY_INDEX_MASK; + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + + if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) + key_info = KEY_UNICAST; + else + key_info = KEY_MCAST; + + if (enc_key->is_igtk_key) + key_info |= KEY_IGTK; + + km->key_param_set.key_info = cpu_to_le16(key_info); + + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(km->action)); + return 0; + } + + memset(&km->key_param_set, 0, + sizeof(struct mwifiex_ie_type_key_param_set_v2)); + + if (enc_key->key_disable) { + dev_dbg(adapter->dev, "%s: Remove key\n", __func__); + km->action = cpu_to_le16(HostCmd_ACT_GEN_REMOVE); + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); + km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; + key_info = KEY_MCAST | KEY_UNICAST; + km->key_param_set.key_info = cpu_to_le16(key_info); + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(km->action)); + return 0; + } + + km->action = cpu_to_le16(HostCmd_ACT_GEN_SET); + km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + key_info = KEY_ENABLED; + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + + if (enc_key->key_len <= WLAN_KEY_LEN_WEP104) { + dev_dbg(adapter->dev, "%s: Set WEP Key\n", __func__); + len += sizeof(struct mwifiex_wep_param); + km->key_param_set.len = cpu_to_le16(len); + km->key_param_set.key_type = KEY_TYPE_ID_WEP; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + key_info |= KEY_MCAST | KEY_UNICAST; + } else { + if (enc_key->is_current_wep_key) { + key_info |= KEY_MCAST | KEY_UNICAST; + if (km->key_param_set.key_idx == + (priv->wep_key_curr_index & KEY_INDEX_MASK)) + key_info |= KEY_DEFAULT; + } else { + if (mac) { + if (is_broadcast_ether_addr(mac)) + key_info |= KEY_MCAST; + else + key_info |= KEY_UNICAST | + KEY_DEFAULT; + } else { + key_info |= KEY_MCAST; + } + } + } + km->key_param_set.key_info = cpu_to_le16(key_info); + + km->key_param_set.key_params.wep.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.wep.key, + enc_key->key_material, enc_key->key_len); + + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + return 0; + } + + if (is_broadcast_ether_addr(mac)) + key_info |= KEY_MCAST | KEY_RX_KEY; + else + key_info |= KEY_UNICAST | KEY_TX_KEY | KEY_RX_KEY; + + if (enc_key->is_wapi_key) { + dev_dbg(adapter->dev, "%s: Set WAPI Key\n", __func__); + km->key_param_set.key_type = KEY_TYPE_ID_WAPI; + memcpy(km->key_param_set.key_params.wapi.pn, enc_key->pn, + PN_LEN); + km->key_param_set.key_params.wapi.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.wapi.key, + enc_key->key_material, enc_key->key_len); + if (is_broadcast_ether_addr(mac)) + priv->sec_info.wapi_key_on = true; + + if (!priv->sec_info.wapi_key_on) + key_info |= KEY_DEFAULT; + km->key_param_set.key_info = cpu_to_le16(key_info); + + len += sizeof(struct mwifiex_wapi_param); + km->key_param_set.len = cpu_to_le16(len); + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + return 0; + } + + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { + key_info |= KEY_DEFAULT; + /* Enable unicast bit for WPA-NONE/ADHOC_AES */ + if (!priv->sec_info.wpa2_enabled && + !is_broadcast_ether_addr(mac)) + key_info |= KEY_UNICAST; + } else { + /* Enable default key for WPA/WPA2 */ + if (!priv->wpa_is_gtk_set) + key_info |= KEY_DEFAULT; + } + + km->key_param_set.key_info = cpu_to_le16(key_info); + + if (enc_key->key_len == WLAN_KEY_LEN_CCMP) + return mwifiex_set_aes_key_v2(priv, cmd, enc_key, km); + + if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { + dev_dbg(adapter->dev, "%s: Set TKIP Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.tkip.pn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_type = KEY_TYPE_ID_TKIP; + km->key_param_set.key_params.tkip.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.tkip.key, + enc_key->key_material, enc_key->key_len); + + len += sizeof(struct mwifiex_tkip_param); + km->key_param_set.len = cpu_to_le16(len); + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + } + + return 0; +} + /* * This function prepares command to set/get/reset network key(s). + * This function prepares key material command for V1 format. * * Preparation includes - * - Setting command ID, action and proper size @@ -542,10 +762,10 @@ mwifiex_set_keyparamset_wep(struct mwifiex_private *priv, * - Ensuring correct endian-ness */ static int -mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u32 cmd_oid, - struct mwifiex_ds_encrypt_key *enc_key) +mwifiex_cmd_802_11_key_material_v1(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + struct mwifiex_ds_encrypt_key *enc_key) { struct host_cmd_ds_802_11_key_material *key_material = &cmd->params.key_material; @@ -724,6 +944,24 @@ mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, return ret; } +/* Wrapper function for setting network key depending upon FW KEY API version */ +static int +mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + struct mwifiex_ds_encrypt_key *enc_key) +{ + if (priv->adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) + return mwifiex_cmd_802_11_key_material_v2(priv, cmd, + cmd_action, cmd_oid, + enc_key); + + else + return mwifiex_cmd_802_11_key_material_v1(priv, cmd, + cmd_action, cmd_oid, + enc_key); +} + /* * This function prepares command to set/get 11d domain information. * diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 48abab6bfce9..1c5e18804074 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -562,13 +562,13 @@ static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv, } /* - * This function handles the command response of set/get key material. + * This function handles the command response of set/get v1 key material. * * Handling includes updating the driver parameters to reflect the * changes. */ -static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) +static int mwifiex_ret_802_11_key_material_v1(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) { struct host_cmd_ds_802_11_key_material *key = &resp->params.key_material; @@ -590,6 +590,51 @@ static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, return 0; } +/* + * This function handles the command response of set/get v2 key material. + * + * Handling includes updating the driver parameters to reflect the + * changes. + */ +static int mwifiex_ret_802_11_key_material_v2(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_key_material_v2 *key_v2; + __le16 len; + + key_v2 = &resp->params.key_material_v2; + if (le16_to_cpu(key_v2->action) == HostCmd_ACT_GEN_SET) { + if ((le16_to_cpu(key_v2->key_param_set.key_info) & KEY_MCAST)) { + dev_dbg(priv->adapter->dev, "info: key: GTK is set\n"); + priv->wpa_is_gtk_set = true; + priv->scan_block = false; + } + } + + if (key_v2->key_param_set.key_type != KEY_TYPE_ID_AES) + return 0; + + memset(priv->aes_key_v2.key_param_set.key_params.aes.key, 0, + WLAN_KEY_LEN_CCMP); + priv->aes_key_v2.key_param_set.key_params.aes.key_len = + key_v2->key_param_set.key_params.aes.key_len; + len = priv->aes_key_v2.key_param_set.key_params.aes.key_len; + memcpy(priv->aes_key_v2.key_param_set.key_params.aes.key, + key_v2->key_param_set.key_params.aes.key, le16_to_cpu(len)); + + return 0; +} + +/* Wrapper function for processing response of key material command */ +static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + if (priv->adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) + return mwifiex_ret_802_11_key_material_v2(priv, resp); + else + return mwifiex_ret_802_11_key_material_v1(priv, resp); +} + /* * This function handles the command response of get 11d domain information. */ diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index c3d3ea55d605..b393d55b3aa0 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -865,6 +865,7 @@ static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv, static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, struct mwifiex_ds_encrypt_key *encrypt_key) { + struct mwifiex_adapter *adapter = priv->adapter; int ret; struct mwifiex_wep_key *wep_key; int index; @@ -879,10 +880,17 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, /* Copy the required key as the current key */ wep_key = &priv->wep_key[index]; if (!wep_key->key_length) { - dev_err(priv->adapter->dev, + dev_err(adapter->dev, "key not set, so cannot enable it\n"); return -1; } + + if (adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) { + memcpy(encrypt_key->key_material, + wep_key->key_material, wep_key->key_length); + encrypt_key->key_len = wep_key->key_length; + } + priv->wep_key_curr_index = (u16) index; priv->sec_info.wep_enabled = 1; } else { @@ -897,13 +905,25 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, priv->sec_info.wep_enabled = 1; } if (wep_key->key_length) { + void *enc_key; + + if (encrypt_key->key_disable) + memset(&priv->wep_key[index], 0, + sizeof(struct mwifiex_wep_key)); + + if (adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) + enc_key = encrypt_key; + else + enc_key = NULL; + /* Send request to firmware */ ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, 0, NULL); + HostCmd_ACT_GEN_SET, 0, enc_key); if (ret) return ret; } + if (priv->sec_info.wep_enabled) priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; else @@ -1044,19 +1064,27 @@ int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key)); encrypt_key.key_len = key_len; + encrypt_key.key_index = key_index; if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC) encrypt_key.is_igtk_key = true; if (!disable) { - encrypt_key.key_index = key_index; if (key_len) memcpy(encrypt_key.key_material, key, key_len); + else + encrypt_key.is_current_wep_key = true; + if (mac_addr) memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); - if (kp && kp->seq && kp->seq_len) + if (kp && kp->seq && kp->seq_len) { memcpy(encrypt_key.pn, kp->seq, kp->seq_len); + encrypt_key.pn_len = kp->seq_len; + encrypt_key.is_rx_seq_valid = true; + } } else { + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) + return 0; encrypt_key.key_disable = true; if (mac_addr) memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); -- cgit