1e9f207f0SJiri Benc /* 2e9f207f0SJiri Benc * mac80211 debugfs for wireless PHYs 3e9f207f0SJiri Benc * 4e9f207f0SJiri Benc * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 5d98ad83eSJohannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH 6e9f207f0SJiri Benc * 7e9f207f0SJiri Benc * GPLv2 8e9f207f0SJiri Benc * 9e9f207f0SJiri Benc */ 10e9f207f0SJiri Benc 11e9f207f0SJiri Benc #include <linux/debugfs.h> 12e9f207f0SJiri Benc #include <linux/rtnetlink.h> 13*9399b86cSMichal Kazior #include <linux/vmalloc.h> 14e9f207f0SJiri Benc #include "ieee80211_i.h" 1524487981SJohannes Berg #include "driver-ops.h" 162c8dccc7SJohannes Berg #include "rate.h" 17e9f207f0SJiri Benc #include "debugfs.h" 18e9f207f0SJiri Benc 1907caf9d6SEliad Peller #define DEBUGFS_FORMAT_BUFFER_SIZE 100 2007caf9d6SEliad Peller 2107caf9d6SEliad Peller int mac80211_format_buffer(char __user *userbuf, size_t count, 2207caf9d6SEliad Peller loff_t *ppos, char *fmt, ...) 2307caf9d6SEliad Peller { 2407caf9d6SEliad Peller va_list args; 2507caf9d6SEliad Peller char buf[DEBUGFS_FORMAT_BUFFER_SIZE]; 2607caf9d6SEliad Peller int res; 2707caf9d6SEliad Peller 2807caf9d6SEliad Peller va_start(args, fmt); 2907caf9d6SEliad Peller res = vscnprintf(buf, sizeof(buf), fmt, args); 3007caf9d6SEliad Peller va_end(args); 3107caf9d6SEliad Peller 3207caf9d6SEliad Peller return simple_read_from_buffer(userbuf, count, ppos, buf, res); 3307caf9d6SEliad Peller } 3407caf9d6SEliad Peller 35279daf64SBen Greear #define DEBUGFS_READONLY_FILE_FN(name, fmt, value...) \ 36e9f207f0SJiri Benc static ssize_t name## _read(struct file *file, char __user *userbuf, \ 37e9f207f0SJiri Benc size_t count, loff_t *ppos) \ 38e9f207f0SJiri Benc { \ 39e9f207f0SJiri Benc struct ieee80211_local *local = file->private_data; \ 40e9f207f0SJiri Benc \ 4107caf9d6SEliad Peller return mac80211_format_buffer(userbuf, count, ppos, \ 4207caf9d6SEliad Peller fmt "\n", ##value); \ 43279daf64SBen Greear } 44279daf64SBen Greear 45279daf64SBen Greear #define DEBUGFS_READONLY_FILE_OPS(name) \ 46e9f207f0SJiri Benc static const struct file_operations name## _ops = { \ 47e9f207f0SJiri Benc .read = name## _read, \ 48234e3405SStephen Boyd .open = simple_open, \ 492b18ab36SArnd Bergmann .llseek = generic_file_llseek, \ 50e9f207f0SJiri Benc }; 51e9f207f0SJiri Benc 52279daf64SBen Greear #define DEBUGFS_READONLY_FILE(name, fmt, value...) \ 53279daf64SBen Greear DEBUGFS_READONLY_FILE_FN(name, fmt, value) \ 54279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(name) 55279daf64SBen Greear 56e9f207f0SJiri Benc #define DEBUGFS_ADD(name) \ 577bcfaf2fSJohannes Berg debugfs_create_file(#name, 0400, phyd, local, &name## _ops); 58e9f207f0SJiri Benc 59827b1fb4SJohannes Berg #define DEBUGFS_ADD_MODE(name, mode) \ 607bcfaf2fSJohannes Berg debugfs_create_file(#name, mode, phyd, local, &name## _ops); 61e9f207f0SJiri Benc 62e9f207f0SJiri Benc 6383bdf2a1SBen Greear DEBUGFS_READONLY_FILE(user_power, "%d", 6483bdf2a1SBen Greear local->user_power_level); 6583bdf2a1SBen Greear DEBUGFS_READONLY_FILE(power, "%d", 6683bdf2a1SBen Greear local->hw.conf.power_level); 6707caf9d6SEliad Peller DEBUGFS_READONLY_FILE(total_ps_buffered, "%d", 68e9f207f0SJiri Benc local->total_ps_buffered); 6907caf9d6SEliad Peller DEBUGFS_READONLY_FILE(wep_iv, "%#08x", 70e9f207f0SJiri Benc local->wep_iv & 0xffffff); 7107caf9d6SEliad Peller DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s", 72af65cd96SJohannes Berg local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver"); 733b5d665bSAlina Friedrichsen 74*9399b86cSMichal Kazior struct aqm_info { 75*9399b86cSMichal Kazior struct ieee80211_local *local; 76*9399b86cSMichal Kazior size_t size; 77*9399b86cSMichal Kazior size_t len; 78*9399b86cSMichal Kazior unsigned char buf[0]; 79*9399b86cSMichal Kazior }; 80*9399b86cSMichal Kazior 81*9399b86cSMichal Kazior #define AQM_HDR_LEN 200 82*9399b86cSMichal Kazior #define AQM_HW_ENTRY_LEN 40 83*9399b86cSMichal Kazior #define AQM_TXQ_ENTRY_LEN 110 84*9399b86cSMichal Kazior 85*9399b86cSMichal Kazior static int aqm_open(struct inode *inode, struct file *file) 86*9399b86cSMichal Kazior { 87*9399b86cSMichal Kazior struct ieee80211_local *local = inode->i_private; 88*9399b86cSMichal Kazior struct ieee80211_sub_if_data *sdata; 89*9399b86cSMichal Kazior struct sta_info *sta; 90*9399b86cSMichal Kazior struct txq_info *txqi; 91*9399b86cSMichal Kazior struct fq *fq = &local->fq; 92*9399b86cSMichal Kazior struct aqm_info *info = NULL; 93*9399b86cSMichal Kazior int len = 0; 94*9399b86cSMichal Kazior int i; 95*9399b86cSMichal Kazior 96*9399b86cSMichal Kazior if (!local->ops->wake_tx_queue) 97*9399b86cSMichal Kazior return -EOPNOTSUPP; 98*9399b86cSMichal Kazior 99*9399b86cSMichal Kazior len += AQM_HDR_LEN; 100*9399b86cSMichal Kazior len += 6 * AQM_HW_ENTRY_LEN; 101*9399b86cSMichal Kazior 102*9399b86cSMichal Kazior rcu_read_lock(); 103*9399b86cSMichal Kazior list_for_each_entry_rcu(sdata, &local->interfaces, list) 104*9399b86cSMichal Kazior len += AQM_TXQ_ENTRY_LEN; 105*9399b86cSMichal Kazior list_for_each_entry_rcu(sta, &local->sta_list, list) 106*9399b86cSMichal Kazior len += AQM_TXQ_ENTRY_LEN * ARRAY_SIZE(sta->sta.txq); 107*9399b86cSMichal Kazior rcu_read_unlock(); 108*9399b86cSMichal Kazior 109*9399b86cSMichal Kazior info = vmalloc(len); 110*9399b86cSMichal Kazior if (!info) 111*9399b86cSMichal Kazior return -ENOMEM; 112*9399b86cSMichal Kazior 113*9399b86cSMichal Kazior spin_lock_bh(&local->fq.lock); 114*9399b86cSMichal Kazior rcu_read_lock(); 115*9399b86cSMichal Kazior 116*9399b86cSMichal Kazior file->private_data = info; 117*9399b86cSMichal Kazior info->local = local; 118*9399b86cSMichal Kazior info->size = len; 119*9399b86cSMichal Kazior len = 0; 120*9399b86cSMichal Kazior 121*9399b86cSMichal Kazior len += scnprintf(info->buf + len, info->size - len, 122*9399b86cSMichal Kazior "* hw\n" 123*9399b86cSMichal Kazior "access name value\n" 124*9399b86cSMichal Kazior "R fq_flows_cnt %u\n" 125*9399b86cSMichal Kazior "R fq_backlog %u\n" 126*9399b86cSMichal Kazior "R fq_overlimit %u\n" 127*9399b86cSMichal Kazior "R fq_collisions %u\n" 128*9399b86cSMichal Kazior "RW fq_limit %u\n" 129*9399b86cSMichal Kazior "RW fq_quantum %u\n", 130*9399b86cSMichal Kazior fq->flows_cnt, 131*9399b86cSMichal Kazior fq->backlog, 132*9399b86cSMichal Kazior fq->overlimit, 133*9399b86cSMichal Kazior fq->collisions, 134*9399b86cSMichal Kazior fq->limit, 135*9399b86cSMichal Kazior fq->quantum); 136*9399b86cSMichal Kazior 137*9399b86cSMichal Kazior len += scnprintf(info->buf + len, 138*9399b86cSMichal Kazior info->size - len, 139*9399b86cSMichal Kazior "* vif\n" 140*9399b86cSMichal Kazior "ifname addr ac backlog-bytes backlog-packets flows overlimit collisions tx-bytes tx-packets\n"); 141*9399b86cSMichal Kazior 142*9399b86cSMichal Kazior list_for_each_entry_rcu(sdata, &local->interfaces, list) { 143*9399b86cSMichal Kazior txqi = to_txq_info(sdata->vif.txq); 144*9399b86cSMichal Kazior len += scnprintf(info->buf + len, info->size - len, 145*9399b86cSMichal Kazior "%s %pM %u %u %u %u %u %u %u %u\n", 146*9399b86cSMichal Kazior sdata->name, 147*9399b86cSMichal Kazior sdata->vif.addr, 148*9399b86cSMichal Kazior txqi->txq.ac, 149*9399b86cSMichal Kazior txqi->tin.backlog_bytes, 150*9399b86cSMichal Kazior txqi->tin.backlog_packets, 151*9399b86cSMichal Kazior txqi->tin.flows, 152*9399b86cSMichal Kazior txqi->tin.overlimit, 153*9399b86cSMichal Kazior txqi->tin.collisions, 154*9399b86cSMichal Kazior txqi->tin.tx_bytes, 155*9399b86cSMichal Kazior txqi->tin.tx_packets); 156*9399b86cSMichal Kazior } 157*9399b86cSMichal Kazior 158*9399b86cSMichal Kazior len += scnprintf(info->buf + len, 159*9399b86cSMichal Kazior info->size - len, 160*9399b86cSMichal Kazior "* sta\n" 161*9399b86cSMichal Kazior "ifname addr tid ac backlog-bytes backlog-packets flows overlimit collisions tx-bytes tx-packets\n"); 162*9399b86cSMichal Kazior 163*9399b86cSMichal Kazior list_for_each_entry_rcu(sta, &local->sta_list, list) { 164*9399b86cSMichal Kazior sdata = sta->sdata; 165*9399b86cSMichal Kazior for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { 166*9399b86cSMichal Kazior txqi = to_txq_info(sta->sta.txq[i]); 167*9399b86cSMichal Kazior len += scnprintf(info->buf + len, info->size - len, 168*9399b86cSMichal Kazior "%s %pM %d %d %u %u %u %u %u %u %u\n", 169*9399b86cSMichal Kazior sdata->name, 170*9399b86cSMichal Kazior sta->sta.addr, 171*9399b86cSMichal Kazior txqi->txq.tid, 172*9399b86cSMichal Kazior txqi->txq.ac, 173*9399b86cSMichal Kazior txqi->tin.backlog_bytes, 174*9399b86cSMichal Kazior txqi->tin.backlog_packets, 175*9399b86cSMichal Kazior txqi->tin.flows, 176*9399b86cSMichal Kazior txqi->tin.overlimit, 177*9399b86cSMichal Kazior txqi->tin.collisions, 178*9399b86cSMichal Kazior txqi->tin.tx_bytes, 179*9399b86cSMichal Kazior txqi->tin.tx_packets); 180*9399b86cSMichal Kazior } 181*9399b86cSMichal Kazior } 182*9399b86cSMichal Kazior 183*9399b86cSMichal Kazior info->len = len; 184*9399b86cSMichal Kazior 185*9399b86cSMichal Kazior rcu_read_unlock(); 186*9399b86cSMichal Kazior spin_unlock_bh(&local->fq.lock); 187*9399b86cSMichal Kazior 188*9399b86cSMichal Kazior return 0; 189*9399b86cSMichal Kazior } 190*9399b86cSMichal Kazior 191*9399b86cSMichal Kazior static int aqm_release(struct inode *inode, struct file *file) 192*9399b86cSMichal Kazior { 193*9399b86cSMichal Kazior vfree(file->private_data); 194*9399b86cSMichal Kazior return 0; 195*9399b86cSMichal Kazior } 196*9399b86cSMichal Kazior 197*9399b86cSMichal Kazior static ssize_t aqm_read(struct file *file, 198*9399b86cSMichal Kazior char __user *user_buf, 199*9399b86cSMichal Kazior size_t count, 200*9399b86cSMichal Kazior loff_t *ppos) 201*9399b86cSMichal Kazior { 202*9399b86cSMichal Kazior struct aqm_info *info = file->private_data; 203*9399b86cSMichal Kazior 204*9399b86cSMichal Kazior return simple_read_from_buffer(user_buf, count, ppos, 205*9399b86cSMichal Kazior info->buf, info->len); 206*9399b86cSMichal Kazior } 207*9399b86cSMichal Kazior 208*9399b86cSMichal Kazior static ssize_t aqm_write(struct file *file, 209*9399b86cSMichal Kazior const char __user *user_buf, 210*9399b86cSMichal Kazior size_t count, 211*9399b86cSMichal Kazior loff_t *ppos) 212*9399b86cSMichal Kazior { 213*9399b86cSMichal Kazior struct aqm_info *info = file->private_data; 214*9399b86cSMichal Kazior struct ieee80211_local *local = info->local; 215*9399b86cSMichal Kazior char buf[100]; 216*9399b86cSMichal Kazior size_t len; 217*9399b86cSMichal Kazior 218*9399b86cSMichal Kazior if (count > sizeof(buf)) 219*9399b86cSMichal Kazior return -EINVAL; 220*9399b86cSMichal Kazior 221*9399b86cSMichal Kazior if (copy_from_user(buf, user_buf, count)) 222*9399b86cSMichal Kazior return -EFAULT; 223*9399b86cSMichal Kazior 224*9399b86cSMichal Kazior buf[sizeof(buf) - 1] = '\0'; 225*9399b86cSMichal Kazior len = strlen(buf); 226*9399b86cSMichal Kazior if (len > 0 && buf[len-1] == '\n') 227*9399b86cSMichal Kazior buf[len-1] = 0; 228*9399b86cSMichal Kazior 229*9399b86cSMichal Kazior if (sscanf(buf, "fq_limit %u", &local->fq.limit) == 1) 230*9399b86cSMichal Kazior return count; 231*9399b86cSMichal Kazior else if (sscanf(buf, "fq_quantum %u", &local->fq.quantum) == 1) 232*9399b86cSMichal Kazior return count; 233*9399b86cSMichal Kazior 234*9399b86cSMichal Kazior return -EINVAL; 235*9399b86cSMichal Kazior } 236*9399b86cSMichal Kazior 237*9399b86cSMichal Kazior static const struct file_operations aqm_ops = { 238*9399b86cSMichal Kazior .write = aqm_write, 239*9399b86cSMichal Kazior .read = aqm_read, 240*9399b86cSMichal Kazior .open = aqm_open, 241*9399b86cSMichal Kazior .release = aqm_release, 242*9399b86cSMichal Kazior .llseek = default_llseek, 243*9399b86cSMichal Kazior }; 244*9399b86cSMichal Kazior 2452ad4814fSJohannes Berg #ifdef CONFIG_PM 246827b1fb4SJohannes Berg static ssize_t reset_write(struct file *file, const char __user *user_buf, 247827b1fb4SJohannes Berg size_t count, loff_t *ppos) 248827b1fb4SJohannes Berg { 249827b1fb4SJohannes Berg struct ieee80211_local *local = file->private_data; 250827b1fb4SJohannes Berg 251827b1fb4SJohannes Berg rtnl_lock(); 252eecc4800SJohannes Berg __ieee80211_suspend(&local->hw, NULL); 253827b1fb4SJohannes Berg __ieee80211_resume(&local->hw); 254827b1fb4SJohannes Berg rtnl_unlock(); 255827b1fb4SJohannes Berg 256827b1fb4SJohannes Berg return count; 257827b1fb4SJohannes Berg } 258827b1fb4SJohannes Berg 259827b1fb4SJohannes Berg static const struct file_operations reset_ops = { 260827b1fb4SJohannes Berg .write = reset_write, 261234e3405SStephen Boyd .open = simple_open, 2626038f373SArnd Bergmann .llseek = noop_llseek, 263827b1fb4SJohannes Berg }; 2642ad4814fSJohannes Berg #endif 265827b1fb4SJohannes Berg 26668920c97SAndrey Ryabinin static const char *hw_flag_names[] = { 26730686bf7SJohannes Berg #define FLAG(F) [IEEE80211_HW_##F] = #F 26830686bf7SJohannes Berg FLAG(HAS_RATE_CONTROL), 26930686bf7SJohannes Berg FLAG(RX_INCLUDES_FCS), 27030686bf7SJohannes Berg FLAG(HOST_BROADCAST_PS_BUFFERING), 27130686bf7SJohannes Berg FLAG(SIGNAL_UNSPEC), 27230686bf7SJohannes Berg FLAG(SIGNAL_DBM), 27330686bf7SJohannes Berg FLAG(NEED_DTIM_BEFORE_ASSOC), 27430686bf7SJohannes Berg FLAG(SPECTRUM_MGMT), 27530686bf7SJohannes Berg FLAG(AMPDU_AGGREGATION), 27630686bf7SJohannes Berg FLAG(SUPPORTS_PS), 27730686bf7SJohannes Berg FLAG(PS_NULLFUNC_STACK), 27830686bf7SJohannes Berg FLAG(SUPPORTS_DYNAMIC_PS), 27930686bf7SJohannes Berg FLAG(MFP_CAPABLE), 28030686bf7SJohannes Berg FLAG(WANT_MONITOR_VIF), 28130686bf7SJohannes Berg FLAG(NO_AUTO_VIF), 28230686bf7SJohannes Berg FLAG(SW_CRYPTO_CONTROL), 28330686bf7SJohannes Berg FLAG(SUPPORT_FAST_XMIT), 28430686bf7SJohannes Berg FLAG(REPORTS_TX_ACK_STATUS), 28530686bf7SJohannes Berg FLAG(CONNECTION_MONITOR), 28630686bf7SJohannes Berg FLAG(QUEUE_CONTROL), 28730686bf7SJohannes Berg FLAG(SUPPORTS_PER_STA_GTK), 28830686bf7SJohannes Berg FLAG(AP_LINK_PS), 28930686bf7SJohannes Berg FLAG(TX_AMPDU_SETUP_IN_HW), 29030686bf7SJohannes Berg FLAG(SUPPORTS_RC_TABLE), 29130686bf7SJohannes Berg FLAG(P2P_DEV_ADDR_FOR_INTF), 29230686bf7SJohannes Berg FLAG(TIMING_BEACON_ONLY), 29330686bf7SJohannes Berg FLAG(SUPPORTS_HT_CCK_RATES), 29430686bf7SJohannes Berg FLAG(CHANCTX_STA_CSA), 29530686bf7SJohannes Berg FLAG(SUPPORTS_CLONED_SKBS), 29630686bf7SJohannes Berg FLAG(SINGLE_SCAN_ON_ALL_BANDS), 297b98fb44fSArik Nemtsov FLAG(TDLS_WIDER_BW), 29899e7ca44SEmmanuel Grumbach FLAG(SUPPORTS_AMSDU_IN_AMPDU), 29935afa588SHelmut Schaa FLAG(BEACON_TX_STATUS), 30031104891SJohannes Berg FLAG(NEEDS_UNIQUE_STA_ADDR), 301412a6d80SSara Sharon FLAG(SUPPORTS_REORDERING_BUFFER), 302c9c5962bSJohannes Berg FLAG(USES_RSS), 3036e0456b5SFelix Fietkau FLAG(TX_AMSDU), 3046e0456b5SFelix Fietkau FLAG(TX_FRAG_LIST), 30530686bf7SJohannes Berg #undef FLAG 30630686bf7SJohannes Berg }; 30730686bf7SJohannes Berg 308279daf64SBen Greear static ssize_t hwflags_read(struct file *file, char __user *user_buf, 309279daf64SBen Greear size_t count, loff_t *ppos) 310279daf64SBen Greear { 311279daf64SBen Greear struct ieee80211_local *local = file->private_data; 31230686bf7SJohannes Berg size_t bufsz = 30 * NUM_IEEE80211_HW_FLAGS; 31330686bf7SJohannes Berg char *buf = kzalloc(bufsz, GFP_KERNEL); 31430686bf7SJohannes Berg char *pos = buf, *end = buf + bufsz - 1; 315279daf64SBen Greear ssize_t rv; 31630686bf7SJohannes Berg int i; 317279daf64SBen Greear 318d15b8459SJoe Perches if (!buf) 31930686bf7SJohannes Berg return -ENOMEM; 320d15b8459SJoe Perches 32130686bf7SJohannes Berg /* fail compilation if somebody adds or removes 32230686bf7SJohannes Berg * a flag without updating the name array above 32330686bf7SJohannes Berg */ 32468920c97SAndrey Ryabinin BUILD_BUG_ON(ARRAY_SIZE(hw_flag_names) != NUM_IEEE80211_HW_FLAGS); 32530686bf7SJohannes Berg 32630686bf7SJohannes Berg for (i = 0; i < NUM_IEEE80211_HW_FLAGS; i++) { 32730686bf7SJohannes Berg if (test_bit(i, local->hw.flags)) 3284633dfc3SMohammed Shafi Shajakhan pos += scnprintf(pos, end - pos, "%s\n", 32930686bf7SJohannes Berg hw_flag_names[i]); 33030686bf7SJohannes Berg } 331279daf64SBen Greear 332279daf64SBen Greear rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); 333279daf64SBen Greear kfree(buf); 334279daf64SBen Greear return rv; 335279daf64SBen Greear } 336199d69f2SBenoit Papillault 337db2e6bd4SJohannes Berg static ssize_t queues_read(struct file *file, char __user *user_buf, 338db2e6bd4SJohannes Berg size_t count, loff_t *ppos) 339db2e6bd4SJohannes Berg { 340db2e6bd4SJohannes Berg struct ieee80211_local *local = file->private_data; 341db2e6bd4SJohannes Berg unsigned long flags; 342db2e6bd4SJohannes Berg char buf[IEEE80211_MAX_QUEUES * 20]; 343db2e6bd4SJohannes Berg int q, res = 0; 344db2e6bd4SJohannes Berg 345db2e6bd4SJohannes Berg spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 346db2e6bd4SJohannes Berg for (q = 0; q < local->hw.queues; q++) 347db2e6bd4SJohannes Berg res += sprintf(buf + res, "%02d: %#.8lx/%d\n", q, 348db2e6bd4SJohannes Berg local->queue_stop_reasons[q], 3493b8d81e0SJohannes Berg skb_queue_len(&local->pending[q])); 350db2e6bd4SJohannes Berg spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 351db2e6bd4SJohannes Berg 352db2e6bd4SJohannes Berg return simple_read_from_buffer(user_buf, count, ppos, buf, res); 353db2e6bd4SJohannes Berg } 354db2e6bd4SJohannes Berg 355279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(hwflags); 356279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(queues); 357db2e6bd4SJohannes Berg 358e9f207f0SJiri Benc /* statistics stuff */ 359e9f207f0SJiri Benc 360e9f207f0SJiri Benc static ssize_t format_devstat_counter(struct ieee80211_local *local, 361e9f207f0SJiri Benc char __user *userbuf, 362e9f207f0SJiri Benc size_t count, loff_t *ppos, 363e9f207f0SJiri Benc int (*printvalue)(struct ieee80211_low_level_stats *stats, char *buf, 364e9f207f0SJiri Benc int buflen)) 365e9f207f0SJiri Benc { 366e9f207f0SJiri Benc struct ieee80211_low_level_stats stats; 367e9f207f0SJiri Benc char buf[20]; 368e9f207f0SJiri Benc int res; 369e9f207f0SJiri Benc 37075636525SJohannes Berg rtnl_lock(); 37124487981SJohannes Berg res = drv_get_stats(local, &stats); 372e9f207f0SJiri Benc rtnl_unlock(); 37324487981SJohannes Berg if (res) 37424487981SJohannes Berg return res; 375e9f207f0SJiri Benc res = printvalue(&stats, buf, sizeof(buf)); 376e9f207f0SJiri Benc return simple_read_from_buffer(userbuf, count, ppos, buf, res); 377e9f207f0SJiri Benc } 378e9f207f0SJiri Benc 379e9f207f0SJiri Benc #define DEBUGFS_DEVSTATS_FILE(name) \ 380e9f207f0SJiri Benc static int print_devstats_##name(struct ieee80211_low_level_stats *stats,\ 381e9f207f0SJiri Benc char *buf, int buflen) \ 382e9f207f0SJiri Benc { \ 383e9f207f0SJiri Benc return scnprintf(buf, buflen, "%u\n", stats->name); \ 384e9f207f0SJiri Benc } \ 385e9f207f0SJiri Benc static ssize_t stats_ ##name## _read(struct file *file, \ 386e9f207f0SJiri Benc char __user *userbuf, \ 387e9f207f0SJiri Benc size_t count, loff_t *ppos) \ 388e9f207f0SJiri Benc { \ 389e9f207f0SJiri Benc return format_devstat_counter(file->private_data, \ 390e9f207f0SJiri Benc userbuf, \ 391e9f207f0SJiri Benc count, \ 392e9f207f0SJiri Benc ppos, \ 393e9f207f0SJiri Benc print_devstats_##name); \ 394e9f207f0SJiri Benc } \ 395e9f207f0SJiri Benc \ 396e9f207f0SJiri Benc static const struct file_operations stats_ ##name## _ops = { \ 397e9f207f0SJiri Benc .read = stats_ ##name## _read, \ 398234e3405SStephen Boyd .open = simple_open, \ 3992b18ab36SArnd Bergmann .llseek = generic_file_llseek, \ 400e9f207f0SJiri Benc }; 401e9f207f0SJiri Benc 402f1160434SJohannes Berg #define DEBUGFS_STATS_ADD(name) \ 403f1160434SJohannes Berg debugfs_create_u32(#name, 0400, statsd, &local->name); 4042826bcd8SFelix Fietkau #define DEBUGFS_DEVSTATS_ADD(name) \ 4057bcfaf2fSJohannes Berg debugfs_create_file(#name, 0400, statsd, local, &stats_ ##name## _ops); 406e9f207f0SJiri Benc 407e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11ACKFailureCount); 408e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11RTSFailureCount); 409e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11FCSErrorCount); 410e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11RTSSuccessCount); 411e9f207f0SJiri Benc 412e9f207f0SJiri Benc void debugfs_hw_add(struct ieee80211_local *local) 413e9f207f0SJiri Benc { 414e9f207f0SJiri Benc struct dentry *phyd = local->hw.wiphy->debugfsdir; 415e9f207f0SJiri Benc struct dentry *statsd; 416e9f207f0SJiri Benc 417e9f207f0SJiri Benc if (!phyd) 418e9f207f0SJiri Benc return; 419e9f207f0SJiri Benc 420e9f207f0SJiri Benc local->debugfs.keys = debugfs_create_dir("keys", phyd); 421e9f207f0SJiri Benc 422e9f207f0SJiri Benc DEBUGFS_ADD(total_ps_buffered); 423e9f207f0SJiri Benc DEBUGFS_ADD(wep_iv); 424db2e6bd4SJohannes Berg DEBUGFS_ADD(queues); 4252ad4814fSJohannes Berg #ifdef CONFIG_PM 426827b1fb4SJohannes Berg DEBUGFS_ADD_MODE(reset, 0200); 4272ad4814fSJohannes Berg #endif 428279daf64SBen Greear DEBUGFS_ADD(hwflags); 42983bdf2a1SBen Greear DEBUGFS_ADD(user_power); 43083bdf2a1SBen Greear DEBUGFS_ADD(power); 431*9399b86cSMichal Kazior DEBUGFS_ADD_MODE(aqm, 0600); 432e9f207f0SJiri Benc 433e9f207f0SJiri Benc statsd = debugfs_create_dir("statistics", phyd); 434e9f207f0SJiri Benc 435e9f207f0SJiri Benc /* if the dir failed, don't put all the other things into the root! */ 436e9f207f0SJiri Benc if (!statsd) 437e9f207f0SJiri Benc return; 438e9f207f0SJiri Benc 439c206ca67SJohannes Berg #ifdef CONFIG_MAC80211_DEBUG_COUNTERS 440f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11TransmittedFragmentCount); 441f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11MulticastTransmittedFrameCount); 442f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11FailedCount); 443f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11RetryCount); 444f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11MultipleRetryCount); 445f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11FrameDuplicateCount); 446f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11ReceivedFragmentCount); 447f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11MulticastReceivedFrameCount); 448f1160434SJohannes Berg DEBUGFS_STATS_ADD(dot11TransmittedFrameCount); 449f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_drop); 450f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_queued); 451f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_drop_wep); 452f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_drop_not_assoc); 453f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_handlers_drop_unauth_port); 454f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_drop); 455f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_queued); 456f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc); 457f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_drop_defrag); 458f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_expand_skb_head); 459f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned); 460f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_expand_skb_head_defrag); 461f1160434SJohannes Berg DEBUGFS_STATS_ADD(rx_handlers_fragments); 462f1160434SJohannes Berg DEBUGFS_STATS_ADD(tx_status_drop); 463e9f207f0SJiri Benc #endif 4642826bcd8SFelix Fietkau DEBUGFS_DEVSTATS_ADD(dot11ACKFailureCount); 4652826bcd8SFelix Fietkau DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount); 4662826bcd8SFelix Fietkau DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount); 4672826bcd8SFelix Fietkau DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount); 468e9f207f0SJiri Benc } 469