0001
0002
0003
0004
0005
0006 #include <net/cfg80211.h>
0007 #include <linux/etherdevice.h>
0008 #include "mvm.h"
0009 #include "constants.h"
0010
0011 struct iwl_mvm_pasn_sta {
0012 struct list_head list;
0013 struct iwl_mvm_int_sta int_sta;
0014 u8 addr[ETH_ALEN];
0015 };
0016
0017 struct iwl_mvm_pasn_hltk_data {
0018 u8 *addr;
0019 u8 cipher;
0020 u8 *hltk;
0021 };
0022
0023 static int iwl_mvm_ftm_responder_set_bw_v1(struct cfg80211_chan_def *chandef,
0024 u8 *bw, u8 *ctrl_ch_position)
0025 {
0026 switch (chandef->width) {
0027 case NL80211_CHAN_WIDTH_20_NOHT:
0028 *bw = IWL_TOF_BW_20_LEGACY;
0029 break;
0030 case NL80211_CHAN_WIDTH_20:
0031 *bw = IWL_TOF_BW_20_HT;
0032 break;
0033 case NL80211_CHAN_WIDTH_40:
0034 *bw = IWL_TOF_BW_40;
0035 *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
0036 break;
0037 case NL80211_CHAN_WIDTH_80:
0038 *bw = IWL_TOF_BW_80;
0039 *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
0040 break;
0041 default:
0042 return -ENOTSUPP;
0043 }
0044
0045 return 0;
0046 }
0047
0048 static int iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def *chandef,
0049 u8 *format_bw, u8 *ctrl_ch_position,
0050 u8 cmd_ver)
0051 {
0052 switch (chandef->width) {
0053 case NL80211_CHAN_WIDTH_20_NOHT:
0054 *format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
0055 *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
0056 break;
0057 case NL80211_CHAN_WIDTH_20:
0058 *format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
0059 *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
0060 break;
0061 case NL80211_CHAN_WIDTH_40:
0062 *format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
0063 *format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
0064 *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
0065 break;
0066 case NL80211_CHAN_WIDTH_80:
0067 *format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
0068 *format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
0069 *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
0070 break;
0071 case NL80211_CHAN_WIDTH_160:
0072 if (cmd_ver >= 9) {
0073 *format_bw = IWL_LOCATION_FRAME_FORMAT_HE;
0074 *format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS;
0075 *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
0076 break;
0077 }
0078 fallthrough;
0079 default:
0080 return -ENOTSUPP;
0081 }
0082
0083 return 0;
0084 }
0085
0086 static void
0087 iwl_mvm_ftm_responder_set_ndp(struct iwl_mvm *mvm,
0088 struct iwl_tof_responder_config_cmd_v9 *cmd)
0089 {
0090
0091 u32 r2i_max_sts = IWL_MVM_FTM_R2I_MAX_STS < 2 ?
0092 IWL_MVM_FTM_R2I_MAX_STS : 1;
0093
0094 cmd->r2i_ndp_params = IWL_MVM_FTM_R2I_MAX_REP |
0095 (r2i_max_sts << IWL_RESPONDER_STS_POS) |
0096 (IWL_MVM_FTM_R2I_MAX_TOTAL_LTF << IWL_RESPONDER_TOTAL_LTF_POS);
0097 cmd->i2r_ndp_params = IWL_MVM_FTM_I2R_MAX_REP |
0098 (IWL_MVM_FTM_I2R_MAX_STS << IWL_RESPONDER_STS_POS) |
0099 (IWL_MVM_FTM_I2R_MAX_TOTAL_LTF << IWL_RESPONDER_TOTAL_LTF_POS);
0100 cmd->cmd_valid_fields |=
0101 cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_NDP_PARAMS);
0102 }
0103
0104 static int
0105 iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm,
0106 struct ieee80211_vif *vif,
0107 struct cfg80211_chan_def *chandef)
0108 {
0109 u32 cmd_id = WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_CONFIG_CMD);
0110 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
0111
0112
0113
0114
0115
0116 struct iwl_tof_responder_config_cmd_v9 cmd = {
0117 .channel_num = chandef->chan->hw_value,
0118 .cmd_valid_fields =
0119 cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_CHAN_INFO |
0120 IWL_TOF_RESPONDER_CMD_VALID_BSSID |
0121 IWL_TOF_RESPONDER_CMD_VALID_STA_ID),
0122 .sta_id = mvmvif->bcast_sta.sta_id,
0123 };
0124 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 6);
0125 int err;
0126 int cmd_size;
0127
0128 lockdep_assert_held(&mvm->mutex);
0129
0130
0131 if (cmd_ver == 9) {
0132 cmd.cmd_valid_fields |=
0133 cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_BSS_COLOR |
0134 IWL_TOF_RESPONDER_CMD_VALID_MIN_MAX_TIME_BETWEEN_MSR);
0135 cmd.bss_color = 1;
0136 cmd.min_time_between_msr =
0137 cpu_to_le16(IWL_MVM_FTM_NON_TB_MIN_TIME_BETWEEN_MSR);
0138 cmd.max_time_between_msr =
0139 cpu_to_le16(IWL_MVM_FTM_NON_TB_MAX_TIME_BETWEEN_MSR);
0140 cmd_size = sizeof(struct iwl_tof_responder_config_cmd_v9);
0141 } else {
0142
0143 cmd_size = sizeof(struct iwl_tof_responder_config_cmd_v8);
0144 }
0145
0146 if (cmd_ver >= 8)
0147 iwl_mvm_ftm_responder_set_ndp(mvm, &cmd);
0148
0149 if (cmd_ver >= 7)
0150 err = iwl_mvm_ftm_responder_set_bw_v2(chandef, &cmd.format_bw,
0151 &cmd.ctrl_ch_position,
0152 cmd_ver);
0153 else
0154 err = iwl_mvm_ftm_responder_set_bw_v1(chandef, &cmd.format_bw,
0155 &cmd.ctrl_ch_position);
0156
0157 if (err) {
0158 IWL_ERR(mvm, "Failed to set responder bandwidth\n");
0159 return err;
0160 }
0161
0162 memcpy(cmd.bssid, vif->addr, ETH_ALEN);
0163
0164 return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, cmd_size, &cmd);
0165 }
0166
0167 static int
0168 iwl_mvm_ftm_responder_dyn_cfg_v2(struct iwl_mvm *mvm,
0169 struct ieee80211_vif *vif,
0170 struct ieee80211_ftm_responder_params *params)
0171 {
0172 struct iwl_tof_responder_dyn_config_cmd_v2 cmd = {
0173 .lci_len = cpu_to_le32(params->lci_len + 2),
0174 .civic_len = cpu_to_le32(params->civicloc_len + 2),
0175 };
0176 u8 data[IWL_LCI_CIVIC_IE_MAX_SIZE] = {0};
0177 struct iwl_host_cmd hcmd = {
0178 .id = WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),
0179 .data[0] = &cmd,
0180 .len[0] = sizeof(cmd),
0181 .data[1] = &data,
0182
0183
0184 .dataflags[1] = IWL_HCMD_DFL_DUP,
0185 };
0186 u32 aligned_lci_len = ALIGN(params->lci_len + 2, 4);
0187 u32 aligned_civicloc_len = ALIGN(params->civicloc_len + 2, 4);
0188 u8 *pos = data;
0189
0190 lockdep_assert_held(&mvm->mutex);
0191
0192 if (aligned_lci_len + aligned_civicloc_len > sizeof(data)) {
0193 IWL_ERR(mvm, "LCI/civicloc data too big (%zd + %zd)\n",
0194 params->lci_len, params->civicloc_len);
0195 return -ENOBUFS;
0196 }
0197
0198 pos[0] = WLAN_EID_MEASURE_REPORT;
0199 pos[1] = params->lci_len;
0200 memcpy(pos + 2, params->lci, params->lci_len);
0201
0202 pos += aligned_lci_len;
0203 pos[0] = WLAN_EID_MEASURE_REPORT;
0204 pos[1] = params->civicloc_len;
0205 memcpy(pos + 2, params->civicloc, params->civicloc_len);
0206
0207 hcmd.len[1] = aligned_lci_len + aligned_civicloc_len;
0208
0209 return iwl_mvm_send_cmd(mvm, &hcmd);
0210 }
0211
0212 static int
0213 iwl_mvm_ftm_responder_dyn_cfg_v3(struct iwl_mvm *mvm,
0214 struct ieee80211_vif *vif,
0215 struct ieee80211_ftm_responder_params *params,
0216 struct iwl_mvm_pasn_hltk_data *hltk_data)
0217 {
0218 struct iwl_tof_responder_dyn_config_cmd cmd;
0219 struct iwl_host_cmd hcmd = {
0220 .id = WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),
0221 .data[0] = &cmd,
0222 .len[0] = sizeof(cmd),
0223
0224 .dataflags[0] = IWL_HCMD_DFL_DUP,
0225 };
0226
0227 lockdep_assert_held(&mvm->mutex);
0228
0229 cmd.valid_flags = 0;
0230
0231 if (params) {
0232 if (params->lci_len + 2 > sizeof(cmd.lci_buf) ||
0233 params->civicloc_len + 2 > sizeof(cmd.civic_buf)) {
0234 IWL_ERR(mvm,
0235 "LCI/civic data too big (lci=%zd, civic=%zd)\n",
0236 params->lci_len, params->civicloc_len);
0237 return -ENOBUFS;
0238 }
0239
0240 cmd.lci_buf[0] = WLAN_EID_MEASURE_REPORT;
0241 cmd.lci_buf[1] = params->lci_len;
0242 memcpy(cmd.lci_buf + 2, params->lci, params->lci_len);
0243 cmd.lci_len = params->lci_len + 2;
0244
0245 cmd.civic_buf[0] = WLAN_EID_MEASURE_REPORT;
0246 cmd.civic_buf[1] = params->civicloc_len;
0247 memcpy(cmd.civic_buf + 2, params->civicloc,
0248 params->civicloc_len);
0249 cmd.civic_len = params->civicloc_len + 2;
0250
0251 cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_LCI |
0252 IWL_RESPONDER_DYN_CFG_VALID_CIVIC;
0253 }
0254
0255 if (hltk_data) {
0256 if (hltk_data->cipher > IWL_LOCATION_CIPHER_GCMP_256) {
0257 IWL_ERR(mvm, "invalid cipher: %u\n",
0258 hltk_data->cipher);
0259 return -EINVAL;
0260 }
0261
0262 cmd.cipher = hltk_data->cipher;
0263 memcpy(cmd.addr, hltk_data->addr, sizeof(cmd.addr));
0264 memcpy(cmd.hltk_buf, hltk_data->hltk, sizeof(cmd.hltk_buf));
0265 cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_PASN_STA;
0266 }
0267
0268 return iwl_mvm_send_cmd(mvm, &hcmd);
0269 }
0270
0271 static int
0272 iwl_mvm_ftm_responder_dyn_cfg_cmd(struct iwl_mvm *mvm,
0273 struct ieee80211_vif *vif,
0274 struct ieee80211_ftm_responder_params *params)
0275 {
0276 int ret;
0277 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
0278 WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),
0279 2);
0280
0281 switch (cmd_ver) {
0282 case 2:
0283 ret = iwl_mvm_ftm_responder_dyn_cfg_v2(mvm, vif,
0284 params);
0285 break;
0286 case 3:
0287 ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif,
0288 params, NULL);
0289 break;
0290 default:
0291 IWL_ERR(mvm, "Unsupported DYN_CONFIG_CMD version %u\n",
0292 cmd_ver);
0293 ret = -ENOTSUPP;
0294 }
0295
0296 return ret;
0297 }
0298
0299 static void iwl_mvm_resp_del_pasn_sta(struct iwl_mvm *mvm,
0300 struct ieee80211_vif *vif,
0301 struct iwl_mvm_pasn_sta *sta)
0302 {
0303 list_del(&sta->list);
0304 iwl_mvm_rm_sta_id(mvm, vif, sta->int_sta.sta_id);
0305 iwl_mvm_dealloc_int_sta(mvm, &sta->int_sta);
0306 kfree(sta);
0307 }
0308
0309 int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm,
0310 struct ieee80211_vif *vif,
0311 u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
0312 u8 *hltk, u32 hltk_len)
0313 {
0314 int ret;
0315 struct iwl_mvm_pasn_sta *sta = NULL;
0316 struct iwl_mvm_pasn_hltk_data hltk_data = {
0317 .addr = addr,
0318 .hltk = hltk,
0319 };
0320 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
0321 WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),
0322 2);
0323
0324 lockdep_assert_held(&mvm->mutex);
0325
0326 if (cmd_ver < 3) {
0327 IWL_ERR(mvm, "Adding PASN station not supported by FW\n");
0328 return -ENOTSUPP;
0329 }
0330
0331 hltk_data.cipher = iwl_mvm_cipher_to_location_cipher(cipher);
0332 if (hltk_data.cipher == IWL_LOCATION_CIPHER_INVALID) {
0333 IWL_ERR(mvm, "invalid cipher: %u\n", cipher);
0334 return -EINVAL;
0335 }
0336
0337 if (tk && tk_len) {
0338 sta = kzalloc(sizeof(*sta), GFP_KERNEL);
0339 if (!sta)
0340 return -ENOBUFS;
0341
0342 ret = iwl_mvm_add_pasn_sta(mvm, vif, &sta->int_sta, addr,
0343 cipher, tk, tk_len);
0344 if (ret) {
0345 kfree(sta);
0346 return ret;
0347 }
0348
0349 memcpy(sta->addr, addr, ETH_ALEN);
0350 list_add_tail(&sta->list, &mvm->resp_pasn_list);
0351 }
0352
0353 ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif, NULL, &hltk_data);
0354 if (ret && sta)
0355 iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
0356
0357 return ret;
0358 }
0359
0360 int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm,
0361 struct ieee80211_vif *vif, u8 *addr)
0362 {
0363 struct iwl_mvm_pasn_sta *sta, *prev;
0364
0365 lockdep_assert_held(&mvm->mutex);
0366
0367 list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list) {
0368 if (!memcmp(sta->addr, addr, ETH_ALEN)) {
0369 iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
0370 return 0;
0371 }
0372 }
0373
0374 IWL_ERR(mvm, "FTM: PASN station %pM not found\n", addr);
0375 return -EINVAL;
0376 }
0377
0378 int iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
0379 {
0380 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
0381 struct ieee80211_ftm_responder_params *params;
0382 struct ieee80211_chanctx_conf ctx, *pctx;
0383 u16 *phy_ctxt_id;
0384 struct iwl_mvm_phy_ctxt *phy_ctxt;
0385 int ret;
0386
0387 params = vif->bss_conf.ftmr_params;
0388
0389 lockdep_assert_held(&mvm->mutex);
0390
0391 if (WARN_ON_ONCE(!vif->bss_conf.ftm_responder))
0392 return -EINVAL;
0393
0394 if (vif->p2p || vif->type != NL80211_IFTYPE_AP ||
0395 !mvmvif->ap_ibss_active) {
0396 IWL_ERR(mvm, "Cannot start responder, not in AP mode\n");
0397 return -EIO;
0398 }
0399
0400 rcu_read_lock();
0401 pctx = rcu_dereference(vif->bss_conf.chanctx_conf);
0402
0403
0404
0405 ctx = *pctx;
0406 phy_ctxt_id = (u16 *)pctx->drv_priv;
0407 rcu_read_unlock();
0408
0409 phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
0410 ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx.def,
0411 ctx.rx_chains_static,
0412 ctx.rx_chains_dynamic);
0413 if (ret)
0414 return ret;
0415
0416 ret = iwl_mvm_ftm_responder_cmd(mvm, vif, &ctx.def);
0417 if (ret)
0418 return ret;
0419
0420 if (params)
0421 ret = iwl_mvm_ftm_responder_dyn_cfg_cmd(mvm, vif, params);
0422
0423 return ret;
0424 }
0425
0426 void iwl_mvm_ftm_responder_clear(struct iwl_mvm *mvm,
0427 struct ieee80211_vif *vif)
0428 {
0429 struct iwl_mvm_pasn_sta *sta, *prev;
0430
0431 lockdep_assert_held(&mvm->mutex);
0432
0433 list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list)
0434 iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
0435 }
0436
0437 void iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm,
0438 struct ieee80211_vif *vif)
0439 {
0440 if (!vif->bss_conf.ftm_responder)
0441 return;
0442
0443 iwl_mvm_ftm_responder_clear(mvm, vif);
0444 iwl_mvm_ftm_start_responder(mvm, vif);
0445 }
0446
0447 void iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm,
0448 struct iwl_rx_cmd_buffer *rxb)
0449 {
0450 struct iwl_rx_packet *pkt = rxb_addr(rxb);
0451 struct iwl_ftm_responder_stats *resp = (void *)pkt->data;
0452 struct cfg80211_ftm_responder_stats *stats = &mvm->ftm_resp_stats;
0453 u32 flags = le32_to_cpu(resp->flags);
0454
0455 if (resp->success_ftm == resp->ftm_per_burst)
0456 stats->success_num++;
0457 else if (resp->success_ftm >= 2)
0458 stats->partial_num++;
0459 else
0460 stats->failed_num++;
0461
0462 if ((flags & FTM_RESP_STAT_ASAP_REQ) &&
0463 (flags & FTM_RESP_STAT_ASAP_RESP))
0464 stats->asap_num++;
0465
0466 if (flags & FTM_RESP_STAT_NON_ASAP_RESP)
0467 stats->non_asap_num++;
0468
0469 stats->total_duration_ms += le32_to_cpu(resp->duration) / USEC_PER_MSEC;
0470
0471 if (flags & FTM_RESP_STAT_TRIGGER_UNKNOWN)
0472 stats->unknown_triggers_num++;
0473
0474 if (flags & FTM_RESP_STAT_DUP)
0475 stats->reschedule_requests_num++;
0476
0477 if (flags & FTM_RESP_STAT_NON_ASAP_OUT_WIN)
0478 stats->out_of_window_triggers_num++;
0479 }