Commit a5dc6883 authored by Dedy Lansky's avatar Dedy Lansky Committed by Kalle Valo

wil6210: support Scheduled scan

Add support for sched_scan_start/stop by sending PNO commands to FW.
Driver reports max_sched_scan_reqs and invokes
cfg80211_sched_scan_results upon receiving WMI_SCHED_SCAN_RESULT_EVENTID
from FW.
Signed-off-by: default avatarDedy Lansky <qca_dlansky@qca.qualcomm.com>
Signed-off-by: default avatarMaya Erez <qca_merez@qca.qualcomm.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 8b1083d6
......@@ -1751,6 +1751,69 @@ static int wil_cfg80211_resume(struct wiphy *wiphy)
return 0;
}
static int
wil_cfg80211_sched_scan_start(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_sched_scan_request *request)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int i, rc;
wil_dbg_misc(wil,
"sched scan start: n_ssids %d, ie_len %zu, flags 0x%x\n",
request->n_ssids, request->ie_len, request->flags);
for (i = 0; i < request->n_ssids; i++) {
wil_dbg_misc(wil, "SSID[%d]:", i);
wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
request->ssids[i].ssid,
request->ssids[i].ssid_len, true);
}
wil_dbg_misc(wil, "channels:");
for (i = 0; i < request->n_channels; i++)
wil_dbg_misc(wil, " %d%s", request->channels[i]->hw_value,
i == request->n_channels - 1 ? "\n" : "");
wil_dbg_misc(wil, "n_match_sets %d, min_rssi_thold %d, delay %d\n",
request->n_match_sets, request->min_rssi_thold,
request->delay);
for (i = 0; i < request->n_match_sets; i++) {
struct cfg80211_match_set *ms = &request->match_sets[i];
wil_dbg_misc(wil, "MATCHSET[%d]: rssi_thold %d\n",
i, ms->rssi_thold);
wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
ms->ssid.ssid,
ms->ssid.ssid_len, true);
}
wil_dbg_misc(wil, "n_scan_plans %d\n", request->n_scan_plans);
for (i = 0; i < request->n_scan_plans; i++) {
struct cfg80211_sched_scan_plan *sp = &request->scan_plans[i];
wil_dbg_misc(wil, "SCAN PLAN[%d]: interval %d iterations %d\n",
i, sp->interval, sp->iterations);
}
rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len, request->ie);
if (rc)
return rc;
return wmi_start_sched_scan(wil, request);
}
static int
wil_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev,
u64 reqid)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
rc = wmi_stop_sched_scan(wil);
/* device would return error if it thinks PNO is already stopped.
* ignore the return code so user space and driver gets back in-sync
*/
wil_dbg_misc(wil, "sched scan stopped (%d)\n", rc);
return 0;
}
static const struct cfg80211_ops wil_cfg80211_ops = {
.add_virtual_intf = wil_cfg80211_add_iface,
.del_virtual_intf = wil_cfg80211_del_iface,
......@@ -1784,6 +1847,8 @@ static const struct cfg80211_ops wil_cfg80211_ops = {
.set_power_mgmt = wil_cfg80211_set_power_mgmt,
.suspend = wil_cfg80211_suspend,
.resume = wil_cfg80211_resume,
.sched_scan_start = wil_cfg80211_sched_scan_start,
.sched_scan_stop = wil_cfg80211_sched_scan_stop,
};
static void wil_wiphy_init(struct wiphy *wiphy)
......
......@@ -785,6 +785,14 @@ void wil_refresh_fw_capabilities(struct wil6210_priv *wil)
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
else
wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
if (test_bit(WMI_FW_CAPABILITY_PNO, wil->fw_capabilities)) {
wiphy->max_sched_scan_reqs = 1;
wiphy->max_sched_scan_ssids = WMI_MAX_PNO_SSID_NUM;
wiphy->max_match_sets = WMI_MAX_PNO_SSID_NUM;
wiphy->max_sched_scan_ie_len = WMI_MAX_IE_LEN;
wiphy->max_sched_scan_plans = WMI_MAX_PLANS_NUM;
}
}
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
......
......@@ -1030,4 +1030,8 @@ void wil_halp_unvote(struct wil6210_priv *wil);
void wil6210_set_halp(struct wil6210_priv *wil);
void wil6210_clear_halp(struct wil6210_priv *wil);
int wmi_start_sched_scan(struct wil6210_priv *wil,
struct cfg80211_sched_scan_request *request);
int wmi_stop_sched_scan(struct wil6210_priv *wil);
#endif /* __WIL6210_H__ */
......@@ -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 @@ bool wil_is_wmi_idle(struct wil6210_priv *wil)
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;
}
......@@ -71,6 +71,7 @@ enum wmi_fw_capability {
WMI_FW_CAPABILITY_RSSI_REPORTING = 12,
WMI_FW_CAPABILITY_SET_SILENT_RSSI_TABLE = 13,
WMI_FW_CAPABILITY_LO_POWER_CALIB_FROM_OTP = 14,
WMI_FW_CAPABILITY_PNO = 15,
WMI_FW_CAPABILITY_MAX,
};
......@@ -87,6 +88,8 @@ enum wmi_command_id {
WMI_CONNECT_CMDID = 0x01,
WMI_DISCONNECT_CMDID = 0x03,
WMI_DISCONNECT_STA_CMDID = 0x04,
WMI_START_SCHED_SCAN_CMDID = 0x05,
WMI_STOP_SCHED_SCAN_CMDID = 0x06,
WMI_START_SCAN_CMDID = 0x07,
WMI_SET_BSS_FILTER_CMDID = 0x09,
WMI_SET_PROBED_SSID_CMDID = 0x0A,
......@@ -385,6 +388,38 @@ struct wmi_start_scan_cmd {
} channel_list[0];
} __packed;
#define WMI_MAX_PNO_SSID_NUM (16)
#define WMI_MAX_CHANNEL_NUM (6)
#define WMI_MAX_PLANS_NUM (2)
/* WMI_START_SCHED_SCAN_CMDID */
struct wmi_sched_scan_ssid_match {
u8 ssid_len;
u8 ssid[WMI_MAX_SSID_LEN];
s8 rssi_threshold;
/* boolean */
u8 add_ssid_to_probe;
u8 reserved;
} __packed;
/* WMI_START_SCHED_SCAN_CMDID */
struct wmi_sched_scan_plan {
__le16 interval_sec;
__le16 num_of_iterations;
} __packed;
/* WMI_START_SCHED_SCAN_CMDID */
struct wmi_start_sched_scan_cmd {
struct wmi_sched_scan_ssid_match ssid_for_match[WMI_MAX_PNO_SSID_NUM];
u8 num_of_ssids;
s8 min_rssi_threshold;
u8 channel_list[WMI_MAX_CHANNEL_NUM];
u8 num_of_channels;
u8 reserved;
__le16 initial_delay_sec;
struct wmi_sched_scan_plan scan_plans[WMI_MAX_PLANS_NUM];
} __packed;
/* WMI_SET_PROBED_SSID_CMDID */
#define MAX_PROBED_SSID_INDEX (3)
......@@ -1238,6 +1273,9 @@ enum wmi_event_id {
WMI_READY_EVENTID = 0x1001,
WMI_CONNECT_EVENTID = 0x1002,
WMI_DISCONNECT_EVENTID = 0x1003,
WMI_START_SCHED_SCAN_EVENTID = 0x1005,
WMI_STOP_SCHED_SCAN_EVENTID = 0x1006,
WMI_SCHED_SCAN_RESULT_EVENTID = 0x1007,
WMI_SCAN_COMPLETE_EVENTID = 0x100A,
WMI_REPORT_STATISTICS_EVENTID = 0x100B,
WMI_RD_MEM_RSP_EVENTID = 0x1800,
......@@ -1600,6 +1638,49 @@ struct wmi_scan_complete_event {
__le32 status;
} __packed;
/* wmi_rx_mgmt_info */
struct wmi_rx_mgmt_info {
u8 mcs;
s8 rssi;
u8 range;
u8 sqi;
__le16 stype;
__le16 status;
__le32 len;
/* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */
u8 qid;
/* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */
u8 mid;
u8 cid;
/* From Radio MNGR */
u8 channel;
} __packed;
/* WMI_START_SCHED_SCAN_EVENTID */
enum wmi_pno_result {
WMI_PNO_SUCCESS = 0x00,
WMI_PNO_REJECT = 0x01,
WMI_PNO_INVALID_PARAMETERS = 0x02,
WMI_PNO_NOT_ENABLED = 0x03,
};
struct wmi_start_sched_scan_event {
/* pno_result */
u8 result;
u8 reserved[3];
} __packed;
struct wmi_stop_sched_scan_event {
/* pno_result */
u8 result;
u8 reserved[3];
} __packed;
struct wmi_sched_scan_result_event {
struct wmi_rx_mgmt_info info;
u8 payload[0];
} __packed;
/* WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENT */
enum wmi_acs_info_bitmask {
WMI_ACS_INFO_BITMASK_BEACON_FOUND = 0x01,
......@@ -1814,24 +1895,6 @@ struct wmi_get_ssid_event {
u8 ssid[WMI_MAX_SSID_LEN];
} __packed;
/* wmi_rx_mgmt_info */
struct wmi_rx_mgmt_info {
u8 mcs;
s8 rssi;
u8 range;
u8 sqi;
__le16 stype;
__le16 status;
__le32 len;
/* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */
u8 qid;
/* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */
u8 mid;
u8 cid;
/* From Radio MNGR */
u8 channel;
} __packed;
/* EVENT: WMI_RF_XPM_READ_RESULT_EVENTID */
struct wmi_rf_xpm_read_result_event {
/* enum wmi_fw_status_e - success=0 or fail=1 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment