17f904d7eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2e9f207f0SJiri Benc /* 3e9f207f0SJiri Benc * mac80211 debugfs for wireless PHYs 4e9f207f0SJiri Benc * 5e9f207f0SJiri Benc * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 6d98ad83eSJohannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH 7adaed1b9SJohannes Berg * Copyright (C) 2018 - 2019, 2021 Intel Corporation 8e9f207f0SJiri Benc */ 9e9f207f0SJiri Benc 10e9f207f0SJiri Benc #include <linux/debugfs.h> 11e9f207f0SJiri Benc #include <linux/rtnetlink.h> 129399b86cSMichal Kazior #include <linux/vmalloc.h> 13e9f207f0SJiri Benc #include "ieee80211_i.h" 1424487981SJohannes Berg #include "driver-ops.h" 152c8dccc7SJohannes Berg #include "rate.h" 16e9f207f0SJiri Benc #include "debugfs.h" 17e9f207f0SJiri Benc 1807caf9d6SEliad Peller #define DEBUGFS_FORMAT_BUFFER_SIZE 100 1907caf9d6SEliad Peller 2007caf9d6SEliad Peller int mac80211_format_buffer(char __user *userbuf, size_t count, 2107caf9d6SEliad Peller loff_t *ppos, char *fmt, ...) 2207caf9d6SEliad Peller { 2307caf9d6SEliad Peller va_list args; 2407caf9d6SEliad Peller char buf[DEBUGFS_FORMAT_BUFFER_SIZE]; 2507caf9d6SEliad Peller int res; 2607caf9d6SEliad Peller 2707caf9d6SEliad Peller va_start(args, fmt); 2807caf9d6SEliad Peller res = vscnprintf(buf, sizeof(buf), fmt, args); 2907caf9d6SEliad Peller va_end(args); 3007caf9d6SEliad Peller 3107caf9d6SEliad Peller return simple_read_from_buffer(userbuf, count, ppos, buf, res); 3207caf9d6SEliad Peller } 3307caf9d6SEliad Peller 34279daf64SBen Greear #define DEBUGFS_READONLY_FILE_FN(name, fmt, value...) \ 35e9f207f0SJiri Benc static ssize_t name## _read(struct file *file, char __user *userbuf, \ 36e9f207f0SJiri Benc size_t count, loff_t *ppos) \ 37e9f207f0SJiri Benc { \ 38e9f207f0SJiri Benc struct ieee80211_local *local = file->private_data; \ 39e9f207f0SJiri Benc \ 4007caf9d6SEliad Peller return mac80211_format_buffer(userbuf, count, ppos, \ 4107caf9d6SEliad Peller fmt "\n", ##value); \ 42279daf64SBen Greear } 43279daf64SBen Greear 44279daf64SBen Greear #define DEBUGFS_READONLY_FILE_OPS(name) \ 45e9f207f0SJiri Benc static const struct file_operations name## _ops = { \ 46e9f207f0SJiri Benc .read = name## _read, \ 47234e3405SStephen Boyd .open = simple_open, \ 482b18ab36SArnd Bergmann .llseek = generic_file_llseek, \ 49e9f207f0SJiri Benc }; 50e9f207f0SJiri Benc 51279daf64SBen Greear #define DEBUGFS_READONLY_FILE(name, fmt, value...) \ 52279daf64SBen Greear DEBUGFS_READONLY_FILE_FN(name, fmt, value) \ 53279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(name) 54279daf64SBen Greear 55e9f207f0SJiri Benc #define DEBUGFS_ADD(name) \ 5684674ef4STom Rix debugfs_create_file(#name, 0400, phyd, local, &name## _ops) 57e9f207f0SJiri Benc 58827b1fb4SJohannes Berg #define DEBUGFS_ADD_MODE(name, mode) \ 597bcfaf2fSJohannes Berg debugfs_create_file(#name, mode, phyd, local, &name## _ops); 60e9f207f0SJiri Benc 61e9f207f0SJiri Benc 62c90142a5SThomas Pedersen DEBUGFS_READONLY_FILE(hw_conf, "%x", 63c90142a5SThomas Pedersen local->hw.conf.flags); 6483bdf2a1SBen Greear DEBUGFS_READONLY_FILE(user_power, "%d", 6583bdf2a1SBen Greear local->user_power_level); 6683bdf2a1SBen Greear DEBUGFS_READONLY_FILE(power, "%d", 6783bdf2a1SBen Greear local->hw.conf.power_level); 6807caf9d6SEliad Peller DEBUGFS_READONLY_FILE(total_ps_buffered, "%d", 69e9f207f0SJiri Benc local->total_ps_buffered); 7007caf9d6SEliad Peller DEBUGFS_READONLY_FILE(wep_iv, "%#08x", 71e9f207f0SJiri Benc local->wep_iv & 0xffffff); 7207caf9d6SEliad Peller DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s", 73af65cd96SJohannes Berg local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver"); 743b5d665bSAlina Friedrichsen 758d51dbb8SToke Høiland-Jørgensen static ssize_t aqm_read(struct file *file, 768d51dbb8SToke Høiland-Jørgensen char __user *user_buf, 778d51dbb8SToke Høiland-Jørgensen size_t count, 788d51dbb8SToke Høiland-Jørgensen loff_t *ppos) 799399b86cSMichal Kazior { 808d51dbb8SToke Høiland-Jørgensen struct ieee80211_local *local = file->private_data; 819399b86cSMichal Kazior struct fq *fq = &local->fq; 828d51dbb8SToke Høiland-Jørgensen char buf[200]; 839399b86cSMichal Kazior int len = 0; 849399b86cSMichal Kazior 859399b86cSMichal Kazior spin_lock_bh(&local->fq.lock); 869399b86cSMichal Kazior rcu_read_lock(); 879399b86cSMichal Kazior 888d51dbb8SToke Høiland-Jørgensen len = scnprintf(buf, sizeof(buf), 899399b86cSMichal Kazior "access name value\n" 909399b86cSMichal Kazior "R fq_flows_cnt %u\n" 919399b86cSMichal Kazior "R fq_backlog %u\n" 929399b86cSMichal Kazior "R fq_overlimit %u\n" 932a4e675dSToke Høiland-Jørgensen "R fq_overmemory %u\n" 949399b86cSMichal Kazior "R fq_collisions %u\n" 952a4e675dSToke Høiland-Jørgensen "R fq_memory_usage %u\n" 962a4e675dSToke Høiland-Jørgensen "RW fq_memory_limit %u\n" 979399b86cSMichal Kazior "RW fq_limit %u\n" 989399b86cSMichal Kazior "RW fq_quantum %u\n", 999399b86cSMichal Kazior fq->flows_cnt, 1009399b86cSMichal Kazior fq->backlog, 1012a4e675dSToke Høiland-Jørgensen fq->overmemory, 1029399b86cSMichal Kazior fq->overlimit, 1039399b86cSMichal Kazior fq->collisions, 1042a4e675dSToke Høiland-Jørgensen fq->memory_usage, 1052a4e675dSToke Høiland-Jørgensen fq->memory_limit, 1069399b86cSMichal Kazior fq->limit, 1079399b86cSMichal Kazior fq->quantum); 1089399b86cSMichal Kazior 1099399b86cSMichal Kazior rcu_read_unlock(); 1109399b86cSMichal Kazior spin_unlock_bh(&local->fq.lock); 1119399b86cSMichal Kazior 1129399b86cSMichal Kazior return simple_read_from_buffer(user_buf, count, ppos, 1138d51dbb8SToke Høiland-Jørgensen buf, len); 1149399b86cSMichal Kazior } 1159399b86cSMichal Kazior 1169399b86cSMichal Kazior static ssize_t aqm_write(struct file *file, 1179399b86cSMichal Kazior const char __user *user_buf, 1189399b86cSMichal Kazior size_t count, 1199399b86cSMichal Kazior loff_t *ppos) 1209399b86cSMichal Kazior { 1218d51dbb8SToke Høiland-Jørgensen struct ieee80211_local *local = file->private_data; 1229399b86cSMichal Kazior char buf[100]; 1239399b86cSMichal Kazior 1246020d534SShayne Chen if (count >= sizeof(buf)) 1259399b86cSMichal Kazior return -EINVAL; 1269399b86cSMichal Kazior 1279399b86cSMichal Kazior if (copy_from_user(buf, user_buf, count)) 1289399b86cSMichal Kazior return -EFAULT; 1299399b86cSMichal Kazior 1306020d534SShayne Chen if (count && buf[count - 1] == '\n') 1316020d534SShayne Chen buf[count - 1] = '\0'; 1326020d534SShayne Chen else 1336020d534SShayne Chen buf[count] = '\0'; 1349399b86cSMichal Kazior 1359399b86cSMichal Kazior if (sscanf(buf, "fq_limit %u", &local->fq.limit) == 1) 1369399b86cSMichal Kazior return count; 1372a4e675dSToke Høiland-Jørgensen else if (sscanf(buf, "fq_memory_limit %u", &local->fq.memory_limit) == 1) 1382a4e675dSToke Høiland-Jørgensen return count; 1399399b86cSMichal Kazior else if (sscanf(buf, "fq_quantum %u", &local->fq.quantum) == 1) 1409399b86cSMichal Kazior return count; 1419399b86cSMichal Kazior 1429399b86cSMichal Kazior return -EINVAL; 1439399b86cSMichal Kazior } 1449399b86cSMichal Kazior 1459399b86cSMichal Kazior static const struct file_operations aqm_ops = { 1469399b86cSMichal Kazior .write = aqm_write, 1479399b86cSMichal Kazior .read = aqm_read, 1488d51dbb8SToke Høiland-Jørgensen .open = simple_open, 1499399b86cSMichal Kazior .llseek = default_llseek, 1509399b86cSMichal Kazior }; 1519399b86cSMichal Kazior 152e322c07fSLorenzo Bianconi static ssize_t airtime_flags_read(struct file *file, 153e322c07fSLorenzo Bianconi char __user *user_buf, 154e322c07fSLorenzo Bianconi size_t count, loff_t *ppos) 155e322c07fSLorenzo Bianconi { 156e322c07fSLorenzo Bianconi struct ieee80211_local *local = file->private_data; 157e322c07fSLorenzo Bianconi char buf[128] = {}, *pos, *end; 158e322c07fSLorenzo Bianconi 159e322c07fSLorenzo Bianconi pos = buf; 160e322c07fSLorenzo Bianconi end = pos + sizeof(buf) - 1; 161e322c07fSLorenzo Bianconi 162e322c07fSLorenzo Bianconi if (local->airtime_flags & AIRTIME_USE_TX) 163e322c07fSLorenzo Bianconi pos += scnprintf(pos, end - pos, "AIRTIME_TX\t(%lx)\n", 164e322c07fSLorenzo Bianconi AIRTIME_USE_TX); 165e322c07fSLorenzo Bianconi if (local->airtime_flags & AIRTIME_USE_RX) 166e322c07fSLorenzo Bianconi pos += scnprintf(pos, end - pos, "AIRTIME_RX\t(%lx)\n", 167e322c07fSLorenzo Bianconi AIRTIME_USE_RX); 168e322c07fSLorenzo Bianconi 169e322c07fSLorenzo Bianconi return simple_read_from_buffer(user_buf, count, ppos, buf, 170e322c07fSLorenzo Bianconi strlen(buf)); 171e322c07fSLorenzo Bianconi } 172e322c07fSLorenzo Bianconi 173e322c07fSLorenzo Bianconi static ssize_t airtime_flags_write(struct file *file, 174e322c07fSLorenzo Bianconi const char __user *user_buf, 175e322c07fSLorenzo Bianconi size_t count, loff_t *ppos) 176e322c07fSLorenzo Bianconi { 177e322c07fSLorenzo Bianconi struct ieee80211_local *local = file->private_data; 178e322c07fSLorenzo Bianconi char buf[16]; 179e322c07fSLorenzo Bianconi 1806020d534SShayne Chen if (count >= sizeof(buf)) 181e322c07fSLorenzo Bianconi return -EINVAL; 182e322c07fSLorenzo Bianconi 183e322c07fSLorenzo Bianconi if (copy_from_user(buf, user_buf, count)) 184e322c07fSLorenzo Bianconi return -EFAULT; 185e322c07fSLorenzo Bianconi 1866020d534SShayne Chen if (count && buf[count - 1] == '\n') 1876020d534SShayne Chen buf[count - 1] = '\0'; 1886020d534SShayne Chen else 1896020d534SShayne Chen buf[count] = '\0'; 190e322c07fSLorenzo Bianconi 191e322c07fSLorenzo Bianconi if (kstrtou16(buf, 0, &local->airtime_flags)) 192e322c07fSLorenzo Bianconi return -EINVAL; 193e322c07fSLorenzo Bianconi 194e322c07fSLorenzo Bianconi return count; 195e322c07fSLorenzo Bianconi } 196e322c07fSLorenzo Bianconi 197e322c07fSLorenzo Bianconi static const struct file_operations airtime_flags_ops = { 198e322c07fSLorenzo Bianconi .write = airtime_flags_write, 199e322c07fSLorenzo Bianconi .read = airtime_flags_read, 200e322c07fSLorenzo Bianconi .open = simple_open, 201e322c07fSLorenzo Bianconi .llseek = default_llseek, 202e322c07fSLorenzo Bianconi }; 203e322c07fSLorenzo Bianconi 2043ace10f5SKan Yan static ssize_t aql_txq_limit_read(struct file *file, 2053ace10f5SKan Yan char __user *user_buf, 2063ace10f5SKan Yan size_t count, 2073ace10f5SKan Yan loff_t *ppos) 2083ace10f5SKan Yan { 2093ace10f5SKan Yan struct ieee80211_local *local = file->private_data; 2103ace10f5SKan Yan char buf[400]; 2113ace10f5SKan Yan int len = 0; 2123ace10f5SKan Yan 2133ace10f5SKan Yan len = scnprintf(buf, sizeof(buf), 2143ace10f5SKan Yan "AC AQL limit low AQL limit high\n" 2153ace10f5SKan Yan "VO %u %u\n" 2163ace10f5SKan Yan "VI %u %u\n" 2173ace10f5SKan Yan "BE %u %u\n" 2183ace10f5SKan Yan "BK %u %u\n", 2193ace10f5SKan Yan local->aql_txq_limit_low[IEEE80211_AC_VO], 2203ace10f5SKan Yan local->aql_txq_limit_high[IEEE80211_AC_VO], 2213ace10f5SKan Yan local->aql_txq_limit_low[IEEE80211_AC_VI], 2223ace10f5SKan Yan local->aql_txq_limit_high[IEEE80211_AC_VI], 2233ace10f5SKan Yan local->aql_txq_limit_low[IEEE80211_AC_BE], 2243ace10f5SKan Yan local->aql_txq_limit_high[IEEE80211_AC_BE], 2253ace10f5SKan Yan local->aql_txq_limit_low[IEEE80211_AC_BK], 2263ace10f5SKan Yan local->aql_txq_limit_high[IEEE80211_AC_BK]); 2273ace10f5SKan Yan return simple_read_from_buffer(user_buf, count, ppos, 2283ace10f5SKan Yan buf, len); 2293ace10f5SKan Yan } 2303ace10f5SKan Yan 2313ace10f5SKan Yan static ssize_t aql_txq_limit_write(struct file *file, 2323ace10f5SKan Yan const char __user *user_buf, 2333ace10f5SKan Yan size_t count, 2343ace10f5SKan Yan loff_t *ppos) 2353ace10f5SKan Yan { 2363ace10f5SKan Yan struct ieee80211_local *local = file->private_data; 2373ace10f5SKan Yan char buf[100]; 2383ace10f5SKan Yan u32 ac, q_limit_low, q_limit_high, q_limit_low_old, q_limit_high_old; 2393ace10f5SKan Yan struct sta_info *sta; 2403ace10f5SKan Yan 2416020d534SShayne Chen if (count >= sizeof(buf)) 2423ace10f5SKan Yan return -EINVAL; 2433ace10f5SKan Yan 2443ace10f5SKan Yan if (copy_from_user(buf, user_buf, count)) 2453ace10f5SKan Yan return -EFAULT; 2463ace10f5SKan Yan 2476020d534SShayne Chen if (count && buf[count - 1] == '\n') 2486020d534SShayne Chen buf[count - 1] = '\0'; 2496020d534SShayne Chen else 2506020d534SShayne Chen buf[count] = '\0'; 2513ace10f5SKan Yan 2523ace10f5SKan Yan if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3) 2533ace10f5SKan Yan return -EINVAL; 2543ace10f5SKan Yan 2553ace10f5SKan Yan if (ac >= IEEE80211_NUM_ACS) 2563ace10f5SKan Yan return -EINVAL; 2573ace10f5SKan Yan 2583ace10f5SKan Yan q_limit_low_old = local->aql_txq_limit_low[ac]; 2593ace10f5SKan Yan q_limit_high_old = local->aql_txq_limit_high[ac]; 2603ace10f5SKan Yan 2613ace10f5SKan Yan local->aql_txq_limit_low[ac] = q_limit_low; 2623ace10f5SKan Yan local->aql_txq_limit_high[ac] = q_limit_high; 2633ace10f5SKan Yan 2643ace10f5SKan Yan mutex_lock(&local->sta_mtx); 2653ace10f5SKan Yan list_for_each_entry(sta, &local->sta_list, list) { 2663ace10f5SKan Yan /* If a sta has customized queue limits, keep it */ 2673ace10f5SKan Yan if (sta->airtime[ac].aql_limit_low == q_limit_low_old && 2683ace10f5SKan Yan sta->airtime[ac].aql_limit_high == q_limit_high_old) { 2693ace10f5SKan Yan sta->airtime[ac].aql_limit_low = q_limit_low; 2703ace10f5SKan Yan sta->airtime[ac].aql_limit_high = q_limit_high; 2713ace10f5SKan Yan } 2723ace10f5SKan Yan } 2733ace10f5SKan Yan mutex_unlock(&local->sta_mtx); 2743ace10f5SKan Yan return count; 2753ace10f5SKan Yan } 2763ace10f5SKan Yan 2773ace10f5SKan Yan static const struct file_operations aql_txq_limit_ops = { 2783ace10f5SKan Yan .write = aql_txq_limit_write, 2793ace10f5SKan Yan .read = aql_txq_limit_read, 2803ace10f5SKan Yan .open = simple_open, 2813ace10f5SKan Yan .llseek = default_llseek, 2823ace10f5SKan Yan }; 2833ace10f5SKan Yan 284e908435eSLorenzo Bianconi static ssize_t aql_enable_read(struct file *file, char __user *user_buf, 285e908435eSLorenzo Bianconi size_t count, loff_t *ppos) 286e908435eSLorenzo Bianconi { 287e908435eSLorenzo Bianconi char buf[3]; 288e908435eSLorenzo Bianconi int len; 289e908435eSLorenzo Bianconi 290e908435eSLorenzo Bianconi len = scnprintf(buf, sizeof(buf), "%d\n", 291e908435eSLorenzo Bianconi !static_key_false(&aql_disable.key)); 292e908435eSLorenzo Bianconi 293e908435eSLorenzo Bianconi return simple_read_from_buffer(user_buf, count, ppos, buf, len); 294e908435eSLorenzo Bianconi } 295e908435eSLorenzo Bianconi 296e908435eSLorenzo Bianconi static ssize_t aql_enable_write(struct file *file, const char __user *user_buf, 297e908435eSLorenzo Bianconi size_t count, loff_t *ppos) 298e908435eSLorenzo Bianconi { 299e908435eSLorenzo Bianconi bool aql_disabled = static_key_false(&aql_disable.key); 300e908435eSLorenzo Bianconi char buf[3]; 301e908435eSLorenzo Bianconi size_t len; 302e908435eSLorenzo Bianconi 303e908435eSLorenzo Bianconi if (count > sizeof(buf)) 304e908435eSLorenzo Bianconi return -EINVAL; 305e908435eSLorenzo Bianconi 306e908435eSLorenzo Bianconi if (copy_from_user(buf, user_buf, count)) 307e908435eSLorenzo Bianconi return -EFAULT; 308e908435eSLorenzo Bianconi 309e908435eSLorenzo Bianconi buf[sizeof(buf) - 1] = '\0'; 310e908435eSLorenzo Bianconi len = strlen(buf); 311e908435eSLorenzo Bianconi if (len > 0 && buf[len - 1] == '\n') 312e908435eSLorenzo Bianconi buf[len - 1] = 0; 313e908435eSLorenzo Bianconi 314e908435eSLorenzo Bianconi if (buf[0] == '0' && buf[1] == '\0') { 315e908435eSLorenzo Bianconi if (!aql_disabled) 316e908435eSLorenzo Bianconi static_branch_inc(&aql_disable); 317e908435eSLorenzo Bianconi } else if (buf[0] == '1' && buf[1] == '\0') { 318e908435eSLorenzo Bianconi if (aql_disabled) 319e908435eSLorenzo Bianconi static_branch_dec(&aql_disable); 320e908435eSLorenzo Bianconi } else { 321e908435eSLorenzo Bianconi return -EINVAL; 322e908435eSLorenzo Bianconi } 323e908435eSLorenzo Bianconi 324e908435eSLorenzo Bianconi return count; 325e908435eSLorenzo Bianconi } 326e908435eSLorenzo Bianconi 327e908435eSLorenzo Bianconi static const struct file_operations aql_enable_ops = { 328e908435eSLorenzo Bianconi .write = aql_enable_write, 329e908435eSLorenzo Bianconi .read = aql_enable_read, 330e908435eSLorenzo Bianconi .open = simple_open, 331e908435eSLorenzo Bianconi .llseek = default_llseek, 332e908435eSLorenzo Bianconi }; 333e908435eSLorenzo Bianconi 334276d9e82SJulius Niedworok static ssize_t force_tx_status_read(struct file *file, 335276d9e82SJulius Niedworok char __user *user_buf, 336276d9e82SJulius Niedworok size_t count, 337276d9e82SJulius Niedworok loff_t *ppos) 338276d9e82SJulius Niedworok { 339276d9e82SJulius Niedworok struct ieee80211_local *local = file->private_data; 340276d9e82SJulius Niedworok char buf[3]; 341276d9e82SJulius Niedworok int len = 0; 342276d9e82SJulius Niedworok 343276d9e82SJulius Niedworok len = scnprintf(buf, sizeof(buf), "%d\n", (int)local->force_tx_status); 344276d9e82SJulius Niedworok 345276d9e82SJulius Niedworok return simple_read_from_buffer(user_buf, count, ppos, 346276d9e82SJulius Niedworok buf, len); 347276d9e82SJulius Niedworok } 348276d9e82SJulius Niedworok 349276d9e82SJulius Niedworok static ssize_t force_tx_status_write(struct file *file, 350276d9e82SJulius Niedworok const char __user *user_buf, 351276d9e82SJulius Niedworok size_t count, 352276d9e82SJulius Niedworok loff_t *ppos) 353276d9e82SJulius Niedworok { 354276d9e82SJulius Niedworok struct ieee80211_local *local = file->private_data; 355276d9e82SJulius Niedworok char buf[3]; 356276d9e82SJulius Niedworok 3576020d534SShayne Chen if (count >= sizeof(buf)) 358276d9e82SJulius Niedworok return -EINVAL; 359276d9e82SJulius Niedworok 360276d9e82SJulius Niedworok if (copy_from_user(buf, user_buf, count)) 361276d9e82SJulius Niedworok return -EFAULT; 362276d9e82SJulius Niedworok 3636020d534SShayne Chen if (count && buf[count - 1] == '\n') 3646020d534SShayne Chen buf[count - 1] = '\0'; 3656020d534SShayne Chen else 3666020d534SShayne Chen buf[count] = '\0'; 367276d9e82SJulius Niedworok 368276d9e82SJulius Niedworok if (buf[0] == '0' && buf[1] == '\0') 369276d9e82SJulius Niedworok local->force_tx_status = 0; 370276d9e82SJulius Niedworok else if (buf[0] == '1' && buf[1] == '\0') 371276d9e82SJulius Niedworok local->force_tx_status = 1; 372276d9e82SJulius Niedworok else 373276d9e82SJulius Niedworok return -EINVAL; 374276d9e82SJulius Niedworok 375276d9e82SJulius Niedworok return count; 376276d9e82SJulius Niedworok } 377276d9e82SJulius Niedworok 378276d9e82SJulius Niedworok static const struct file_operations force_tx_status_ops = { 379276d9e82SJulius Niedworok .write = force_tx_status_write, 380276d9e82SJulius Niedworok .read = force_tx_status_read, 381276d9e82SJulius Niedworok .open = simple_open, 382276d9e82SJulius Niedworok .llseek = default_llseek, 383276d9e82SJulius Niedworok }; 384276d9e82SJulius Niedworok 3852ad4814fSJohannes Berg #ifdef CONFIG_PM 386827b1fb4SJohannes Berg static ssize_t reset_write(struct file *file, const char __user *user_buf, 387827b1fb4SJohannes Berg size_t count, loff_t *ppos) 388827b1fb4SJohannes Berg { 389827b1fb4SJohannes Berg struct ieee80211_local *local = file->private_data; 390*f5baf287SJohannes Berg int ret; 391827b1fb4SJohannes Berg 392827b1fb4SJohannes Berg rtnl_lock(); 393adaed1b9SJohannes Berg wiphy_lock(local->hw.wiphy); 394eecc4800SJohannes Berg __ieee80211_suspend(&local->hw, NULL); 395*f5baf287SJohannes Berg ret = __ieee80211_resume(&local->hw); 396adaed1b9SJohannes Berg wiphy_unlock(local->hw.wiphy); 397*f5baf287SJohannes Berg 398*f5baf287SJohannes Berg if (ret) 399*f5baf287SJohannes Berg cfg80211_shutdown_all_interfaces(local->hw.wiphy); 400*f5baf287SJohannes Berg 401827b1fb4SJohannes Berg rtnl_unlock(); 402827b1fb4SJohannes Berg 403827b1fb4SJohannes Berg return count; 404827b1fb4SJohannes Berg } 405827b1fb4SJohannes Berg 406827b1fb4SJohannes Berg static const struct file_operations reset_ops = { 407827b1fb4SJohannes Berg .write = reset_write, 408234e3405SStephen Boyd .open = simple_open, 4096038f373SArnd Bergmann .llseek = noop_llseek, 410827b1fb4SJohannes Berg }; 4112ad4814fSJohannes Berg #endif 412827b1fb4SJohannes Berg 41368920c97SAndrey Ryabinin static const char *hw_flag_names[] = { 41430686bf7SJohannes Berg #define FLAG(F) [IEEE80211_HW_##F] = #F 41530686bf7SJohannes Berg FLAG(HAS_RATE_CONTROL), 41630686bf7SJohannes Berg FLAG(RX_INCLUDES_FCS), 41730686bf7SJohannes Berg FLAG(HOST_BROADCAST_PS_BUFFERING), 41830686bf7SJohannes Berg FLAG(SIGNAL_UNSPEC), 41930686bf7SJohannes Berg FLAG(SIGNAL_DBM), 42030686bf7SJohannes Berg FLAG(NEED_DTIM_BEFORE_ASSOC), 42130686bf7SJohannes Berg FLAG(SPECTRUM_MGMT), 42230686bf7SJohannes Berg FLAG(AMPDU_AGGREGATION), 42330686bf7SJohannes Berg FLAG(SUPPORTS_PS), 42430686bf7SJohannes Berg FLAG(PS_NULLFUNC_STACK), 42530686bf7SJohannes Berg FLAG(SUPPORTS_DYNAMIC_PS), 42630686bf7SJohannes Berg FLAG(MFP_CAPABLE), 42730686bf7SJohannes Berg FLAG(WANT_MONITOR_VIF), 42830686bf7SJohannes Berg FLAG(NO_AUTO_VIF), 42930686bf7SJohannes Berg FLAG(SW_CRYPTO_CONTROL), 43030686bf7SJohannes Berg FLAG(SUPPORT_FAST_XMIT), 43130686bf7SJohannes Berg FLAG(REPORTS_TX_ACK_STATUS), 43230686bf7SJohannes Berg FLAG(CONNECTION_MONITOR), 43330686bf7SJohannes Berg FLAG(QUEUE_CONTROL), 43430686bf7SJohannes Berg FLAG(SUPPORTS_PER_STA_GTK), 43530686bf7SJohannes Berg FLAG(AP_LINK_PS), 43630686bf7SJohannes Berg FLAG(TX_AMPDU_SETUP_IN_HW), 43730686bf7SJohannes Berg FLAG(SUPPORTS_RC_TABLE), 43830686bf7SJohannes Berg FLAG(P2P_DEV_ADDR_FOR_INTF), 43930686bf7SJohannes Berg FLAG(TIMING_BEACON_ONLY), 44030686bf7SJohannes Berg FLAG(SUPPORTS_HT_CCK_RATES), 44130686bf7SJohannes Berg FLAG(CHANCTX_STA_CSA), 44230686bf7SJohannes Berg FLAG(SUPPORTS_CLONED_SKBS), 44330686bf7SJohannes Berg FLAG(SINGLE_SCAN_ON_ALL_BANDS), 444b98fb44fSArik Nemtsov FLAG(TDLS_WIDER_BW), 44599e7ca44SEmmanuel Grumbach FLAG(SUPPORTS_AMSDU_IN_AMPDU), 44635afa588SHelmut Schaa FLAG(BEACON_TX_STATUS), 44731104891SJohannes Berg FLAG(NEEDS_UNIQUE_STA_ADDR), 448412a6d80SSara Sharon FLAG(SUPPORTS_REORDERING_BUFFER), 449c9c5962bSJohannes Berg FLAG(USES_RSS), 4506e0456b5SFelix Fietkau FLAG(TX_AMSDU), 4516e0456b5SFelix Fietkau FLAG(TX_FRAG_LIST), 452e8a24cd4SRajkumar Manoharan FLAG(REPORTS_LOW_ACK), 453f3fe4e93SSara Sharon FLAG(SUPPORTS_TX_FRAG), 454e2fb1b83SYingying Tang FLAG(SUPPORTS_TDLS_BUFFER_STA), 45594ba9271SIlan Peer FLAG(DEAUTH_NEED_MGD_TX_PREP), 4567c181f4fSBen Caradoc-Davies FLAG(DOESNT_SUPPORT_QOS_NDP), 457adf8ed01SJohannes Berg FLAG(BUFF_MMPDU_TXQ), 45809b4a4faSJohannes Berg FLAG(SUPPORTS_VHT_EXT_NSS_BW), 4590eeb2b67SSara Sharon FLAG(STA_MMPDU_TXQ), 46077f7ffdcSFelix Fietkau FLAG(TX_STATUS_NO_AMPDU_LEN), 461caf56338SSara Sharon FLAG(SUPPORTS_MULTI_BSSID), 462caf56338SSara Sharon FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID), 463dc3998ecSAlexander Wetzel FLAG(AMPDU_KEYBORDER_SUPPORT), 4646aea26ceSFelix Fietkau FLAG(SUPPORTS_TX_ENCAP_OFFLOAD), 46580a915ecSFelix Fietkau FLAG(SUPPORTS_RX_DECAP_OFFLOAD), 46655f8205eSSriram R FLAG(SUPPORTS_CONC_MON_RX_DECAP), 46730686bf7SJohannes Berg #undef FLAG 46830686bf7SJohannes Berg }; 46930686bf7SJohannes Berg 470279daf64SBen Greear static ssize_t hwflags_read(struct file *file, char __user *user_buf, 471279daf64SBen Greear size_t count, loff_t *ppos) 472279daf64SBen Greear { 473279daf64SBen Greear struct ieee80211_local *local = file->private_data; 47430686bf7SJohannes Berg size_t bufsz = 30 * NUM_IEEE80211_HW_FLAGS; 47530686bf7SJohannes Berg char *buf = kzalloc(bufsz, GFP_KERNEL); 47630686bf7SJohannes Berg char *pos = buf, *end = buf + bufsz - 1; 477279daf64SBen Greear ssize_t rv; 47830686bf7SJohannes Berg int i; 479279daf64SBen Greear 480d15b8459SJoe Perches if (!buf) 48130686bf7SJohannes Berg return -ENOMEM; 482d15b8459SJoe Perches 48330686bf7SJohannes Berg /* fail compilation if somebody adds or removes 48430686bf7SJohannes Berg * a flag without updating the name array above 48530686bf7SJohannes Berg */ 48668920c97SAndrey Ryabinin BUILD_BUG_ON(ARRAY_SIZE(hw_flag_names) != NUM_IEEE80211_HW_FLAGS); 48730686bf7SJohannes Berg 48830686bf7SJohannes Berg for (i = 0; i < NUM_IEEE80211_HW_FLAGS; i++) { 48930686bf7SJohannes Berg if (test_bit(i, local->hw.flags)) 4904633dfc3SMohammed Shafi Shajakhan pos += scnprintf(pos, end - pos, "%s\n", 49130686bf7SJohannes Berg hw_flag_names[i]); 49230686bf7SJohannes Berg } 493279daf64SBen Greear 494279daf64SBen Greear rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); 495279daf64SBen Greear kfree(buf); 496279daf64SBen Greear return rv; 497279daf64SBen Greear } 498199d69f2SBenoit Papillault 4994a5eccaaSBen Greear static ssize_t misc_read(struct file *file, char __user *user_buf, 5004a5eccaaSBen Greear size_t count, loff_t *ppos) 5014a5eccaaSBen Greear { 5024a5eccaaSBen Greear struct ieee80211_local *local = file->private_data; 5034a5eccaaSBen Greear /* Max len of each line is 16 characters, plus 9 for 'pending:\n' */ 5044a5eccaaSBen Greear size_t bufsz = IEEE80211_MAX_QUEUES * 16 + 9; 505b2347a32SDan Carpenter char *buf; 506b2347a32SDan Carpenter char *pos, *end; 5074a5eccaaSBen Greear ssize_t rv; 5084a5eccaaSBen Greear int i; 5094a5eccaaSBen Greear int ln; 5104a5eccaaSBen Greear 511b2347a32SDan Carpenter buf = kzalloc(bufsz, GFP_KERNEL); 512b2347a32SDan Carpenter if (!buf) 513b2347a32SDan Carpenter return -ENOMEM; 514b2347a32SDan Carpenter 515b2347a32SDan Carpenter pos = buf; 516b2347a32SDan Carpenter end = buf + bufsz - 1; 517b2347a32SDan Carpenter 5184a5eccaaSBen Greear pos += scnprintf(pos, end - pos, "pending:\n"); 5194a5eccaaSBen Greear 5204a5eccaaSBen Greear for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { 5214a5eccaaSBen Greear ln = skb_queue_len(&local->pending[i]); 5224a5eccaaSBen Greear pos += scnprintf(pos, end - pos, "[%i] %d\n", 5234a5eccaaSBen Greear i, ln); 5244a5eccaaSBen Greear } 5254a5eccaaSBen Greear 5264a5eccaaSBen Greear rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); 5274a5eccaaSBen Greear kfree(buf); 5284a5eccaaSBen Greear return rv; 5294a5eccaaSBen Greear } 5304a5eccaaSBen Greear 531db2e6bd4SJohannes Berg static ssize_t queues_read(struct file *file, char __user *user_buf, 532db2e6bd4SJohannes Berg size_t count, loff_t *ppos) 533db2e6bd4SJohannes Berg { 534db2e6bd4SJohannes Berg struct ieee80211_local *local = file->private_data; 535db2e6bd4SJohannes Berg unsigned long flags; 536db2e6bd4SJohannes Berg char buf[IEEE80211_MAX_QUEUES * 20]; 537db2e6bd4SJohannes Berg int q, res = 0; 538db2e6bd4SJohannes Berg 539db2e6bd4SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 540db2e6bd4SJohannes Berg for (q = 0; q < local->hw.queues; q++) 541db2e6bd4SJohannes Berg res += sprintf(buf + res, "%02d: %#.8lx/%d\n", q, 542db2e6bd4SJohannes Berg local->queue_stop_reasons[q], 5433b8d81e0SJohannes Berg skb_queue_len(&local->pending[q])); 544db2e6bd4SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 545db2e6bd4SJohannes Berg 546db2e6bd4SJohannes Berg return simple_read_from_buffer(user_buf, count, ppos, buf, res); 547db2e6bd4SJohannes Berg } 548db2e6bd4SJohannes Berg 549279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(hwflags); 550279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(queues); 5514a5eccaaSBen Greear DEBUGFS_READONLY_FILE_OPS(misc); 552db2e6bd4SJohannes Berg 553e9f207f0SJiri Benc /* statistics stuff */ 554e9f207f0SJiri Benc 555e9f207f0SJiri Benc static ssize_t format_devstat_counter(struct ieee80211_local *local, 556e9f207f0SJiri Benc char __user *userbuf, 557e9f207f0SJiri Benc size_t count, loff_t *ppos, 558e9f207f0SJiri Benc int (*printvalue)(struct ieee80211_low_level_stats *stats, char *buf, 559e9f207f0SJiri Benc int buflen)) 560e9f207f0SJiri Benc { 561e9f207f0SJiri Benc struct ieee80211_low_level_stats stats; 562e9f207f0SJiri Benc char buf[20]; 563e9f207f0SJiri Benc int res; 564e9f207f0SJiri Benc 56575636525SJohannes Berg rtnl_lock(); 56624487981SJohannes Berg res = drv_get_stats(local, &stats); 567e9f207f0SJiri Benc rtnl_unlock(); 56824487981SJohannes Berg if (res) 56924487981SJohannes Berg return res; 570e9f207f0SJiri Benc res = printvalue(&stats, buf, sizeof(buf)); 571e9f207f0SJiri Benc return simple_read_from_buffer(userbuf, count, ppos, buf, res); 572e9f207f0SJiri Benc } 573e9f207f0SJiri Benc 574e9f207f0SJiri Benc #define DEBUGFS_DEVSTATS_FILE(name) \ 575e9f207f0SJiri Benc static int print_devstats_##name(struct ieee80211_low_level_stats *stats,\ 576e9f207f0SJiri Benc char *buf, int buflen) \ 577e9f207f0SJiri Benc { \ 578e9f207f0SJiri Benc return scnprintf(buf, buflen, "%u\n", stats->name); \ 579e9f207f0SJiri Benc } \ 580e9f207f0SJiri Benc static ssize_t stats_ ##name## _read(struct file *file, \ 581e9f207f0SJiri Benc char __user *userbuf, \ 582e9f207f0SJiri Benc size_t count, loff_t *ppos) \ 583e9f207f0SJiri Benc { \ 584e9f207f0SJiri Benc return format_devstat_counter(file->private_data, \ 585e9f207f0SJiri Benc userbuf, \ 586e9f207f0SJiri Benc count, \ 587e9f207f0SJiri Benc ppos, \ 588e9f207f0SJiri Benc print_devstats_##name); \ 589e9f207f0SJiri Benc } \ 590e9f207f0SJiri Benc \ 591e9f207f0SJiri Benc static const struct file_operations stats_ ##name## _ops = { \ 592e9f207f0SJiri Benc .read = stats_ ##name## _read, \ 593234e3405SStephen Boyd .open = simple_open, \ 5942b18ab36SArnd Bergmann .llseek = generic_file_llseek, \ 595e9f207f0SJiri Benc }; 596e9f207f0SJiri Benc 597f1160434SJohannes Berg #define DEBUGFS_STATS_ADD(name) \ 598f1160434SJohannes Berg debugfs_create_u32(#name, 0400, statsd, &local->name); 5992826bcd8SFelix Fietkau #define DEBUGFS_DEVSTATS_ADD(name) \ 6007bcfaf2fSJohannes Berg debugfs_create_file(#name, 0400, statsd, local, &stats_ ##name## _ops); 601e9f207f0SJiri Benc 602e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11ACKFailureCount); 603e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11RTSFailureCount); 604e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11FCSErrorCount); 605e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11RTSSuccessCount); 606e9f207f0SJiri Benc 607e9f207f0SJiri Benc void debugfs_hw_add(struct ieee80211_local *local) 608e9f207f0SJiri Benc { 609e9f207f0SJiri Benc struct dentry *phyd = local->hw.wiphy->debugfsdir; 610e9f207f0SJiri Benc struct dentry *statsd; 611e9f207f0SJiri Benc 612e9f207f0SJiri Benc if (!phyd) 613e9f207f0SJiri Benc return; 614e9f207f0SJiri Benc 615e9f207f0SJiri Benc local->debugfs.keys = debugfs_create_dir("keys", phyd); 616e9f207f0SJiri Benc 617e9f207f0SJiri Benc DEBUGFS_ADD(total_ps_buffered); 618e9f207f0SJiri Benc DEBUGFS_ADD(wep_iv); 619bddb2afcSJohannes Berg DEBUGFS_ADD(rate_ctrl_alg); 620db2e6bd4SJohannes Berg DEBUGFS_ADD(queues); 6214a5eccaaSBen Greear DEBUGFS_ADD(misc); 6222ad4814fSJohannes Berg #ifdef CONFIG_PM 623827b1fb4SJohannes Berg DEBUGFS_ADD_MODE(reset, 0200); 6242ad4814fSJohannes Berg #endif 625279daf64SBen Greear DEBUGFS_ADD(hwflags); 62683bdf2a1SBen Greear DEBUGFS_ADD(user_power); 62783bdf2a1SBen Greear DEBUGFS_ADD(power); 628c90142a5SThomas Pedersen DEBUGFS_ADD(hw_conf); 629276d9e82SJulius Niedworok DEBUGFS_ADD_MODE(force_tx_status, 0600); 630e908435eSLorenzo Bianconi DEBUGFS_ADD_MODE(aql_enable, 0600); 6318d51dbb8SToke Høiland-Jørgensen 6328d51dbb8SToke Høiland-Jørgensen if (local->ops->wake_tx_queue) 6339399b86cSMichal Kazior DEBUGFS_ADD_MODE(aqm, 0600); 634e9f207f0SJiri Benc 635e322c07fSLorenzo Bianconi DEBUGFS_ADD_MODE(airtime_flags, 0600); 636b4809e94SToke Høiland-Jørgensen 6373ace10f5SKan Yan DEBUGFS_ADD(aql_txq_limit); 6383ace10f5SKan Yan debugfs_create_u32("aql_threshold", 0600, 6393ace10f5SKan Yan phyd, &local->aql_threshold); 6403ace10f5SKan Yan 641e9f207f0SJiri Benc statsd = debugfs_create_dir("statistics", phyd); 642e9f207f0SJiri Benc 643e9f207f0SJiri Benc /* if the dir failed, don't put all the other things into the root! */ 644e9f207f0SJiri Benc if (!statsd) 645e9f207f0SJiri Benc return; 646e9f207f0SJiri Benc 647c206ca67SJohannes Berg #ifdef CONFIG_MAC80211_DEBUG_COUNTERS 648f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11TransmittedFragmentCount); 649f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11MulticastTransmittedFrameCount); 650f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11FailedCount); 651f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11RetryCount); 652f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11MultipleRetryCount); 653f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11FrameDuplicateCount); 654f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11ReceivedFragmentCount); 655f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11MulticastReceivedFrameCount); 656f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11TransmittedFrameCount); 657f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_drop); 658f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_queued); 659f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_drop_wep); 660f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_drop_not_assoc); 661f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_drop_unauth_port); 662f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_drop); 663f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_queued); 664f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc); 665f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_drop_defrag); 666f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_expand_skb_head); 667f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned); 668f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_expand_skb_head_defrag); 669f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_fragments); 670f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_status_drop); 671e9f207f0SJiri Benc #endif 6722826bcd8SFelix Fietkau DEBUGFS_DEVSTATS_ADD(dot11ACKFailureCount); 6732826bcd8SFelix Fietkau DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount); 6742826bcd8SFelix Fietkau DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount); 6752826bcd8SFelix Fietkau DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount); 676e9f207f0SJiri Benc } 677