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) 2012 - 2014 Intel Corporation. All rights reserved. 9 * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH 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 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, 23 * USA 24 * 25 * The full GNU General Public License is included in this distribution 26 * in the file called COPYING. 27 * 28 * Contact Information: 29 * Intel Linux Wireless <ilw@linux.intel.com> 30 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 31 * 32 * BSD LICENSE 33 * 34 * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. 35 * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH 36 * All rights reserved. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 42 * * Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * * Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in 46 * the documentation and/or other materials provided with the 47 * distribution. 48 * * Neither the name Intel Corporation nor the names of its 49 * contributors may be used to endorse or promote products derived 50 * from this software without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 53 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 54 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 55 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 56 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 57 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 58 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 59 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 60 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 61 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 62 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 63 * 64 *****************************************************************************/ 65#include "mvm.h" 66#include "debugfs.h" 67 68static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, 69 struct ieee80211_vif *vif, 70 enum iwl_dbgfs_pm_mask param, int val) 71{ 72 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 73 struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm; 74 75 dbgfs_pm->mask |= param; 76 77 switch (param) { 78 case MVM_DEBUGFS_PM_KEEP_ALIVE: { 79 int dtimper = vif->bss_conf.dtim_period ?: 1; 80 int dtimper_msec = dtimper * vif->bss_conf.beacon_int; 81 82 IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val); 83 if (val * MSEC_PER_SEC < 3 * dtimper_msec) 84 IWL_WARN(mvm, 85 "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n", 86 val * MSEC_PER_SEC, 3 * dtimper_msec); 87 dbgfs_pm->keep_alive_seconds = val; 88 break; 89 } 90 case MVM_DEBUGFS_PM_SKIP_OVER_DTIM: 91 IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n", 92 val ? "enabled" : "disabled"); 93 dbgfs_pm->skip_over_dtim = val; 94 break; 95 case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS: 96 IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val); 97 dbgfs_pm->skip_dtim_periods = val; 98 break; 99 case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT: 100 IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val); 101 dbgfs_pm->rx_data_timeout = val; 102 break; 103 case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT: 104 IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val); 105 dbgfs_pm->tx_data_timeout = val; 106 break; 107 case MVM_DEBUGFS_PM_LPRX_ENA: 108 IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled"); 109 dbgfs_pm->lprx_ena = val; 110 break; 111 case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD: 112 IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val); 113 dbgfs_pm->lprx_rssi_threshold = val; 114 break; 115 case MVM_DEBUGFS_PM_SNOOZE_ENABLE: 116 IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val); 117 dbgfs_pm->snooze_ena = val; 118 break; 119 case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING: 120 IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val); 121 dbgfs_pm->uapsd_misbehaving = val; 122 break; 123 case MVM_DEBUGFS_PM_USE_PS_POLL: 124 IWL_DEBUG_POWER(mvm, "use_ps_poll=%d\n", val); 125 dbgfs_pm->use_ps_poll = val; 126 break; 127 } 128} 129 130static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf, 131 size_t count, loff_t *ppos) 132{ 133 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 134 struct iwl_mvm *mvm = mvmvif->mvm; 135 enum iwl_dbgfs_pm_mask param; 136 int val, ret; 137 138 if (!strncmp("keep_alive=", buf, 11)) { 139 if (sscanf(buf + 11, "%d", &val) != 1) 140 return -EINVAL; 141 param = MVM_DEBUGFS_PM_KEEP_ALIVE; 142 } else if (!strncmp("skip_over_dtim=", buf, 15)) { 143 if (sscanf(buf + 15, "%d", &val) != 1) 144 return -EINVAL; 145 param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM; 146 } else if (!strncmp("skip_dtim_periods=", buf, 18)) { 147 if (sscanf(buf + 18, "%d", &val) != 1) 148 return -EINVAL; 149 param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS; 150 } else if (!strncmp("rx_data_timeout=", buf, 16)) { 151 if (sscanf(buf + 16, "%d", &val) != 1) 152 return -EINVAL; 153 param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT; 154 } else if (!strncmp("tx_data_timeout=", buf, 16)) { 155 if (sscanf(buf + 16, "%d", &val) != 1) 156 return -EINVAL; 157 param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT; 158 } else if (!strncmp("lprx=", buf, 5)) { 159 if (sscanf(buf + 5, "%d", &val) != 1) 160 return -EINVAL; 161 param = MVM_DEBUGFS_PM_LPRX_ENA; 162 } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) { 163 if (sscanf(buf + 20, "%d", &val) != 1) 164 return -EINVAL; 165 if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val < 166 POWER_LPRX_RSSI_THRESHOLD_MIN) 167 return -EINVAL; 168 param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD; 169 } else if (!strncmp("snooze_enable=", buf, 14)) { 170 if (sscanf(buf + 14, "%d", &val) != 1) 171 return -EINVAL; 172 param = MVM_DEBUGFS_PM_SNOOZE_ENABLE; 173 } else if (!strncmp("uapsd_misbehaving=", buf, 18)) { 174 if (sscanf(buf + 18, "%d", &val) != 1) 175 return -EINVAL; 176 param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING; 177 } else if (!strncmp("use_ps_poll=", buf, 12)) { 178 if (sscanf(buf + 12, "%d", &val) != 1) 179 return -EINVAL; 180 param = MVM_DEBUGFS_PM_USE_PS_POLL; 181 } else { 182 return -EINVAL; 183 } 184 185 mutex_lock(&mvm->mutex); 186 iwl_dbgfs_update_pm(mvm, vif, param, val); 187 ret = iwl_mvm_power_update_mac(mvm); 188 mutex_unlock(&mvm->mutex); 189 190 return ret ?: count; 191} 192 193static ssize_t iwl_dbgfs_pm_params_read(struct file *file, 194 char __user *user_buf, 195 size_t count, loff_t *ppos) 196{ 197 struct ieee80211_vif *vif = file->private_data; 198 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 199 struct iwl_mvm *mvm = mvmvif->mvm; 200 char buf[512]; 201 int bufsz = sizeof(buf); 202 int pos; 203 204 pos = iwl_mvm_power_mac_dbgfs_read(mvm, vif, buf, bufsz); 205 206 return simple_read_from_buffer(user_buf, count, ppos, buf, pos); 207} 208 209static ssize_t iwl_dbgfs_mac_params_read(struct file *file, 210 char __user *user_buf, 211 size_t count, loff_t *ppos) 212{ 213 struct ieee80211_vif *vif = file->private_data; 214 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 215 struct iwl_mvm *mvm = mvmvif->mvm; 216 u8 ap_sta_id; 217 struct ieee80211_chanctx_conf *chanctx_conf; 218 char buf[512]; 219 int bufsz = sizeof(buf); 220 int pos = 0; 221 int i; 222 223 mutex_lock(&mvm->mutex); 224 225 ap_sta_id = mvmvif->ap_sta_id; 226 227 switch (ieee80211_vif_type_p2p(vif)) { 228 case NL80211_IFTYPE_ADHOC: 229 pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n"); 230 break; 231 case NL80211_IFTYPE_STATION: 232 pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n"); 233 break; 234 case NL80211_IFTYPE_AP: 235 pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n"); 236 break; 237 case NL80211_IFTYPE_P2P_CLIENT: 238 pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n"); 239 break; 240 case NL80211_IFTYPE_P2P_GO: 241 pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n"); 242 break; 243 case NL80211_IFTYPE_P2P_DEVICE: 244 pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n"); 245 break; 246 default: 247 break; 248 } 249 250 pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n", 251 mvmvif->id, mvmvif->color); 252 pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n", 253 vif->bss_conf.bssid); 254 pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n"); 255 for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++) 256 pos += scnprintf(buf+pos, bufsz-pos, 257 "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n", 258 i, mvmvif->queue_params[i].txop, 259 mvmvif->queue_params[i].cw_min, 260 mvmvif->queue_params[i].cw_max, 261 mvmvif->queue_params[i].aifs, 262 mvmvif->queue_params[i].uapsd); 263 264 if (vif->type == NL80211_IFTYPE_STATION && 265 ap_sta_id != IWL_MVM_STATION_COUNT) { 266 struct ieee80211_sta *sta; 267 268 sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id], 269 lockdep_is_held(&mvm->mutex)); 270 if (!IS_ERR_OR_NULL(sta)) { 271 struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; 272 273 pos += scnprintf(buf+pos, bufsz-pos, 274 "ap_sta_id %d - reduced Tx power %d\n", 275 ap_sta_id, 276 mvm_sta->bt_reduced_txpower); 277 } 278 } 279 280 rcu_read_lock(); 281 chanctx_conf = rcu_dereference(vif->chanctx_conf); 282 if (chanctx_conf) 283 pos += scnprintf(buf+pos, bufsz-pos, 284 "idle rx chains %d, active rx chains: %d\n", 285 chanctx_conf->rx_chains_static, 286 chanctx_conf->rx_chains_dynamic); 287 rcu_read_unlock(); 288 289 mutex_unlock(&mvm->mutex); 290 291 return simple_read_from_buffer(user_buf, count, ppos, buf, pos); 292} 293 294static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, 295 enum iwl_dbgfs_bf_mask param, int value) 296{ 297 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 298 struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; 299 300 dbgfs_bf->mask |= param; 301 302 switch (param) { 303 case MVM_DEBUGFS_BF_ENERGY_DELTA: 304 dbgfs_bf->bf_energy_delta = value; 305 break; 306 case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA: 307 dbgfs_bf->bf_roaming_energy_delta = value; 308 break; 309 case MVM_DEBUGFS_BF_ROAMING_STATE: 310 dbgfs_bf->bf_roaming_state = value; 311 break; 312 case MVM_DEBUGFS_BF_TEMP_THRESHOLD: 313 dbgfs_bf->bf_temp_threshold = value; 314 break; 315 case MVM_DEBUGFS_BF_TEMP_FAST_FILTER: 316 dbgfs_bf->bf_temp_fast_filter = value; 317 break; 318 case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER: 319 dbgfs_bf->bf_temp_slow_filter = value; 320 break; 321 case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER: 322 dbgfs_bf->bf_enable_beacon_filter = value; 323 break; 324 case MVM_DEBUGFS_BF_DEBUG_FLAG: 325 dbgfs_bf->bf_debug_flag = value; 326 break; 327 case MVM_DEBUGFS_BF_ESCAPE_TIMER: 328 dbgfs_bf->bf_escape_timer = value; 329 break; 330 case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT: 331 dbgfs_bf->ba_enable_beacon_abort = value; 332 break; 333 case MVM_DEBUGFS_BA_ESCAPE_TIMER: 334 dbgfs_bf->ba_escape_timer = value; 335 break; 336 } 337} 338 339static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf, 340 size_t count, loff_t *ppos) 341{ 342 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 343 struct iwl_mvm *mvm = mvmvif->mvm; 344 enum iwl_dbgfs_bf_mask param; 345 int value, ret = 0; 346 347 if (!strncmp("bf_energy_delta=", buf, 16)) { 348 if (sscanf(buf+16, "%d", &value) != 1) 349 return -EINVAL; 350 if (value < IWL_BF_ENERGY_DELTA_MIN || 351 value > IWL_BF_ENERGY_DELTA_MAX) 352 return -EINVAL; 353 param = MVM_DEBUGFS_BF_ENERGY_DELTA; 354 } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) { 355 if (sscanf(buf+24, "%d", &value) != 1) 356 return -EINVAL; 357 if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN || 358 value > IWL_BF_ROAMING_ENERGY_DELTA_MAX) 359 return -EINVAL; 360 param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA; 361 } else if (!strncmp("bf_roaming_state=", buf, 17)) { 362 if (sscanf(buf+17, "%d", &value) != 1) 363 return -EINVAL; 364 if (value < IWL_BF_ROAMING_STATE_MIN || 365 value > IWL_BF_ROAMING_STATE_MAX) 366 return -EINVAL; 367 param = MVM_DEBUGFS_BF_ROAMING_STATE; 368 } else if (!strncmp("bf_temp_threshold=", buf, 18)) { 369 if (sscanf(buf+18, "%d", &value) != 1) 370 return -EINVAL; 371 if (value < IWL_BF_TEMP_THRESHOLD_MIN || 372 value > IWL_BF_TEMP_THRESHOLD_MAX) 373 return -EINVAL; 374 param = MVM_DEBUGFS_BF_TEMP_THRESHOLD; 375 } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) { 376 if (sscanf(buf+20, "%d", &value) != 1) 377 return -EINVAL; 378 if (value < IWL_BF_TEMP_FAST_FILTER_MIN || 379 value > IWL_BF_TEMP_FAST_FILTER_MAX) 380 return -EINVAL; 381 param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER; 382 } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) { 383 if (sscanf(buf+20, "%d", &value) != 1) 384 return -EINVAL; 385 if (value < IWL_BF_TEMP_SLOW_FILTER_MIN || 386 value > IWL_BF_TEMP_SLOW_FILTER_MAX) 387 return -EINVAL; 388 param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER; 389 } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { 390 if (sscanf(buf+24, "%d", &value) != 1) 391 return -EINVAL; 392 if (value < 0 || value > 1) 393 return -EINVAL; 394 param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER; 395 } else if (!strncmp("bf_debug_flag=", buf, 14)) { 396 if (sscanf(buf+14, "%d", &value) != 1) 397 return -EINVAL; 398 if (value < 0 || value > 1) 399 return -EINVAL; 400 param = MVM_DEBUGFS_BF_DEBUG_FLAG; 401 } else if (!strncmp("bf_escape_timer=", buf, 16)) { 402 if (sscanf(buf+16, "%d", &value) != 1) 403 return -EINVAL; 404 if (value < IWL_BF_ESCAPE_TIMER_MIN || 405 value > IWL_BF_ESCAPE_TIMER_MAX) 406 return -EINVAL; 407 param = MVM_DEBUGFS_BF_ESCAPE_TIMER; 408 } else if (!strncmp("ba_escape_timer=", buf, 16)) { 409 if (sscanf(buf+16, "%d", &value) != 1) 410 return -EINVAL; 411 if (value < IWL_BA_ESCAPE_TIMER_MIN || 412 value > IWL_BA_ESCAPE_TIMER_MAX) 413 return -EINVAL; 414 param = MVM_DEBUGFS_BA_ESCAPE_TIMER; 415 } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) { 416 if (sscanf(buf+23, "%d", &value) != 1) 417 return -EINVAL; 418 if (value < 0 || value > 1) 419 return -EINVAL; 420 param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT; 421 } else { 422 return -EINVAL; 423 } 424 425 mutex_lock(&mvm->mutex); 426 iwl_dbgfs_update_bf(vif, param, value); 427 if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) 428 ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); 429 else 430 ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); 431 mutex_unlock(&mvm->mutex); 432 433 return ret ?: count; 434} 435 436static ssize_t iwl_dbgfs_bf_params_read(struct file *file, 437 char __user *user_buf, 438 size_t count, loff_t *ppos) 439{ 440 struct ieee80211_vif *vif = file->private_data; 441 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 442 char buf[256]; 443 int pos = 0; 444 const size_t bufsz = sizeof(buf); 445 struct iwl_beacon_filter_cmd cmd = { 446 IWL_BF_CMD_CONFIG_DEFAULTS, 447 .bf_enable_beacon_filter = 448 cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT), 449 .ba_enable_beacon_abort = 450 cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT), 451 }; 452 453 iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); 454 if (mvmvif->bf_data.bf_enabled) 455 cmd.bf_enable_beacon_filter = cpu_to_le32(1); 456 else 457 cmd.bf_enable_beacon_filter = 0; 458 459 pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n", 460 le32_to_cpu(cmd.bf_energy_delta)); 461 pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n", 462 le32_to_cpu(cmd.bf_roaming_energy_delta)); 463 pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n", 464 le32_to_cpu(cmd.bf_roaming_state)); 465 pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n", 466 le32_to_cpu(cmd.bf_temp_threshold)); 467 pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n", 468 le32_to_cpu(cmd.bf_temp_fast_filter)); 469 pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n", 470 le32_to_cpu(cmd.bf_temp_slow_filter)); 471 pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n", 472 le32_to_cpu(cmd.bf_enable_beacon_filter)); 473 pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n", 474 le32_to_cpu(cmd.bf_debug_flag)); 475 pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n", 476 le32_to_cpu(cmd.bf_escape_timer)); 477 pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n", 478 le32_to_cpu(cmd.ba_escape_timer)); 479 pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n", 480 le32_to_cpu(cmd.ba_enable_beacon_abort)); 481 482 return simple_read_from_buffer(user_buf, count, ppos, buf, pos); 483} 484 485static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf, 486 size_t count, loff_t *ppos) 487{ 488 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 489 struct iwl_mvm *mvm = mvmvif->mvm; 490 u8 value; 491 int ret; 492 493 ret = kstrtou8(buf, 0, &value); 494 if (ret) 495 return ret; 496 if (value > 1) 497 return -EINVAL; 498 499 mutex_lock(&mvm->mutex); 500 iwl_mvm_update_low_latency(mvm, vif, value); 501 mutex_unlock(&mvm->mutex); 502 503 return count; 504} 505 506static ssize_t iwl_dbgfs_low_latency_read(struct file *file, 507 char __user *user_buf, 508 size_t count, loff_t *ppos) 509{ 510 struct ieee80211_vif *vif = file->private_data; 511 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 512 char buf[3]; 513 514 buf[0] = mvmvif->low_latency ? '1' : '0'; 515 buf[1] = '\n'; 516 buf[2] = '\0'; 517 return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf)); 518} 519 520#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ 521 _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) 522#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ 523 _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) 524#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \ 525 if (!debugfs_create_file(#name, mode, parent, vif, \ 526 &iwl_dbgfs_##name##_ops)) \ 527 goto err; \ 528 } while (0) 529 530MVM_DEBUGFS_READ_FILE_OPS(mac_params); 531MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32); 532MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256); 533MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10); 534 535void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 536{ 537 struct dentry *dbgfs_dir = vif->debugfs_dir; 538 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 539 char buf[100]; 540 541 /* 542 * Check if debugfs directory already exist before creating it. 543 * This may happen when, for example, resetting hw or suspend-resume 544 */ 545 if (!dbgfs_dir || mvmvif->dbgfs_dir) 546 return; 547 548 mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir); 549 mvmvif->mvm = mvm; 550 551 if (!mvmvif->dbgfs_dir) { 552 IWL_ERR(mvm, "Failed to create debugfs directory under %s\n", 553 dbgfs_dir->d_name.name); 554 return; 555 } 556 557 if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && 558 ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) || 559 (vif->type == NL80211_IFTYPE_STATION && vif->p2p && 560 mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM))) 561 MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR | 562 S_IRUSR); 563 564 MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR); 565 MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir, 566 S_IRUSR | S_IWUSR); 567 568 if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && 569 mvmvif == mvm->bf_allowed_vif) 570 MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, 571 S_IRUSR | S_IWUSR); 572 573 /* 574 * Create symlink for convenience pointing to interface specific 575 * debugfs entries for the driver. For example, under 576 * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/ 577 * find 578 * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/ 579 */ 580 snprintf(buf, 100, "../../../%s/%s/%s/%s", 581 dbgfs_dir->d_parent->d_parent->d_name.name, 582 dbgfs_dir->d_parent->d_name.name, 583 dbgfs_dir->d_name.name, 584 mvmvif->dbgfs_dir->d_name.name); 585 586 mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name, 587 mvm->debugfs_dir, buf); 588 if (!mvmvif->dbgfs_slink) 589 IWL_ERR(mvm, "Can't create debugfs symbolic link under %s\n", 590 dbgfs_dir->d_name.name); 591 return; 592err: 593 IWL_ERR(mvm, "Can't create debugfs entity\n"); 594} 595 596void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 597{ 598 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 599 600 debugfs_remove(mvmvif->dbgfs_slink); 601 mvmvif->dbgfs_slink = NULL; 602 603 debugfs_remove_recursive(mvmvif->dbgfs_dir); 604 mvmvif->dbgfs_dir = NULL; 605} 606