1362 lines · 41.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright 2003-2005	Devicescape Software, Inc. * Copyright (c) 2006	Jiri Benc <jbenc@suse.cz> * Copyright 2007	Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014  Intel Mobile Communications GmbH * Copyright(c) 2016 Intel Deutschland GmbH * Copyright (C) 2018 - 2023 Intel Corporation */ #include <linux/debugfs.h>#include <linux/ieee80211.h>#include "ieee80211_i.h"#include "debugfs.h"#include "debugfs_sta.h"#include "sta_info.h"#include "driver-ops.h" /* sta attributes */ #define STA_READ(name, field, format_string)				\static ssize_t sta_ ##name## _read(struct file *file,			\				   char __user *userbuf,		\				   size_t count, loff_t *ppos)		\{									\	struct sta_info *sta = file->private_data;			\	return mac80211_format_buffer(userbuf, count, ppos, 		\				      format_string, sta->field);	\}#define STA_READ_D(name, field) STA_READ(name, field, "%d\n") #define STA_OPS(name)							\static const struct debugfs_short_fops sta_ ##name## _ops = {		\	.read = sta_##name##_read,					\	.llseek = generic_file_llseek,					\} #define STA_OPS_RW(name)						\static const struct debugfs_short_fops sta_ ##name## _ops = {		\	.read = sta_##name##_read,					\	.write = sta_##name##_write,					\	.llseek = generic_file_llseek,					\} #define STA_FILE(name, field, format)					\		STA_READ_##format(name, field)				\		STA_OPS(name) STA_FILE(aid, sta.aid, D); static const char * const sta_flag_names[] = {#define FLAG(F) [WLAN_STA_##F] = #F	FLAG(AUTH),	FLAG(ASSOC),	FLAG(PS_STA),	FLAG(AUTHORIZED),	FLAG(SHORT_PREAMBLE),	FLAG(WDS),	FLAG(CLEAR_PS_FILT),	FLAG(MFP),	FLAG(BLOCK_BA),	FLAG(PS_DRIVER),	FLAG(PSPOLL),	FLAG(TDLS_PEER),	FLAG(TDLS_PEER_AUTH),	FLAG(TDLS_INITIATOR),	FLAG(TDLS_CHAN_SWITCH),	FLAG(TDLS_OFF_CHANNEL),	FLAG(TDLS_WIDER_BW),	FLAG(UAPSD),	FLAG(SP),	FLAG(4ADDR_EVENT),	FLAG(INSERTED),	FLAG(RATE_CONTROL),	FLAG(TOFFSET_KNOWN),	FLAG(MPSP_OWNER),	FLAG(MPSP_RECIPIENT),	FLAG(PS_DELIVER),	FLAG(USES_ENCRYPTION),	FLAG(DECAP_OFFLOAD),#undef FLAG}; static ssize_t sta_flags_read(struct file *file, char __user *userbuf,			      size_t count, loff_t *ppos){	char buf[16 * NUM_WLAN_STA_FLAGS], *pos = buf;	char *end = buf + sizeof(buf) - 1;	struct sta_info *sta = file->private_data;	unsigned int flg; 	BUILD_BUG_ON(ARRAY_SIZE(sta_flag_names) != NUM_WLAN_STA_FLAGS); 	for (flg = 0; flg < NUM_WLAN_STA_FLAGS; flg++) {		if (test_sta_flag(sta, flg))			pos += scnprintf(pos, end - pos, "%s\n",					 sta_flag_names[flg]);	} 	return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));}STA_OPS(flags); static ssize_t sta_num_ps_buf_frames_read(struct file *file,					  char __user *userbuf,					  size_t count, loff_t *ppos){	struct sta_info *sta = file->private_data;	char buf[17*IEEE80211_NUM_ACS], *p = buf;	int ac; 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)		p += scnprintf(p, sizeof(buf)+buf-p, "AC%d: %d\n", ac,			       skb_queue_len(&sta->ps_tx_buf[ac]) +			       skb_queue_len(&sta->tx_filtered[ac]));	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);}STA_OPS(num_ps_buf_frames); static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf,				      size_t count, loff_t *ppos){	char buf[15*IEEE80211_NUM_TIDS], *p = buf;	int i;	struct sta_info *sta = file->private_data;	for (i = 0; i < IEEE80211_NUM_TIDS; i++)		p += scnprintf(p, sizeof(buf)+buf-p, "%x ",			       le16_to_cpu(sta->last_seq_ctrl[i]));	p += scnprintf(p, sizeof(buf)+buf-p, "\n");	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);}STA_OPS(last_seq_ctrl); #define AQM_TXQ_ENTRY_LEN 130 static ssize_t sta_aqm_read(struct file *file, char __user *userbuf,			size_t count, loff_t *ppos){	struct sta_info *sta = file->private_data;	struct ieee80211_local *local = sta->local;	size_t bufsz = AQM_TXQ_ENTRY_LEN * (IEEE80211_NUM_TIDS + 2);	char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;	struct txq_info *txqi;	ssize_t rv;	int i; 	if (!buf)		return -ENOMEM; 	spin_lock_bh(&local->fq.lock); 	p += scnprintf(p,		       bufsz + buf - p,		       "tid ac backlog-bytes backlog-packets new-flows drops marks overlimit collisions tx-bytes tx-packets flags\n"); 	for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {		if (!sta->sta.txq[i])			continue;		txqi = to_txq_info(sta->sta.txq[i]);		p += scnprintf(p, bufsz + buf - p,			       "%d %d %u %u %u %u %u %u %u %u %u 0x%lx(%s%s%s%s)\n",			       txqi->txq.tid,			       txqi->txq.ac,			       txqi->tin.backlog_bytes,			       txqi->tin.backlog_packets,			       txqi->tin.flows,			       txqi->cstats.drop_count,			       txqi->cstats.ecn_mark,			       txqi->tin.overlimit,			       txqi->tin.collisions,			       txqi->tin.tx_bytes,			       txqi->tin.tx_packets,			       txqi->flags,			       test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ? "STOP" : "RUN",			       test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags) ? " AMPDU" : "",			       test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags) ? " NO-AMSDU" : "",			       test_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ? " DIRTY" : "");	} 	spin_unlock_bh(&local->fq.lock); 	rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);	kfree(buf);	return rv;}STA_OPS(aqm); static ssize_t sta_airtime_read(struct file *file, char __user *userbuf,				size_t count, loff_t *ppos){	struct sta_info *sta = file->private_data;	struct ieee80211_local *local = sta->sdata->local;	size_t bufsz = 400;	char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;	u64 rx_airtime = 0, tx_airtime = 0;	s32 deficit[IEEE80211_NUM_ACS];	ssize_t rv;	int ac; 	if (!buf)		return -ENOMEM; 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {		spin_lock_bh(&local->active_txq_lock[ac]);		rx_airtime += sta->airtime[ac].rx_airtime;		tx_airtime += sta->airtime[ac].tx_airtime;		deficit[ac] = sta->airtime[ac].deficit;		spin_unlock_bh(&local->active_txq_lock[ac]);	} 	p += scnprintf(p, bufsz + buf - p,		"RX: %llu us\nTX: %llu us\nWeight: %u\n"		"Deficit: VO: %d us VI: %d us BE: %d us BK: %d us\n",		rx_airtime, tx_airtime, sta->airtime_weight,		deficit[0], deficit[1], deficit[2], deficit[3]); 	rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);	kfree(buf);	return rv;} static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf,				 size_t count, loff_t *ppos){	struct sta_info *sta = file->private_data;	struct ieee80211_local *local = sta->sdata->local;	int ac; 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {		spin_lock_bh(&local->active_txq_lock[ac]);		sta->airtime[ac].rx_airtime = 0;		sta->airtime[ac].tx_airtime = 0;		sta->airtime[ac].deficit = sta->airtime_weight;		spin_unlock_bh(&local->active_txq_lock[ac]);	} 	return count;}STA_OPS_RW(airtime); static ssize_t sta_aql_read(struct file *file, char __user *userbuf,				size_t count, loff_t *ppos){	struct sta_info *sta = file->private_data;	struct ieee80211_local *local = sta->sdata->local;	size_t bufsz = 400;	char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;	u32 q_depth[IEEE80211_NUM_ACS];	u32 q_limit_l[IEEE80211_NUM_ACS], q_limit_h[IEEE80211_NUM_ACS];	ssize_t rv;	int ac; 	if (!buf)		return -ENOMEM; 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {		spin_lock_bh(&local->active_txq_lock[ac]);		q_limit_l[ac] = sta->airtime[ac].aql_limit_low;		q_limit_h[ac] = sta->airtime[ac].aql_limit_high;		spin_unlock_bh(&local->active_txq_lock[ac]);		q_depth[ac] = atomic_read(&sta->airtime[ac].aql_tx_pending);	} 	p += scnprintf(p, bufsz + buf - p,		"Q depth: VO: %u us VI: %u us BE: %u us BK: %u us\n"		"Q limit[low/high]: VO: %u/%u VI: %u/%u BE: %u/%u BK: %u/%u\n",		q_depth[0], q_depth[1], q_depth[2], q_depth[3],		q_limit_l[0], q_limit_h[0], q_limit_l[1], q_limit_h[1],		q_limit_l[2], q_limit_h[2], q_limit_l[3], q_limit_h[3]); 	rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);	kfree(buf);	return rv;} static ssize_t sta_aql_write(struct file *file, const char __user *userbuf,				 size_t count, loff_t *ppos){	struct sta_info *sta = file->private_data;	u32 ac, q_limit_l, q_limit_h;	char _buf[100] = {}, *buf = _buf; 	if (count > sizeof(_buf))		return -EINVAL; 	if (copy_from_user(buf, userbuf, count))		return -EFAULT; 	buf[sizeof(_buf) - 1] = '\0';	if (sscanf(buf, "limit %u %u %u", &ac, &q_limit_l, &q_limit_h)	    != 3)		return -EINVAL; 	if (ac >= IEEE80211_NUM_ACS)		return -EINVAL; 	sta->airtime[ac].aql_limit_low = q_limit_l;	sta->airtime[ac].aql_limit_high = q_limit_h; 	return count;}STA_OPS_RW(aql);  static ssize_t sta_agg_status_do_read(struct wiphy *wiphy, struct file *file,				      char *buf, size_t bufsz, void *data){	struct sta_info *sta = data;	char *p = buf;	int i;	struct tid_ampdu_rx *tid_rx;	struct tid_ampdu_tx *tid_tx; 	p += scnprintf(p, bufsz + buf - p, "next dialog_token: %#02x\n",			sta->ampdu_mlme.dialog_token_allocator + 1);	p += scnprintf(p, bufsz + buf - p,		       "TID\t\tRX\tDTKN\tSSN\t\tTX\tDTKN\tpending\n"); 	for (i = 0; i < IEEE80211_NUM_TIDS; i++) {		bool tid_rx_valid; 		tid_rx = wiphy_dereference(wiphy, sta->ampdu_mlme.tid_rx[i]);		tid_tx = wiphy_dereference(wiphy, sta->ampdu_mlme.tid_tx[i]);		tid_rx_valid = test_bit(i, sta->ampdu_mlme.agg_session_valid); 		p += scnprintf(p, bufsz + buf - p, "%02d", i);		p += scnprintf(p, bufsz + buf - p, "\t\t%x",			       tid_rx_valid);		p += scnprintf(p, bufsz + buf - p, "\t%#.2x",			       tid_rx_valid ?					sta->ampdu_mlme.tid_rx_token[i] : 0);		p += scnprintf(p, bufsz + buf - p, "\t%#.3x",				tid_rx ? tid_rx->ssn : 0); 		p += scnprintf(p, bufsz + buf - p, "\t\t%x", !!tid_tx);		p += scnprintf(p, bufsz + buf - p, "\t%#.2x",				tid_tx ? tid_tx->dialog_token : 0);		p += scnprintf(p, bufsz + buf - p, "\t%03d",				tid_tx ? skb_queue_len(&tid_tx->pending) : 0);		p += scnprintf(p, bufsz + buf - p, "\n");	} 	return p - buf;} static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,				   size_t count, loff_t *ppos){	struct sta_info *sta = file->private_data;	struct wiphy *wiphy = sta->local->hw.wiphy;	size_t bufsz = 71 + IEEE80211_NUM_TIDS * 40;	char *buf = kmalloc(bufsz, GFP_KERNEL);	ssize_t ret; 	if (!buf)		return -ENOMEM; 	ret = wiphy_locked_debugfs_read(wiphy, file, buf, bufsz,					userbuf, count, ppos,					sta_agg_status_do_read, sta);	kfree(buf); 	return ret;} static ssize_t sta_agg_status_do_write(struct wiphy *wiphy, struct file *file,				       char *buf, size_t count, void *data){	struct sta_info *sta = data;	bool start, tx;	unsigned long tid;	char *pos = buf;	int ret, timeout = 5000; 	buf = strsep(&pos, " ");	if (!buf)		return -EINVAL; 	if (!strcmp(buf, "tx"))		tx = true;	else if (!strcmp(buf, "rx"))		tx = false;	else		return -EINVAL; 	buf = strsep(&pos, " ");	if (!buf)		return -EINVAL;	if (!strcmp(buf, "start")) {		start = true;		if (!tx)			return -EINVAL;	} else if (!strcmp(buf, "stop")) {		start = false;	} else {		return -EINVAL;	} 	buf = strsep(&pos, " ");	if (!buf)		return -EINVAL;	if (sscanf(buf, "timeout=%d", &timeout) == 1) {		buf = strsep(&pos, " ");		if (!buf || !tx || !start)			return -EINVAL;	} 	ret = kstrtoul(buf, 0, &tid);	if (ret || tid >= IEEE80211_NUM_TIDS)		return -EINVAL; 	if (tx) {		if (start)			ret = ieee80211_start_tx_ba_session(&sta->sta, tid,							    timeout);		else			ret = ieee80211_stop_tx_ba_session(&sta->sta, tid);	} else {		__ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,					       3, true);		ret = 0;	} 	return ret ?: count;} static ssize_t sta_agg_status_write(struct file *file,				    const char __user *userbuf,				    size_t count, loff_t *ppos){	struct sta_info *sta = file->private_data;	struct wiphy *wiphy = sta->local->hw.wiphy;	char _buf[26]; 	return wiphy_locked_debugfs_write(wiphy, file, _buf, sizeof(_buf),					  userbuf, count,					  sta_agg_status_do_write, sta);}STA_OPS_RW(agg_status); /* link sta attributes */#define LINK_STA_OPS(name)						\static const struct debugfs_short_fops link_sta_ ##name## _ops = {		\	.read = link_sta_##name##_read,					\	.llseek = generic_file_llseek,					\} static ssize_t link_sta_addr_read(struct file *file, char __user *userbuf,				  size_t count, loff_t *ppos){	struct link_sta_info *link_sta = file->private_data;	u8 mac[MAC_ADDR_STR_LEN + 2]; 	snprintf(mac, sizeof(mac), "%pM\n", link_sta->pub->addr); 	return simple_read_from_buffer(userbuf, count, ppos, mac,				       MAC_ADDR_STR_LEN + 1);} LINK_STA_OPS(addr); static ssize_t link_sta_ht_capa_read(struct file *file, char __user *userbuf,				     size_t count, loff_t *ppos){#define PRINT_HT_CAP(_cond, _str) \	do { \	if (_cond) \			p += scnprintf(p, bufsz + buf - p, "\t" _str "\n"); \	} while (0)	char *buf, *p;	int i;	ssize_t bufsz = 512;	struct link_sta_info *link_sta = file->private_data;	struct ieee80211_sta_ht_cap *htc = &link_sta->pub->ht_cap;	ssize_t ret; 	buf = kzalloc(bufsz, GFP_KERNEL);	if (!buf)		return -ENOMEM;	p = buf; 	p += scnprintf(p, bufsz + buf - p, "ht %ssupported\n",			htc->ht_supported ? "" : "not ");	if (htc->ht_supported) {		p += scnprintf(p, bufsz + buf - p, "cap: %#.4x\n", htc->cap); 		PRINT_HT_CAP((htc->cap & BIT(0)), "RX LDPC");		PRINT_HT_CAP((htc->cap & BIT(1)), "HT20/HT40");		PRINT_HT_CAP(!(htc->cap & BIT(1)), "HT20"); 		PRINT_HT_CAP(((htc->cap >> 2) & 0x3) == 0, "Static SM Power Save");		PRINT_HT_CAP(((htc->cap >> 2) & 0x3) == 1, "Dynamic SM Power Save");		PRINT_HT_CAP(((htc->cap >> 2) & 0x3) == 3, "SM Power Save disabled"); 		PRINT_HT_CAP((htc->cap & BIT(4)), "RX Greenfield");		PRINT_HT_CAP((htc->cap & BIT(5)), "RX HT20 SGI");		PRINT_HT_CAP((htc->cap & BIT(6)), "RX HT40 SGI");		PRINT_HT_CAP((htc->cap & BIT(7)), "TX STBC"); 		PRINT_HT_CAP(((htc->cap >> 8) & 0x3) == 0, "No RX STBC");

Lines 1–500 of 1362.