mwifiex: add cfg80211 change_station handler support
authorAvinash Patil <patila@marvell.com>
Sat, 8 Feb 2014 00:30:34 +0000 (16:30 -0800)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 12 Feb 2014 20:36:21 +0000 (15:36 -0500)
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 <patila@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/tdls.c

index cac8aea..436ba43 100644 (file)
@@ -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
index 3180fc6..8c119bc 100644 (file)
@@ -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;
index d35c995..c8c30a4 100644 (file)
@@ -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 {
index 1e36fa7..8f1bcc3 100644 (file)
@@ -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;
 }
index 396b936..48abab6 100644 (file)
@@ -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);
index 1d5ed70..f37862b 100644 (file)
@@ -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;
 }