File Index
Files and directories
Sign in
FILE

debugfs.c

File preview with syntax highlighting.

linux-6.19.14/net/wireless/debugfs.c 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...)		\
 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);
~/ Read-only file preview.