|
@@ -38,6 +38,7 @@ MODULE_PARM_DESC(led_id,
|
|
|
" 60G device led enablement. Set the led ID (0-2) to enable");
|
|
|
|
|
|
#define WIL_WAIT_FOR_SUSPEND_RESUME_COMP 200
|
|
|
+#define WIL_WMI_CALL_GENERAL_TO_MS 100
|
|
|
|
|
|
/**
|
|
|
* WMI event receiving - theory of operations
|
|
@@ -314,6 +315,10 @@ static const char *cmdid2name(u16 cmdid)
|
|
|
return "WMI_LINK_MAINTAIN_CFG_WRITE_CMD";
|
|
|
case WMI_LO_POWER_CALIB_FROM_OTP_CMDID:
|
|
|
return "WMI_LO_POWER_CALIB_FROM_OTP_CMD";
|
|
|
+ case WMI_START_SCHED_SCAN_CMDID:
|
|
|
+ return "WMI_START_SCHED_SCAN_CMD";
|
|
|
+ case WMI_STOP_SCHED_SCAN_CMDID:
|
|
|
+ return "WMI_STOP_SCHED_SCAN_CMD";
|
|
|
default:
|
|
|
return "Untracked CMD";
|
|
|
}
|
|
@@ -428,6 +433,12 @@ static const char *eventid2name(u16 eventid)
|
|
|
return "WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENT";
|
|
|
case WMI_LO_POWER_CALIB_FROM_OTP_EVENTID:
|
|
|
return "WMI_LO_POWER_CALIB_FROM_OTP_EVENT";
|
|
|
+ case WMI_START_SCHED_SCAN_EVENTID:
|
|
|
+ return "WMI_START_SCHED_SCAN_EVENT";
|
|
|
+ case WMI_STOP_SCHED_SCAN_EVENTID:
|
|
|
+ return "WMI_STOP_SCHED_SCAN_EVENT";
|
|
|
+ case WMI_SCHED_SCAN_RESULT_EVENTID:
|
|
|
+ return "WMI_SCHED_SCAN_RESULT_EVENT";
|
|
|
default:
|
|
|
return "Untracked EVENT";
|
|
|
}
|
|
@@ -1066,6 +1077,75 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
|
|
|
spin_unlock_bh(&sta->tid_rx_lock);
|
|
|
}
|
|
|
|
|
|
+static void
|
|
|
+wmi_evt_sched_scan_result(struct wil6210_priv *wil, int id, void *d, int len)
|
|
|
+{
|
|
|
+ struct wmi_sched_scan_result_event *data = d;
|
|
|
+ struct wiphy *wiphy = wil_to_wiphy(wil);
|
|
|
+ struct ieee80211_mgmt *rx_mgmt_frame =
|
|
|
+ (struct ieee80211_mgmt *)data->payload;
|
|
|
+ int flen = len - offsetof(struct wmi_sched_scan_result_event, payload);
|
|
|
+ int ch_no;
|
|
|
+ u32 freq;
|
|
|
+ struct ieee80211_channel *channel;
|
|
|
+ s32 signal;
|
|
|
+ __le16 fc;
|
|
|
+ u32 d_len;
|
|
|
+ struct cfg80211_bss *bss;
|
|
|
+
|
|
|
+ if (flen < 0) {
|
|
|
+ wil_err(wil, "sched scan result event too short, len %d\n",
|
|
|
+ len);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ d_len = le32_to_cpu(data->info.len);
|
|
|
+ if (d_len != flen) {
|
|
|
+ wil_err(wil,
|
|
|
+ "sched scan result length mismatch, d_len %d should be %d\n",
|
|
|
+ d_len, flen);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ fc = rx_mgmt_frame->frame_control;
|
|
|
+ if (!ieee80211_is_probe_resp(fc)) {
|
|
|
+ wil_err(wil, "sched scan result invalid frame, fc 0x%04x\n",
|
|
|
+ fc);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ ch_no = data->info.channel + 1;
|
|
|
+ freq = ieee80211_channel_to_frequency(ch_no, NL80211_BAND_60GHZ);
|
|
|
+ channel = ieee80211_get_channel(wiphy, freq);
|
|
|
+ if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities))
|
|
|
+ signal = 100 * data->info.rssi;
|
|
|
+ else
|
|
|
+ signal = data->info.sqi;
|
|
|
+
|
|
|
+ wil_dbg_wmi(wil, "sched scan result: channel %d MCS %d RSSI %d\n",
|
|
|
+ data->info.channel, data->info.mcs, data->info.rssi);
|
|
|
+ wil_dbg_wmi(wil, "len %d qid %d mid %d cid %d\n",
|
|
|
+ d_len, data->info.qid, data->info.mid, data->info.cid);
|
|
|
+ wil_hex_dump_wmi("PROBE ", DUMP_PREFIX_OFFSET, 16, 1, rx_mgmt_frame,
|
|
|
+ d_len, true);
|
|
|
+
|
|
|
+ if (!channel) {
|
|
|
+ wil_err(wil, "Frame on unsupported channel\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
|
|
|
+ d_len, signal, GFP_KERNEL);
|
|
|
+ if (bss) {
|
|
|
+ wil_dbg_wmi(wil, "Added BSS %pM\n", rx_mgmt_frame->bssid);
|
|
|
+ cfg80211_put_bss(wiphy, bss);
|
|
|
+ } else {
|
|
|
+ wil_err(wil, "cfg80211_inform_bss_frame() failed\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ cfg80211_sched_scan_results(wiphy, 0);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* Some events are ignored for purpose; and need not be interpreted as
|
|
|
* "unhandled events"
|
|
@@ -1093,6 +1173,7 @@ static const struct {
|
|
|
{WMI_DELBA_EVENTID, wmi_evt_delba},
|
|
|
{WMI_VRING_EN_EVENTID, wmi_evt_vring_en},
|
|
|
{WMI_DATA_PORT_OPEN_EVENTID, wmi_evt_ignore},
|
|
|
+ {WMI_SCHED_SCAN_RESULT_EVENTID, wmi_evt_sched_scan_result},
|
|
|
};
|
|
|
|
|
|
/*
|
|
@@ -2284,3 +2365,159 @@ out:
|
|
|
spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
|
|
|
return rc;
|
|
|
}
|
|
|
+
|
|
|
+static void
|
|
|
+wmi_sched_scan_set_ssids(struct wil6210_priv *wil,
|
|
|
+ struct wmi_start_sched_scan_cmd *cmd,
|
|
|
+ struct cfg80211_ssid *ssids, int n_ssids,
|
|
|
+ struct cfg80211_match_set *match_sets,
|
|
|
+ int n_match_sets)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (n_match_sets > WMI_MAX_PNO_SSID_NUM) {
|
|
|
+ wil_dbg_wmi(wil, "too many match sets (%d), use first %d\n",
|
|
|
+ n_match_sets, WMI_MAX_PNO_SSID_NUM);
|
|
|
+ n_match_sets = WMI_MAX_PNO_SSID_NUM;
|
|
|
+ }
|
|
|
+ cmd->num_of_ssids = n_match_sets;
|
|
|
+
|
|
|
+ for (i = 0; i < n_match_sets; i++) {
|
|
|
+ struct wmi_sched_scan_ssid_match *wmi_match =
|
|
|
+ &cmd->ssid_for_match[i];
|
|
|
+ struct cfg80211_match_set *cfg_match = &match_sets[i];
|
|
|
+ int j;
|
|
|
+
|
|
|
+ wmi_match->ssid_len = cfg_match->ssid.ssid_len;
|
|
|
+ memcpy(wmi_match->ssid, cfg_match->ssid.ssid,
|
|
|
+ min_t(u8, wmi_match->ssid_len, WMI_MAX_SSID_LEN));
|
|
|
+ wmi_match->rssi_threshold = S8_MIN;
|
|
|
+ if (cfg_match->rssi_thold >= S8_MIN &&
|
|
|
+ cfg_match->rssi_thold <= S8_MAX)
|
|
|
+ wmi_match->rssi_threshold = cfg_match->rssi_thold;
|
|
|
+
|
|
|
+ for (j = 0; j < n_ssids; j++)
|
|
|
+ if (wmi_match->ssid_len == ssids[j].ssid_len &&
|
|
|
+ memcmp(wmi_match->ssid, ssids[j].ssid,
|
|
|
+ wmi_match->ssid_len) == 0)
|
|
|
+ wmi_match->add_ssid_to_probe = true;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+wmi_sched_scan_set_channels(struct wil6210_priv *wil,
|
|
|
+ struct wmi_start_sched_scan_cmd *cmd,
|
|
|
+ u32 n_channels,
|
|
|
+ struct ieee80211_channel **channels)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (n_channels > WMI_MAX_CHANNEL_NUM) {
|
|
|
+ wil_dbg_wmi(wil, "too many channels (%d), use first %d\n",
|
|
|
+ n_channels, WMI_MAX_CHANNEL_NUM);
|
|
|
+ n_channels = WMI_MAX_CHANNEL_NUM;
|
|
|
+ }
|
|
|
+ cmd->num_of_channels = n_channels;
|
|
|
+
|
|
|
+ for (i = 0; i < n_channels; i++) {
|
|
|
+ struct ieee80211_channel *cfg_chan = channels[i];
|
|
|
+
|
|
|
+ cmd->channel_list[i] = cfg_chan->hw_value - 1;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+wmi_sched_scan_set_plans(struct wil6210_priv *wil,
|
|
|
+ struct wmi_start_sched_scan_cmd *cmd,
|
|
|
+ struct cfg80211_sched_scan_plan *scan_plans,
|
|
|
+ int n_scan_plans)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (n_scan_plans > WMI_MAX_PLANS_NUM) {
|
|
|
+ wil_dbg_wmi(wil, "too many plans (%d), use first %d\n",
|
|
|
+ n_scan_plans, WMI_MAX_PLANS_NUM);
|
|
|
+ n_scan_plans = WMI_MAX_PLANS_NUM;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < n_scan_plans; i++) {
|
|
|
+ struct cfg80211_sched_scan_plan *cfg_plan = &scan_plans[i];
|
|
|
+
|
|
|
+ cmd->scan_plans[i].interval_sec =
|
|
|
+ cpu_to_le16(cfg_plan->interval);
|
|
|
+ cmd->scan_plans[i].num_of_iterations =
|
|
|
+ cpu_to_le16(cfg_plan->iterations);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int wmi_start_sched_scan(struct wil6210_priv *wil,
|
|
|
+ struct cfg80211_sched_scan_request *request)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ struct wmi_start_sched_scan_cmd cmd = {
|
|
|
+ .min_rssi_threshold = S8_MIN,
|
|
|
+ .initial_delay_sec = cpu_to_le16(request->delay),
|
|
|
+ };
|
|
|
+ struct {
|
|
|
+ struct wmi_cmd_hdr wmi;
|
|
|
+ struct wmi_start_sched_scan_event evt;
|
|
|
+ } __packed reply;
|
|
|
+
|
|
|
+ if (!test_bit(WMI_FW_CAPABILITY_PNO, wil->fw_capabilities))
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ if (request->min_rssi_thold >= S8_MIN &&
|
|
|
+ request->min_rssi_thold <= S8_MAX)
|
|
|
+ cmd.min_rssi_threshold = request->min_rssi_thold;
|
|
|
+
|
|
|
+ wmi_sched_scan_set_ssids(wil, &cmd, request->ssids, request->n_ssids,
|
|
|
+ request->match_sets, request->n_match_sets);
|
|
|
+ wmi_sched_scan_set_channels(wil, &cmd,
|
|
|
+ request->n_channels, request->channels);
|
|
|
+ wmi_sched_scan_set_plans(wil, &cmd,
|
|
|
+ request->scan_plans, request->n_scan_plans);
|
|
|
+
|
|
|
+ reply.evt.result = WMI_PNO_REJECT;
|
|
|
+
|
|
|
+ rc = wmi_call(wil, WMI_START_SCHED_SCAN_CMDID, &cmd, sizeof(cmd),
|
|
|
+ WMI_START_SCHED_SCAN_EVENTID, &reply, sizeof(reply),
|
|
|
+ WIL_WMI_CALL_GENERAL_TO_MS);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ if (reply.evt.result != WMI_PNO_SUCCESS) {
|
|
|
+ wil_err(wil, "start sched scan failed, result %d\n",
|
|
|
+ reply.evt.result);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int wmi_stop_sched_scan(struct wil6210_priv *wil)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ struct {
|
|
|
+ struct wmi_cmd_hdr wmi;
|
|
|
+ struct wmi_stop_sched_scan_event evt;
|
|
|
+ } __packed reply;
|
|
|
+
|
|
|
+ if (!test_bit(WMI_FW_CAPABILITY_PNO, wil->fw_capabilities))
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ reply.evt.result = WMI_PNO_REJECT;
|
|
|
+
|
|
|
+ rc = wmi_call(wil, WMI_STOP_SCHED_SCAN_CMDID, NULL, 0,
|
|
|
+ WMI_STOP_SCHED_SCAN_EVENTID, &reply, sizeof(reply),
|
|
|
+ WIL_WMI_CALL_GENERAL_TO_MS);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ if (reply.evt.result != WMI_PNO_SUCCESS) {
|
|
|
+ wil_err(wil, "stop sched scan failed, result %d\n",
|
|
|
+ reply.evt.result);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|