Linux 6.10-rc3
[linux-block.git] / net / wireless / debugfs.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * cfg80211 debugfs
4  *
5  * Copyright 2009       Luis R. Rodriguez <lrodriguez@atheros.com>
6  * Copyright 2007       Johannes Berg <johannes@sipsolutions.net>
7  * Copyright (C) 2023 Intel Corporation
8  */
9
10 #include <linux/slab.h>
11 #include "core.h"
12 #include "debugfs.h"
13
14 #define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...)              \
15 static ssize_t name## _read(struct file *file, char __user *userbuf,    \
16                             size_t count, loff_t *ppos)                 \
17 {                                                                       \
18         struct wiphy *wiphy = file->private_data;                       \
19         char buf[buflen];                                               \
20         int res;                                                        \
21                                                                         \
22         res = scnprintf(buf, buflen, fmt "\n", ##value);                \
23         return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
24 }                                                                       \
25                                                                         \
26 static const struct file_operations name## _ops = {                     \
27         .read = name## _read,                                           \
28         .open = simple_open,                                            \
29         .llseek = generic_file_llseek,                                  \
30 }
31
32 DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
33                       wiphy->rts_threshold);
34 DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
35                       wiphy->frag_threshold);
36 DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
37                       wiphy->retry_short);
38 DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
39                       wiphy->retry_long);
40
41 static int ht_print_chan(struct ieee80211_channel *chan,
42                          char *buf, int buf_size, int offset)
43 {
44         if (WARN_ON(offset > buf_size))
45                 return 0;
46
47         if (chan->flags & IEEE80211_CHAN_DISABLED)
48                 return scnprintf(buf + offset,
49                                  buf_size - offset,
50                                  "%d Disabled\n",
51                                  chan->center_freq);
52
53         return scnprintf(buf + offset,
54                          buf_size - offset,
55                          "%d HT40 %c%c\n",
56                          chan->center_freq,
57                          (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ?
58                                 ' ' : '-',
59                          (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ?
60                                 ' ' : '+');
61 }
62
63 static ssize_t ht40allow_map_read(struct file *file,
64                                   char __user *user_buf,
65                                   size_t count, loff_t *ppos)
66 {
67         struct wiphy *wiphy = file->private_data;
68         char *buf;
69         unsigned int offset = 0, buf_size = PAGE_SIZE, i;
70         enum nl80211_band band;
71         struct ieee80211_supported_band *sband;
72         ssize_t r;
73
74         buf = kzalloc(buf_size, GFP_KERNEL);
75         if (!buf)
76                 return -ENOMEM;
77
78         for (band = 0; band < NUM_NL80211_BANDS; band++) {
79                 sband = wiphy->bands[band];
80                 if (!sband)
81                         continue;
82                 for (i = 0; i < sband->n_channels; i++)
83                         offset += ht_print_chan(&sband->channels[i],
84                                                 buf, buf_size, offset);
85         }
86
87         r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
88
89         kfree(buf);
90
91         return r;
92 }
93
94 static const struct file_operations ht40allow_map_ops = {
95         .read = ht40allow_map_read,
96         .open = simple_open,
97         .llseek = default_llseek,
98 };
99
100 #define DEBUGFS_ADD(name)                                               \
101         debugfs_create_file(#name, 0444, phyd, &rdev->wiphy, &name## _ops)
102
103 void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev)
104 {
105         struct dentry *phyd = rdev->wiphy.debugfsdir;
106
107         DEBUGFS_ADD(rts_threshold);
108         DEBUGFS_ADD(fragmentation_threshold);
109         DEBUGFS_ADD(short_retry_limit);
110         DEBUGFS_ADD(long_retry_limit);
111         DEBUGFS_ADD(ht40allow_map);
112 }
113
114 struct debugfs_read_work {
115         struct wiphy_work work;
116         ssize_t (*handler)(struct wiphy *wiphy,
117                            struct file *file,
118                            char *buf,
119                            size_t count,
120                            void *data);
121         struct wiphy *wiphy;
122         struct file *file;
123         char *buf;
124         size_t bufsize;
125         void *data;
126         ssize_t ret;
127         struct completion completion;
128 };
129
130 static void wiphy_locked_debugfs_read_work(struct wiphy *wiphy,
131                                            struct wiphy_work *work)
132 {
133         struct debugfs_read_work *w = container_of(work, typeof(*w), work);
134
135         w->ret = w->handler(w->wiphy, w->file, w->buf, w->bufsize, w->data);
136         complete(&w->completion);
137 }
138
139 static void wiphy_locked_debugfs_read_cancel(struct dentry *dentry,
140                                              void *data)
141 {
142         struct debugfs_read_work *w = data;
143
144         wiphy_work_cancel(w->wiphy, &w->work);
145         complete(&w->completion);
146 }
147
148 ssize_t wiphy_locked_debugfs_read(struct wiphy *wiphy, struct file *file,
149                                   char *buf, size_t bufsize,
150                                   char __user *userbuf, size_t count,
151                                   loff_t *ppos,
152                                   ssize_t (*handler)(struct wiphy *wiphy,
153                                                      struct file *file,
154                                                      char *buf,
155                                                      size_t bufsize,
156                                                      void *data),
157                                   void *data)
158 {
159         struct debugfs_read_work work = {
160                 .handler = handler,
161                 .wiphy = wiphy,
162                 .file = file,
163                 .buf = buf,
164                 .bufsize = bufsize,
165                 .data = data,
166                 .ret = -ENODEV,
167                 .completion = COMPLETION_INITIALIZER_ONSTACK(work.completion),
168         };
169         struct debugfs_cancellation cancellation = {
170                 .cancel = wiphy_locked_debugfs_read_cancel,
171                 .cancel_data = &work,
172         };
173
174         /* don't leak stack data or whatever */
175         memset(buf, 0, bufsize);
176
177         wiphy_work_init(&work.work, wiphy_locked_debugfs_read_work);
178         wiphy_work_queue(wiphy, &work.work);
179
180         debugfs_enter_cancellation(file, &cancellation);
181         wait_for_completion(&work.completion);
182         debugfs_leave_cancellation(file, &cancellation);
183
184         if (work.ret < 0)
185                 return work.ret;
186
187         if (WARN_ON(work.ret > bufsize))
188                 return -EINVAL;
189
190         return simple_read_from_buffer(userbuf, count, ppos, buf, work.ret);
191 }
192 EXPORT_SYMBOL_GPL(wiphy_locked_debugfs_read);
193
194 struct debugfs_write_work {
195         struct wiphy_work work;
196         ssize_t (*handler)(struct wiphy *wiphy,
197                            struct file *file,
198                            char *buf,
199                            size_t count,
200                            void *data);
201         struct wiphy *wiphy;
202         struct file *file;
203         char *buf;
204         size_t count;
205         void *data;
206         ssize_t ret;
207         struct completion completion;
208 };
209
210 static void wiphy_locked_debugfs_write_work(struct wiphy *wiphy,
211                                             struct wiphy_work *work)
212 {
213         struct debugfs_write_work *w = container_of(work, typeof(*w), work);
214
215         w->ret = w->handler(w->wiphy, w->file, w->buf, w->count, w->data);
216         complete(&w->completion);
217 }
218
219 static void wiphy_locked_debugfs_write_cancel(struct dentry *dentry,
220                                               void *data)
221 {
222         struct debugfs_write_work *w = data;
223
224         wiphy_work_cancel(w->wiphy, &w->work);
225         complete(&w->completion);
226 }
227
228 ssize_t wiphy_locked_debugfs_write(struct wiphy *wiphy,
229                                    struct file *file, char *buf, size_t bufsize,
230                                    const char __user *userbuf, size_t count,
231                                    ssize_t (*handler)(struct wiphy *wiphy,
232                                                       struct file *file,
233                                                       char *buf,
234                                                       size_t count,
235                                                       void *data),
236                                    void *data)
237 {
238         struct debugfs_write_work work = {
239                 .handler = handler,
240                 .wiphy = wiphy,
241                 .file = file,
242                 .buf = buf,
243                 .count = count,
244                 .data = data,
245                 .ret = -ENODEV,
246                 .completion = COMPLETION_INITIALIZER_ONSTACK(work.completion),
247         };
248         struct debugfs_cancellation cancellation = {
249                 .cancel = wiphy_locked_debugfs_write_cancel,
250                 .cancel_data = &work,
251         };
252
253         /* mostly used for strings so enforce NUL-termination for safety */
254         if (count >= bufsize)
255                 return -EINVAL;
256
257         memset(buf, 0, bufsize);
258
259         if (copy_from_user(buf, userbuf, count))
260                 return -EFAULT;
261
262         wiphy_work_init(&work.work, wiphy_locked_debugfs_write_work);
263         wiphy_work_queue(wiphy, &work.work);
264
265         debugfs_enter_cancellation(file, &cancellation);
266         wait_for_completion(&work.completion);
267         debugfs_leave_cancellation(file, &cancellation);
268
269         return work.ret;
270 }
271 EXPORT_SYMBOL_GPL(wiphy_locked_debugfs_write);