blob: 2aaf1144593502bc6be3f4cf0a6ffb59c3ac6767 [file] [log] [blame]
From eca521242b94855825f085d1bca67a5958420baa Mon Sep 17 00:00:00 2001
From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
Date: Thu, 22 Sep 2022 14:27:41 +0800
Subject: [PATCH] cfg80211: implement DFS status show, cac and nop skip command
via debugfs
Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
---
net/wireless/debugfs.c | 228 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 221 insertions(+), 7 deletions(-)
diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
index aab4346..19c3091 100644
--- a/net/wireless/debugfs.c
+++ b/net/wireless/debugfs.c
@@ -95,16 +95,230 @@ static const struct file_operations ht40allow_map_ops = {
.llseek = default_llseek,
};
-#define DEBUGFS_ADD(name) \
- debugfs_create_file(#name, 0444, phyd, &rdev->wiphy, &name## _ops)
+static int dfs_print_chan(struct ieee80211_channel *chan, int remain_time,
+ char *buf, int buf_size, int offset)
+{
+ if (WARN_ON(offset > buf_size))
+ return 0;
+
+ if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
+ offset += scnprintf(buf + offset, buf_size - offset,
+ " Channel = %d, DFS_state = Unavailable",
+ chan->hw_value);
+ if (remain_time > 0)
+ offset += scnprintf(buf + offset, buf_size - offset,
+ ", Non-occupancy Remain Time = %d [sec]\n",
+ remain_time);
+ else
+ offset += scnprintf(buf + offset, buf_size - offset,
+ ", Changing state...\n");
+ } else if (chan->dfs_state == NL80211_DFS_USABLE) {
+ offset += scnprintf(buf + offset, buf_size - offset,
+ " Channel = %d, DFS_state = Usable",
+ chan->hw_value);
+ if (remain_time > 0)
+ offset += scnprintf(buf + offset, buf_size - offset,
+ ", CAC Remain Time = %d [sec]\n",
+ remain_time);
+ else
+ offset += scnprintf(buf + offset, buf_size - offset,
+ "\n");
+ } else if (chan->dfs_state == NL80211_DFS_AVAILABLE) {
+ offset += scnprintf(buf + offset, buf_size - offset,
+ " Channel = %d, DFS_state = Available\n",
+ chan->hw_value);
+ } else {
+ offset += scnprintf(buf + offset, buf_size - offset,
+ " Channel = %d, DFS_state = Unknown\n",
+ chan->hw_value);
+ }
+
+ return offset;
+}
+
+static int dfs_status_read_wdev(struct wiphy *wiphy, struct wireless_dev *wdev, char *buf,
+ unsigned int buf_size, unsigned int offset)
+{
+ struct cfg80211_chan_def *chandef;
+ enum nl80211_band band;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+ unsigned long jiffies_passed;
+ int i, remain_time = 0;
+
+ offset += scnprintf(buf + offset, buf_size - offset, "DFS Channel:\n");
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ sband = wiphy->bands[band];
+ if (!sband)
+ continue;
+ for (i = 0; i < sband->n_channels; i++) {
+ chan = &sband->channels[i];
+
+ if (!(chan->flags & IEEE80211_CHAN_RADAR))
+ continue;
+
+ if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
+ jiffies_passed = jiffies - chan->dfs_state_entered;
+ remain_time = (IEEE80211_DFS_MIN_NOP_TIME_MS -
+ jiffies_to_msecs(jiffies_passed));
+ if (remain_time > IEEE80211_DFS_MIN_NOP_TIME_MS)
+ remain_time = 0;
+ } else if (chan->dfs_state == NL80211_DFS_USABLE) {
+ chandef = &wdev->chandef;
+ if (wdev->cac_started && cfg80211_is_sub_chan(chandef, chan)) {
+ jiffies_passed = jiffies - wdev->cac_start_time;
+ remain_time = (wdev->cac_time_ms -
+ jiffies_to_msecs(jiffies_passed));
+ }
+ if (remain_time > wdev->cac_time_ms)
+ remain_time = 0;
+ }
+ offset = dfs_print_chan(chan, remain_time / 1000, buf, buf_size, offset);
+ remain_time = 0;
+ }
+ }
+
+ return offset;
+}
+
+static ssize_t dfs_status_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wiphy *wiphy = file->private_data;
+ struct wireless_dev *wdev;
+ char *buf;
+ unsigned int offset = 0, buf_size = PAGE_SIZE, r;
+ const char * const iftype_str[] = {
+ [NL80211_IFTYPE_UNSPECIFIED] = "unspecified",
+ [NL80211_IFTYPE_ADHOC] = "adhoc",
+ [NL80211_IFTYPE_STATION] = "station",
+ [NL80211_IFTYPE_AP] = "ap",
+ [NL80211_IFTYPE_AP_VLAN] = "ap vlan",
+ [NL80211_IFTYPE_WDS] = "wds",
+ [NL80211_IFTYPE_MONITOR] = "monitor",
+ [NL80211_IFTYPE_MESH_POINT] = "mesh point",
+ [NL80211_IFTYPE_P2P_CLIENT] = "p2p client",
+ [NL80211_IFTYPE_P2P_GO] = "p2p go",
+ [NL80211_IFTYPE_P2P_DEVICE] = "p2p device",
+ [NL80211_IFTYPE_OCB] = "ocb",
+ [NL80211_IFTYPE_NAN] = "nan",
+ };
+
+ buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ list_for_each_entry(wdev, &wiphy->wdev_list, list) {
+ offset += scnprintf(buf + offset, buf_size - offset,
+ "wdev 0x%x\n"
+ "interface type %s\n",
+ wdev->identifier, iftype_str[wdev->iftype]);
+ offset = dfs_status_read_wdev(wiphy, wdev, buf, buf_size, offset);
+ }
+
+ r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
+
+ kfree(buf);
+
+ return r;
+}
+
+static const struct file_operations dfs_status_ops = {
+ .read = dfs_status_read,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
+static int
+dfs_nop_skip(void *data, u64 val)
+{
+ struct wiphy *wiphy = data;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ bool en = !!val;
+ enum nl80211_band band;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+ u32 nop_time = IEEE80211_DFS_MIN_NOP_TIME_MS;
+ int i;
+
+ if (!en)
+ return 0;
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ sband = wiphy->bands[band];
+ if (!sband)
+ continue;
+ for (i = 0; i < sband->n_channels; i++) {
+ chan = &sband->channels[i];
+
+ if (!(chan->flags & IEEE80211_CHAN_RADAR))
+ continue;
+
+ if (chan->dfs_state == NL80211_DFS_UNAVAILABLE) {
+ // Let current jiffies > dfs_state_entered_jiffies + NOP time
+ chan->dfs_state_entered = jiffies -
+ msecs_to_jiffies(nop_time + 1);
+ }
+ }
+ }
+
+ cfg80211_sched_dfs_chan_update(rdev);
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(dfs_skip_nop_ops, NULL,
+ dfs_nop_skip, "0x%08llx\n");
+
+static int
+dfs_cac_skip(void *data, u64 val)
+{
+ struct wiphy *wiphy = data;
+ struct wireless_dev *wdev;
+ struct cfg80211_chan_def *chandef;
+ bool en = !!val;
+ struct ieee80211_channel *chan;
+
+ if (!en)
+ return 0;
+
+ list_for_each_entry(wdev, &wiphy->wdev_list, list) {
+ chandef = &wdev->chandef;
+ if (chandef->chan) {
+ chan = chandef->chan;
+ if (!(chan->flags & IEEE80211_CHAN_RADAR))
+ continue;
+
+ if (chan->dfs_state == NL80211_DFS_USABLE && wdev->cac_started) {
+ // Let current jiffies > dfs_state_entered_jiffies + CAC time
+ wdev->cac_start_time = jiffies -
+ msecs_to_jiffies(wdev->cac_time_ms + 1);
+ cfg80211_cac_event(wdev->netdev, chandef,
+ NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+ }
+ }
+ }
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(dfs_skip_cac_ops, NULL,
+ dfs_cac_skip, "0x%08llx\n");
+
+#define DEBUGFS_ADD(name, chmod) \
+ debugfs_create_file(#name, chmod, phyd, &rdev->wiphy, &name## _ops)
void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev)
{
struct dentry *phyd = rdev->wiphy.debugfsdir;
- DEBUGFS_ADD(rts_threshold);
- DEBUGFS_ADD(fragmentation_threshold);
- DEBUGFS_ADD(short_retry_limit);
- DEBUGFS_ADD(long_retry_limit);
- DEBUGFS_ADD(ht40allow_map);
+ DEBUGFS_ADD(rts_threshold, 0444);
+ DEBUGFS_ADD(fragmentation_threshold, 0444);
+ DEBUGFS_ADD(short_retry_limit, 0444);
+ DEBUGFS_ADD(long_retry_limit, 0444);
+ DEBUGFS_ADD(ht40allow_map, 0444);
+ DEBUGFS_ADD(dfs_status, 0444);
+ DEBUGFS_ADD(dfs_skip_nop, 0600);
+ DEBUGFS_ADD(dfs_skip_cac, 0600);
}
--
2.18.0