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 7caf56338SSara Sharon * Copyright (C) 2018 - 2019 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) \ 567bcfaf2fSJohannes Berg 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 size_t len; 1249399b86cSMichal Kazior 1259399b86cSMichal Kazior if (count > sizeof(buf)) 1269399b86cSMichal Kazior return -EINVAL; 1279399b86cSMichal Kazior 1289399b86cSMichal Kazior if (copy_from_user(buf, user_buf, count)) 1299399b86cSMichal Kazior return -EFAULT; 1309399b86cSMichal Kazior 1319399b86cSMichal Kazior buf[sizeof(buf) - 1] = '\0'; 1329399b86cSMichal Kazior len = strlen(buf); 1339399b86cSMichal Kazior if (len > 0 && buf[len-1] == '\n') 1349399b86cSMichal Kazior buf[len-1] = 0; 1359399b86cSMichal Kazior 1369399b86cSMichal Kazior if (sscanf(buf, "fq_limit %u", &local->fq.limit) == 1) 1379399b86cSMichal Kazior return count; 1382a4e675dSToke Høiland-Jørgensen else if (sscanf(buf, "fq_memory_limit %u", &local->fq.memory_limit) == 1) 1392a4e675dSToke Høiland-Jørgensen return count; 1409399b86cSMichal Kazior else if (sscanf(buf, "fq_quantum %u", &local->fq.quantum) == 1) 1419399b86cSMichal Kazior return count; 1429399b86cSMichal Kazior 1439399b86cSMichal Kazior return -EINVAL; 1449399b86cSMichal Kazior } 1459399b86cSMichal Kazior 1469399b86cSMichal Kazior static const struct file_operations aqm_ops = { 1479399b86cSMichal Kazior .write = aqm_write, 1489399b86cSMichal Kazior .read = aqm_read, 1498d51dbb8SToke Høiland-Jørgensen .open = simple_open, 1509399b86cSMichal Kazior .llseek = default_llseek, 1519399b86cSMichal Kazior }; 1529399b86cSMichal Kazior 153*e322c07fSLorenzo Bianconi static ssize_t airtime_flags_read(struct file *file, 154*e322c07fSLorenzo Bianconi char __user *user_buf, 155*e322c07fSLorenzo Bianconi size_t count, loff_t *ppos) 156*e322c07fSLorenzo Bianconi { 157*e322c07fSLorenzo Bianconi struct ieee80211_local *local = file->private_data; 158*e322c07fSLorenzo Bianconi char buf[128] = {}, *pos, *end; 159*e322c07fSLorenzo Bianconi 160*e322c07fSLorenzo Bianconi pos = buf; 161*e322c07fSLorenzo Bianconi end = pos + sizeof(buf) - 1; 162*e322c07fSLorenzo Bianconi 163*e322c07fSLorenzo Bianconi if (local->airtime_flags & AIRTIME_USE_TX) 164*e322c07fSLorenzo Bianconi pos += scnprintf(pos, end - pos, "AIRTIME_TX\t(%lx)\n", 165*e322c07fSLorenzo Bianconi AIRTIME_USE_TX); 166*e322c07fSLorenzo Bianconi if (local->airtime_flags & AIRTIME_USE_RX) 167*e322c07fSLorenzo Bianconi pos += scnprintf(pos, end - pos, "AIRTIME_RX\t(%lx)\n", 168*e322c07fSLorenzo Bianconi AIRTIME_USE_RX); 169*e322c07fSLorenzo Bianconi 170*e322c07fSLorenzo Bianconi return simple_read_from_buffer(user_buf, count, ppos, buf, 171*e322c07fSLorenzo Bianconi strlen(buf)); 172*e322c07fSLorenzo Bianconi } 173*e322c07fSLorenzo Bianconi 174*e322c07fSLorenzo Bianconi static ssize_t airtime_flags_write(struct file *file, 175*e322c07fSLorenzo Bianconi const char __user *user_buf, 176*e322c07fSLorenzo Bianconi size_t count, loff_t *ppos) 177*e322c07fSLorenzo Bianconi { 178*e322c07fSLorenzo Bianconi struct ieee80211_local *local = file->private_data; 179*e322c07fSLorenzo Bianconi char buf[16]; 180*e322c07fSLorenzo Bianconi size_t len; 181*e322c07fSLorenzo Bianconi 182*e322c07fSLorenzo Bianconi if (count > sizeof(buf)) 183*e322c07fSLorenzo Bianconi return -EINVAL; 184*e322c07fSLorenzo Bianconi 185*e322c07fSLorenzo Bianconi if (copy_from_user(buf, user_buf, count)) 186*e322c07fSLorenzo Bianconi return -EFAULT; 187*e322c07fSLorenzo Bianconi 188*e322c07fSLorenzo Bianconi buf[sizeof(buf) - 1] = 0; 189*e322c07fSLorenzo Bianconi len = strlen(buf); 190*e322c07fSLorenzo Bianconi if (len > 0 && buf[len - 1] == '\n') 191*e322c07fSLorenzo Bianconi buf[len - 1] = 0; 192*e322c07fSLorenzo Bianconi 193*e322c07fSLorenzo Bianconi if (kstrtou16(buf, 0, &local->airtime_flags)) 194*e322c07fSLorenzo Bianconi return -EINVAL; 195*e322c07fSLorenzo Bianconi 196*e322c07fSLorenzo Bianconi return count; 197*e322c07fSLorenzo Bianconi } 198*e322c07fSLorenzo Bianconi 199*e322c07fSLorenzo Bianconi static const struct file_operations airtime_flags_ops = { 200*e322c07fSLorenzo Bianconi .write = airtime_flags_write, 201*e322c07fSLorenzo Bianconi .read = airtime_flags_read, 202*e322c07fSLorenzo Bianconi .open = simple_open, 203*e322c07fSLorenzo Bianconi .llseek = default_llseek, 204*e322c07fSLorenzo Bianconi }; 205*e322c07fSLorenzo Bianconi 2063ace10f5SKan Yan static ssize_t aql_txq_limit_read(struct file *file, 2073ace10f5SKan Yan char __user *user_buf, 2083ace10f5SKan Yan size_t count, 2093ace10f5SKan Yan loff_t *ppos) 2103ace10f5SKan Yan { 2113ace10f5SKan Yan struct ieee80211_local *local = file->private_data; 2123ace10f5SKan Yan char buf[400]; 2133ace10f5SKan Yan int len = 0; 2143ace10f5SKan Yan 2153ace10f5SKan Yan len = scnprintf(buf, sizeof(buf), 2163ace10f5SKan Yan "AC AQL limit low AQL limit high\n" 2173ace10f5SKan Yan "VO %u %u\n" 2183ace10f5SKan Yan "VI %u %u\n" 2193ace10f5SKan Yan "BE %u %u\n" 2203ace10f5SKan Yan "BK %u %u\n", 2213ace10f5SKan Yan local->aql_txq_limit_low[IEEE80211_AC_VO], 2223ace10f5SKan Yan local->aql_txq_limit_high[IEEE80211_AC_VO], 2233ace10f5SKan Yan local->aql_txq_limit_low[IEEE80211_AC_VI], 2243ace10f5SKan Yan local->aql_txq_limit_high[IEEE80211_AC_VI], 2253ace10f5SKan Yan local->aql_txq_limit_low[IEEE80211_AC_BE], 2263ace10f5SKan Yan local->aql_txq_limit_high[IEEE80211_AC_BE], 2273ace10f5SKan Yan local->aql_txq_limit_low[IEEE80211_AC_BK], 2283ace10f5SKan Yan local->aql_txq_limit_high[IEEE80211_AC_BK]); 2293ace10f5SKan Yan return simple_read_from_buffer(user_buf, count, ppos, 2303ace10f5SKan Yan buf, len); 2313ace10f5SKan Yan } 2323ace10f5SKan Yan 2333ace10f5SKan Yan static ssize_t aql_txq_limit_write(struct file *file, 2343ace10f5SKan Yan const char __user *user_buf, 2353ace10f5SKan Yan size_t count, 2363ace10f5SKan Yan loff_t *ppos) 2373ace10f5SKan Yan { 2383ace10f5SKan Yan struct ieee80211_local *local = file->private_data; 2393ace10f5SKan Yan char buf[100]; 2403ace10f5SKan Yan size_t len; 2413ace10f5SKan Yan u32 ac, q_limit_low, q_limit_high, q_limit_low_old, q_limit_high_old; 2423ace10f5SKan Yan struct sta_info *sta; 2433ace10f5SKan Yan 2443ace10f5SKan Yan if (count > sizeof(buf)) 2453ace10f5SKan Yan return -EINVAL; 2463ace10f5SKan Yan 2473ace10f5SKan Yan if (copy_from_user(buf, user_buf, count)) 2483ace10f5SKan Yan return -EFAULT; 2493ace10f5SKan Yan 2503ace10f5SKan Yan buf[sizeof(buf) - 1] = 0; 2513ace10f5SKan Yan len = strlen(buf); 2523ace10f5SKan Yan if (len > 0 && buf[len - 1] == '\n') 2533ace10f5SKan Yan buf[len - 1] = 0; 2543ace10f5SKan Yan 2553ace10f5SKan Yan if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3) 2563ace10f5SKan Yan return -EINVAL; 2573ace10f5SKan Yan 2583ace10f5SKan Yan if (ac >= IEEE80211_NUM_ACS) 2593ace10f5SKan Yan return -EINVAL; 2603ace10f5SKan Yan 2613ace10f5SKan Yan q_limit_low_old = local->aql_txq_limit_low[ac]; 2623ace10f5SKan Yan q_limit_high_old = local->aql_txq_limit_high[ac]; 2633ace10f5SKan Yan 2643ace10f5SKan Yan local->aql_txq_limit_low[ac] = q_limit_low; 2653ace10f5SKan Yan local->aql_txq_limit_high[ac] = q_limit_high; 2663ace10f5SKan Yan 2673ace10f5SKan Yan mutex_lock(&local->sta_mtx); 2683ace10f5SKan Yan list_for_each_entry(sta, &local->sta_list, list) { 2693ace10f5SKan Yan /* If a sta has customized queue limits, keep it */ 2703ace10f5SKan Yan if (sta->airtime[ac].aql_limit_low == q_limit_low_old && 2713ace10f5SKan Yan sta->airtime[ac].aql_limit_high == q_limit_high_old) { 2723ace10f5SKan Yan sta->airtime[ac].aql_limit_low = q_limit_low; 2733ace10f5SKan Yan sta->airtime[ac].aql_limit_high = q_limit_high; 2743ace10f5SKan Yan } 2753ace10f5SKan Yan } 2763ace10f5SKan Yan mutex_unlock(&local->sta_mtx); 2773ace10f5SKan Yan return count; 2783ace10f5SKan Yan } 2793ace10f5SKan Yan 2803ace10f5SKan Yan static const struct file_operations aql_txq_limit_ops = { 2813ace10f5SKan Yan .write = aql_txq_limit_write, 2823ace10f5SKan Yan .read = aql_txq_limit_read, 2833ace10f5SKan Yan .open = simple_open, 2843ace10f5SKan Yan .llseek = default_llseek, 2853ace10f5SKan Yan }; 2863ace10f5SKan Yan 287276d9e82SJulius Niedworok static ssize_t force_tx_status_read(struct file *file, 288276d9e82SJulius Niedworok char __user *user_buf, 289276d9e82SJulius Niedworok size_t count, 290276d9e82SJulius Niedworok loff_t *ppos) 291276d9e82SJulius Niedworok { 292276d9e82SJulius Niedworok struct ieee80211_local *local = file->private_data; 293276d9e82SJulius Niedworok char buf[3]; 294276d9e82SJulius Niedworok int len = 0; 295276d9e82SJulius Niedworok 296276d9e82SJulius Niedworok len = scnprintf(buf, sizeof(buf), "%d\n", (int)local->force_tx_status); 297276d9e82SJulius Niedworok 298276d9e82SJulius Niedworok return simple_read_from_buffer(user_buf, count, ppos, 299276d9e82SJulius Niedworok buf, len); 300276d9e82SJulius Niedworok } 301276d9e82SJulius Niedworok 302276d9e82SJulius Niedworok static ssize_t force_tx_status_write(struct file *file, 303276d9e82SJulius Niedworok const char __user *user_buf, 304276d9e82SJulius Niedworok size_t count, 305276d9e82SJulius Niedworok loff_t *ppos) 306276d9e82SJulius Niedworok { 307276d9e82SJulius Niedworok struct ieee80211_local *local = file->private_data; 308276d9e82SJulius Niedworok char buf[3]; 309276d9e82SJulius Niedworok size_t len; 310276d9e82SJulius Niedworok 311276d9e82SJulius Niedworok if (count > sizeof(buf)) 312276d9e82SJulius Niedworok return -EINVAL; 313276d9e82SJulius Niedworok 314276d9e82SJulius Niedworok if (copy_from_user(buf, user_buf, count)) 315276d9e82SJulius Niedworok return -EFAULT; 316276d9e82SJulius Niedworok 317276d9e82SJulius Niedworok buf[sizeof(buf) - 1] = '\0'; 318276d9e82SJulius Niedworok len = strlen(buf); 319276d9e82SJulius Niedworok if (len > 0 && buf[len - 1] == '\n') 320276d9e82SJulius Niedworok buf[len - 1] = 0; 321276d9e82SJulius Niedworok 322276d9e82SJulius Niedworok if (buf[0] == '0' && buf[1] == '\0') 323276d9e82SJulius Niedworok local->force_tx_status = 0; 324276d9e82SJulius Niedworok else if (buf[0] == '1' && buf[1] == '\0') 325276d9e82SJulius Niedworok local->force_tx_status = 1; 326276d9e82SJulius Niedworok else 327276d9e82SJulius Niedworok return -EINVAL; 328276d9e82SJulius Niedworok 329276d9e82SJulius Niedworok return count; 330276d9e82SJulius Niedworok } 331276d9e82SJulius Niedworok 332276d9e82SJulius Niedworok static const struct file_operations force_tx_status_ops = { 333276d9e82SJulius Niedworok .write = force_tx_status_write, 334276d9e82SJulius Niedworok .read = force_tx_status_read, 335276d9e82SJulius Niedworok .open = simple_open, 336276d9e82SJulius Niedworok .llseek = default_llseek, 337276d9e82SJulius Niedworok }; 338276d9e82SJulius Niedworok 3392ad4814fSJohannes Berg #ifdef CONFIG_PM 340827b1fb4SJohannes Berg static ssize_t reset_write(struct file *file, const char __user *user_buf, 341827b1fb4SJohannes Berg size_t count, loff_t *ppos) 342827b1fb4SJohannes Berg { 343827b1fb4SJohannes Berg struct ieee80211_local *local = file->private_data; 344827b1fb4SJohannes Berg 345827b1fb4SJohannes Berg rtnl_lock(); 346eecc4800SJohannes Berg __ieee80211_suspend(&local->hw, NULL); 347827b1fb4SJohannes Berg __ieee80211_resume(&local->hw); 348827b1fb4SJohannes Berg rtnl_unlock(); 349827b1fb4SJohannes Berg 350827b1fb4SJohannes Berg return count; 351827b1fb4SJohannes Berg } 352827b1fb4SJohannes Berg 353827b1fb4SJohannes Berg static const struct file_operations reset_ops = { 354827b1fb4SJohannes Berg .write = reset_write, 355234e3405SStephen Boyd .open = simple_open, 3566038f373SArnd Bergmann .llseek = noop_llseek, 357827b1fb4SJohannes Berg }; 3582ad4814fSJohannes Berg #endif 359827b1fb4SJohannes Berg 36068920c97SAndrey Ryabinin static const char *hw_flag_names[] = { 36130686bf7SJohannes Berg #define FLAG(F) [IEEE80211_HW_##F] = #F 36230686bf7SJohannes Berg FLAG(HAS_RATE_CONTROL), 36330686bf7SJohannes Berg FLAG(RX_INCLUDES_FCS), 36430686bf7SJohannes Berg FLAG(HOST_BROADCAST_PS_BUFFERING), 36530686bf7SJohannes Berg FLAG(SIGNAL_UNSPEC), 36630686bf7SJohannes Berg FLAG(SIGNAL_DBM), 36730686bf7SJohannes Berg FLAG(NEED_DTIM_BEFORE_ASSOC), 36830686bf7SJohannes Berg FLAG(SPECTRUM_MGMT), 36930686bf7SJohannes Berg FLAG(AMPDU_AGGREGATION), 37030686bf7SJohannes Berg FLAG(SUPPORTS_PS), 37130686bf7SJohannes Berg FLAG(PS_NULLFUNC_STACK), 37230686bf7SJohannes Berg FLAG(SUPPORTS_DYNAMIC_PS), 37330686bf7SJohannes Berg FLAG(MFP_CAPABLE), 37430686bf7SJohannes Berg FLAG(WANT_MONITOR_VIF), 37530686bf7SJohannes Berg FLAG(NO_AUTO_VIF), 37630686bf7SJohannes Berg FLAG(SW_CRYPTO_CONTROL), 37730686bf7SJohannes Berg FLAG(SUPPORT_FAST_XMIT), 37830686bf7SJohannes Berg FLAG(REPORTS_TX_ACK_STATUS), 37930686bf7SJohannes Berg FLAG(CONNECTION_MONITOR), 38030686bf7SJohannes Berg FLAG(QUEUE_CONTROL), 38130686bf7SJohannes Berg FLAG(SUPPORTS_PER_STA_GTK), 38230686bf7SJohannes Berg FLAG(AP_LINK_PS), 38330686bf7SJohannes Berg FLAG(TX_AMPDU_SETUP_IN_HW), 38430686bf7SJohannes Berg FLAG(SUPPORTS_RC_TABLE), 38530686bf7SJohannes Berg FLAG(P2P_DEV_ADDR_FOR_INTF), 38630686bf7SJohannes Berg FLAG(TIMING_BEACON_ONLY), 38730686bf7SJohannes Berg FLAG(SUPPORTS_HT_CCK_RATES), 38830686bf7SJohannes Berg FLAG(CHANCTX_STA_CSA), 38930686bf7SJohannes Berg FLAG(SUPPORTS_CLONED_SKBS), 39030686bf7SJohannes Berg FLAG(SINGLE_SCAN_ON_ALL_BANDS), 391b98fb44fSArik Nemtsov FLAG(TDLS_WIDER_BW), 39299e7ca44SEmmanuel Grumbach FLAG(SUPPORTS_AMSDU_IN_AMPDU), 39335afa588SHelmut Schaa FLAG(BEACON_TX_STATUS), 39431104891SJohannes Berg FLAG(NEEDS_UNIQUE_STA_ADDR), 395412a6d80SSara Sharon FLAG(SUPPORTS_REORDERING_BUFFER), 396c9c5962bSJohannes Berg FLAG(USES_RSS), 3976e0456b5SFelix Fietkau FLAG(TX_AMSDU), 3986e0456b5SFelix Fietkau FLAG(TX_FRAG_LIST), 399e8a24cd4SRajkumar Manoharan FLAG(REPORTS_LOW_ACK), 400f3fe4e93SSara Sharon FLAG(SUPPORTS_TX_FRAG), 401e2fb1b83SYingying Tang FLAG(SUPPORTS_TDLS_BUFFER_STA), 40294ba9271SIlan Peer FLAG(DEAUTH_NEED_MGD_TX_PREP), 4037c181f4fSBen Caradoc-Davies FLAG(DOESNT_SUPPORT_QOS_NDP), 404adf8ed01SJohannes Berg FLAG(BUFF_MMPDU_TXQ), 40509b4a4faSJohannes Berg FLAG(SUPPORTS_VHT_EXT_NSS_BW), 4060eeb2b67SSara Sharon FLAG(STA_MMPDU_TXQ), 40777f7ffdcSFelix Fietkau FLAG(TX_STATUS_NO_AMPDU_LEN), 408caf56338SSara Sharon FLAG(SUPPORTS_MULTI_BSSID), 409caf56338SSara Sharon FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID), 410dc3998ecSAlexander Wetzel FLAG(AMPDU_KEYBORDER_SUPPORT), 41130686bf7SJohannes Berg #undef FLAG 41230686bf7SJohannes Berg }; 41330686bf7SJohannes Berg 414279daf64SBen Greear static ssize_t hwflags_read(struct file *file, char __user *user_buf, 415279daf64SBen Greear size_t count, loff_t *ppos) 416279daf64SBen Greear { 417279daf64SBen Greear struct ieee80211_local *local = file->private_data; 41830686bf7SJohannes Berg size_t bufsz = 30 * NUM_IEEE80211_HW_FLAGS; 41930686bf7SJohannes Berg char *buf = kzalloc(bufsz, GFP_KERNEL); 42030686bf7SJohannes Berg char *pos = buf, *end = buf + bufsz - 1; 421279daf64SBen Greear ssize_t rv; 42230686bf7SJohannes Berg int i; 423279daf64SBen Greear 424d15b8459SJoe Perches if (!buf) 42530686bf7SJohannes Berg return -ENOMEM; 426d15b8459SJoe Perches 42730686bf7SJohannes Berg /* fail compilation if somebody adds or removes 42830686bf7SJohannes Berg * a flag without updating the name array above 42930686bf7SJohannes Berg */ 43068920c97SAndrey Ryabinin BUILD_BUG_ON(ARRAY_SIZE(hw_flag_names) != NUM_IEEE80211_HW_FLAGS); 43130686bf7SJohannes Berg 43230686bf7SJohannes Berg for (i = 0; i < NUM_IEEE80211_HW_FLAGS; i++) { 43330686bf7SJohannes Berg if (test_bit(i, local->hw.flags)) 4344633dfc3SMohammed Shafi Shajakhan pos += scnprintf(pos, end - pos, "%s\n", 43530686bf7SJohannes Berg hw_flag_names[i]); 43630686bf7SJohannes Berg } 437279daf64SBen Greear 438279daf64SBen Greear rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); 439279daf64SBen Greear kfree(buf); 440279daf64SBen Greear return rv; 441279daf64SBen Greear } 442199d69f2SBenoit Papillault 4434a5eccaaSBen Greear static ssize_t misc_read(struct file *file, char __user *user_buf, 4444a5eccaaSBen Greear size_t count, loff_t *ppos) 4454a5eccaaSBen Greear { 4464a5eccaaSBen Greear struct ieee80211_local *local = file->private_data; 4474a5eccaaSBen Greear /* Max len of each line is 16 characters, plus 9 for 'pending:\n' */ 4484a5eccaaSBen Greear size_t bufsz = IEEE80211_MAX_QUEUES * 16 + 9; 449b2347a32SDan Carpenter char *buf; 450b2347a32SDan Carpenter char *pos, *end; 4514a5eccaaSBen Greear ssize_t rv; 4524a5eccaaSBen Greear int i; 4534a5eccaaSBen Greear int ln; 4544a5eccaaSBen Greear 455b2347a32SDan Carpenter buf = kzalloc(bufsz, GFP_KERNEL); 456b2347a32SDan Carpenter if (!buf) 457b2347a32SDan Carpenter return -ENOMEM; 458b2347a32SDan Carpenter 459b2347a32SDan Carpenter pos = buf; 460b2347a32SDan Carpenter end = buf + bufsz - 1; 461b2347a32SDan Carpenter 4624a5eccaaSBen Greear pos += scnprintf(pos, end - pos, "pending:\n"); 4634a5eccaaSBen Greear 4644a5eccaaSBen Greear for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { 4654a5eccaaSBen Greear ln = skb_queue_len(&local->pending[i]); 4664a5eccaaSBen Greear pos += scnprintf(pos, end - pos, "[%i] %d\n", 4674a5eccaaSBen Greear i, ln); 4684a5eccaaSBen Greear } 4694a5eccaaSBen Greear 4704a5eccaaSBen Greear rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); 4714a5eccaaSBen Greear kfree(buf); 4724a5eccaaSBen Greear return rv; 4734a5eccaaSBen Greear } 4744a5eccaaSBen Greear 475db2e6bd4SJohannes Berg static ssize_t queues_read(struct file *file, char __user *user_buf, 476db2e6bd4SJohannes Berg size_t count, loff_t *ppos) 477db2e6bd4SJohannes Berg { 478db2e6bd4SJohannes Berg struct ieee80211_local *local = file->private_data; 479db2e6bd4SJohannes Berg unsigned long flags; 480db2e6bd4SJohannes Berg char buf[IEEE80211_MAX_QUEUES * 20]; 481db2e6bd4SJohannes Berg int q, res = 0; 482db2e6bd4SJohannes Berg 483db2e6bd4SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 484db2e6bd4SJohannes Berg for (q = 0; q < local->hw.queues; q++) 485db2e6bd4SJohannes Berg res += sprintf(buf + res, "%02d: %#.8lx/%d\n", q, 486db2e6bd4SJohannes Berg local->queue_stop_reasons[q], 4873b8d81e0SJohannes Berg skb_queue_len(&local->pending[q])); 488db2e6bd4SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 489db2e6bd4SJohannes Berg 490db2e6bd4SJohannes Berg return simple_read_from_buffer(user_buf, count, ppos, buf, res); 491db2e6bd4SJohannes Berg } 492db2e6bd4SJohannes Berg 493279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(hwflags); 494279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(queues); 4954a5eccaaSBen Greear DEBUGFS_READONLY_FILE_OPS(misc); 496db2e6bd4SJohannes Berg 497e9f207f0SJiri Benc /* statistics stuff */ 498e9f207f0SJiri Benc 499e9f207f0SJiri Benc static ssize_t format_devstat_counter(struct ieee80211_local *local, 500e9f207f0SJiri Benc char __user *userbuf, 501e9f207f0SJiri Benc size_t count, loff_t *ppos, 502e9f207f0SJiri Benc int (*printvalue)(struct ieee80211_low_level_stats *stats, char *buf, 503e9f207f0SJiri Benc int buflen)) 504e9f207f0SJiri Benc { 505e9f207f0SJiri Benc struct ieee80211_low_level_stats stats; 506e9f207f0SJiri Benc char buf[20]; 507e9f207f0SJiri Benc int res; 508e9f207f0SJiri Benc 50975636525SJohannes Berg rtnl_lock(); 51024487981SJohannes Berg res = drv_get_stats(local, &stats); 511e9f207f0SJiri Benc rtnl_unlock(); 51224487981SJohannes Berg if (res) 51324487981SJohannes Berg return res; 514e9f207f0SJiri Benc res = printvalue(&stats, buf, sizeof(buf)); 515e9f207f0SJiri Benc return simple_read_from_buffer(userbuf, count, ppos, buf, res); 516e9f207f0SJiri Benc } 517e9f207f0SJiri Benc 518e9f207f0SJiri Benc #define DEBUGFS_DEVSTATS_FILE(name) \ 519e9f207f0SJiri Benc static int print_devstats_##name(struct ieee80211_low_level_stats *stats,\ 520e9f207f0SJiri Benc char *buf, int buflen) \ 521e9f207f0SJiri Benc { \ 522e9f207f0SJiri Benc return scnprintf(buf, buflen, "%u\n", stats->name); \ 523e9f207f0SJiri Benc } \ 524e9f207f0SJiri Benc static ssize_t stats_ ##name## _read(struct file *file, \ 525e9f207f0SJiri Benc char __user *userbuf, \ 526e9f207f0SJiri Benc size_t count, loff_t *ppos) \ 527e9f207f0SJiri Benc { \ 528e9f207f0SJiri Benc return format_devstat_counter(file->private_data, \ 529e9f207f0SJiri Benc userbuf, \ 530e9f207f0SJiri Benc count, \ 531e9f207f0SJiri Benc ppos, \ 532e9f207f0SJiri Benc print_devstats_##name); \ 533e9f207f0SJiri Benc } \ 534e9f207f0SJiri Benc \ 535e9f207f0SJiri Benc static const struct file_operations stats_ ##name## _ops = { \ 536e9f207f0SJiri Benc .read = stats_ ##name## _read, \ 537234e3405SStephen Boyd .open = simple_open, \ 5382b18ab36SArnd Bergmann .llseek = generic_file_llseek, \ 539e9f207f0SJiri Benc }; 540e9f207f0SJiri Benc 541f1160434SJohannes Berg #define DEBUGFS_STATS_ADD(name) \ 542f1160434SJohannes Berg debugfs_create_u32(#name, 0400, statsd, &local->name); 5432826bcd8SFelix Fietkau #define DEBUGFS_DEVSTATS_ADD(name) \ 5447bcfaf2fSJohannes Berg debugfs_create_file(#name, 0400, statsd, local, &stats_ ##name## _ops); 545e9f207f0SJiri Benc 546e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11ACKFailureCount); 547e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11RTSFailureCount); 548e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11FCSErrorCount); 549e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11RTSSuccessCount); 550e9f207f0SJiri Benc 551e9f207f0SJiri Benc void debugfs_hw_add(struct ieee80211_local *local) 552e9f207f0SJiri Benc { 553e9f207f0SJiri Benc struct dentry *phyd = local->hw.wiphy->debugfsdir; 554e9f207f0SJiri Benc struct dentry *statsd; 555e9f207f0SJiri Benc 556e9f207f0SJiri Benc if (!phyd) 557e9f207f0SJiri Benc return; 558e9f207f0SJiri Benc 559e9f207f0SJiri Benc local->debugfs.keys = debugfs_create_dir("keys", phyd); 560e9f207f0SJiri Benc 561e9f207f0SJiri Benc DEBUGFS_ADD(total_ps_buffered); 562e9f207f0SJiri Benc DEBUGFS_ADD(wep_iv); 563bddb2afcSJohannes Berg DEBUGFS_ADD(rate_ctrl_alg); 564db2e6bd4SJohannes Berg DEBUGFS_ADD(queues); 5654a5eccaaSBen Greear DEBUGFS_ADD(misc); 5662ad4814fSJohannes Berg #ifdef CONFIG_PM 567827b1fb4SJohannes Berg DEBUGFS_ADD_MODE(reset, 0200); 5682ad4814fSJohannes Berg #endif 569279daf64SBen Greear DEBUGFS_ADD(hwflags); 57083bdf2a1SBen Greear DEBUGFS_ADD(user_power); 57183bdf2a1SBen Greear DEBUGFS_ADD(power); 572c90142a5SThomas Pedersen DEBUGFS_ADD(hw_conf); 573276d9e82SJulius Niedworok DEBUGFS_ADD_MODE(force_tx_status, 0600); 5748d51dbb8SToke Høiland-Jørgensen 5758d51dbb8SToke Høiland-Jørgensen if (local->ops->wake_tx_queue) 5769399b86cSMichal Kazior DEBUGFS_ADD_MODE(aqm, 0600); 577e9f207f0SJiri Benc 578*e322c07fSLorenzo Bianconi DEBUGFS_ADD_MODE(airtime_flags, 0600); 579b4809e94SToke Høiland-Jørgensen 5803ace10f5SKan Yan DEBUGFS_ADD(aql_txq_limit); 5813ace10f5SKan Yan debugfs_create_u32("aql_threshold", 0600, 5823ace10f5SKan Yan phyd, &local->aql_threshold); 5833ace10f5SKan Yan 584e9f207f0SJiri Benc statsd = debugfs_create_dir("statistics", phyd); 585e9f207f0SJiri Benc 586e9f207f0SJiri Benc /* if the dir failed, don't put all the other things into the root! */ 587e9f207f0SJiri Benc if (!statsd) 588e9f207f0SJiri Benc return; 589e9f207f0SJiri Benc 590c206ca67SJohannes Berg #ifdef CONFIG_MAC80211_DEBUG_COUNTERS 591f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11TransmittedFragmentCount); 592f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11MulticastTransmittedFrameCount); 593f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11FailedCount); 594f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11RetryCount); 595f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11MultipleRetryCount); 596f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11FrameDuplicateCount); 597f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11ReceivedFragmentCount); 598f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11MulticastReceivedFrameCount); 599f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11TransmittedFrameCount); 600f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_drop); 601f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_queued); 602f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_drop_wep); 603f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_drop_not_assoc); 604f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_drop_unauth_port); 605f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_drop); 606f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_queued); 607f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc); 608f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_drop_defrag); 609f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_expand_skb_head); 610f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned); 611f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_expand_skb_head_defrag); 612f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_fragments); 613f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_status_drop); 614e9f207f0SJiri Benc #endif 6152826bcd8SFelix Fietkau DEBUGFS_DEVSTATS_ADD(dot11ACKFailureCount); 6162826bcd8SFelix Fietkau DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount); 6172826bcd8SFelix Fietkau DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount); 6182826bcd8SFelix Fietkau DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount); 619e9f207f0SJiri Benc } 620