162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2003-2005	Devicescape Software, Inc.
462306a36Sopenharmony_ci * Copyright (c) 2006	Jiri Benc <jbenc@suse.cz>
562306a36Sopenharmony_ci * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
662306a36Sopenharmony_ci * Copyright 2013-2014  Intel Mobile Communications GmbH
762306a36Sopenharmony_ci * Copyright(c) 2016 Intel Deutschland GmbH
862306a36Sopenharmony_ci * Copyright (C) 2018 - 2022 Intel Corporation
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/debugfs.h>
1262306a36Sopenharmony_ci#include <linux/ieee80211.h>
1362306a36Sopenharmony_ci#include "ieee80211_i.h"
1462306a36Sopenharmony_ci#include "debugfs.h"
1562306a36Sopenharmony_ci#include "debugfs_sta.h"
1662306a36Sopenharmony_ci#include "sta_info.h"
1762306a36Sopenharmony_ci#include "driver-ops.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* sta attributtes */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define STA_READ(name, field, format_string)				\
2262306a36Sopenharmony_cistatic ssize_t sta_ ##name## _read(struct file *file,			\
2362306a36Sopenharmony_ci				   char __user *userbuf,		\
2462306a36Sopenharmony_ci				   size_t count, loff_t *ppos)		\
2562306a36Sopenharmony_ci{									\
2662306a36Sopenharmony_ci	struct sta_info *sta = file->private_data;			\
2762306a36Sopenharmony_ci	return mac80211_format_buffer(userbuf, count, ppos, 		\
2862306a36Sopenharmony_ci				      format_string, sta->field);	\
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci#define STA_READ_D(name, field) STA_READ(name, field, "%d\n")
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define STA_OPS(name)							\
3362306a36Sopenharmony_cistatic const struct file_operations sta_ ##name## _ops = {		\
3462306a36Sopenharmony_ci	.read = sta_##name##_read,					\
3562306a36Sopenharmony_ci	.open = simple_open,						\
3662306a36Sopenharmony_ci	.llseek = generic_file_llseek,					\
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define STA_OPS_RW(name)						\
4062306a36Sopenharmony_cistatic const struct file_operations sta_ ##name## _ops = {		\
4162306a36Sopenharmony_ci	.read = sta_##name##_read,					\
4262306a36Sopenharmony_ci	.write = sta_##name##_write,					\
4362306a36Sopenharmony_ci	.open = simple_open,						\
4462306a36Sopenharmony_ci	.llseek = generic_file_llseek,					\
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define STA_FILE(name, field, format)					\
4862306a36Sopenharmony_ci		STA_READ_##format(name, field)				\
4962306a36Sopenharmony_ci		STA_OPS(name)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ciSTA_FILE(aid, sta.aid, D);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic const char * const sta_flag_names[] = {
5462306a36Sopenharmony_ci#define FLAG(F) [WLAN_STA_##F] = #F
5562306a36Sopenharmony_ci	FLAG(AUTH),
5662306a36Sopenharmony_ci	FLAG(ASSOC),
5762306a36Sopenharmony_ci	FLAG(PS_STA),
5862306a36Sopenharmony_ci	FLAG(AUTHORIZED),
5962306a36Sopenharmony_ci	FLAG(SHORT_PREAMBLE),
6062306a36Sopenharmony_ci	FLAG(WDS),
6162306a36Sopenharmony_ci	FLAG(CLEAR_PS_FILT),
6262306a36Sopenharmony_ci	FLAG(MFP),
6362306a36Sopenharmony_ci	FLAG(BLOCK_BA),
6462306a36Sopenharmony_ci	FLAG(PS_DRIVER),
6562306a36Sopenharmony_ci	FLAG(PSPOLL),
6662306a36Sopenharmony_ci	FLAG(TDLS_PEER),
6762306a36Sopenharmony_ci	FLAG(TDLS_PEER_AUTH),
6862306a36Sopenharmony_ci	FLAG(TDLS_INITIATOR),
6962306a36Sopenharmony_ci	FLAG(TDLS_CHAN_SWITCH),
7062306a36Sopenharmony_ci	FLAG(TDLS_OFF_CHANNEL),
7162306a36Sopenharmony_ci	FLAG(TDLS_WIDER_BW),
7262306a36Sopenharmony_ci	FLAG(UAPSD),
7362306a36Sopenharmony_ci	FLAG(SP),
7462306a36Sopenharmony_ci	FLAG(4ADDR_EVENT),
7562306a36Sopenharmony_ci	FLAG(INSERTED),
7662306a36Sopenharmony_ci	FLAG(RATE_CONTROL),
7762306a36Sopenharmony_ci	FLAG(TOFFSET_KNOWN),
7862306a36Sopenharmony_ci	FLAG(MPSP_OWNER),
7962306a36Sopenharmony_ci	FLAG(MPSP_RECIPIENT),
8062306a36Sopenharmony_ci	FLAG(PS_DELIVER),
8162306a36Sopenharmony_ci	FLAG(USES_ENCRYPTION),
8262306a36Sopenharmony_ci	FLAG(DECAP_OFFLOAD),
8362306a36Sopenharmony_ci#undef FLAG
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic ssize_t sta_flags_read(struct file *file, char __user *userbuf,
8762306a36Sopenharmony_ci			      size_t count, loff_t *ppos)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	char buf[16 * NUM_WLAN_STA_FLAGS], *pos = buf;
9062306a36Sopenharmony_ci	char *end = buf + sizeof(buf) - 1;
9162306a36Sopenharmony_ci	struct sta_info *sta = file->private_data;
9262306a36Sopenharmony_ci	unsigned int flg;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(sta_flag_names) != NUM_WLAN_STA_FLAGS);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	for (flg = 0; flg < NUM_WLAN_STA_FLAGS; flg++) {
9762306a36Sopenharmony_ci		if (test_sta_flag(sta, flg))
9862306a36Sopenharmony_ci			pos += scnprintf(pos, end - pos, "%s\n",
9962306a36Sopenharmony_ci					 sta_flag_names[flg]);
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ciSTA_OPS(flags);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic ssize_t sta_num_ps_buf_frames_read(struct file *file,
10762306a36Sopenharmony_ci					  char __user *userbuf,
10862306a36Sopenharmony_ci					  size_t count, loff_t *ppos)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct sta_info *sta = file->private_data;
11162306a36Sopenharmony_ci	char buf[17*IEEE80211_NUM_ACS], *p = buf;
11262306a36Sopenharmony_ci	int ac;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
11562306a36Sopenharmony_ci		p += scnprintf(p, sizeof(buf)+buf-p, "AC%d: %d\n", ac,
11662306a36Sopenharmony_ci			       skb_queue_len(&sta->ps_tx_buf[ac]) +
11762306a36Sopenharmony_ci			       skb_queue_len(&sta->tx_filtered[ac]));
11862306a36Sopenharmony_ci	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ciSTA_OPS(num_ps_buf_frames);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf,
12362306a36Sopenharmony_ci				      size_t count, loff_t *ppos)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	char buf[15*IEEE80211_NUM_TIDS], *p = buf;
12662306a36Sopenharmony_ci	int i;
12762306a36Sopenharmony_ci	struct sta_info *sta = file->private_data;
12862306a36Sopenharmony_ci	for (i = 0; i < IEEE80211_NUM_TIDS; i++)
12962306a36Sopenharmony_ci		p += scnprintf(p, sizeof(buf)+buf-p, "%x ",
13062306a36Sopenharmony_ci			       le16_to_cpu(sta->last_seq_ctrl[i]));
13162306a36Sopenharmony_ci	p += scnprintf(p, sizeof(buf)+buf-p, "\n");
13262306a36Sopenharmony_ci	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ciSTA_OPS(last_seq_ctrl);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci#define AQM_TXQ_ENTRY_LEN 130
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic ssize_t sta_aqm_read(struct file *file, char __user *userbuf,
13962306a36Sopenharmony_ci			size_t count, loff_t *ppos)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct sta_info *sta = file->private_data;
14262306a36Sopenharmony_ci	struct ieee80211_local *local = sta->local;
14362306a36Sopenharmony_ci	size_t bufsz = AQM_TXQ_ENTRY_LEN * (IEEE80211_NUM_TIDS + 2);
14462306a36Sopenharmony_ci	char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
14562306a36Sopenharmony_ci	struct txq_info *txqi;
14662306a36Sopenharmony_ci	ssize_t rv;
14762306a36Sopenharmony_ci	int i;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (!buf)
15062306a36Sopenharmony_ci		return -ENOMEM;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	spin_lock_bh(&local->fq.lock);
15362306a36Sopenharmony_ci	rcu_read_lock();
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	p += scnprintf(p,
15662306a36Sopenharmony_ci		       bufsz + buf - p,
15762306a36Sopenharmony_ci		       "target %uus interval %uus ecn %s\n",
15862306a36Sopenharmony_ci		       codel_time_to_us(sta->cparams.target),
15962306a36Sopenharmony_ci		       codel_time_to_us(sta->cparams.interval),
16062306a36Sopenharmony_ci		       sta->cparams.ecn ? "yes" : "no");
16162306a36Sopenharmony_ci	p += scnprintf(p,
16262306a36Sopenharmony_ci		       bufsz + buf - p,
16362306a36Sopenharmony_ci		       "tid ac backlog-bytes backlog-packets new-flows drops marks overlimit collisions tx-bytes tx-packets flags\n");
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
16662306a36Sopenharmony_ci		if (!sta->sta.txq[i])
16762306a36Sopenharmony_ci			continue;
16862306a36Sopenharmony_ci		txqi = to_txq_info(sta->sta.txq[i]);
16962306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p,
17062306a36Sopenharmony_ci			       "%d %d %u %u %u %u %u %u %u %u %u 0x%lx(%s%s%s%s)\n",
17162306a36Sopenharmony_ci			       txqi->txq.tid,
17262306a36Sopenharmony_ci			       txqi->txq.ac,
17362306a36Sopenharmony_ci			       txqi->tin.backlog_bytes,
17462306a36Sopenharmony_ci			       txqi->tin.backlog_packets,
17562306a36Sopenharmony_ci			       txqi->tin.flows,
17662306a36Sopenharmony_ci			       txqi->cstats.drop_count,
17762306a36Sopenharmony_ci			       txqi->cstats.ecn_mark,
17862306a36Sopenharmony_ci			       txqi->tin.overlimit,
17962306a36Sopenharmony_ci			       txqi->tin.collisions,
18062306a36Sopenharmony_ci			       txqi->tin.tx_bytes,
18162306a36Sopenharmony_ci			       txqi->tin.tx_packets,
18262306a36Sopenharmony_ci			       txqi->flags,
18362306a36Sopenharmony_ci			       test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ? "STOP" : "RUN",
18462306a36Sopenharmony_ci			       test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags) ? " AMPDU" : "",
18562306a36Sopenharmony_ci			       test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags) ? " NO-AMSDU" : "",
18662306a36Sopenharmony_ci			       test_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ? " DIRTY" : "");
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	rcu_read_unlock();
19062306a36Sopenharmony_ci	spin_unlock_bh(&local->fq.lock);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
19362306a36Sopenharmony_ci	kfree(buf);
19462306a36Sopenharmony_ci	return rv;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ciSTA_OPS(aqm);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic ssize_t sta_airtime_read(struct file *file, char __user *userbuf,
19962306a36Sopenharmony_ci				size_t count, loff_t *ppos)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct sta_info *sta = file->private_data;
20262306a36Sopenharmony_ci	struct ieee80211_local *local = sta->sdata->local;
20362306a36Sopenharmony_ci	size_t bufsz = 400;
20462306a36Sopenharmony_ci	char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
20562306a36Sopenharmony_ci	u64 rx_airtime = 0, tx_airtime = 0;
20662306a36Sopenharmony_ci	s32 deficit[IEEE80211_NUM_ACS];
20762306a36Sopenharmony_ci	ssize_t rv;
20862306a36Sopenharmony_ci	int ac;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (!buf)
21162306a36Sopenharmony_ci		return -ENOMEM;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
21462306a36Sopenharmony_ci		spin_lock_bh(&local->active_txq_lock[ac]);
21562306a36Sopenharmony_ci		rx_airtime += sta->airtime[ac].rx_airtime;
21662306a36Sopenharmony_ci		tx_airtime += sta->airtime[ac].tx_airtime;
21762306a36Sopenharmony_ci		deficit[ac] = sta->airtime[ac].deficit;
21862306a36Sopenharmony_ci		spin_unlock_bh(&local->active_txq_lock[ac]);
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	p += scnprintf(p, bufsz + buf - p,
22262306a36Sopenharmony_ci		"RX: %llu us\nTX: %llu us\nWeight: %u\n"
22362306a36Sopenharmony_ci		"Deficit: VO: %d us VI: %d us BE: %d us BK: %d us\n",
22462306a36Sopenharmony_ci		rx_airtime, tx_airtime, sta->airtime_weight,
22562306a36Sopenharmony_ci		deficit[0], deficit[1], deficit[2], deficit[3]);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
22862306a36Sopenharmony_ci	kfree(buf);
22962306a36Sopenharmony_ci	return rv;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic ssize_t sta_airtime_write(struct file *file, const char __user *userbuf,
23362306a36Sopenharmony_ci				 size_t count, loff_t *ppos)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct sta_info *sta = file->private_data;
23662306a36Sopenharmony_ci	struct ieee80211_local *local = sta->sdata->local;
23762306a36Sopenharmony_ci	int ac;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
24062306a36Sopenharmony_ci		spin_lock_bh(&local->active_txq_lock[ac]);
24162306a36Sopenharmony_ci		sta->airtime[ac].rx_airtime = 0;
24262306a36Sopenharmony_ci		sta->airtime[ac].tx_airtime = 0;
24362306a36Sopenharmony_ci		sta->airtime[ac].deficit = sta->airtime_weight;
24462306a36Sopenharmony_ci		spin_unlock_bh(&local->active_txq_lock[ac]);
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return count;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ciSTA_OPS_RW(airtime);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic ssize_t sta_aql_read(struct file *file, char __user *userbuf,
25262306a36Sopenharmony_ci				size_t count, loff_t *ppos)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct sta_info *sta = file->private_data;
25562306a36Sopenharmony_ci	struct ieee80211_local *local = sta->sdata->local;
25662306a36Sopenharmony_ci	size_t bufsz = 400;
25762306a36Sopenharmony_ci	char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
25862306a36Sopenharmony_ci	u32 q_depth[IEEE80211_NUM_ACS];
25962306a36Sopenharmony_ci	u32 q_limit_l[IEEE80211_NUM_ACS], q_limit_h[IEEE80211_NUM_ACS];
26062306a36Sopenharmony_ci	ssize_t rv;
26162306a36Sopenharmony_ci	int ac;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (!buf)
26462306a36Sopenharmony_ci		return -ENOMEM;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
26762306a36Sopenharmony_ci		spin_lock_bh(&local->active_txq_lock[ac]);
26862306a36Sopenharmony_ci		q_limit_l[ac] = sta->airtime[ac].aql_limit_low;
26962306a36Sopenharmony_ci		q_limit_h[ac] = sta->airtime[ac].aql_limit_high;
27062306a36Sopenharmony_ci		spin_unlock_bh(&local->active_txq_lock[ac]);
27162306a36Sopenharmony_ci		q_depth[ac] = atomic_read(&sta->airtime[ac].aql_tx_pending);
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	p += scnprintf(p, bufsz + buf - p,
27562306a36Sopenharmony_ci		"Q depth: VO: %u us VI: %u us BE: %u us BK: %u us\n"
27662306a36Sopenharmony_ci		"Q limit[low/high]: VO: %u/%u VI: %u/%u BE: %u/%u BK: %u/%u\n",
27762306a36Sopenharmony_ci		q_depth[0], q_depth[1], q_depth[2], q_depth[3],
27862306a36Sopenharmony_ci		q_limit_l[0], q_limit_h[0], q_limit_l[1], q_limit_h[1],
27962306a36Sopenharmony_ci		q_limit_l[2], q_limit_h[2], q_limit_l[3], q_limit_h[3]);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
28262306a36Sopenharmony_ci	kfree(buf);
28362306a36Sopenharmony_ci	return rv;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic ssize_t sta_aql_write(struct file *file, const char __user *userbuf,
28762306a36Sopenharmony_ci				 size_t count, loff_t *ppos)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct sta_info *sta = file->private_data;
29062306a36Sopenharmony_ci	u32 ac, q_limit_l, q_limit_h;
29162306a36Sopenharmony_ci	char _buf[100] = {}, *buf = _buf;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (count > sizeof(_buf))
29462306a36Sopenharmony_ci		return -EINVAL;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (copy_from_user(buf, userbuf, count))
29762306a36Sopenharmony_ci		return -EFAULT;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	buf[sizeof(_buf) - 1] = '\0';
30062306a36Sopenharmony_ci	if (sscanf(buf, "limit %u %u %u", &ac, &q_limit_l, &q_limit_h)
30162306a36Sopenharmony_ci	    != 3)
30262306a36Sopenharmony_ci		return -EINVAL;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (ac >= IEEE80211_NUM_ACS)
30562306a36Sopenharmony_ci		return -EINVAL;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	sta->airtime[ac].aql_limit_low = q_limit_l;
30862306a36Sopenharmony_ci	sta->airtime[ac].aql_limit_high = q_limit_h;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	return count;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ciSTA_OPS_RW(aql);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
31662306a36Sopenharmony_ci					size_t count, loff_t *ppos)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	char *buf, *p;
31962306a36Sopenharmony_ci	ssize_t bufsz = 71 + IEEE80211_NUM_TIDS * 40;
32062306a36Sopenharmony_ci	int i;
32162306a36Sopenharmony_ci	struct sta_info *sta = file->private_data;
32262306a36Sopenharmony_ci	struct tid_ampdu_rx *tid_rx;
32362306a36Sopenharmony_ci	struct tid_ampdu_tx *tid_tx;
32462306a36Sopenharmony_ci	ssize_t ret;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	buf = kzalloc(bufsz, GFP_KERNEL);
32762306a36Sopenharmony_ci	if (!buf)
32862306a36Sopenharmony_ci		return -ENOMEM;
32962306a36Sopenharmony_ci	p = buf;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	rcu_read_lock();
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	p += scnprintf(p, bufsz + buf - p, "next dialog_token: %#02x\n",
33462306a36Sopenharmony_ci			sta->ampdu_mlme.dialog_token_allocator + 1);
33562306a36Sopenharmony_ci	p += scnprintf(p, bufsz + buf - p,
33662306a36Sopenharmony_ci		       "TID\t\tRX\tDTKN\tSSN\t\tTX\tDTKN\tpending\n");
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
33962306a36Sopenharmony_ci		bool tid_rx_valid;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[i]);
34262306a36Sopenharmony_ci		tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[i]);
34362306a36Sopenharmony_ci		tid_rx_valid = test_bit(i, sta->ampdu_mlme.agg_session_valid);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "%02d", i);
34662306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "\t\t%x",
34762306a36Sopenharmony_ci			       tid_rx_valid);
34862306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "\t%#.2x",
34962306a36Sopenharmony_ci			       tid_rx_valid ?
35062306a36Sopenharmony_ci					sta->ampdu_mlme.tid_rx_token[i] : 0);
35162306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "\t%#.3x",
35262306a36Sopenharmony_ci				tid_rx ? tid_rx->ssn : 0);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "\t\t%x", !!tid_tx);
35562306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "\t%#.2x",
35662306a36Sopenharmony_ci				tid_tx ? tid_tx->dialog_token : 0);
35762306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "\t%03d",
35862306a36Sopenharmony_ci				tid_tx ? skb_queue_len(&tid_tx->pending) : 0);
35962306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "\n");
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci	rcu_read_unlock();
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	ret = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
36462306a36Sopenharmony_ci	kfree(buf);
36562306a36Sopenharmony_ci	return ret;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic ssize_t sta_agg_status_write(struct file *file, const char __user *userbuf,
36962306a36Sopenharmony_ci				    size_t count, loff_t *ppos)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	char _buf[25] = {}, *buf = _buf;
37262306a36Sopenharmony_ci	struct sta_info *sta = file->private_data;
37362306a36Sopenharmony_ci	bool start, tx;
37462306a36Sopenharmony_ci	unsigned long tid;
37562306a36Sopenharmony_ci	char *pos;
37662306a36Sopenharmony_ci	int ret, timeout = 5000;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (count > sizeof(_buf))
37962306a36Sopenharmony_ci		return -EINVAL;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (copy_from_user(buf, userbuf, count))
38262306a36Sopenharmony_ci		return -EFAULT;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	buf[sizeof(_buf) - 1] = '\0';
38562306a36Sopenharmony_ci	pos = buf;
38662306a36Sopenharmony_ci	buf = strsep(&pos, " ");
38762306a36Sopenharmony_ci	if (!buf)
38862306a36Sopenharmony_ci		return -EINVAL;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (!strcmp(buf, "tx"))
39162306a36Sopenharmony_ci		tx = true;
39262306a36Sopenharmony_ci	else if (!strcmp(buf, "rx"))
39362306a36Sopenharmony_ci		tx = false;
39462306a36Sopenharmony_ci	else
39562306a36Sopenharmony_ci		return -EINVAL;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	buf = strsep(&pos, " ");
39862306a36Sopenharmony_ci	if (!buf)
39962306a36Sopenharmony_ci		return -EINVAL;
40062306a36Sopenharmony_ci	if (!strcmp(buf, "start")) {
40162306a36Sopenharmony_ci		start = true;
40262306a36Sopenharmony_ci		if (!tx)
40362306a36Sopenharmony_ci			return -EINVAL;
40462306a36Sopenharmony_ci	} else if (!strcmp(buf, "stop")) {
40562306a36Sopenharmony_ci		start = false;
40662306a36Sopenharmony_ci	} else {
40762306a36Sopenharmony_ci		return -EINVAL;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	buf = strsep(&pos, " ");
41162306a36Sopenharmony_ci	if (!buf)
41262306a36Sopenharmony_ci		return -EINVAL;
41362306a36Sopenharmony_ci	if (sscanf(buf, "timeout=%d", &timeout) == 1) {
41462306a36Sopenharmony_ci		buf = strsep(&pos, " ");
41562306a36Sopenharmony_ci		if (!buf || !tx || !start)
41662306a36Sopenharmony_ci			return -EINVAL;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	ret = kstrtoul(buf, 0, &tid);
42062306a36Sopenharmony_ci	if (ret || tid >= IEEE80211_NUM_TIDS)
42162306a36Sopenharmony_ci		return -EINVAL;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (tx) {
42462306a36Sopenharmony_ci		if (start)
42562306a36Sopenharmony_ci			ret = ieee80211_start_tx_ba_session(&sta->sta, tid,
42662306a36Sopenharmony_ci							    timeout);
42762306a36Sopenharmony_ci		else
42862306a36Sopenharmony_ci			ret = ieee80211_stop_tx_ba_session(&sta->sta, tid);
42962306a36Sopenharmony_ci	} else {
43062306a36Sopenharmony_ci		__ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
43162306a36Sopenharmony_ci					       3, true);
43262306a36Sopenharmony_ci		ret = 0;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	return ret ?: count;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ciSTA_OPS_RW(agg_status);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/* link sta attributes */
44062306a36Sopenharmony_ci#define LINK_STA_OPS(name)						\
44162306a36Sopenharmony_cistatic const struct file_operations link_sta_ ##name## _ops = {		\
44262306a36Sopenharmony_ci	.read = link_sta_##name##_read,					\
44362306a36Sopenharmony_ci	.open = simple_open,						\
44462306a36Sopenharmony_ci	.llseek = generic_file_llseek,					\
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic ssize_t link_sta_addr_read(struct file *file, char __user *userbuf,
44862306a36Sopenharmony_ci				  size_t count, loff_t *ppos)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct link_sta_info *link_sta = file->private_data;
45162306a36Sopenharmony_ci	u8 mac[3 * ETH_ALEN + 1];
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	snprintf(mac, sizeof(mac), "%pM\n", link_sta->pub->addr);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	return simple_read_from_buffer(userbuf, count, ppos, mac, 3 * ETH_ALEN);
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ciLINK_STA_OPS(addr);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic ssize_t link_sta_ht_capa_read(struct file *file, char __user *userbuf,
46162306a36Sopenharmony_ci				     size_t count, loff_t *ppos)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci#define PRINT_HT_CAP(_cond, _str) \
46462306a36Sopenharmony_ci	do { \
46562306a36Sopenharmony_ci	if (_cond) \
46662306a36Sopenharmony_ci			p += scnprintf(p, bufsz + buf - p, "\t" _str "\n"); \
46762306a36Sopenharmony_ci	} while (0)
46862306a36Sopenharmony_ci	char *buf, *p;
46962306a36Sopenharmony_ci	int i;
47062306a36Sopenharmony_ci	ssize_t bufsz = 512;
47162306a36Sopenharmony_ci	struct link_sta_info *link_sta = file->private_data;
47262306a36Sopenharmony_ci	struct ieee80211_sta_ht_cap *htc = &link_sta->pub->ht_cap;
47362306a36Sopenharmony_ci	ssize_t ret;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	buf = kzalloc(bufsz, GFP_KERNEL);
47662306a36Sopenharmony_ci	if (!buf)
47762306a36Sopenharmony_ci		return -ENOMEM;
47862306a36Sopenharmony_ci	p = buf;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	p += scnprintf(p, bufsz + buf - p, "ht %ssupported\n",
48162306a36Sopenharmony_ci			htc->ht_supported ? "" : "not ");
48262306a36Sopenharmony_ci	if (htc->ht_supported) {
48362306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "cap: %#.4x\n", htc->cap);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		PRINT_HT_CAP((htc->cap & BIT(0)), "RX LDPC");
48662306a36Sopenharmony_ci		PRINT_HT_CAP((htc->cap & BIT(1)), "HT20/HT40");
48762306a36Sopenharmony_ci		PRINT_HT_CAP(!(htc->cap & BIT(1)), "HT20");
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		PRINT_HT_CAP(((htc->cap >> 2) & 0x3) == 0, "Static SM Power Save");
49062306a36Sopenharmony_ci		PRINT_HT_CAP(((htc->cap >> 2) & 0x3) == 1, "Dynamic SM Power Save");
49162306a36Sopenharmony_ci		PRINT_HT_CAP(((htc->cap >> 2) & 0x3) == 3, "SM Power Save disabled");
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		PRINT_HT_CAP((htc->cap & BIT(4)), "RX Greenfield");
49462306a36Sopenharmony_ci		PRINT_HT_CAP((htc->cap & BIT(5)), "RX HT20 SGI");
49562306a36Sopenharmony_ci		PRINT_HT_CAP((htc->cap & BIT(6)), "RX HT40 SGI");
49662306a36Sopenharmony_ci		PRINT_HT_CAP((htc->cap & BIT(7)), "TX STBC");
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		PRINT_HT_CAP(((htc->cap >> 8) & 0x3) == 0, "No RX STBC");
49962306a36Sopenharmony_ci		PRINT_HT_CAP(((htc->cap >> 8) & 0x3) == 1, "RX STBC 1-stream");
50062306a36Sopenharmony_ci		PRINT_HT_CAP(((htc->cap >> 8) & 0x3) == 2, "RX STBC 2-streams");
50162306a36Sopenharmony_ci		PRINT_HT_CAP(((htc->cap >> 8) & 0x3) == 3, "RX STBC 3-streams");
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		PRINT_HT_CAP((htc->cap & BIT(10)), "HT Delayed Block Ack");
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci		PRINT_HT_CAP(!(htc->cap & BIT(11)), "Max AMSDU length: "
50662306a36Sopenharmony_ci			     "3839 bytes");
50762306a36Sopenharmony_ci		PRINT_HT_CAP((htc->cap & BIT(11)), "Max AMSDU length: "
50862306a36Sopenharmony_ci			     "7935 bytes");
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		/*
51162306a36Sopenharmony_ci		 * For beacons and probe response this would mean the BSS
51262306a36Sopenharmony_ci		 * does or does not allow the usage of DSSS/CCK HT40.
51362306a36Sopenharmony_ci		 * Otherwise it means the STA does or does not use
51462306a36Sopenharmony_ci		 * DSSS/CCK HT40.
51562306a36Sopenharmony_ci		 */
51662306a36Sopenharmony_ci		PRINT_HT_CAP((htc->cap & BIT(12)), "DSSS/CCK HT40");
51762306a36Sopenharmony_ci		PRINT_HT_CAP(!(htc->cap & BIT(12)), "No DSSS/CCK HT40");
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		/* BIT(13) is reserved */
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		PRINT_HT_CAP((htc->cap & BIT(14)), "40 MHz Intolerant");
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		PRINT_HT_CAP((htc->cap & BIT(15)), "L-SIG TXOP protection");
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "ampdu factor/density: %d/%d\n",
52662306a36Sopenharmony_ci				htc->ampdu_factor, htc->ampdu_density);
52762306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "MCS mask:");
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
53062306a36Sopenharmony_ci			p += scnprintf(p, bufsz + buf - p, " %.2x",
53162306a36Sopenharmony_ci					htc->mcs.rx_mask[i]);
53262306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "\n");
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci		/* If not set this is meaningless */
53562306a36Sopenharmony_ci		if (le16_to_cpu(htc->mcs.rx_highest)) {
53662306a36Sopenharmony_ci			p += scnprintf(p, bufsz + buf - p,
53762306a36Sopenharmony_ci				       "MCS rx highest: %d Mbps\n",
53862306a36Sopenharmony_ci				       le16_to_cpu(htc->mcs.rx_highest));
53962306a36Sopenharmony_ci		}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "MCS tx params: %x\n",
54262306a36Sopenharmony_ci				htc->mcs.tx_params);
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	ret = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
54662306a36Sopenharmony_ci	kfree(buf);
54762306a36Sopenharmony_ci	return ret;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ciLINK_STA_OPS(ht_capa);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic ssize_t link_sta_vht_capa_read(struct file *file, char __user *userbuf,
55262306a36Sopenharmony_ci				      size_t count, loff_t *ppos)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	char *buf, *p;
55562306a36Sopenharmony_ci	struct link_sta_info *link_sta = file->private_data;
55662306a36Sopenharmony_ci	struct ieee80211_sta_vht_cap *vhtc = &link_sta->pub->vht_cap;
55762306a36Sopenharmony_ci	ssize_t ret;
55862306a36Sopenharmony_ci	ssize_t bufsz = 512;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	buf = kzalloc(bufsz, GFP_KERNEL);
56162306a36Sopenharmony_ci	if (!buf)
56262306a36Sopenharmony_ci		return -ENOMEM;
56362306a36Sopenharmony_ci	p = buf;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	p += scnprintf(p, bufsz + buf - p, "VHT %ssupported\n",
56662306a36Sopenharmony_ci			vhtc->vht_supported ? "" : "not ");
56762306a36Sopenharmony_ci	if (vhtc->vht_supported) {
56862306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "cap: %#.8x\n",
56962306a36Sopenharmony_ci			       vhtc->cap);
57062306a36Sopenharmony_ci#define PFLAG(a, b)							\
57162306a36Sopenharmony_ci		do {							\
57262306a36Sopenharmony_ci			if (vhtc->cap & IEEE80211_VHT_CAP_ ## a)	\
57362306a36Sopenharmony_ci				p += scnprintf(p, bufsz + buf - p, \
57462306a36Sopenharmony_ci					       "\t\t%s\n", b);		\
57562306a36Sopenharmony_ci		} while (0)
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci		switch (vhtc->cap & 0x3) {
57862306a36Sopenharmony_ci		case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895:
57962306a36Sopenharmony_ci			p += scnprintf(p, bufsz + buf - p,
58062306a36Sopenharmony_ci				       "\t\tMAX-MPDU-3895\n");
58162306a36Sopenharmony_ci			break;
58262306a36Sopenharmony_ci		case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
58362306a36Sopenharmony_ci			p += scnprintf(p, bufsz + buf - p,
58462306a36Sopenharmony_ci				       "\t\tMAX-MPDU-7991\n");
58562306a36Sopenharmony_ci			break;
58662306a36Sopenharmony_ci		case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
58762306a36Sopenharmony_ci			p += scnprintf(p, bufsz + buf - p,
58862306a36Sopenharmony_ci				       "\t\tMAX-MPDU-11454\n");
58962306a36Sopenharmony_ci			break;
59062306a36Sopenharmony_ci		default:
59162306a36Sopenharmony_ci			p += scnprintf(p, bufsz + buf - p,
59262306a36Sopenharmony_ci				       "\t\tMAX-MPDU-UNKNOWN\n");
59362306a36Sopenharmony_ci		}
59462306a36Sopenharmony_ci		switch (vhtc->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
59562306a36Sopenharmony_ci		case 0:
59662306a36Sopenharmony_ci			p += scnprintf(p, bufsz + buf - p,
59762306a36Sopenharmony_ci				       "\t\t80Mhz\n");
59862306a36Sopenharmony_ci			break;
59962306a36Sopenharmony_ci		case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
60062306a36Sopenharmony_ci			p += scnprintf(p, bufsz + buf - p,
60162306a36Sopenharmony_ci				       "\t\t160Mhz\n");
60262306a36Sopenharmony_ci			break;
60362306a36Sopenharmony_ci		case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
60462306a36Sopenharmony_ci			p += scnprintf(p, bufsz + buf - p,
60562306a36Sopenharmony_ci				       "\t\t80+80Mhz\n");
60662306a36Sopenharmony_ci			break;
60762306a36Sopenharmony_ci		default:
60862306a36Sopenharmony_ci			p += scnprintf(p, bufsz + buf - p,
60962306a36Sopenharmony_ci				       "\t\tUNKNOWN-MHZ: 0x%x\n",
61062306a36Sopenharmony_ci				       (vhtc->cap >> 2) & 0x3);
61162306a36Sopenharmony_ci		}
61262306a36Sopenharmony_ci		PFLAG(RXLDPC, "RXLDPC");
61362306a36Sopenharmony_ci		PFLAG(SHORT_GI_80, "SHORT-GI-80");
61462306a36Sopenharmony_ci		PFLAG(SHORT_GI_160, "SHORT-GI-160");
61562306a36Sopenharmony_ci		PFLAG(TXSTBC, "TXSTBC");
61662306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p,
61762306a36Sopenharmony_ci			       "\t\tRXSTBC_%d\n", (vhtc->cap >> 8) & 0x7);
61862306a36Sopenharmony_ci		PFLAG(SU_BEAMFORMER_CAPABLE, "SU-BEAMFORMER-CAPABLE");
61962306a36Sopenharmony_ci		PFLAG(SU_BEAMFORMEE_CAPABLE, "SU-BEAMFORMEE-CAPABLE");
62062306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p,
62162306a36Sopenharmony_ci			"\t\tBEAMFORMEE-STS: 0x%x\n",
62262306a36Sopenharmony_ci			(vhtc->cap & IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK) >>
62362306a36Sopenharmony_ci			IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
62462306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p,
62562306a36Sopenharmony_ci			"\t\tSOUNDING-DIMENSIONS: 0x%x\n",
62662306a36Sopenharmony_ci			(vhtc->cap & IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK)
62762306a36Sopenharmony_ci			>> IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
62862306a36Sopenharmony_ci		PFLAG(MU_BEAMFORMER_CAPABLE, "MU-BEAMFORMER-CAPABLE");
62962306a36Sopenharmony_ci		PFLAG(MU_BEAMFORMEE_CAPABLE, "MU-BEAMFORMEE-CAPABLE");
63062306a36Sopenharmony_ci		PFLAG(VHT_TXOP_PS, "TXOP-PS");
63162306a36Sopenharmony_ci		PFLAG(HTC_VHT, "HTC-VHT");
63262306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p,
63362306a36Sopenharmony_ci			"\t\tMPDU-LENGTH-EXPONENT: 0x%x\n",
63462306a36Sopenharmony_ci			(vhtc->cap & IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >>
63562306a36Sopenharmony_ci			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT);
63662306a36Sopenharmony_ci		PFLAG(VHT_LINK_ADAPTATION_VHT_UNSOL_MFB,
63762306a36Sopenharmony_ci		      "LINK-ADAPTATION-VHT-UNSOL-MFB");
63862306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p,
63962306a36Sopenharmony_ci			"\t\tLINK-ADAPTATION-VHT-MRQ-MFB: 0x%x\n",
64062306a36Sopenharmony_ci			(vhtc->cap & IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB) >> 26);
64162306a36Sopenharmony_ci		PFLAG(RX_ANTENNA_PATTERN, "RX-ANTENNA-PATTERN");
64262306a36Sopenharmony_ci		PFLAG(TX_ANTENNA_PATTERN, "TX-ANTENNA-PATTERN");
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "RX MCS: %.4x\n",
64562306a36Sopenharmony_ci			       le16_to_cpu(vhtc->vht_mcs.rx_mcs_map));
64662306a36Sopenharmony_ci		if (vhtc->vht_mcs.rx_highest)
64762306a36Sopenharmony_ci			p += scnprintf(p, bufsz + buf - p,
64862306a36Sopenharmony_ci				       "MCS RX highest: %d Mbps\n",
64962306a36Sopenharmony_ci				       le16_to_cpu(vhtc->vht_mcs.rx_highest));
65062306a36Sopenharmony_ci		p += scnprintf(p, bufsz + buf - p, "TX MCS: %.4x\n",
65162306a36Sopenharmony_ci			       le16_to_cpu(vhtc->vht_mcs.tx_mcs_map));
65262306a36Sopenharmony_ci		if (vhtc->vht_mcs.tx_highest)
65362306a36Sopenharmony_ci			p += scnprintf(p, bufsz + buf - p,
65462306a36Sopenharmony_ci				       "MCS TX highest: %d Mbps\n",
65562306a36Sopenharmony_ci				       le16_to_cpu(vhtc->vht_mcs.tx_highest));
65662306a36Sopenharmony_ci#undef PFLAG
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	ret = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
66062306a36Sopenharmony_ci	kfree(buf);
66162306a36Sopenharmony_ci	return ret;
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ciLINK_STA_OPS(vht_capa);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic ssize_t link_sta_he_capa_read(struct file *file, char __user *userbuf,
66662306a36Sopenharmony_ci				     size_t count, loff_t *ppos)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	char *buf, *p;
66962306a36Sopenharmony_ci	size_t buf_sz = PAGE_SIZE;
67062306a36Sopenharmony_ci	struct link_sta_info *link_sta = file->private_data;
67162306a36Sopenharmony_ci	struct ieee80211_sta_he_cap *hec = &link_sta->pub->he_cap;
67262306a36Sopenharmony_ci	struct ieee80211_he_mcs_nss_supp *nss = &hec->he_mcs_nss_supp;
67362306a36Sopenharmony_ci	u8 ppe_size;
67462306a36Sopenharmony_ci	u8 *cap;
67562306a36Sopenharmony_ci	int i;
67662306a36Sopenharmony_ci	ssize_t ret;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	buf = kmalloc(buf_sz, GFP_KERNEL);
67962306a36Sopenharmony_ci	if (!buf)
68062306a36Sopenharmony_ci		return -ENOMEM;
68162306a36Sopenharmony_ci	p = buf;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	p += scnprintf(p, buf_sz + buf - p, "HE %ssupported\n",
68462306a36Sopenharmony_ci		       hec->has_he ? "" : "not ");
68562306a36Sopenharmony_ci	if (!hec->has_he)
68662306a36Sopenharmony_ci		goto out;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	cap = hec->he_cap_elem.mac_cap_info;
68962306a36Sopenharmony_ci	p += scnprintf(p, buf_sz + buf - p,
69062306a36Sopenharmony_ci		       "MAC-CAP: %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x\n",
69162306a36Sopenharmony_ci		       cap[0], cap[1], cap[2], cap[3], cap[4], cap[5]);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci#define PRINT(fmt, ...)							\
69462306a36Sopenharmony_ci	p += scnprintf(p, buf_sz + buf - p, "\t\t" fmt "\n",		\
69562306a36Sopenharmony_ci		       ##__VA_ARGS__)
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci#define PFLAG(t, n, a, b)						\
69862306a36Sopenharmony_ci	do {								\
69962306a36Sopenharmony_ci		if (cap[n] & IEEE80211_HE_##t##_CAP##n##_##a)		\
70062306a36Sopenharmony_ci			PRINT("%s", b);					\
70162306a36Sopenharmony_ci	} while (0)
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci#define PFLAG_RANGE(t, i, n, s, m, off, fmt)				\
70462306a36Sopenharmony_ci	do {								\
70562306a36Sopenharmony_ci		u8 msk = IEEE80211_HE_##t##_CAP##i##_##n##_MASK;	\
70662306a36Sopenharmony_ci		u8 idx = ((cap[i] & msk) >> (ffs(msk) - 1)) + off;	\
70762306a36Sopenharmony_ci		PRINT(fmt, (s << idx) + (m * idx));			\
70862306a36Sopenharmony_ci	} while (0)
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci#define PFLAG_RANGE_DEFAULT(t, i, n, s, m, off, fmt, a, b)		\
71162306a36Sopenharmony_ci	do {								\
71262306a36Sopenharmony_ci		if (cap[i] == IEEE80211_HE_##t ##_CAP##i##_##n##_##a) {	\
71362306a36Sopenharmony_ci			PRINT("%s", b);					\
71462306a36Sopenharmony_ci			break;						\
71562306a36Sopenharmony_ci		}							\
71662306a36Sopenharmony_ci		PFLAG_RANGE(t, i, n, s, m, off, fmt);			\
71762306a36Sopenharmony_ci	} while (0)
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	PFLAG(MAC, 0, HTC_HE, "HTC-HE");
72062306a36Sopenharmony_ci	PFLAG(MAC, 0, TWT_REQ, "TWT-REQ");
72162306a36Sopenharmony_ci	PFLAG(MAC, 0, TWT_RES, "TWT-RES");
72262306a36Sopenharmony_ci	PFLAG_RANGE_DEFAULT(MAC, 0, DYNAMIC_FRAG, 0, 1, 0,
72362306a36Sopenharmony_ci			    "DYNAMIC-FRAG-LEVEL-%d", NOT_SUPP, "NOT-SUPP");
72462306a36Sopenharmony_ci	PFLAG_RANGE_DEFAULT(MAC, 0, MAX_NUM_FRAG_MSDU, 1, 0, 0,
72562306a36Sopenharmony_ci			    "MAX-NUM-FRAG-MSDU-%d", UNLIMITED, "UNLIMITED");
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	PFLAG_RANGE_DEFAULT(MAC, 1, MIN_FRAG_SIZE, 128, 0, -1,
72862306a36Sopenharmony_ci			    "MIN-FRAG-SIZE-%d", UNLIMITED, "UNLIMITED");
72962306a36Sopenharmony_ci	PFLAG_RANGE_DEFAULT(MAC, 1, TF_MAC_PAD_DUR, 0, 8, 0,
73062306a36Sopenharmony_ci			    "TF-MAC-PAD-DUR-%dUS", MASK, "UNKNOWN");
73162306a36Sopenharmony_ci	PFLAG_RANGE(MAC, 1, MULTI_TID_AGG_RX_QOS, 0, 1, 1,
73262306a36Sopenharmony_ci		    "MULTI-TID-AGG-RX-QOS-%d");
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (cap[0] & IEEE80211_HE_MAC_CAP0_HTC_HE) {
73562306a36Sopenharmony_ci		switch (((cap[2] << 1) | (cap[1] >> 7)) & 0x3) {
73662306a36Sopenharmony_ci		case 0:
73762306a36Sopenharmony_ci			PRINT("LINK-ADAPTATION-NO-FEEDBACK");
73862306a36Sopenharmony_ci			break;
73962306a36Sopenharmony_ci		case 1:
74062306a36Sopenharmony_ci			PRINT("LINK-ADAPTATION-RESERVED");
74162306a36Sopenharmony_ci			break;
74262306a36Sopenharmony_ci		case 2:
74362306a36Sopenharmony_ci			PRINT("LINK-ADAPTATION-UNSOLICITED-FEEDBACK");
74462306a36Sopenharmony_ci			break;
74562306a36Sopenharmony_ci		case 3:
74662306a36Sopenharmony_ci			PRINT("LINK-ADAPTATION-BOTH");
74762306a36Sopenharmony_ci			break;
74862306a36Sopenharmony_ci		}
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	PFLAG(MAC, 2, ALL_ACK, "ALL-ACK");
75262306a36Sopenharmony_ci	PFLAG(MAC, 2, TRS, "TRS");
75362306a36Sopenharmony_ci	PFLAG(MAC, 2, BSR, "BSR");
75462306a36Sopenharmony_ci	PFLAG(MAC, 2, BCAST_TWT, "BCAST-TWT");
75562306a36Sopenharmony_ci	PFLAG(MAC, 2, 32BIT_BA_BITMAP, "32BIT-BA-BITMAP");
75662306a36Sopenharmony_ci	PFLAG(MAC, 2, MU_CASCADING, "MU-CASCADING");
75762306a36Sopenharmony_ci	PFLAG(MAC, 2, ACK_EN, "ACK-EN");
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	PFLAG(MAC, 3, OMI_CONTROL, "OMI-CONTROL");
76062306a36Sopenharmony_ci	PFLAG(MAC, 3, OFDMA_RA, "OFDMA-RA");
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	switch (cap[3] & IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK) {
76362306a36Sopenharmony_ci	case IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_0:
76462306a36Sopenharmony_ci		PRINT("MAX-AMPDU-LEN-EXP-USE-EXT-0");
76562306a36Sopenharmony_ci		break;
76662306a36Sopenharmony_ci	case IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_1:
76762306a36Sopenharmony_ci		PRINT("MAX-AMPDU-LEN-EXP-VHT-EXT-1");
76862306a36Sopenharmony_ci		break;
76962306a36Sopenharmony_ci	case IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_2:
77062306a36Sopenharmony_ci		PRINT("MAX-AMPDU-LEN-EXP-VHT-EXT-2");
77162306a36Sopenharmony_ci		break;
77262306a36Sopenharmony_ci	case IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3:
77362306a36Sopenharmony_ci		PRINT("MAX-AMPDU-LEN-EXP-VHT-EXT-3");
77462306a36Sopenharmony_ci		break;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	PFLAG(MAC, 3, AMSDU_FRAG, "AMSDU-FRAG");
77862306a36Sopenharmony_ci	PFLAG(MAC, 3, FLEX_TWT_SCHED, "FLEX-TWT-SCHED");
77962306a36Sopenharmony_ci	PFLAG(MAC, 3, RX_CTRL_FRAME_TO_MULTIBSS, "RX-CTRL-FRAME-TO-MULTIBSS");
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	PFLAG(MAC, 4, BSRP_BQRP_A_MPDU_AGG, "BSRP-BQRP-A-MPDU-AGG");
78262306a36Sopenharmony_ci	PFLAG(MAC, 4, QTP, "QTP");
78362306a36Sopenharmony_ci	PFLAG(MAC, 4, BQR, "BQR");
78462306a36Sopenharmony_ci	PFLAG(MAC, 4, PSR_RESP, "PSR-RESP");
78562306a36Sopenharmony_ci	PFLAG(MAC, 4, NDP_FB_REP, "NDP-FB-REP");
78662306a36Sopenharmony_ci	PFLAG(MAC, 4, OPS, "OPS");
78762306a36Sopenharmony_ci	PFLAG(MAC, 4, AMSDU_IN_AMPDU, "AMSDU-IN-AMPDU");
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	PRINT("MULTI-TID-AGG-TX-QOS-%d", ((cap[5] << 1) | (cap[4] >> 7)) & 0x7);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	PFLAG(MAC, 5, SUBCHAN_SELECTIVE_TRANSMISSION,
79262306a36Sopenharmony_ci	      "SUBCHAN-SELECTIVE-TRANSMISSION");
79362306a36Sopenharmony_ci	PFLAG(MAC, 5, UL_2x996_TONE_RU, "UL-2x996-TONE-RU");
79462306a36Sopenharmony_ci	PFLAG(MAC, 5, OM_CTRL_UL_MU_DATA_DIS_RX, "OM-CTRL-UL-MU-DATA-DIS-RX");
79562306a36Sopenharmony_ci	PFLAG(MAC, 5, HE_DYNAMIC_SM_PS, "HE-DYNAMIC-SM-PS");
79662306a36Sopenharmony_ci	PFLAG(MAC, 5, PUNCTURED_SOUNDING, "PUNCTURED-SOUNDING");
79762306a36Sopenharmony_ci	PFLAG(MAC, 5, HT_VHT_TRIG_FRAME_RX, "HT-VHT-TRIG-FRAME-RX");
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	cap = hec->he_cap_elem.phy_cap_info;
80062306a36Sopenharmony_ci	p += scnprintf(p, buf_sz + buf - p,
80162306a36Sopenharmony_ci		       "PHY CAP: %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x\n",
80262306a36Sopenharmony_ci		       cap[0], cap[1], cap[2], cap[3], cap[4], cap[5], cap[6],
80362306a36Sopenharmony_ci		       cap[7], cap[8], cap[9], cap[10]);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	PFLAG(PHY, 0, CHANNEL_WIDTH_SET_40MHZ_IN_2G,
80662306a36Sopenharmony_ci	      "CHANNEL-WIDTH-SET-40MHZ-IN-2G");
80762306a36Sopenharmony_ci	PFLAG(PHY, 0, CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G,
80862306a36Sopenharmony_ci	      "CHANNEL-WIDTH-SET-40MHZ-80MHZ-IN-5G");
80962306a36Sopenharmony_ci	PFLAG(PHY, 0, CHANNEL_WIDTH_SET_160MHZ_IN_5G,
81062306a36Sopenharmony_ci	      "CHANNEL-WIDTH-SET-160MHZ-IN-5G");
81162306a36Sopenharmony_ci	PFLAG(PHY, 0, CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G,
81262306a36Sopenharmony_ci	      "CHANNEL-WIDTH-SET-80PLUS80-MHZ-IN-5G");
81362306a36Sopenharmony_ci	PFLAG(PHY, 0, CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G,
81462306a36Sopenharmony_ci	      "CHANNEL-WIDTH-SET-RU-MAPPING-IN-2G");
81562306a36Sopenharmony_ci	PFLAG(PHY, 0, CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G,
81662306a36Sopenharmony_ci	      "CHANNEL-WIDTH-SET-RU-MAPPING-IN-5G");
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	switch (cap[1] & IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK) {
81962306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_80MHZ_ONLY_SECOND_20MHZ:
82062306a36Sopenharmony_ci		PRINT("PREAMBLE-PUNC-RX-80MHZ-ONLY-SECOND-20MHZ");
82162306a36Sopenharmony_ci		break;
82262306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_80MHZ_ONLY_SECOND_40MHZ:
82362306a36Sopenharmony_ci		PRINT("PREAMBLE-PUNC-RX-80MHZ-ONLY-SECOND-40MHZ");
82462306a36Sopenharmony_ci		break;
82562306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_160MHZ_ONLY_SECOND_20MHZ:
82662306a36Sopenharmony_ci		PRINT("PREAMBLE-PUNC-RX-160MHZ-ONLY-SECOND-20MHZ");
82762306a36Sopenharmony_ci		break;
82862306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_160MHZ_ONLY_SECOND_40MHZ:
82962306a36Sopenharmony_ci		PRINT("PREAMBLE-PUNC-RX-160MHZ-ONLY-SECOND-40MHZ");
83062306a36Sopenharmony_ci		break;
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	PFLAG(PHY, 1, DEVICE_CLASS_A,
83462306a36Sopenharmony_ci	      "IEEE80211-HE-PHY-CAP1-DEVICE-CLASS-A");
83562306a36Sopenharmony_ci	PFLAG(PHY, 1, LDPC_CODING_IN_PAYLOAD,
83662306a36Sopenharmony_ci	      "LDPC-CODING-IN-PAYLOAD");
83762306a36Sopenharmony_ci	PFLAG(PHY, 1, HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US,
83862306a36Sopenharmony_ci	      "HY-CAP1-HE-LTF-AND-GI-FOR-HE-PPDUS-0-8US");
83962306a36Sopenharmony_ci	PRINT("MIDAMBLE-RX-MAX-NSTS-%d", ((cap[2] << 1) | (cap[1] >> 7)) & 0x3);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	PFLAG(PHY, 2, NDP_4x_LTF_AND_3_2US, "NDP-4X-LTF-AND-3-2US");
84262306a36Sopenharmony_ci	PFLAG(PHY, 2, STBC_TX_UNDER_80MHZ, "STBC-TX-UNDER-80MHZ");
84362306a36Sopenharmony_ci	PFLAG(PHY, 2, STBC_RX_UNDER_80MHZ, "STBC-RX-UNDER-80MHZ");
84462306a36Sopenharmony_ci	PFLAG(PHY, 2, DOPPLER_TX, "DOPPLER-TX");
84562306a36Sopenharmony_ci	PFLAG(PHY, 2, DOPPLER_RX, "DOPPLER-RX");
84662306a36Sopenharmony_ci	PFLAG(PHY, 2, UL_MU_FULL_MU_MIMO, "UL-MU-FULL-MU-MIMO");
84762306a36Sopenharmony_ci	PFLAG(PHY, 2, UL_MU_PARTIAL_MU_MIMO, "UL-MU-PARTIAL-MU-MIMO");
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	switch (cap[3] & IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK) {
85062306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_NO_DCM:
85162306a36Sopenharmony_ci		PRINT("DCM-MAX-CONST-TX-NO-DCM");
85262306a36Sopenharmony_ci		break;
85362306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK:
85462306a36Sopenharmony_ci		PRINT("DCM-MAX-CONST-TX-BPSK");
85562306a36Sopenharmony_ci		break;
85662306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK:
85762306a36Sopenharmony_ci		PRINT("DCM-MAX-CONST-TX-QPSK");
85862306a36Sopenharmony_ci		break;
85962306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_16_QAM:
86062306a36Sopenharmony_ci		PRINT("DCM-MAX-CONST-TX-16-QAM");
86162306a36Sopenharmony_ci		break;
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	PFLAG(PHY, 3, DCM_MAX_TX_NSS_1, "DCM-MAX-TX-NSS-1");
86562306a36Sopenharmony_ci	PFLAG(PHY, 3, DCM_MAX_TX_NSS_2, "DCM-MAX-TX-NSS-2");
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	switch (cap[3] & IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK) {
86862306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_NO_DCM:
86962306a36Sopenharmony_ci		PRINT("DCM-MAX-CONST-RX-NO-DCM");
87062306a36Sopenharmony_ci		break;
87162306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK:
87262306a36Sopenharmony_ci		PRINT("DCM-MAX-CONST-RX-BPSK");
87362306a36Sopenharmony_ci		break;
87462306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK:
87562306a36Sopenharmony_ci		PRINT("DCM-MAX-CONST-RX-QPSK");
87662306a36Sopenharmony_ci		break;
87762306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM:
87862306a36Sopenharmony_ci		PRINT("DCM-MAX-CONST-RX-16-QAM");
87962306a36Sopenharmony_ci		break;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	PFLAG(PHY, 3, DCM_MAX_RX_NSS_1, "DCM-MAX-RX-NSS-1");
88362306a36Sopenharmony_ci	PFLAG(PHY, 3, DCM_MAX_RX_NSS_2, "DCM-MAX-RX-NSS-2");
88462306a36Sopenharmony_ci	PFLAG(PHY, 3, RX_PARTIAL_BW_SU_IN_20MHZ_MU,
88562306a36Sopenharmony_ci	      "RX-PARTIAL-BW-SU-IN-20MHZ-MU");
88662306a36Sopenharmony_ci	PFLAG(PHY, 3, SU_BEAMFORMER, "SU-BEAMFORMER");
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	PFLAG(PHY, 4, SU_BEAMFORMEE, "SU-BEAMFORMEE");
88962306a36Sopenharmony_ci	PFLAG(PHY, 4, MU_BEAMFORMER, "MU-BEAMFORMER");
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	PFLAG_RANGE(PHY, 4, BEAMFORMEE_MAX_STS_UNDER_80MHZ, 0, 1, 4,
89262306a36Sopenharmony_ci		    "BEAMFORMEE-MAX-STS-UNDER-%d");
89362306a36Sopenharmony_ci	PFLAG_RANGE(PHY, 4, BEAMFORMEE_MAX_STS_ABOVE_80MHZ, 0, 1, 4,
89462306a36Sopenharmony_ci		    "BEAMFORMEE-MAX-STS-ABOVE-%d");
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	PFLAG_RANGE(PHY, 5, BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ, 0, 1, 1,
89762306a36Sopenharmony_ci		    "NUM-SND-DIM-UNDER-80MHZ-%d");
89862306a36Sopenharmony_ci	PFLAG_RANGE(PHY, 5, BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ, 0, 1, 1,
89962306a36Sopenharmony_ci		    "NUM-SND-DIM-ABOVE-80MHZ-%d");
90062306a36Sopenharmony_ci	PFLAG(PHY, 5, NG16_SU_FEEDBACK, "NG16-SU-FEEDBACK");
90162306a36Sopenharmony_ci	PFLAG(PHY, 5, NG16_MU_FEEDBACK, "NG16-MU-FEEDBACK");
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	PFLAG(PHY, 6, CODEBOOK_SIZE_42_SU, "CODEBOOK-SIZE-42-SU");
90462306a36Sopenharmony_ci	PFLAG(PHY, 6, CODEBOOK_SIZE_75_MU, "CODEBOOK-SIZE-75-MU");
90562306a36Sopenharmony_ci	PFLAG(PHY, 6, TRIG_SU_BEAMFORMING_FB, "TRIG-SU-BEAMFORMING-FB");
90662306a36Sopenharmony_ci	PFLAG(PHY, 6, TRIG_MU_BEAMFORMING_PARTIAL_BW_FB,
90762306a36Sopenharmony_ci	      "MU-BEAMFORMING-PARTIAL-BW-FB");
90862306a36Sopenharmony_ci	PFLAG(PHY, 6, TRIG_CQI_FB, "TRIG-CQI-FB");
90962306a36Sopenharmony_ci	PFLAG(PHY, 6, PARTIAL_BW_EXT_RANGE, "PARTIAL-BW-EXT-RANGE");
91062306a36Sopenharmony_ci	PFLAG(PHY, 6, PARTIAL_BANDWIDTH_DL_MUMIMO,
91162306a36Sopenharmony_ci	      "PARTIAL-BANDWIDTH-DL-MUMIMO");
91262306a36Sopenharmony_ci	PFLAG(PHY, 6, PPE_THRESHOLD_PRESENT, "PPE-THRESHOLD-PRESENT");
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	PFLAG(PHY, 7, PSR_BASED_SR, "PSR-BASED-SR");
91562306a36Sopenharmony_ci	PFLAG(PHY, 7, POWER_BOOST_FACTOR_SUPP, "POWER-BOOST-FACTOR-SUPP");
91662306a36Sopenharmony_ci	PFLAG(PHY, 7, HE_SU_MU_PPDU_4XLTF_AND_08_US_GI,
91762306a36Sopenharmony_ci	      "HE-SU-MU-PPDU-4XLTF-AND-08-US-GI");
91862306a36Sopenharmony_ci	PFLAG_RANGE(PHY, 7, MAX_NC, 0, 1, 1, "MAX-NC-%d");
91962306a36Sopenharmony_ci	PFLAG(PHY, 7, STBC_TX_ABOVE_80MHZ, "STBC-TX-ABOVE-80MHZ");
92062306a36Sopenharmony_ci	PFLAG(PHY, 7, STBC_RX_ABOVE_80MHZ, "STBC-RX-ABOVE-80MHZ");
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	PFLAG(PHY, 8, HE_ER_SU_PPDU_4XLTF_AND_08_US_GI,
92362306a36Sopenharmony_ci	      "HE-ER-SU-PPDU-4XLTF-AND-08-US-GI");
92462306a36Sopenharmony_ci	PFLAG(PHY, 8, 20MHZ_IN_40MHZ_HE_PPDU_IN_2G,
92562306a36Sopenharmony_ci	      "20MHZ-IN-40MHZ-HE-PPDU-IN-2G");
92662306a36Sopenharmony_ci	PFLAG(PHY, 8, 20MHZ_IN_160MHZ_HE_PPDU, "20MHZ-IN-160MHZ-HE-PPDU");
92762306a36Sopenharmony_ci	PFLAG(PHY, 8, 80MHZ_IN_160MHZ_HE_PPDU, "80MHZ-IN-160MHZ-HE-PPDU");
92862306a36Sopenharmony_ci	PFLAG(PHY, 8, HE_ER_SU_1XLTF_AND_08_US_GI,
92962306a36Sopenharmony_ci	      "HE-ER-SU-1XLTF-AND-08-US-GI");
93062306a36Sopenharmony_ci	PFLAG(PHY, 8, MIDAMBLE_RX_TX_2X_AND_1XLTF,
93162306a36Sopenharmony_ci	      "MIDAMBLE-RX-TX-2X-AND-1XLTF");
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	switch (cap[8] & IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_MASK) {
93462306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242:
93562306a36Sopenharmony_ci		PRINT("DCM-MAX-RU-242");
93662306a36Sopenharmony_ci		break;
93762306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484:
93862306a36Sopenharmony_ci		PRINT("DCM-MAX-RU-484");
93962306a36Sopenharmony_ci		break;
94062306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996:
94162306a36Sopenharmony_ci		PRINT("DCM-MAX-RU-996");
94262306a36Sopenharmony_ci		break;
94362306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_2x996:
94462306a36Sopenharmony_ci		PRINT("DCM-MAX-RU-2x996");
94562306a36Sopenharmony_ci		break;
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	PFLAG(PHY, 9, LONGER_THAN_16_SIGB_OFDM_SYM,
94962306a36Sopenharmony_ci	      "LONGER-THAN-16-SIGB-OFDM-SYM");
95062306a36Sopenharmony_ci	PFLAG(PHY, 9, NON_TRIGGERED_CQI_FEEDBACK,
95162306a36Sopenharmony_ci	      "NON-TRIGGERED-CQI-FEEDBACK");
95262306a36Sopenharmony_ci	PFLAG(PHY, 9, TX_1024_QAM_LESS_THAN_242_TONE_RU,
95362306a36Sopenharmony_ci	      "TX-1024-QAM-LESS-THAN-242-TONE-RU");
95462306a36Sopenharmony_ci	PFLAG(PHY, 9, RX_1024_QAM_LESS_THAN_242_TONE_RU,
95562306a36Sopenharmony_ci	      "RX-1024-QAM-LESS-THAN-242-TONE-RU");
95662306a36Sopenharmony_ci	PFLAG(PHY, 9, RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB,
95762306a36Sopenharmony_ci	      "RX-FULL-BW-SU-USING-MU-WITH-COMP-SIGB");
95862306a36Sopenharmony_ci	PFLAG(PHY, 9, RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB,
95962306a36Sopenharmony_ci	      "RX-FULL-BW-SU-USING-MU-WITH-NON-COMP-SIGB");
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	switch (u8_get_bits(cap[9],
96262306a36Sopenharmony_ci			    IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK)) {
96362306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_0US:
96462306a36Sopenharmony_ci		PRINT("NOMINAL-PACKET-PADDING-0US");
96562306a36Sopenharmony_ci		break;
96662306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_8US:
96762306a36Sopenharmony_ci		PRINT("NOMINAL-PACKET-PADDING-8US");
96862306a36Sopenharmony_ci		break;
96962306a36Sopenharmony_ci	case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US:
97062306a36Sopenharmony_ci		PRINT("NOMINAL-PACKET-PADDING-16US");
97162306a36Sopenharmony_ci		break;
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci#undef PFLAG_RANGE_DEFAULT
97562306a36Sopenharmony_ci#undef PFLAG_RANGE
97662306a36Sopenharmony_ci#undef PFLAG
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci#define PRINT_NSS_SUPP(f, n)						\
97962306a36Sopenharmony_ci	do {								\
98062306a36Sopenharmony_ci		int _i;							\
98162306a36Sopenharmony_ci		u16 v = le16_to_cpu(nss->f);				\
98262306a36Sopenharmony_ci		p += scnprintf(p, buf_sz + buf - p, n ": %#.4x\n", v);	\
98362306a36Sopenharmony_ci		for (_i = 0; _i < 8; _i += 2) {				\
98462306a36Sopenharmony_ci			switch ((v >> _i) & 0x3) {			\
98562306a36Sopenharmony_ci			case 0:						\
98662306a36Sopenharmony_ci				PRINT(n "-%d-SUPPORT-0-7", _i / 2);	\
98762306a36Sopenharmony_ci				break;					\
98862306a36Sopenharmony_ci			case 1:						\
98962306a36Sopenharmony_ci				PRINT(n "-%d-SUPPORT-0-9", _i / 2);	\
99062306a36Sopenharmony_ci				break;					\
99162306a36Sopenharmony_ci			case 2:						\
99262306a36Sopenharmony_ci				PRINT(n "-%d-SUPPORT-0-11", _i / 2);	\
99362306a36Sopenharmony_ci				break;					\
99462306a36Sopenharmony_ci			case 3:						\
99562306a36Sopenharmony_ci				PRINT(n "-%d-NOT-SUPPORTED", _i / 2);	\
99662306a36Sopenharmony_ci				break;					\
99762306a36Sopenharmony_ci			}						\
99862306a36Sopenharmony_ci		}							\
99962306a36Sopenharmony_ci	} while (0)
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	PRINT_NSS_SUPP(rx_mcs_80, "RX-MCS-80");
100262306a36Sopenharmony_ci	PRINT_NSS_SUPP(tx_mcs_80, "TX-MCS-80");
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	if (cap[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) {
100562306a36Sopenharmony_ci		PRINT_NSS_SUPP(rx_mcs_160, "RX-MCS-160");
100662306a36Sopenharmony_ci		PRINT_NSS_SUPP(tx_mcs_160, "TX-MCS-160");
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	if (cap[0] &
101062306a36Sopenharmony_ci	    IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) {
101162306a36Sopenharmony_ci		PRINT_NSS_SUPP(rx_mcs_80p80, "RX-MCS-80P80");
101262306a36Sopenharmony_ci		PRINT_NSS_SUPP(tx_mcs_80p80, "TX-MCS-80P80");
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci#undef PRINT_NSS_SUPP
101662306a36Sopenharmony_ci#undef PRINT
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	if (!(cap[6] & IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT))
101962306a36Sopenharmony_ci		goto out;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	p += scnprintf(p, buf_sz + buf - p, "PPE-THRESHOLDS: %#.2x",
102262306a36Sopenharmony_ci		       hec->ppe_thres[0]);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	ppe_size = ieee80211_he_ppe_size(hec->ppe_thres[0], cap);
102562306a36Sopenharmony_ci	for (i = 1; i < ppe_size; i++) {
102662306a36Sopenharmony_ci		p += scnprintf(p, buf_sz + buf - p, " %#.2x",
102762306a36Sopenharmony_ci			       hec->ppe_thres[i]);
102862306a36Sopenharmony_ci	}
102962306a36Sopenharmony_ci	p += scnprintf(p, buf_sz + buf - p, "\n");
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ciout:
103262306a36Sopenharmony_ci	ret = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
103362306a36Sopenharmony_ci	kfree(buf);
103462306a36Sopenharmony_ci	return ret;
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ciLINK_STA_OPS(he_capa);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_cistatic ssize_t link_sta_eht_capa_read(struct file *file, char __user *userbuf,
103962306a36Sopenharmony_ci				      size_t count, loff_t *ppos)
104062306a36Sopenharmony_ci{
104162306a36Sopenharmony_ci	char *buf, *p;
104262306a36Sopenharmony_ci	size_t buf_sz = PAGE_SIZE;
104362306a36Sopenharmony_ci	struct link_sta_info *link_sta = file->private_data;
104462306a36Sopenharmony_ci	struct ieee80211_sta_eht_cap *bec = &link_sta->pub->eht_cap;
104562306a36Sopenharmony_ci	struct ieee80211_eht_cap_elem_fixed *fixed = &bec->eht_cap_elem;
104662306a36Sopenharmony_ci	struct ieee80211_eht_mcs_nss_supp *nss = &bec->eht_mcs_nss_supp;
104762306a36Sopenharmony_ci	u8 *cap;
104862306a36Sopenharmony_ci	int i;
104962306a36Sopenharmony_ci	ssize_t ret;
105062306a36Sopenharmony_ci	static const char *mcs_desc[] = { "0-7", "8-9", "10-11", "12-13"};
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	buf = kmalloc(buf_sz, GFP_KERNEL);
105362306a36Sopenharmony_ci	if (!buf)
105462306a36Sopenharmony_ci		return -ENOMEM;
105562306a36Sopenharmony_ci	p = buf;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	p += scnprintf(p, buf_sz + buf - p, "EHT %ssupported\n",
105862306a36Sopenharmony_ci		       bec->has_eht ? "" : "not ");
105962306a36Sopenharmony_ci	if (!bec->has_eht)
106062306a36Sopenharmony_ci		goto out;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	p += scnprintf(p, buf_sz + buf - p,
106362306a36Sopenharmony_ci		       "MAC-CAP: %#.2x %#.2x\n",
106462306a36Sopenharmony_ci		       fixed->mac_cap_info[0], fixed->mac_cap_info[1]);
106562306a36Sopenharmony_ci	p += scnprintf(p, buf_sz + buf - p,
106662306a36Sopenharmony_ci		       "PHY-CAP: %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x\n",
106762306a36Sopenharmony_ci		       fixed->phy_cap_info[0], fixed->phy_cap_info[1],
106862306a36Sopenharmony_ci		       fixed->phy_cap_info[2], fixed->phy_cap_info[3],
106962306a36Sopenharmony_ci		       fixed->phy_cap_info[4], fixed->phy_cap_info[5],
107062306a36Sopenharmony_ci		       fixed->phy_cap_info[6], fixed->phy_cap_info[7],
107162306a36Sopenharmony_ci		       fixed->phy_cap_info[8]);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci#define PRINT(fmt, ...)							\
107462306a36Sopenharmony_ci	p += scnprintf(p, buf_sz + buf - p, "\t\t" fmt "\n",		\
107562306a36Sopenharmony_ci		       ##__VA_ARGS__)
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci#define PFLAG(t, n, a, b)						\
107862306a36Sopenharmony_ci	do {								\
107962306a36Sopenharmony_ci		if (cap[n] & IEEE80211_EHT_##t##_CAP##n##_##a)		\
108062306a36Sopenharmony_ci			PRINT("%s", b);					\
108162306a36Sopenharmony_ci	} while (0)
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	cap = fixed->mac_cap_info;
108462306a36Sopenharmony_ci	PFLAG(MAC, 0, EPCS_PRIO_ACCESS, "EPCS-PRIO-ACCESS");
108562306a36Sopenharmony_ci	PFLAG(MAC, 0, OM_CONTROL, "OM-CONTROL");
108662306a36Sopenharmony_ci	PFLAG(MAC, 0, TRIG_TXOP_SHARING_MODE1, "TRIG-TXOP-SHARING-MODE1");
108762306a36Sopenharmony_ci	PFLAG(MAC, 0, TRIG_TXOP_SHARING_MODE2, "TRIG-TXOP-SHARING-MODE2");
108862306a36Sopenharmony_ci	PFLAG(MAC, 0, RESTRICTED_TWT, "RESTRICTED-TWT");
108962306a36Sopenharmony_ci	PFLAG(MAC, 0, SCS_TRAFFIC_DESC, "SCS-TRAFFIC-DESC");
109062306a36Sopenharmony_ci	switch ((cap[0] & 0xc0) >> 6) {
109162306a36Sopenharmony_ci	case IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_3895:
109262306a36Sopenharmony_ci		PRINT("MAX-MPDU-LEN: 3985");
109362306a36Sopenharmony_ci		break;
109462306a36Sopenharmony_ci	case IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_7991:
109562306a36Sopenharmony_ci		PRINT("MAX-MPDU-LEN: 7991");
109662306a36Sopenharmony_ci		break;
109762306a36Sopenharmony_ci	case IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454:
109862306a36Sopenharmony_ci		PRINT("MAX-MPDU-LEN: 11454");
109962306a36Sopenharmony_ci		break;
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	cap = fixed->phy_cap_info;
110362306a36Sopenharmony_ci	PFLAG(PHY, 0, 320MHZ_IN_6GHZ, "320MHZ-IN-6GHZ");
110462306a36Sopenharmony_ci	PFLAG(PHY, 0, 242_TONE_RU_GT20MHZ, "242-TONE-RU-GT20MHZ");
110562306a36Sopenharmony_ci	PFLAG(PHY, 0, NDP_4_EHT_LFT_32_GI, "NDP-4-EHT-LFT-32-GI");
110662306a36Sopenharmony_ci	PFLAG(PHY, 0, PARTIAL_BW_UL_MU_MIMO, "PARTIAL-BW-UL-MU-MIMO");
110762306a36Sopenharmony_ci	PFLAG(PHY, 0, SU_BEAMFORMER, "SU-BEAMFORMER");
110862306a36Sopenharmony_ci	PFLAG(PHY, 0, SU_BEAMFORMEE, "SU-BEAMFORMEE");
110962306a36Sopenharmony_ci	i = cap[0] >> 7;
111062306a36Sopenharmony_ci	i |= (cap[1] & 0x3) << 1;
111162306a36Sopenharmony_ci	PRINT("BEAMFORMEE-80-NSS: %i", i);
111262306a36Sopenharmony_ci	PRINT("BEAMFORMEE-160-NSS: %i", (cap[1] >> 2) & 0x7);
111362306a36Sopenharmony_ci	PRINT("BEAMFORMEE-320-NSS: %i", (cap[1] >> 5) & 0x7);
111462306a36Sopenharmony_ci	PRINT("SOUNDING-DIM-80-NSS: %i", (cap[2] & 0x7));
111562306a36Sopenharmony_ci	PRINT("SOUNDING-DIM-160-NSS: %i", (cap[2] >> 3) & 0x7);
111662306a36Sopenharmony_ci	i = cap[2] >> 6;
111762306a36Sopenharmony_ci	i |= (cap[3] & 0x1) << 3;
111862306a36Sopenharmony_ci	PRINT("SOUNDING-DIM-320-NSS: %i", i);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	PFLAG(PHY, 3, NG_16_SU_FEEDBACK, "NG-16-SU-FEEDBACK");
112162306a36Sopenharmony_ci	PFLAG(PHY, 3, NG_16_MU_FEEDBACK, "NG-16-MU-FEEDBACK");
112262306a36Sopenharmony_ci	PFLAG(PHY, 3, CODEBOOK_4_2_SU_FDBK, "CODEBOOK-4-2-SU-FDBK");
112362306a36Sopenharmony_ci	PFLAG(PHY, 3, CODEBOOK_7_5_MU_FDBK, "CODEBOOK-7-5-MU-FDBK");
112462306a36Sopenharmony_ci	PFLAG(PHY, 3, TRIG_SU_BF_FDBK, "TRIG-SU-BF-FDBK");
112562306a36Sopenharmony_ci	PFLAG(PHY, 3, TRIG_MU_BF_PART_BW_FDBK, "TRIG-MU-BF-PART-BW-FDBK");
112662306a36Sopenharmony_ci	PFLAG(PHY, 3, TRIG_CQI_FDBK, "TRIG-CQI-FDBK");
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	PFLAG(PHY, 4, PART_BW_DL_MU_MIMO, "PART-BW-DL-MU-MIMO");
112962306a36Sopenharmony_ci	PFLAG(PHY, 4, PSR_SR_SUPP, "PSR-SR-SUPP");
113062306a36Sopenharmony_ci	PFLAG(PHY, 4, POWER_BOOST_FACT_SUPP, "POWER-BOOST-FACT-SUPP");
113162306a36Sopenharmony_ci	PFLAG(PHY, 4, EHT_MU_PPDU_4_EHT_LTF_08_GI, "EHT-MU-PPDU-4-EHT-LTF-08-GI");
113262306a36Sopenharmony_ci	PRINT("MAX_NC: %i", cap[4] >> 4);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	PFLAG(PHY, 5, NON_TRIG_CQI_FEEDBACK, "NON-TRIG-CQI-FEEDBACK");
113562306a36Sopenharmony_ci	PFLAG(PHY, 5, TX_LESS_242_TONE_RU_SUPP, "TX-LESS-242-TONE-RU-SUPP");
113662306a36Sopenharmony_ci	PFLAG(PHY, 5, RX_LESS_242_TONE_RU_SUPP, "RX-LESS-242-TONE-RU-SUPP");
113762306a36Sopenharmony_ci	PFLAG(PHY, 5, PPE_THRESHOLD_PRESENT, "PPE_THRESHOLD_PRESENT");
113862306a36Sopenharmony_ci	switch (cap[5] >> 4 & 0x3) {
113962306a36Sopenharmony_ci	case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_0US:
114062306a36Sopenharmony_ci		PRINT("NOMINAL_PKT_PAD: 0us");
114162306a36Sopenharmony_ci		break;
114262306a36Sopenharmony_ci	case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US:
114362306a36Sopenharmony_ci		PRINT("NOMINAL_PKT_PAD: 8us");
114462306a36Sopenharmony_ci		break;
114562306a36Sopenharmony_ci	case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US:
114662306a36Sopenharmony_ci		PRINT("NOMINAL_PKT_PAD: 16us");
114762306a36Sopenharmony_ci		break;
114862306a36Sopenharmony_ci	case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_20US:
114962306a36Sopenharmony_ci		PRINT("NOMINAL_PKT_PAD: 20us");
115062306a36Sopenharmony_ci		break;
115162306a36Sopenharmony_ci	}
115262306a36Sopenharmony_ci	i = cap[5] >> 6;
115362306a36Sopenharmony_ci	i |= cap[6] & 0x7;
115462306a36Sopenharmony_ci	PRINT("MAX-NUM-SUPP-EHT-LTF: %i", i);
115562306a36Sopenharmony_ci	PFLAG(PHY, 5, SUPP_EXTRA_EHT_LTF, "SUPP-EXTRA-EHT-LTF");
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	i = (cap[6] >> 3) & 0xf;
115862306a36Sopenharmony_ci	PRINT("MCS15-SUPP-MASK: %i", i);
115962306a36Sopenharmony_ci	PFLAG(PHY, 6, EHT_DUP_6GHZ_SUPP, "EHT-DUP-6GHZ-SUPP");
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	PFLAG(PHY, 7, 20MHZ_STA_RX_NDP_WIDER_BW, "20MHZ-STA-RX-NDP-WIDER-BW");
116262306a36Sopenharmony_ci	PFLAG(PHY, 7, NON_OFDMA_UL_MU_MIMO_80MHZ, "NON-OFDMA-UL-MU-MIMO-80MHZ");
116362306a36Sopenharmony_ci	PFLAG(PHY, 7, NON_OFDMA_UL_MU_MIMO_160MHZ, "NON-OFDMA-UL-MU-MIMO-160MHZ");
116462306a36Sopenharmony_ci	PFLAG(PHY, 7, NON_OFDMA_UL_MU_MIMO_320MHZ, "NON-OFDMA-UL-MU-MIMO-320MHZ");
116562306a36Sopenharmony_ci	PFLAG(PHY, 7, MU_BEAMFORMER_80MHZ, "MU-BEAMFORMER-80MHZ");
116662306a36Sopenharmony_ci	PFLAG(PHY, 7, MU_BEAMFORMER_160MHZ, "MU-BEAMFORMER-160MHZ");
116762306a36Sopenharmony_ci	PFLAG(PHY, 7, MU_BEAMFORMER_320MHZ, "MU-BEAMFORMER-320MHZ");
116862306a36Sopenharmony_ci	PFLAG(PHY, 7, TB_SOUNDING_FDBK_RATE_LIMIT, "TB-SOUNDING-FDBK-RATE-LIMIT");
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	PFLAG(PHY, 8, RX_1024QAM_WIDER_BW_DL_OFDMA, "RX-1024QAM-WIDER-BW-DL-OFDMA");
117162306a36Sopenharmony_ci	PFLAG(PHY, 8, RX_4096QAM_WIDER_BW_DL_OFDMA, "RX-4096QAM-WIDER-BW-DL-OFDMA");
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci#undef PFLAG
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	PRINT(""); /* newline */
117662306a36Sopenharmony_ci	if (!(link_sta->pub->he_cap.he_cap_elem.phy_cap_info[0] &
117762306a36Sopenharmony_ci	      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
117862306a36Sopenharmony_ci		u8 *mcs_vals = (u8 *)(&nss->only_20mhz);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci		for (i = 0; i < 4; i++)
118162306a36Sopenharmony_ci			PRINT("EHT bw=20 MHz, max NSS for MCS %s: Rx=%u, Tx=%u",
118262306a36Sopenharmony_ci			      mcs_desc[i],
118362306a36Sopenharmony_ci			      mcs_vals[i] & 0xf, mcs_vals[i] >> 4);
118462306a36Sopenharmony_ci	} else {
118562306a36Sopenharmony_ci		u8 *mcs_vals = (u8 *)(&nss->bw._80);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		for (i = 0; i < 3; i++)
118862306a36Sopenharmony_ci			PRINT("EHT bw <= 80 MHz, max NSS for MCS %s: Rx=%u, Tx=%u",
118962306a36Sopenharmony_ci			      mcs_desc[i + 1],
119062306a36Sopenharmony_ci			      mcs_vals[i] & 0xf, mcs_vals[i] >> 4);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci		mcs_vals = (u8 *)(&nss->bw._160);
119362306a36Sopenharmony_ci		for (i = 0; i < 3; i++)
119462306a36Sopenharmony_ci			PRINT("EHT bw <= 160 MHz, max NSS for MCS %s: Rx=%u, Tx=%u",
119562306a36Sopenharmony_ci			      mcs_desc[i + 1],
119662306a36Sopenharmony_ci			      mcs_vals[i] & 0xf, mcs_vals[i] >> 4);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci		mcs_vals = (u8 *)(&nss->bw._320);
119962306a36Sopenharmony_ci		for (i = 0; i < 3; i++)
120062306a36Sopenharmony_ci			PRINT("EHT bw <= 320 MHz, max NSS for MCS %s: Rx=%u, Tx=%u",
120162306a36Sopenharmony_ci			      mcs_desc[i + 1],
120262306a36Sopenharmony_ci			      mcs_vals[i] & 0xf, mcs_vals[i] >> 4);
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	if (cap[5] & IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) {
120662306a36Sopenharmony_ci		u8 ppe_size = ieee80211_eht_ppe_size(bec->eht_ppe_thres[0], cap);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci		p += scnprintf(p, buf_sz + buf - p, "EHT PPE Thresholds: ");
120962306a36Sopenharmony_ci		for (i = 0; i < ppe_size; i++)
121062306a36Sopenharmony_ci			p += scnprintf(p, buf_sz + buf - p, "0x%02x ",
121162306a36Sopenharmony_ci				       bec->eht_ppe_thres[i]);
121262306a36Sopenharmony_ci		PRINT(""); /* newline */
121362306a36Sopenharmony_ci	}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ciout:
121662306a36Sopenharmony_ci	ret = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
121762306a36Sopenharmony_ci	kfree(buf);
121862306a36Sopenharmony_ci	return ret;
121962306a36Sopenharmony_ci}
122062306a36Sopenharmony_ciLINK_STA_OPS(eht_capa);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci#define DEBUGFS_ADD(name) \
122362306a36Sopenharmony_ci	debugfs_create_file(#name, 0400, \
122462306a36Sopenharmony_ci		sta->debugfs_dir, sta, &sta_ ##name## _ops)
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci#define DEBUGFS_ADD_COUNTER(name, field)				\
122762306a36Sopenharmony_ci	debugfs_create_ulong(#name, 0400, sta->debugfs_dir, &sta->field);
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_civoid ieee80211_sta_debugfs_add(struct sta_info *sta)
123062306a36Sopenharmony_ci{
123162306a36Sopenharmony_ci	struct ieee80211_local *local = sta->local;
123262306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = sta->sdata;
123362306a36Sopenharmony_ci	struct dentry *stations_dir = sta->sdata->debugfs.subdir_stations;
123462306a36Sopenharmony_ci	u8 mac[3*ETH_ALEN];
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	if (!stations_dir)
123762306a36Sopenharmony_ci		return;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	snprintf(mac, sizeof(mac), "%pM", sta->sta.addr);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	/*
124262306a36Sopenharmony_ci	 * This might fail due to a race condition:
124362306a36Sopenharmony_ci	 * When mac80211 unlinks a station, the debugfs entries
124462306a36Sopenharmony_ci	 * remain, but it is already possible to link a new
124562306a36Sopenharmony_ci	 * station with the same address which triggers adding
124662306a36Sopenharmony_ci	 * it to debugfs; therefore, if the old station isn't
124762306a36Sopenharmony_ci	 * destroyed quickly enough the old station's debugfs
124862306a36Sopenharmony_ci	 * dir might still be around.
124962306a36Sopenharmony_ci	 */
125062306a36Sopenharmony_ci	sta->debugfs_dir = debugfs_create_dir(mac, stations_dir);
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	DEBUGFS_ADD(flags);
125362306a36Sopenharmony_ci	DEBUGFS_ADD(aid);
125462306a36Sopenharmony_ci	DEBUGFS_ADD(num_ps_buf_frames);
125562306a36Sopenharmony_ci	DEBUGFS_ADD(last_seq_ctrl);
125662306a36Sopenharmony_ci	DEBUGFS_ADD(agg_status);
125762306a36Sopenharmony_ci	/* FIXME: Kept here as the statistics are only done on the deflink */
125862306a36Sopenharmony_ci	DEBUGFS_ADD_COUNTER(tx_filtered, deflink.status_stats.filtered);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	DEBUGFS_ADD(aqm);
126162306a36Sopenharmony_ci	DEBUGFS_ADD(airtime);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	if (wiphy_ext_feature_isset(local->hw.wiphy,
126462306a36Sopenharmony_ci				    NL80211_EXT_FEATURE_AQL))
126562306a36Sopenharmony_ci		DEBUGFS_ADD(aql);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	debugfs_create_xul("driver_buffered_tids", 0400, sta->debugfs_dir,
126862306a36Sopenharmony_ci			   &sta->driver_buffered_tids);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs_dir);
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_civoid ieee80211_sta_debugfs_remove(struct sta_info *sta)
127462306a36Sopenharmony_ci{
127562306a36Sopenharmony_ci	debugfs_remove_recursive(sta->debugfs_dir);
127662306a36Sopenharmony_ci	sta->debugfs_dir = NULL;
127762306a36Sopenharmony_ci}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci#undef DEBUGFS_ADD
128062306a36Sopenharmony_ci#undef DEBUGFS_ADD_COUNTER
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci#define DEBUGFS_ADD(name) \
128362306a36Sopenharmony_ci	debugfs_create_file(#name, 0400, \
128462306a36Sopenharmony_ci		link_sta->debugfs_dir, link_sta, &link_sta_ ##name## _ops)
128562306a36Sopenharmony_ci#define DEBUGFS_ADD_COUNTER(name, field)				\
128662306a36Sopenharmony_ci	debugfs_create_ulong(#name, 0400, link_sta->debugfs_dir, &link_sta->field)
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_civoid ieee80211_link_sta_debugfs_add(struct link_sta_info *link_sta)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	if (WARN_ON(!link_sta->sta->debugfs_dir))
129162306a36Sopenharmony_ci		return;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	/* For non-MLO, leave the files in the main directory. */
129462306a36Sopenharmony_ci	if (link_sta->sta->sta.valid_links) {
129562306a36Sopenharmony_ci		char link_dir_name[10];
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci		snprintf(link_dir_name, sizeof(link_dir_name),
129862306a36Sopenharmony_ci			 "link-%d", link_sta->link_id);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci		link_sta->debugfs_dir =
130162306a36Sopenharmony_ci			debugfs_create_dir(link_dir_name,
130262306a36Sopenharmony_ci					   link_sta->sta->debugfs_dir);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci		DEBUGFS_ADD(addr);
130562306a36Sopenharmony_ci	} else {
130662306a36Sopenharmony_ci		if (WARN_ON(link_sta != &link_sta->sta->deflink))
130762306a36Sopenharmony_ci			return;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci		link_sta->debugfs_dir = link_sta->sta->debugfs_dir;
131062306a36Sopenharmony_ci	}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	DEBUGFS_ADD(ht_capa);
131362306a36Sopenharmony_ci	DEBUGFS_ADD(vht_capa);
131462306a36Sopenharmony_ci	DEBUGFS_ADD(he_capa);
131562306a36Sopenharmony_ci	DEBUGFS_ADD(eht_capa);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	DEBUGFS_ADD_COUNTER(rx_duplicates, rx_stats.num_duplicates);
131862306a36Sopenharmony_ci	DEBUGFS_ADD_COUNTER(rx_fragments, rx_stats.fragments);
131962306a36Sopenharmony_ci}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_civoid ieee80211_link_sta_debugfs_remove(struct link_sta_info *link_sta)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	if (!link_sta->debugfs_dir || !link_sta->sta->debugfs_dir) {
132462306a36Sopenharmony_ci		link_sta->debugfs_dir = NULL;
132562306a36Sopenharmony_ci		return;
132662306a36Sopenharmony_ci	}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	if (link_sta->debugfs_dir == link_sta->sta->debugfs_dir) {
132962306a36Sopenharmony_ci		WARN_ON(link_sta != &link_sta->sta->deflink);
133062306a36Sopenharmony_ci		link_sta->sta->debugfs_dir = NULL;
133162306a36Sopenharmony_ci		return;
133262306a36Sopenharmony_ci	}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	debugfs_remove_recursive(link_sta->debugfs_dir);
133562306a36Sopenharmony_ci	link_sta->debugfs_dir = NULL;
133662306a36Sopenharmony_ci}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_civoid ieee80211_link_sta_debugfs_drv_add(struct link_sta_info *link_sta)
133962306a36Sopenharmony_ci{
134062306a36Sopenharmony_ci	if (WARN_ON(!link_sta->debugfs_dir))
134162306a36Sopenharmony_ci		return;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	drv_link_sta_add_debugfs(link_sta->sta->local, link_sta->sta->sdata,
134462306a36Sopenharmony_ci				 link_sta->pub, link_sta->debugfs_dir);
134562306a36Sopenharmony_ci}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_civoid ieee80211_link_sta_debugfs_drv_remove(struct link_sta_info *link_sta)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	if (!link_sta->debugfs_dir)
135062306a36Sopenharmony_ci		return;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	if (WARN_ON(link_sta->debugfs_dir == link_sta->sta->debugfs_dir))
135362306a36Sopenharmony_ci		return;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	/* Recreate the directory excluding the driver data */
135662306a36Sopenharmony_ci	debugfs_remove_recursive(link_sta->debugfs_dir);
135762306a36Sopenharmony_ci	link_sta->debugfs_dir = NULL;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	ieee80211_link_sta_debugfs_add(link_sta);
136062306a36Sopenharmony_ci}
1361