]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
wil6210: off channel transmit management frames in AP mode
authorAhmad Masri <amasri@codeaurora.org>
Tue, 24 Jul 2018 07:44:38 +0000 (10:44 +0300)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 31 Jul 2018 08:01:52 +0000 (11:01 +0300)
Currently wil6210 ignores the channel field in the
cfg80211_mgmt_tx_params struct for wil_cfg80211_ops mgmt_tx operation
and sends all management frames on the serving channel.

Add support for off-channel transmission of management frames
(WIPHY_FLAG_OFFCHAN_TX) in AP mode. This is useful in enterprise APs
for sending custom probe request frames.

Signed-off-by: Ahmad Masri <amasri@codeaurora.org>
Signed-off-by: Maya Erez <merez@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c

index 3c754abb008c25d265ef3de1a5a13b86c28214da..f79c337105cb465f6895f39e7d37d7d1d624db74 100644 (file)
@@ -1090,18 +1090,51 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        int rc;
        bool tx_status;
 
-       /* Note, currently we do not support the "wait" parameter, user-space
-        * must call remain_on_channel before mgmt_tx or listen on a channel
-        * another way (AP/PCP or connected station)
-        * in addition we need to check if specified "chan" argument is
-        * different from currently "listened" channel and fail if it is.
+       wil_dbg_misc(wil, "mgmt_tx: channel %d offchan %d, wait %d\n",
+                    params->chan ? params->chan->hw_value : -1,
+                    params->offchan,
+                    params->wait);
+
+       /* Note, currently we support the "wait" parameter only on AP mode.
+        * In other modes, user-space must call remain_on_channel before
+        * mgmt_tx or listen on a channel other than active one.
         */
 
-       rc = wmi_mgmt_tx(vif, buf, len);
-       tx_status = (rc == 0);
+       if (params->chan && params->chan->hw_value == 0) {
+               wil_err(wil, "invalid channel\n");
+               return -EINVAL;
+       }
+
+       if (wdev->iftype != NL80211_IFTYPE_AP) {
+               wil_dbg_misc(wil,
+                            "send WMI_SW_TX_REQ_CMDID on non-AP interfaces\n");
+               rc = wmi_mgmt_tx(vif, buf, len);
+               goto out;
+       }
 
+       if (!params->chan || params->chan->hw_value == vif->channel) {
+               wil_dbg_misc(wil,
+                            "send WMI_SW_TX_REQ_CMDID for on-channel\n");
+               rc = wmi_mgmt_tx(vif, buf, len);
+               goto out;
+       }
+
+       if (params->offchan == 0) {
+               wil_err(wil,
+                       "invalid channel params: current %d requested %d, off-channel not allowed\n",
+                       vif->channel, params->chan->hw_value);
+               return -EBUSY;
+       }
+
+       /* use the wmi_mgmt_tx_ext only on AP mode and off-channel */
+       rc = wmi_mgmt_tx_ext(vif, buf, len, params->chan->hw_value,
+                            params->wait);
+
+out:
+       tx_status = (rc == 0);
        cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len,
                                tx_status, GFP_KERNEL);
+
        return rc;
 }
 
index 64de67975866ca02872ef79bc047d453210e8f90..7ad22dfd66985c3800a5bc9a60dae8e8309c0aa8 100644 (file)
@@ -1130,6 +1130,9 @@ void wil_refresh_fw_capabilities(struct wil6210_priv *wil)
                wiphy->max_sched_scan_plans = WMI_MAX_PLANS_NUM;
        }
 
+       if (test_bit(WMI_FW_CAPABILITY_TX_REQ_EXT, wil->fw_capabilities))
+               wiphy->flags |= WIPHY_FLAG_OFFCHAN_TX;
+
        if (wil->platform_ops.set_features) {
                features = (test_bit(WMI_FW_CAPABILITY_REF_CLOCK_CONTROL,
                                     wil->fw_capabilities) &&
index 542075da69f123e6bd87389760bb7a88b311c161..9fc51f79d75b3b0eaf4b60999018726edd00fc39 100644 (file)
@@ -1350,6 +1350,8 @@ int wmi_start_sched_scan(struct wil6210_priv *wil,
                         struct cfg80211_sched_scan_request *request);
 int wmi_stop_sched_scan(struct wil6210_priv *wil);
 int wmi_mgmt_tx(struct wil6210_vif *vif, const u8 *buf, size_t len);
+int wmi_mgmt_tx_ext(struct wil6210_vif *vif, const u8 *buf, size_t len,
+                   u8 channel, u16 duration_ms);
 
 int reverse_memcmp(const void *cs, const void *ct, size_t count);
 
index 45a71fd7cc28208b1c77052a3e23584b7e365049..42c02a20ec97cafa5336c63ccae019c5735021ae 100644 (file)
@@ -466,6 +466,8 @@ static const char *cmdid2name(u16 cmdid)
                return "WMI_CFG_DEF_RX_OFFLOAD_CMD";
        case WMI_LINK_STATS_CMDID:
                return "WMI_LINK_STATS_CMD";
+       case WMI_SW_TX_REQ_EXT_CMDID:
+               return "WMI_SW_TX_REQ_EXT_CMDID";
        default:
                return "Untracked CMD";
        }
@@ -3114,6 +3116,60 @@ int wmi_mgmt_tx(struct wil6210_vif *vif, const u8 *buf, size_t len)
        return rc;
 }
 
+int wmi_mgmt_tx_ext(struct wil6210_vif *vif, const u8 *buf, size_t len,
+                   u8 channel, u16 duration_ms)
+{
+       size_t total;
+       struct wil6210_priv *wil = vif_to_wil(vif);
+       struct ieee80211_mgmt *mgmt_frame = (void *)buf;
+       struct wmi_sw_tx_req_ext_cmd *cmd;
+       struct {
+               struct wmi_cmd_hdr wmi;
+               struct wmi_sw_tx_complete_event evt;
+       } __packed evt = {
+               .evt = {.status = WMI_FW_STATUS_FAILURE},
+       };
+       int rc;
+
+       wil_dbg_wmi(wil, "mgmt_tx_ext mid %d channel %d duration %d\n",
+                   vif->mid, channel, duration_ms);
+       wil_hex_dump_wmi("mgmt_tx_ext frame ", DUMP_PREFIX_OFFSET, 16, 1, buf,
+                        len, true);
+
+       if (len < sizeof(struct ieee80211_hdr_3addr)) {
+               wil_err(wil, "short frame. len %zu\n", len);
+               return -EINVAL;
+       }
+
+       total = sizeof(*cmd) + len;
+       if (total < len) {
+               wil_err(wil, "mgmt_tx_ext invalid len %zu\n", len);
+               return -EINVAL;
+       }
+
+       cmd = kzalloc(total, GFP_KERNEL);
+       if (!cmd)
+               return -ENOMEM;
+
+       memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN);
+       cmd->len = cpu_to_le16(len);
+       memcpy(cmd->payload, buf, len);
+       cmd->channel = channel - 1;
+       cmd->duration_ms = cpu_to_le16(duration_ms);
+
+       rc = wmi_call(wil, WMI_SW_TX_REQ_EXT_CMDID, vif->mid, cmd, total,
+                     WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
+       if (!rc && evt.evt.status != WMI_FW_STATUS_SUCCESS) {
+               wil_err(wil, "mgmt_tx_ext failed with status %d\n",
+                       evt.evt.status);
+               rc = -EINVAL;
+       }
+
+       kfree(cmd);
+
+       return rc;
+}
+
 int wil_wmi_tx_sring_cfg(struct wil6210_priv *wil, int ring_id)
 {
        int rc;