linux/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c
<<
>>
Prefs
   1/******************************************************************************
   2 *
   3 * This file is provided under a dual BSD/GPLv2 license.  When using or
   4 * redistributing this file, you may do so under either license.
   5 *
   6 * GPL LICENSE SUMMARY
   7 *
   8 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
   9 * Copyright (C) 2018 - 2019 Intel Corporation
  10 *
  11 * This program is free software; you can redistribute it and/or modify
  12 * it under the terms of version 2 of the GNU General Public License as
  13 * published by the Free Software Foundation.
  14 *
  15 * This program is distributed in the hope that it will be useful, but
  16 * WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * General Public License for more details.
  19 *
  20 * The full GNU General Public License is included in this distribution
  21 * in the file called COPYING.
  22 *
  23 * Contact Information:
  24 * Intel Linux Wireless <linuxwifi@intel.com>
  25 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  26 *
  27 * BSD LICENSE
  28 *
  29 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
  30 * Copyright (C) 2018 - 2019 Intel Corporation
  31 * All rights reserved.
  32 *
  33 * Redistribution and use in source and binary forms, with or without
  34 * modification, are permitted provided that the following conditions
  35 * are met:
  36 *
  37 *  * Redistributions of source code must retain the above copyright
  38 *    notice, this list of conditions and the following disclaimer.
  39 *  * Redistributions in binary form must reproduce the above copyright
  40 *    notice, this list of conditions and the following disclaimer in
  41 *    the documentation and/or other materials provided with the
  42 *    distribution.
  43 *  * Neither the name Intel Corporation nor the names of its
  44 *    contributors may be used to endorse or promote products derived
  45 *    from this software without specific prior written permission.
  46 *
  47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  48 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  49 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  50 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  51 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  52 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  53 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  54 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  55 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  56 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  57 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  58 *
  59 *****************************************************************************/
  60#include <net/cfg80211.h>
  61#include <linux/etherdevice.h>
  62#include "mvm.h"
  63#include "constants.h"
  64
  65static int iwl_mvm_ftm_responder_set_bw_v1(struct cfg80211_chan_def *chandef,
  66                                           u8 *bw, u8 *ctrl_ch_position)
  67{
  68        switch (chandef->width) {
  69        case NL80211_CHAN_WIDTH_20_NOHT:
  70                *bw = IWL_TOF_BW_20_LEGACY;
  71                break;
  72        case NL80211_CHAN_WIDTH_20:
  73                *bw = IWL_TOF_BW_20_HT;
  74                break;
  75        case NL80211_CHAN_WIDTH_40:
  76                *bw = IWL_TOF_BW_40;
  77                *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
  78                break;
  79        case NL80211_CHAN_WIDTH_80:
  80                *bw = IWL_TOF_BW_80;
  81                *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
  82                break;
  83        default:
  84                return -ENOTSUPP;
  85        }
  86
  87        return 0;
  88}
  89
  90static int iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def *chandef,
  91                                           u8 *format_bw,
  92                                           u8 *ctrl_ch_position)
  93{
  94        switch (chandef->width) {
  95        case NL80211_CHAN_WIDTH_20_NOHT:
  96                *format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
  97                *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
  98                break;
  99        case NL80211_CHAN_WIDTH_20:
 100                *format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
 101                *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
 102                break;
 103        case NL80211_CHAN_WIDTH_40:
 104                *format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
 105                *format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
 106                *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
 107                break;
 108        case NL80211_CHAN_WIDTH_80:
 109                *format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
 110                *format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
 111                *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
 112                break;
 113        default:
 114                return -ENOTSUPP;
 115        }
 116
 117        return 0;
 118}
 119
 120static int
 121iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm,
 122                          struct ieee80211_vif *vif,
 123                          struct cfg80211_chan_def *chandef)
 124{
 125        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 126        /*
 127         * The command structure is the same for versions 6 and 7, (only the
 128         * field interpretation is different), so the same struct can be use
 129         * for all cases.
 130         */
 131        struct iwl_tof_responder_config_cmd cmd = {
 132                .channel_num = chandef->chan->hw_value,
 133                .cmd_valid_fields =
 134                        cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_CHAN_INFO |
 135                                    IWL_TOF_RESPONDER_CMD_VALID_BSSID |
 136                                    IWL_TOF_RESPONDER_CMD_VALID_STA_ID),
 137                .sta_id = mvmvif->bcast_sta.sta_id,
 138        };
 139        u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP,
 140                                           TOF_RESPONDER_CONFIG_CMD);
 141        int err;
 142
 143        lockdep_assert_held(&mvm->mutex);
 144
 145        if (cmd_ver == 7)
 146                err = iwl_mvm_ftm_responder_set_bw_v2(chandef, &cmd.format_bw,
 147                                                      &cmd.ctrl_ch_position);
 148        else
 149                err = iwl_mvm_ftm_responder_set_bw_v1(chandef, &cmd.format_bw,
 150                                                      &cmd.ctrl_ch_position);
 151
 152        if (err) {
 153                IWL_ERR(mvm, "Failed to set responder bandwidth\n");
 154                return err;
 155        }
 156
 157        memcpy(cmd.bssid, vif->addr, ETH_ALEN);
 158
 159        return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_RESPONDER_CONFIG_CMD,
 160                                                    LOCATION_GROUP, 0),
 161                                    0, sizeof(cmd), &cmd);
 162}
 163
 164static int
 165iwl_mvm_ftm_responder_dyn_cfg_cmd(struct iwl_mvm *mvm,
 166                                  struct ieee80211_vif *vif,
 167                                  struct ieee80211_ftm_responder_params *params)
 168{
 169        struct iwl_tof_responder_dyn_config_cmd cmd = {
 170                .lci_len = cpu_to_le32(params->lci_len + 2),
 171                .civic_len = cpu_to_le32(params->civicloc_len + 2),
 172        };
 173        u8 data[IWL_LCI_CIVIC_IE_MAX_SIZE] = {0};
 174        struct iwl_host_cmd hcmd = {
 175                .id = iwl_cmd_id(TOF_RESPONDER_DYN_CONFIG_CMD,
 176                                 LOCATION_GROUP, 0),
 177                .data[0] = &cmd,
 178                .len[0] = sizeof(cmd),
 179                .data[1] = &data,
 180                /* .len[1] set later */
 181                /* may not be able to DMA from stack */
 182                .dataflags[1] = IWL_HCMD_DFL_DUP,
 183        };
 184        u32 aligned_lci_len = ALIGN(params->lci_len + 2, 4);
 185        u32 aligned_civicloc_len = ALIGN(params->civicloc_len + 2, 4);
 186        u8 *pos = data;
 187
 188        lockdep_assert_held(&mvm->mutex);
 189
 190        if (aligned_lci_len + aligned_civicloc_len > sizeof(data)) {
 191                IWL_ERR(mvm, "LCI/civicloc data too big (%zd + %zd)\n",
 192                        params->lci_len, params->civicloc_len);
 193                return -ENOBUFS;
 194        }
 195
 196        pos[0] = WLAN_EID_MEASURE_REPORT;
 197        pos[1] = params->lci_len;
 198        memcpy(pos + 2, params->lci, params->lci_len);
 199
 200        pos += aligned_lci_len;
 201        pos[0] = WLAN_EID_MEASURE_REPORT;
 202        pos[1] = params->civicloc_len;
 203        memcpy(pos + 2, params->civicloc, params->civicloc_len);
 204
 205        hcmd.len[1] = aligned_lci_len + aligned_civicloc_len;
 206
 207        return iwl_mvm_send_cmd(mvm, &hcmd);
 208}
 209
 210int iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 211{
 212        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 213        struct ieee80211_ftm_responder_params *params;
 214        struct ieee80211_chanctx_conf ctx, *pctx;
 215        u16 *phy_ctxt_id;
 216        struct iwl_mvm_phy_ctxt *phy_ctxt;
 217        int ret;
 218
 219        params = vif->bss_conf.ftmr_params;
 220
 221        lockdep_assert_held(&mvm->mutex);
 222
 223        if (WARN_ON_ONCE(!vif->bss_conf.ftm_responder))
 224                return -EINVAL;
 225
 226        if (vif->p2p || vif->type != NL80211_IFTYPE_AP ||
 227            !mvmvif->ap_ibss_active) {
 228                IWL_ERR(mvm, "Cannot start responder, not in AP mode\n");
 229                return -EIO;
 230        }
 231
 232        rcu_read_lock();
 233        pctx = rcu_dereference(vif->chanctx_conf);
 234        /* Copy the ctx to unlock the rcu and send the phy ctxt. We don't care
 235         * about changes in the ctx after releasing the lock because the driver
 236         * is still protected by the mutex. */
 237        ctx = *pctx;
 238        phy_ctxt_id  = (u16 *)pctx->drv_priv;
 239        rcu_read_unlock();
 240
 241        phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
 242        ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx.def,
 243                                       ctx.rx_chains_static,
 244                                       ctx.rx_chains_dynamic);
 245        if (ret)
 246                return ret;
 247
 248        ret = iwl_mvm_ftm_responder_cmd(mvm, vif, &ctx.def);
 249        if (ret)
 250                return ret;
 251
 252        if (params)
 253                ret = iwl_mvm_ftm_responder_dyn_cfg_cmd(mvm, vif, params);
 254
 255        return ret;
 256}
 257
 258void iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm,
 259                                   struct ieee80211_vif *vif)
 260{
 261        if (!vif->bss_conf.ftm_responder)
 262                return;
 263
 264        iwl_mvm_ftm_start_responder(mvm, vif);
 265}
 266
 267void iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm,
 268                                 struct iwl_rx_cmd_buffer *rxb)
 269{
 270        struct iwl_rx_packet *pkt = rxb_addr(rxb);
 271        struct iwl_ftm_responder_stats *resp = (void *)pkt->data;
 272        struct cfg80211_ftm_responder_stats *stats = &mvm->ftm_resp_stats;
 273        u32 flags = le32_to_cpu(resp->flags);
 274
 275        if (resp->success_ftm == resp->ftm_per_burst)
 276                stats->success_num++;
 277        else if (resp->success_ftm >= 2)
 278                stats->partial_num++;
 279        else
 280                stats->failed_num++;
 281
 282        if ((flags & FTM_RESP_STAT_ASAP_REQ) &&
 283            (flags & FTM_RESP_STAT_ASAP_RESP))
 284                stats->asap_num++;
 285
 286        if (flags & FTM_RESP_STAT_NON_ASAP_RESP)
 287                stats->non_asap_num++;
 288
 289        stats->total_duration_ms += le32_to_cpu(resp->duration) / USEC_PER_MSEC;
 290
 291        if (flags & FTM_RESP_STAT_TRIGGER_UNKNOWN)
 292                stats->unknown_triggers_num++;
 293
 294        if (flags & FTM_RESP_STAT_DUP)
 295                stats->reschedule_requests_num++;
 296
 297        if (flags & FTM_RESP_STAT_NON_ASAP_OUT_WIN)
 298                stats->out_of_window_triggers_num++;
 299}
 300