Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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...) \
15static 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 \
26static const struct file_operations name## _ops = { \
27 .read = name## _read, \
28 .open = simple_open, \
29 .llseek = generic_file_llseek, \
30}
31
32#define DEBUGFS_RADIO_READONLY_FILE(name, buflen, fmt, value...) \
33static ssize_t name## _read(struct file *file, char __user *userbuf, \
34 size_t count, loff_t *ppos) \
35{ \
36 struct wiphy_radio_cfg *radio_cfg = file->private_data; \
37 char buf[buflen]; \
38 int res; \
39 \
40 res = scnprintf(buf, buflen, fmt "\n", ##value); \
41 return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
42} \
43 \
44static const struct file_operations name## _ops = { \
45 .read = name## _read, \
46 .open = simple_open, \
47 .llseek = generic_file_llseek, \
48}
49
50DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
51 wiphy->rts_threshold);
52DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
53 wiphy->frag_threshold);
54DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
55 wiphy->retry_short);
56DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
57 wiphy->retry_long);
58
59DEBUGFS_RADIO_READONLY_FILE(radio_rts_threshold, 20, "%d",
60 radio_cfg->rts_threshold);
61
62static int ht_print_chan(struct ieee80211_channel *chan,
63 char *buf, int buf_size, int offset)
64{
65 if (WARN_ON(offset > buf_size))
66 return 0;
67
68 if (chan->flags & IEEE80211_CHAN_DISABLED)
69 return scnprintf(buf + offset,
70 buf_size - offset,
71 "%d Disabled\n",
72 chan->center_freq);
73
74 return scnprintf(buf + offset,
75 buf_size - offset,
76 "%d HT40 %c%c\n",
77 chan->center_freq,
78 (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ?
79 ' ' : '-',
80 (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ?
81 ' ' : '+');
82}
83
84static ssize_t ht40allow_map_read(struct file *file,
85 char __user *user_buf,
86 size_t count, loff_t *ppos)
87{
88 struct wiphy *wiphy = file->private_data;
89 char *buf;
90 unsigned int offset = 0, buf_size = PAGE_SIZE, i;
91 enum nl80211_band band;
92 struct ieee80211_supported_band *sband;
93 ssize_t r;
94
95 buf = kzalloc(buf_size, GFP_KERNEL);
96 if (!buf)
97 return -ENOMEM;
98
99 for (band = 0; band < NUM_NL80211_BANDS; band++) {
100 sband = wiphy->bands[band];
101 if (!sband)
102 continue;
103 for (i = 0; i < sband->n_channels; i++)
104 offset += ht_print_chan(&sband->channels[i],
105 buf, buf_size, offset);
106 }
107
108 r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
109
110 kfree(buf);
111
112 return r;
113}
114
115static const struct file_operations ht40allow_map_ops = {
116 .read = ht40allow_map_read,
117 .open = simple_open,
118 .llseek = default_llseek,
119};
120
121#define DEBUGFS_ADD(name) \
122 debugfs_create_file(#name, 0444, phyd, &rdev->wiphy, &name## _ops)
123
124#define DEBUGFS_RADIO_ADD(name, radio_idx) \
125 debugfs_create_file(#name, 0444, radiod, \
126 &rdev->wiphy.radio_cfg[radio_idx], \
127 &name## _ops)
128
129void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev)
130{
131 struct dentry *phyd = rdev->wiphy.debugfsdir;
132 struct dentry *radiod;
133 u8 i;
134
135 DEBUGFS_ADD(rts_threshold);
136 DEBUGFS_ADD(fragmentation_threshold);
137 DEBUGFS_ADD(short_retry_limit);
138 DEBUGFS_ADD(long_retry_limit);
139 DEBUGFS_ADD(ht40allow_map);
140
141 for (i = 0; i < rdev->wiphy.n_radio; i++) {
142 radiod = rdev->wiphy.radio_cfg[i].radio_debugfsdir;
143 DEBUGFS_RADIO_ADD(radio_rts_threshold, i);
144 }
145}
146
147struct debugfs_read_work {
148 struct wiphy_work work;
149 ssize_t (*handler)(struct wiphy *wiphy,
150 struct file *file,
151 char *buf,
152 size_t count,
153 void *data);
154 struct wiphy *wiphy;
155 struct file *file;
156 char *buf;
157 size_t bufsize;
158 void *data;
159 ssize_t ret;
160 struct completion completion;
161};
162
163static void wiphy_locked_debugfs_read_work(struct wiphy *wiphy,
164 struct wiphy_work *work)
165{
166 struct debugfs_read_work *w = container_of(work, typeof(*w), work);
167
168 w->ret = w->handler(w->wiphy, w->file, w->buf, w->bufsize, w->data);
169 complete(&w->completion);
170}
171
172static void wiphy_locked_debugfs_read_cancel(struct dentry *dentry,
173 void *data)
174{
175 struct debugfs_read_work *w = data;
176
177 wiphy_work_cancel(w->wiphy, &w->work);
178 complete(&w->completion);
179}
180
181ssize_t wiphy_locked_debugfs_read(struct wiphy *wiphy, struct file *file,
182 char *buf, size_t bufsize,
183 char __user *userbuf, size_t count,
184 loff_t *ppos,
185 ssize_t (*handler)(struct wiphy *wiphy,
186 struct file *file,
187 char *buf,
188 size_t bufsize,
189 void *data),
190 void *data)
191{
192 struct debugfs_read_work work = {
193 .handler = handler,
194 .wiphy = wiphy,
195 .file = file,
196 .buf = buf,
197 .bufsize = bufsize,
198 .data = data,
199 .ret = -ENODEV,
200 .completion = COMPLETION_INITIALIZER_ONSTACK(work.completion),
201 };
202 struct debugfs_cancellation cancellation = {
203 .cancel = wiphy_locked_debugfs_read_cancel,
204 .cancel_data = &work,
205 };
206
207 /* don't leak stack data or whatever */
208 memset(buf, 0, bufsize);
209
210 wiphy_work_init(&work.work, wiphy_locked_debugfs_read_work);
211 wiphy_work_queue(wiphy, &work.work);
212
213 debugfs_enter_cancellation(file, &cancellation);
214 wait_for_completion(&work.completion);
215 debugfs_leave_cancellation(file, &cancellation);
216
217 if (work.ret < 0)
218 return work.ret;
219
220 if (WARN_ON(work.ret > bufsize))
221 return -EINVAL;
222
223 return simple_read_from_buffer(userbuf, count, ppos, buf, work.ret);
224}
225EXPORT_SYMBOL_GPL(wiphy_locked_debugfs_read);
226
227struct debugfs_write_work {
228 struct wiphy_work work;
229 ssize_t (*handler)(struct wiphy *wiphy,
230 struct file *file,
231 char *buf,
232 size_t count,
233 void *data);
234 struct wiphy *wiphy;
235 struct file *file;
236 char *buf;
237 size_t count;
238 void *data;
239 ssize_t ret;
240 struct completion completion;
241};
242
243static void wiphy_locked_debugfs_write_work(struct wiphy *wiphy,
244 struct wiphy_work *work)
245{
246 struct debugfs_write_work *w = container_of(work, typeof(*w), work);
247
248 w->ret = w->handler(w->wiphy, w->file, w->buf, w->count, w->data);
249 complete(&w->completion);
250}
251
252static void wiphy_locked_debugfs_write_cancel(struct dentry *dentry,
253 void *data)
254{
255 struct debugfs_write_work *w = data;
256
257 wiphy_work_cancel(w->wiphy, &w->work);
258 complete(&w->completion);
259}
260
261ssize_t wiphy_locked_debugfs_write(struct wiphy *wiphy,
262 struct file *file, char *buf, size_t bufsize,
263 const char __user *userbuf, size_t count,
264 ssize_t (*handler)(struct wiphy *wiphy,
265 struct file *file,
266 char *buf,
267 size_t count,
268 void *data),
269 void *data)
270{
271 struct debugfs_write_work work = {
272 .handler = handler,
273 .wiphy = wiphy,
274 .file = file,
275 .buf = buf,
276 .count = count,
277 .data = data,
278 .ret = -ENODEV,
279 .completion = COMPLETION_INITIALIZER_ONSTACK(work.completion),
280 };
281 struct debugfs_cancellation cancellation = {
282 .cancel = wiphy_locked_debugfs_write_cancel,
283 .cancel_data = &work,
284 };
285
286 /* mostly used for strings so enforce NUL-termination for safety */
287 if (count >= bufsize)
288 return -EINVAL;
289
290 memset(buf, 0, bufsize);
291
292 if (copy_from_user(buf, userbuf, count))
293 return -EFAULT;
294
295 wiphy_work_init(&work.work, wiphy_locked_debugfs_write_work);
296 wiphy_work_queue(wiphy, &work.work);
297
298 debugfs_enter_cancellation(file, &cancellation);
299 wait_for_completion(&work.completion);
300 debugfs_leave_cancellation(file, &cancellation);
301
302 return work.ret;
303}
304EXPORT_SYMBOL_GPL(wiphy_locked_debugfs_write);