Loading drivers/net/wireless/ath/wil6210/cfg80211.c +64 −0 Original line number Diff line number Diff line Loading @@ -1826,6 +1826,68 @@ 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) { 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 struct cfg80211_ops wil_cfg80211_ops = { .add_virtual_intf = wil_cfg80211_add_iface, .del_virtual_intf = wil_cfg80211_del_iface, Loading Loading @@ -1859,6 +1921,8 @@ static 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) Loading drivers/net/wireless/ath/wil6210/main.c +8 −0 Original line number Diff line number Diff line Loading @@ -791,6 +791,14 @@ void wil_refresh_fw_capabilities(struct wil6210_priv *wil) else wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; if (test_bit(WMI_FW_CAPABILITY_PNO, wil->fw_capabilities)) { wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; 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; } if (wil->platform_ops.set_features) { features = (test_bit(WMI_FW_CAPABILITY_REF_CLOCK_CONTROL, wil->fw_capabilities) && Loading drivers/net/wireless/ath/wil6210/wil6210.h +5 −0 Original line number Diff line number Diff line Loading @@ -1076,4 +1076,9 @@ int wmi_link_maintain_cfg_write(struct wil6210_priv *wil, bool fst_link_loss); int wmi_set_snr_thresh(struct wil6210_priv *wil, short omni, short direct); 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__ */ drivers/net/wireless/ath/wil6210/wmi.c +237 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,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 Loading Loading @@ -315,6 +316,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"; } Loading Loading @@ -429,6 +434,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"; } Loading Loading @@ -1096,6 +1107,75 @@ static void wmi_evt_per_dest_res(struct wil6210_priv *wil, int id, wil_ftm_evt_per_dest_res(wil, evt); } 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); } /** * Some events are ignored for purpose; and need not be interpreted as * "unhandled events" Loading Loading @@ -1130,6 +1210,7 @@ static const struct { {WMI_TOF_SET_LCI_EVENTID, wmi_evt_ignore}, {WMI_TOF_FTM_PER_DEST_RES_EVENTID, wmi_evt_per_dest_res}, {WMI_TOF_CHANNEL_INFO_EVENTID, wmi_evt_ignore}, {WMI_SCHED_SCAN_RESULT_EVENTID, wmi_evt_sched_scan_result}, }; /* Loading Loading @@ -2466,3 +2547,159 @@ int wmi_set_snr_thresh(struct wil6210_priv *wil, short omni, short direct) return 0; } 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; } drivers/net/wireless/ath/wil6210/wmi.h +81 −18 Original line number Diff line number Diff line Loading @@ -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_CONNECT_SNR_THR = 16, WMI_FW_CAPABILITY_REF_CLOCK_CONTROL = 18, WMI_FW_CAPABILITY_MAX, Loading @@ -89,6 +90,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, Loading Loading @@ -387,6 +390,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) Loading Loading @@ -1240,6 +1275,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, Loading Loading @@ -1602,6 +1640,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 snr; __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, Loading Loading @@ -1816,24 +1897,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 snr; __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 */ Loading Loading
drivers/net/wireless/ath/wil6210/cfg80211.c +64 −0 Original line number Diff line number Diff line Loading @@ -1826,6 +1826,68 @@ 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) { 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 struct cfg80211_ops wil_cfg80211_ops = { .add_virtual_intf = wil_cfg80211_add_iface, .del_virtual_intf = wil_cfg80211_del_iface, Loading Loading @@ -1859,6 +1921,8 @@ static 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) Loading
drivers/net/wireless/ath/wil6210/main.c +8 −0 Original line number Diff line number Diff line Loading @@ -791,6 +791,14 @@ void wil_refresh_fw_capabilities(struct wil6210_priv *wil) else wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; if (test_bit(WMI_FW_CAPABILITY_PNO, wil->fw_capabilities)) { wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; 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; } if (wil->platform_ops.set_features) { features = (test_bit(WMI_FW_CAPABILITY_REF_CLOCK_CONTROL, wil->fw_capabilities) && Loading
drivers/net/wireless/ath/wil6210/wil6210.h +5 −0 Original line number Diff line number Diff line Loading @@ -1076,4 +1076,9 @@ int wmi_link_maintain_cfg_write(struct wil6210_priv *wil, bool fst_link_loss); int wmi_set_snr_thresh(struct wil6210_priv *wil, short omni, short direct); 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__ */
drivers/net/wireless/ath/wil6210/wmi.c +237 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,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 Loading Loading @@ -315,6 +316,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"; } Loading Loading @@ -429,6 +434,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"; } Loading Loading @@ -1096,6 +1107,75 @@ static void wmi_evt_per_dest_res(struct wil6210_priv *wil, int id, wil_ftm_evt_per_dest_res(wil, evt); } 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); } /** * Some events are ignored for purpose; and need not be interpreted as * "unhandled events" Loading Loading @@ -1130,6 +1210,7 @@ static const struct { {WMI_TOF_SET_LCI_EVENTID, wmi_evt_ignore}, {WMI_TOF_FTM_PER_DEST_RES_EVENTID, wmi_evt_per_dest_res}, {WMI_TOF_CHANNEL_INFO_EVENTID, wmi_evt_ignore}, {WMI_SCHED_SCAN_RESULT_EVENTID, wmi_evt_sched_scan_result}, }; /* Loading Loading @@ -2466,3 +2547,159 @@ int wmi_set_snr_thresh(struct wil6210_priv *wil, short omni, short direct) return 0; } 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; }
drivers/net/wireless/ath/wil6210/wmi.h +81 −18 Original line number Diff line number Diff line Loading @@ -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_CONNECT_SNR_THR = 16, WMI_FW_CAPABILITY_REF_CLOCK_CONTROL = 18, WMI_FW_CAPABILITY_MAX, Loading @@ -89,6 +90,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, Loading Loading @@ -387,6 +390,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) Loading Loading @@ -1240,6 +1275,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, Loading Loading @@ -1602,6 +1640,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 snr; __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, Loading Loading @@ -1816,24 +1897,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 snr; __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 */ Loading