linux/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2012-2014, 2018-2021 Intel Corporation
   4 * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
   5 * Copyright (C) 2016-2017 Intel Deutschland GmbH
   6 */
   7#include "mvm.h"
   8#include "debugfs.h"
   9
  10static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
  11                                 struct ieee80211_vif *vif,
  12                                 enum iwl_dbgfs_pm_mask param, int val)
  13{
  14        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
  15        struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm;
  16
  17        dbgfs_pm->mask |= param;
  18
  19        switch (param) {
  20        case MVM_DEBUGFS_PM_KEEP_ALIVE: {
  21                int dtimper = vif->bss_conf.dtim_period ?: 1;
  22                int dtimper_msec = dtimper * vif->bss_conf.beacon_int;
  23
  24                IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val);
  25                if (val * MSEC_PER_SEC < 3 * dtimper_msec)
  26                        IWL_WARN(mvm,
  27                                 "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n",
  28                                 val * MSEC_PER_SEC, 3 * dtimper_msec);
  29                dbgfs_pm->keep_alive_seconds = val;
  30                break;
  31        }
  32        case MVM_DEBUGFS_PM_SKIP_OVER_DTIM:
  33                IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n",
  34                                val ? "enabled" : "disabled");
  35                dbgfs_pm->skip_over_dtim = val;
  36                break;
  37        case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS:
  38                IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val);
  39                dbgfs_pm->skip_dtim_periods = val;
  40                break;
  41        case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT:
  42                IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val);
  43                dbgfs_pm->rx_data_timeout = val;
  44                break;
  45        case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT:
  46                IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val);
  47                dbgfs_pm->tx_data_timeout = val;
  48                break;
  49        case MVM_DEBUGFS_PM_LPRX_ENA:
  50                IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled");
  51                dbgfs_pm->lprx_ena = val;
  52                break;
  53        case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD:
  54                IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val);
  55                dbgfs_pm->lprx_rssi_threshold = val;
  56                break;
  57        case MVM_DEBUGFS_PM_SNOOZE_ENABLE:
  58                IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val);
  59                dbgfs_pm->snooze_ena = val;
  60                break;
  61        case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING:
  62                IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val);
  63                dbgfs_pm->uapsd_misbehaving = val;
  64                break;
  65        case MVM_DEBUGFS_PM_USE_PS_POLL:
  66                IWL_DEBUG_POWER(mvm, "use_ps_poll=%d\n", val);
  67                dbgfs_pm->use_ps_poll = val;
  68                break;
  69        }
  70}
  71
  72static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
  73                                         size_t count, loff_t *ppos)
  74{
  75        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
  76        struct iwl_mvm *mvm = mvmvif->mvm;
  77        enum iwl_dbgfs_pm_mask param;
  78        int val, ret;
  79
  80        if (!strncmp("keep_alive=", buf, 11)) {
  81                if (sscanf(buf + 11, "%d", &val) != 1)
  82                        return -EINVAL;
  83                param = MVM_DEBUGFS_PM_KEEP_ALIVE;
  84        } else if (!strncmp("skip_over_dtim=", buf, 15)) {
  85                if (sscanf(buf + 15, "%d", &val) != 1)
  86                        return -EINVAL;
  87                param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM;
  88        } else if (!strncmp("skip_dtim_periods=", buf, 18)) {
  89                if (sscanf(buf + 18, "%d", &val) != 1)
  90                        return -EINVAL;
  91                param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS;
  92        } else if (!strncmp("rx_data_timeout=", buf, 16)) {
  93                if (sscanf(buf + 16, "%d", &val) != 1)
  94                        return -EINVAL;
  95                param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT;
  96        } else if (!strncmp("tx_data_timeout=", buf, 16)) {
  97                if (sscanf(buf + 16, "%d", &val) != 1)
  98                        return -EINVAL;
  99                param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT;
 100        } else if (!strncmp("lprx=", buf, 5)) {
 101                if (sscanf(buf + 5, "%d", &val) != 1)
 102                        return -EINVAL;
 103                param = MVM_DEBUGFS_PM_LPRX_ENA;
 104        } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) {
 105                if (sscanf(buf + 20, "%d", &val) != 1)
 106                        return -EINVAL;
 107                if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val <
 108                    POWER_LPRX_RSSI_THRESHOLD_MIN)
 109                        return -EINVAL;
 110                param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD;
 111        } else if (!strncmp("snooze_enable=", buf, 14)) {
 112                if (sscanf(buf + 14, "%d", &val) != 1)
 113                        return -EINVAL;
 114                param = MVM_DEBUGFS_PM_SNOOZE_ENABLE;
 115        } else if (!strncmp("uapsd_misbehaving=", buf, 18)) {
 116                if (sscanf(buf + 18, "%d", &val) != 1)
 117                        return -EINVAL;
 118                param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING;
 119        } else if (!strncmp("use_ps_poll=", buf, 12)) {
 120                if (sscanf(buf + 12, "%d", &val) != 1)
 121                        return -EINVAL;
 122                param = MVM_DEBUGFS_PM_USE_PS_POLL;
 123        } else {
 124                return -EINVAL;
 125        }
 126
 127        mutex_lock(&mvm->mutex);
 128        iwl_dbgfs_update_pm(mvm, vif, param, val);
 129        ret = iwl_mvm_power_update_mac(mvm);
 130        mutex_unlock(&mvm->mutex);
 131
 132        return ret ?: count;
 133}
 134
 135static ssize_t iwl_dbgfs_tx_pwr_lmt_read(struct file *file,
 136                                         char __user *user_buf,
 137                                         size_t count, loff_t *ppos)
 138{
 139        struct ieee80211_vif *vif = file->private_data;
 140        char buf[64];
 141        int bufsz = sizeof(buf);
 142        int pos;
 143
 144        pos = scnprintf(buf, bufsz, "bss limit = %d\n",
 145                        vif->bss_conf.txpower);
 146
 147        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 148}
 149
 150static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
 151                                        char __user *user_buf,
 152                                        size_t count, loff_t *ppos)
 153{
 154        struct ieee80211_vif *vif = file->private_data;
 155        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 156        struct iwl_mvm *mvm = mvmvif->mvm;
 157        char buf[512];
 158        int bufsz = sizeof(buf);
 159        int pos;
 160
 161        pos = iwl_mvm_power_mac_dbgfs_read(mvm, vif, buf, bufsz);
 162
 163        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 164}
 165
 166static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
 167                                         char __user *user_buf,
 168                                         size_t count, loff_t *ppos)
 169{
 170        struct ieee80211_vif *vif = file->private_data;
 171        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 172        struct iwl_mvm *mvm = mvmvif->mvm;
 173        u8 ap_sta_id;
 174        struct ieee80211_chanctx_conf *chanctx_conf;
 175        char buf[512];
 176        int bufsz = sizeof(buf);
 177        int pos = 0;
 178        int i;
 179
 180        mutex_lock(&mvm->mutex);
 181
 182        ap_sta_id = mvmvif->ap_sta_id;
 183
 184        switch (ieee80211_vif_type_p2p(vif)) {
 185        case NL80211_IFTYPE_ADHOC:
 186                pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n");
 187                break;
 188        case NL80211_IFTYPE_STATION:
 189                pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n");
 190                break;
 191        case NL80211_IFTYPE_AP:
 192                pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n");
 193                break;
 194        case NL80211_IFTYPE_P2P_CLIENT:
 195                pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n");
 196                break;
 197        case NL80211_IFTYPE_P2P_GO:
 198                pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n");
 199                break;
 200        case NL80211_IFTYPE_P2P_DEVICE:
 201                pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n");
 202                break;
 203        default:
 204                break;
 205        }
 206
 207        pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n",
 208                         mvmvif->id, mvmvif->color);
 209        pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n",
 210                         vif->bss_conf.bssid);
 211        pos += scnprintf(buf+pos, bufsz-pos, "Load: %d\n",
 212                         mvm->tcm.result.load[mvmvif->id]);
 213        pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n");
 214        for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++)
 215                pos += scnprintf(buf+pos, bufsz-pos,
 216                                 "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n",
 217                                 i, mvmvif->queue_params[i].txop,
 218                                 mvmvif->queue_params[i].cw_min,
 219                                 mvmvif->queue_params[i].cw_max,
 220                                 mvmvif->queue_params[i].aifs,
 221                                 mvmvif->queue_params[i].uapsd);
 222
 223        if (vif->type == NL80211_IFTYPE_STATION &&
 224            ap_sta_id != IWL_MVM_INVALID_STA) {
 225                struct iwl_mvm_sta *mvm_sta;
 226
 227                mvm_sta = iwl_mvm_sta_from_staid_protected(mvm, ap_sta_id);
 228                if (mvm_sta) {
 229                        pos += scnprintf(buf+pos, bufsz-pos,
 230                                         "ap_sta_id %d - reduced Tx power %d\n",
 231                                         ap_sta_id,
 232                                         mvm_sta->bt_reduced_txpower);
 233                }
 234        }
 235
 236        rcu_read_lock();
 237        chanctx_conf = rcu_dereference(vif->chanctx_conf);
 238        if (chanctx_conf)
 239                pos += scnprintf(buf+pos, bufsz-pos,
 240                                 "idle rx chains %d, active rx chains: %d\n",
 241                                 chanctx_conf->rx_chains_static,
 242                                 chanctx_conf->rx_chains_dynamic);
 243        rcu_read_unlock();
 244
 245        mutex_unlock(&mvm->mutex);
 246
 247        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 248}
 249
 250static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
 251                                enum iwl_dbgfs_bf_mask param, int value)
 252{
 253        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 254        struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
 255
 256        dbgfs_bf->mask |= param;
 257
 258        switch (param) {
 259        case MVM_DEBUGFS_BF_ENERGY_DELTA:
 260                dbgfs_bf->bf_energy_delta = value;
 261                break;
 262        case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA:
 263                dbgfs_bf->bf_roaming_energy_delta = value;
 264                break;
 265        case MVM_DEBUGFS_BF_ROAMING_STATE:
 266                dbgfs_bf->bf_roaming_state = value;
 267                break;
 268        case MVM_DEBUGFS_BF_TEMP_THRESHOLD:
 269                dbgfs_bf->bf_temp_threshold = value;
 270                break;
 271        case MVM_DEBUGFS_BF_TEMP_FAST_FILTER:
 272                dbgfs_bf->bf_temp_fast_filter = value;
 273                break;
 274        case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER:
 275                dbgfs_bf->bf_temp_slow_filter = value;
 276                break;
 277        case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER:
 278                dbgfs_bf->bf_enable_beacon_filter = value;
 279                break;
 280        case MVM_DEBUGFS_BF_DEBUG_FLAG:
 281                dbgfs_bf->bf_debug_flag = value;
 282                break;
 283        case MVM_DEBUGFS_BF_ESCAPE_TIMER:
 284                dbgfs_bf->bf_escape_timer = value;
 285                break;
 286        case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT:
 287                dbgfs_bf->ba_enable_beacon_abort = value;
 288                break;
 289        case MVM_DEBUGFS_BA_ESCAPE_TIMER:
 290                dbgfs_bf->ba_escape_timer = value;
 291                break;
 292        }
 293}
 294
 295static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf,
 296                                         size_t count, loff_t *ppos)
 297{
 298        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 299        struct iwl_mvm *mvm = mvmvif->mvm;
 300        enum iwl_dbgfs_bf_mask param;
 301        int value, ret = 0;
 302
 303        if (!strncmp("bf_energy_delta=", buf, 16)) {
 304                if (sscanf(buf+16, "%d", &value) != 1)
 305                        return -EINVAL;
 306                if (value < IWL_BF_ENERGY_DELTA_MIN ||
 307                    value > IWL_BF_ENERGY_DELTA_MAX)
 308                        return -EINVAL;
 309                param = MVM_DEBUGFS_BF_ENERGY_DELTA;
 310        } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) {
 311                if (sscanf(buf+24, "%d", &value) != 1)
 312                        return -EINVAL;
 313                if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN ||
 314                    value > IWL_BF_ROAMING_ENERGY_DELTA_MAX)
 315                        return -EINVAL;
 316                param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA;
 317        } else if (!strncmp("bf_roaming_state=", buf, 17)) {
 318                if (sscanf(buf+17, "%d", &value) != 1)
 319                        return -EINVAL;
 320                if (value < IWL_BF_ROAMING_STATE_MIN ||
 321                    value > IWL_BF_ROAMING_STATE_MAX)
 322                        return -EINVAL;
 323                param = MVM_DEBUGFS_BF_ROAMING_STATE;
 324        } else if (!strncmp("bf_temp_threshold=", buf, 18)) {
 325                if (sscanf(buf+18, "%d", &value) != 1)
 326                        return -EINVAL;
 327                if (value < IWL_BF_TEMP_THRESHOLD_MIN ||
 328                    value > IWL_BF_TEMP_THRESHOLD_MAX)
 329                        return -EINVAL;
 330                param = MVM_DEBUGFS_BF_TEMP_THRESHOLD;
 331        } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) {
 332                if (sscanf(buf+20, "%d", &value) != 1)
 333                        return -EINVAL;
 334                if (value < IWL_BF_TEMP_FAST_FILTER_MIN ||
 335                    value > IWL_BF_TEMP_FAST_FILTER_MAX)
 336                        return -EINVAL;
 337                param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER;
 338        } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) {
 339                if (sscanf(buf+20, "%d", &value) != 1)
 340                        return -EINVAL;
 341                if (value < IWL_BF_TEMP_SLOW_FILTER_MIN ||
 342                    value > IWL_BF_TEMP_SLOW_FILTER_MAX)
 343                        return -EINVAL;
 344                param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER;
 345        } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
 346                if (sscanf(buf+24, "%d", &value) != 1)
 347                        return -EINVAL;
 348                if (value < 0 || value > 1)
 349                        return -EINVAL;
 350                param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER;
 351        } else if (!strncmp("bf_debug_flag=", buf, 14)) {
 352                if (sscanf(buf+14, "%d", &value) != 1)
 353                        return -EINVAL;
 354                if (value < 0 || value > 1)
 355                        return -EINVAL;
 356                param = MVM_DEBUGFS_BF_DEBUG_FLAG;
 357        } else if (!strncmp("bf_escape_timer=", buf, 16)) {
 358                if (sscanf(buf+16, "%d", &value) != 1)
 359                        return -EINVAL;
 360                if (value < IWL_BF_ESCAPE_TIMER_MIN ||
 361                    value > IWL_BF_ESCAPE_TIMER_MAX)
 362                        return -EINVAL;
 363                param = MVM_DEBUGFS_BF_ESCAPE_TIMER;
 364        } else if (!strncmp("ba_escape_timer=", buf, 16)) {
 365                if (sscanf(buf+16, "%d", &value) != 1)
 366                        return -EINVAL;
 367                if (value < IWL_BA_ESCAPE_TIMER_MIN ||
 368                    value > IWL_BA_ESCAPE_TIMER_MAX)
 369                        return -EINVAL;
 370                param = MVM_DEBUGFS_BA_ESCAPE_TIMER;
 371        } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) {
 372                if (sscanf(buf+23, "%d", &value) != 1)
 373                        return -EINVAL;
 374                if (value < 0 || value > 1)
 375                        return -EINVAL;
 376                param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT;
 377        } else {
 378                return -EINVAL;
 379        }
 380
 381        mutex_lock(&mvm->mutex);
 382        iwl_dbgfs_update_bf(vif, param, value);
 383        if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value)
 384                ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
 385        else
 386                ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
 387        mutex_unlock(&mvm->mutex);
 388
 389        return ret ?: count;
 390}
 391
 392static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
 393                                        char __user *user_buf,
 394                                        size_t count, loff_t *ppos)
 395{
 396        struct ieee80211_vif *vif = file->private_data;
 397        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 398        char buf[256];
 399        int pos = 0;
 400        const size_t bufsz = sizeof(buf);
 401        struct iwl_beacon_filter_cmd cmd = {
 402                IWL_BF_CMD_CONFIG_DEFAULTS,
 403                .bf_enable_beacon_filter =
 404                        cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT),
 405                .ba_enable_beacon_abort =
 406                        cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT),
 407        };
 408
 409        iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
 410        if (mvmvif->bf_data.bf_enabled)
 411                cmd.bf_enable_beacon_filter = cpu_to_le32(1);
 412        else
 413                cmd.bf_enable_beacon_filter = 0;
 414
 415        pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n",
 416                         le32_to_cpu(cmd.bf_energy_delta));
 417        pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n",
 418                         le32_to_cpu(cmd.bf_roaming_energy_delta));
 419        pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n",
 420                         le32_to_cpu(cmd.bf_roaming_state));
 421        pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n",
 422                         le32_to_cpu(cmd.bf_temp_threshold));
 423        pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n",
 424                         le32_to_cpu(cmd.bf_temp_fast_filter));
 425        pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n",
 426                         le32_to_cpu(cmd.bf_temp_slow_filter));
 427        pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n",
 428                         le32_to_cpu(cmd.bf_enable_beacon_filter));
 429        pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n",
 430                         le32_to_cpu(cmd.bf_debug_flag));
 431        pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n",
 432                         le32_to_cpu(cmd.bf_escape_timer));
 433        pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n",
 434                         le32_to_cpu(cmd.ba_escape_timer));
 435        pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n",
 436                         le32_to_cpu(cmd.ba_enable_beacon_abort));
 437
 438        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 439}
 440
 441static inline char *iwl_dbgfs_is_match(char *name, char *buf)
 442{
 443        int len = strlen(name);
 444
 445        return !strncmp(name, buf, len) ? buf + len : NULL;
 446}
 447
 448static ssize_t iwl_dbgfs_os_device_timediff_read(struct file *file,
 449                                                 char __user *user_buf,
 450                                                 size_t count, loff_t *ppos)
 451{
 452        struct ieee80211_vif *vif = file->private_data;
 453        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 454        struct iwl_mvm *mvm = mvmvif->mvm;
 455        u32 curr_gp2;
 456        u64 curr_os;
 457        s64 diff;
 458        char buf[64];
 459        const size_t bufsz = sizeof(buf);
 460        int pos = 0;
 461
 462        mutex_lock(&mvm->mutex);
 463        iwl_mvm_get_sync_time(mvm, CLOCK_BOOTTIME, &curr_gp2, &curr_os, NULL);
 464        mutex_unlock(&mvm->mutex);
 465
 466        do_div(curr_os, NSEC_PER_USEC);
 467        diff = curr_os - curr_gp2;
 468        pos += scnprintf(buf + pos, bufsz - pos, "diff=%lld\n", diff);
 469
 470        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 471}
 472
 473static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf,
 474                                           size_t count, loff_t *ppos)
 475{
 476        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 477        struct iwl_mvm *mvm = mvmvif->mvm;
 478        u8 value;
 479        int ret;
 480
 481        ret = kstrtou8(buf, 0, &value);
 482        if (ret)
 483                return ret;
 484        if (value > 1)
 485                return -EINVAL;
 486
 487        mutex_lock(&mvm->mutex);
 488        iwl_mvm_update_low_latency(mvm, vif, value, LOW_LATENCY_DEBUGFS);
 489        mutex_unlock(&mvm->mutex);
 490
 491        return count;
 492}
 493
 494static ssize_t
 495iwl_dbgfs_low_latency_force_write(struct ieee80211_vif *vif, char *buf,
 496                                  size_t count, loff_t *ppos)
 497{
 498        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 499        struct iwl_mvm *mvm = mvmvif->mvm;
 500        u8 value;
 501        int ret;
 502
 503        ret = kstrtou8(buf, 0, &value);
 504        if (ret)
 505                return ret;
 506
 507        if (value > NUM_LOW_LATENCY_FORCE)
 508                return -EINVAL;
 509
 510        mutex_lock(&mvm->mutex);
 511        if (value == LOW_LATENCY_FORCE_UNSET) {
 512                iwl_mvm_update_low_latency(mvm, vif, false,
 513                                           LOW_LATENCY_DEBUGFS_FORCE);
 514                iwl_mvm_update_low_latency(mvm, vif, false,
 515                                           LOW_LATENCY_DEBUGFS_FORCE_ENABLE);
 516        } else {
 517                iwl_mvm_update_low_latency(mvm, vif,
 518                                           value == LOW_LATENCY_FORCE_ON,
 519                                           LOW_LATENCY_DEBUGFS_FORCE);
 520                iwl_mvm_update_low_latency(mvm, vif, true,
 521                                           LOW_LATENCY_DEBUGFS_FORCE_ENABLE);
 522        }
 523        mutex_unlock(&mvm->mutex);
 524        return count;
 525}
 526
 527static ssize_t iwl_dbgfs_low_latency_read(struct file *file,
 528                                          char __user *user_buf,
 529                                          size_t count, loff_t *ppos)
 530{
 531        struct ieee80211_vif *vif = file->private_data;
 532        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 533        char format[] = "traffic=%d\ndbgfs=%d\nvcmd=%d\nvif_type=%d\n"
 534                        "dbgfs_force_enable=%d\ndbgfs_force=%d\nactual=%d\n";
 535
 536        /*
 537         * all values in format are boolean so the size of format is enough
 538         * for holding the result string
 539         */
 540        char buf[sizeof(format) + 1] = {};
 541        int len;
 542
 543        len = scnprintf(buf, sizeof(buf) - 1, format,
 544                        !!(mvmvif->low_latency & LOW_LATENCY_TRAFFIC),
 545                        !!(mvmvif->low_latency & LOW_LATENCY_DEBUGFS),
 546                        !!(mvmvif->low_latency & LOW_LATENCY_VCMD),
 547                        !!(mvmvif->low_latency & LOW_LATENCY_VIF_TYPE),
 548                        !!(mvmvif->low_latency &
 549                           LOW_LATENCY_DEBUGFS_FORCE_ENABLE),
 550                        !!(mvmvif->low_latency & LOW_LATENCY_DEBUGFS_FORCE),
 551                        !!(mvmvif->low_latency_actual));
 552        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 553}
 554
 555static ssize_t iwl_dbgfs_uapsd_misbehaving_read(struct file *file,
 556                                                char __user *user_buf,
 557                                                size_t count, loff_t *ppos)
 558{
 559        struct ieee80211_vif *vif = file->private_data;
 560        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 561        char buf[20];
 562        int len;
 563
 564        len = sprintf(buf, "%pM\n", mvmvif->uapsd_misbehaving_bssid);
 565        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 566}
 567
 568static ssize_t iwl_dbgfs_uapsd_misbehaving_write(struct ieee80211_vif *vif,
 569                                                 char *buf, size_t count,
 570                                                 loff_t *ppos)
 571{
 572        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 573        struct iwl_mvm *mvm = mvmvif->mvm;
 574        bool ret;
 575
 576        mutex_lock(&mvm->mutex);
 577        ret = mac_pton(buf, mvmvif->uapsd_misbehaving_bssid);
 578        mutex_unlock(&mvm->mutex);
 579
 580        return ret ? count : -EINVAL;
 581}
 582
 583static ssize_t iwl_dbgfs_rx_phyinfo_write(struct ieee80211_vif *vif, char *buf,
 584                                          size_t count, loff_t *ppos)
 585{
 586        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 587        struct iwl_mvm *mvm = mvmvif->mvm;
 588        struct ieee80211_chanctx_conf *chanctx_conf;
 589        struct iwl_mvm_phy_ctxt *phy_ctxt;
 590        u16 value;
 591        int ret;
 592
 593        ret = kstrtou16(buf, 0, &value);
 594        if (ret)
 595                return ret;
 596
 597        mutex_lock(&mvm->mutex);
 598        rcu_read_lock();
 599
 600        chanctx_conf = rcu_dereference(vif->chanctx_conf);
 601        /* make sure the channel context is assigned */
 602        if (!chanctx_conf) {
 603                rcu_read_unlock();
 604                mutex_unlock(&mvm->mutex);
 605                return -EINVAL;
 606        }
 607
 608        phy_ctxt = &mvm->phy_ctxts[*(u16 *)chanctx_conf->drv_priv];
 609        rcu_read_unlock();
 610
 611        mvm->dbgfs_rx_phyinfo = value;
 612
 613        ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chanctx_conf->min_def,
 614                                       chanctx_conf->rx_chains_static,
 615                                       chanctx_conf->rx_chains_dynamic);
 616        mutex_unlock(&mvm->mutex);
 617
 618        return ret ?: count;
 619}
 620
 621static ssize_t iwl_dbgfs_rx_phyinfo_read(struct file *file,
 622                                         char __user *user_buf,
 623                                         size_t count, loff_t *ppos)
 624{
 625        struct ieee80211_vif *vif = file->private_data;
 626        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 627        char buf[8];
 628        int len;
 629
 630        len = scnprintf(buf, sizeof(buf), "0x%04x\n",
 631                        mvmvif->mvm->dbgfs_rx_phyinfo);
 632
 633        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 634}
 635
 636static void iwl_dbgfs_quota_check(void *data, u8 *mac,
 637                                  struct ieee80211_vif *vif)
 638{
 639        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 640        int *ret = data;
 641
 642        if (mvmvif->dbgfs_quota_min)
 643                *ret = -EINVAL;
 644}
 645
 646static ssize_t iwl_dbgfs_quota_min_write(struct ieee80211_vif *vif, char *buf,
 647                                         size_t count, loff_t *ppos)
 648{
 649        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 650        struct iwl_mvm *mvm = mvmvif->mvm;
 651        u16 value;
 652        int ret;
 653
 654        ret = kstrtou16(buf, 0, &value);
 655        if (ret)
 656                return ret;
 657
 658        if (value > 95)
 659                return -EINVAL;
 660
 661        mutex_lock(&mvm->mutex);
 662
 663        mvmvif->dbgfs_quota_min = 0;
 664        ieee80211_iterate_interfaces(mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 665                                     iwl_dbgfs_quota_check, &ret);
 666        if (ret == 0) {
 667                mvmvif->dbgfs_quota_min = value;
 668                iwl_mvm_update_quotas(mvm, false, NULL);
 669        }
 670        mutex_unlock(&mvm->mutex);
 671
 672        return ret ?: count;
 673}
 674
 675static ssize_t iwl_dbgfs_quota_min_read(struct file *file,
 676                                        char __user *user_buf,
 677                                        size_t count, loff_t *ppos)
 678{
 679        struct ieee80211_vif *vif = file->private_data;
 680        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 681        char buf[10];
 682        int len;
 683
 684        len = scnprintf(buf, sizeof(buf), "%d\n", mvmvif->dbgfs_quota_min);
 685
 686        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 687}
 688
 689#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
 690        _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
 691#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
 692        _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
 693#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do {               \
 694                debugfs_create_file(#name, mode, parent, vif,           \
 695                                    &iwl_dbgfs_##name##_ops);           \
 696        } while (0)
 697
 698MVM_DEBUGFS_READ_FILE_OPS(mac_params);
 699MVM_DEBUGFS_READ_FILE_OPS(tx_pwr_lmt);
 700MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
 701MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
 702MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10);
 703MVM_DEBUGFS_WRITE_FILE_OPS(low_latency_force, 10);
 704MVM_DEBUGFS_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20);
 705MVM_DEBUGFS_READ_WRITE_FILE_OPS(rx_phyinfo, 10);
 706MVM_DEBUGFS_READ_WRITE_FILE_OPS(quota_min, 32);
 707MVM_DEBUGFS_READ_FILE_OPS(os_device_timediff);
 708
 709
 710void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 711{
 712        struct dentry *dbgfs_dir = vif->debugfs_dir;
 713        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 714        char buf[100];
 715
 716        /*
 717         * Check if debugfs directory already exist before creating it.
 718         * This may happen when, for example, resetting hw or suspend-resume
 719         */
 720        if (!dbgfs_dir || mvmvif->dbgfs_dir)
 721                return;
 722
 723        mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir);
 724        if (IS_ERR_OR_NULL(mvmvif->dbgfs_dir)) {
 725                IWL_ERR(mvm, "Failed to create debugfs directory under %pd\n",
 726                        dbgfs_dir);
 727                return;
 728        }
 729
 730        if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
 731            ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) ||
 732             (vif->type == NL80211_IFTYPE_STATION && vif->p2p)))
 733                MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, 0600);
 734
 735        MVM_DEBUGFS_ADD_FILE_VIF(tx_pwr_lmt, mvmvif->dbgfs_dir, 0400);
 736        MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, 0400);
 737        MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir, 0600);
 738        MVM_DEBUGFS_ADD_FILE_VIF(low_latency_force, mvmvif->dbgfs_dir, 0600);
 739        MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir, 0600);
 740        MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir, 0600);
 741        MVM_DEBUGFS_ADD_FILE_VIF(quota_min, mvmvif->dbgfs_dir, 0600);
 742        MVM_DEBUGFS_ADD_FILE_VIF(os_device_timediff, mvmvif->dbgfs_dir, 0400);
 743
 744        if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
 745            mvmvif == mvm->bf_allowed_vif)
 746                MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, 0600);
 747
 748        /*
 749         * Create symlink for convenience pointing to interface specific
 750         * debugfs entries for the driver. For example, under
 751         * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/
 752         * find
 753         * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/
 754         */
 755        snprintf(buf, 100, "../../../%pd3/%pd",
 756                 dbgfs_dir,
 757                 mvmvif->dbgfs_dir);
 758
 759        mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name,
 760                                                     mvm->debugfs_dir, buf);
 761}
 762
 763void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 764{
 765        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 766
 767        debugfs_remove(mvmvif->dbgfs_slink);
 768        mvmvif->dbgfs_slink = NULL;
 769
 770        debugfs_remove_recursive(mvmvif->dbgfs_dir);
 771        mvmvif->dbgfs_dir = NULL;
 772}
 773