xref: /linux-6.15/net/mac80211/debugfs_netdev.c (revision f7862dfe)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e9f207f0SJiri Benc /*
3e9f207f0SJiri Benc  * Copyright (c) 2006	Jiri Benc <[email protected]>
4e9f207f0SJiri Benc  * Copyright 2007	Johannes Berg <[email protected]>
5bc1be54dSMiri Korenblit  * Copyright (C) 2020-2023 Intel Corporation
6e9f207f0SJiri Benc  */
7e9f207f0SJiri Benc 
8e9f207f0SJiri Benc #include <linux/kernel.h>
9e9f207f0SJiri Benc #include <linux/device.h>
10e9f207f0SJiri Benc #include <linux/if.h>
1128656a11SJohannes Berg #include <linux/if_ether.h>
12e9f207f0SJiri Benc #include <linux/interrupt.h>
13e9f207f0SJiri Benc #include <linux/netdevice.h>
14e9f207f0SJiri Benc #include <linux/rtnetlink.h>
155a0e3ad6STejun Heo #include <linux/slab.h>
16e9f207f0SJiri Benc #include <linux/notifier.h>
17e9f207f0SJiri Benc #include <net/mac80211.h>
18e9f207f0SJiri Benc #include <net/cfg80211.h>
19e9f207f0SJiri Benc #include "ieee80211_i.h"
202c8dccc7SJohannes Berg #include "rate.h"
21e9f207f0SJiri Benc #include "debugfs.h"
22e9f207f0SJiri Benc #include "debugfs_netdev.h"
2337a41b4aSEliad Peller #include "driver-ops.h"
24e9f207f0SJiri Benc 
254ded3bfeSJohannes Berg struct ieee80211_if_read_sdata_data {
264ded3bfeSJohannes Berg 	ssize_t (*format)(const struct ieee80211_sub_if_data *, char *, int);
274ded3bfeSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
284ded3bfeSJohannes Berg };
294ded3bfeSJohannes Berg 
ieee80211_if_read_sdata_handler(struct wiphy * wiphy,struct file * file,char * buf,size_t bufsize,void * data)304ded3bfeSJohannes Berg static ssize_t ieee80211_if_read_sdata_handler(struct wiphy *wiphy,
314ded3bfeSJohannes Berg 					       struct file *file,
324ded3bfeSJohannes Berg 					       char *buf,
334ded3bfeSJohannes Berg 					       size_t bufsize,
344ded3bfeSJohannes Berg 					       void *data)
354ded3bfeSJohannes Berg {
364ded3bfeSJohannes Berg 	struct ieee80211_if_read_sdata_data *d = data;
374ded3bfeSJohannes Berg 
384ded3bfeSJohannes Berg 	return d->format(d->sdata, buf, bufsize);
394ded3bfeSJohannes Berg }
404ded3bfeSJohannes Berg 
ieee80211_if_read_sdata(struct file * file,char __user * userbuf,size_t count,loff_t * ppos,ssize_t (* format)(const struct ieee80211_sub_if_data * sdata,char *,int))410ab6cba0SJohannes Berg static ssize_t ieee80211_if_read_sdata(
424ded3bfeSJohannes Berg 	struct file *file,
43e9f207f0SJiri Benc 	char __user *userbuf,
44e9f207f0SJiri Benc 	size_t count, loff_t *ppos,
450ab6cba0SJohannes Berg 	ssize_t (*format)(const struct ieee80211_sub_if_data *sdata, char *, int))
46e9f207f0SJiri Benc {
474ded3bfeSJohannes Berg 	struct ieee80211_sub_if_data *sdata = file->private_data;
484ded3bfeSJohannes Berg 	struct ieee80211_if_read_sdata_data data = {
494ded3bfeSJohannes Berg 		.format = format,
504ded3bfeSJohannes Berg 		.sdata = sdata,
514ded3bfeSJohannes Berg 	};
528d51dbb8SToke Høiland-Jørgensen 	char buf[200];
53e9f207f0SJiri Benc 
544ded3bfeSJohannes Berg 	return wiphy_locked_debugfs_read(sdata->local->hw.wiphy,
554ded3bfeSJohannes Berg 					 file, buf, sizeof(buf),
564ded3bfeSJohannes Berg 					 userbuf, count, ppos,
574ded3bfeSJohannes Berg 					 ieee80211_if_read_sdata_handler,
584ded3bfeSJohannes Berg 					 &data);
594ded3bfeSJohannes Berg }
6073bb3e4aSLuis Carlos Cobo 
614ded3bfeSJohannes Berg struct ieee80211_if_write_sdata_data {
624ded3bfeSJohannes Berg 	ssize_t (*write)(struct ieee80211_sub_if_data *, const char *, int);
634ded3bfeSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
644ded3bfeSJohannes Berg };
6573bb3e4aSLuis Carlos Cobo 
ieee80211_if_write_sdata_handler(struct wiphy * wiphy,struct file * file,char * buf,size_t count,void * data)664ded3bfeSJohannes Berg static ssize_t ieee80211_if_write_sdata_handler(struct wiphy *wiphy,
674ded3bfeSJohannes Berg 						struct file *file,
684ded3bfeSJohannes Berg 						char *buf,
694ded3bfeSJohannes Berg 						size_t count,
704ded3bfeSJohannes Berg 						void *data)
714ded3bfeSJohannes Berg {
724ded3bfeSJohannes Berg 	struct ieee80211_if_write_sdata_data *d = data;
734ded3bfeSJohannes Berg 
744ded3bfeSJohannes Berg 	return d->write(d->sdata, buf, count);
75e9f207f0SJiri Benc }
76e9f207f0SJiri Benc 
ieee80211_if_write_sdata(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos,ssize_t (* write)(struct ieee80211_sub_if_data * sdata,const char *,int))770ab6cba0SJohannes Berg static ssize_t ieee80211_if_write_sdata(
784ded3bfeSJohannes Berg 	struct file *file,
790f78231bSJohannes Berg 	const char __user *userbuf,
800f78231bSJohannes Berg 	size_t count, loff_t *ppos,
810ab6cba0SJohannes Berg 	ssize_t (*write)(struct ieee80211_sub_if_data *sdata, const char *, int))
820f78231bSJohannes Berg {
834ded3bfeSJohannes Berg 	struct ieee80211_sub_if_data *sdata = file->private_data;
844ded3bfeSJohannes Berg 	struct ieee80211_if_write_sdata_data data = {
854ded3bfeSJohannes Berg 		.write = write,
864ded3bfeSJohannes Berg 		.sdata = sdata,
874ded3bfeSJohannes Berg 	};
88ada577c1SEliad Peller 	char buf[64];
890f78231bSJohannes Berg 
904ded3bfeSJohannes Berg 	return wiphy_locked_debugfs_write(sdata->local->hw.wiphy,
914ded3bfeSJohannes Berg 					  file, buf, sizeof(buf),
924ded3bfeSJohannes Berg 					  userbuf, count,
934ded3bfeSJohannes Berg 					  ieee80211_if_write_sdata_handler,
944ded3bfeSJohannes Berg 					  &data);
954ded3bfeSJohannes Berg }
960f78231bSJohannes Berg 
974ded3bfeSJohannes Berg struct ieee80211_if_read_link_data {
984ded3bfeSJohannes Berg 	ssize_t (*format)(const struct ieee80211_link_data *, char *, int);
994ded3bfeSJohannes Berg 	struct ieee80211_link_data *link;
1004ded3bfeSJohannes Berg };
1010f78231bSJohannes Berg 
ieee80211_if_read_link_handler(struct wiphy * wiphy,struct file * file,char * buf,size_t bufsize,void * data)1024ded3bfeSJohannes Berg static ssize_t ieee80211_if_read_link_handler(struct wiphy *wiphy,
1034ded3bfeSJohannes Berg 					      struct file *file,
1044ded3bfeSJohannes Berg 					      char *buf,
1054ded3bfeSJohannes Berg 					      size_t bufsize,
1064ded3bfeSJohannes Berg 					      void *data)
1074ded3bfeSJohannes Berg {
1084ded3bfeSJohannes Berg 	struct ieee80211_if_read_link_data *d = data;
1090ab6cba0SJohannes Berg 
1104ded3bfeSJohannes Berg 	return d->format(d->link, buf, bufsize);
1110ab6cba0SJohannes Berg }
1120ab6cba0SJohannes Berg 
ieee80211_if_read_link(struct file * file,char __user * userbuf,size_t count,loff_t * ppos,ssize_t (* format)(const struct ieee80211_link_data * link,char *,int))1130ab6cba0SJohannes Berg static ssize_t ieee80211_if_read_link(
1144ded3bfeSJohannes Berg 	struct file *file,
1150ab6cba0SJohannes Berg 	char __user *userbuf,
1160ab6cba0SJohannes Berg 	size_t count, loff_t *ppos,
1170ab6cba0SJohannes Berg 	ssize_t (*format)(const struct ieee80211_link_data *link, char *, int))
1180ab6cba0SJohannes Berg {
1194ded3bfeSJohannes Berg 	struct ieee80211_link_data *link = file->private_data;
1204ded3bfeSJohannes Berg 	struct ieee80211_if_read_link_data data = {
1214ded3bfeSJohannes Berg 		.format = format,
1224ded3bfeSJohannes Berg 		.link = link,
1234ded3bfeSJohannes Berg 	};
1240ab6cba0SJohannes Berg 	char buf[200];
1250ab6cba0SJohannes Berg 
1264ded3bfeSJohannes Berg 	return wiphy_locked_debugfs_read(link->sdata->local->hw.wiphy,
1274ded3bfeSJohannes Berg 					 file, buf, sizeof(buf),
1284ded3bfeSJohannes Berg 					 userbuf, count, ppos,
1294ded3bfeSJohannes Berg 					 ieee80211_if_read_link_handler,
1304ded3bfeSJohannes Berg 					 &data);
1314ded3bfeSJohannes Berg }
1320ab6cba0SJohannes Berg 
1334ded3bfeSJohannes Berg struct ieee80211_if_write_link_data {
1344ded3bfeSJohannes Berg 	ssize_t (*write)(struct ieee80211_link_data *, const char *, int);
1354ded3bfeSJohannes Berg 	struct ieee80211_link_data *link;
1364ded3bfeSJohannes Berg };
1370ab6cba0SJohannes Berg 
ieee80211_if_write_link_handler(struct wiphy * wiphy,struct file * file,char * buf,size_t count,void * data)1384ded3bfeSJohannes Berg static ssize_t ieee80211_if_write_link_handler(struct wiphy *wiphy,
1394ded3bfeSJohannes Berg 					       struct file *file,
1404ded3bfeSJohannes Berg 					       char *buf,
1414ded3bfeSJohannes Berg 					       size_t count,
1424ded3bfeSJohannes Berg 					       void *data)
1434ded3bfeSJohannes Berg {
1444ded3bfeSJohannes Berg 	struct ieee80211_if_write_sdata_data *d = data;
1454ded3bfeSJohannes Berg 
1464ded3bfeSJohannes Berg 	return d->write(d->sdata, buf, count);
1470ab6cba0SJohannes Berg }
1480ab6cba0SJohannes Berg 
ieee80211_if_write_link(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos,ssize_t (* write)(struct ieee80211_link_data * link,const char *,int))1490ab6cba0SJohannes Berg static ssize_t ieee80211_if_write_link(
1504ded3bfeSJohannes Berg 	struct file *file,
1510ab6cba0SJohannes Berg 	const char __user *userbuf,
1520ab6cba0SJohannes Berg 	size_t count, loff_t *ppos,
1530ab6cba0SJohannes Berg 	ssize_t (*write)(struct ieee80211_link_data *link, const char *, int))
1540ab6cba0SJohannes Berg {
1554ded3bfeSJohannes Berg 	struct ieee80211_link_data *link = file->private_data;
1564ded3bfeSJohannes Berg 	struct ieee80211_if_write_link_data data = {
1574ded3bfeSJohannes Berg 		.write = write,
1584ded3bfeSJohannes Berg 		.link = link,
1594ded3bfeSJohannes Berg 	};
1600ab6cba0SJohannes Berg 	char buf[64];
1610ab6cba0SJohannes Berg 
1624ded3bfeSJohannes Berg 	return wiphy_locked_debugfs_write(link->sdata->local->hw.wiphy,
1634ded3bfeSJohannes Berg 					  file, buf, sizeof(buf),
1644ded3bfeSJohannes Berg 					  userbuf, count,
1654ded3bfeSJohannes Berg 					  ieee80211_if_write_link_handler,
1664ded3bfeSJohannes Berg 					  &data);
1670f78231bSJohannes Berg }
1680f78231bSJohannes Berg 
169170cd6a6SBenjamin Berg #define IEEE80211_IF_FMT(name, type, field, format_string)		\
170e9f207f0SJiri Benc static ssize_t ieee80211_if_fmt_##name(					\
171170cd6a6SBenjamin Berg 	const type *data, char *buf,					\
172e9f207f0SJiri Benc 	int buflen)							\
173e9f207f0SJiri Benc {									\
174170cd6a6SBenjamin Berg 	return scnprintf(buf, buflen, format_string, data->field);	\
175e9f207f0SJiri Benc }
176170cd6a6SBenjamin Berg #define IEEE80211_IF_FMT_DEC(name, type, field)				\
177170cd6a6SBenjamin Berg 		IEEE80211_IF_FMT(name, type, field, "%d\n")
178170cd6a6SBenjamin Berg #define IEEE80211_IF_FMT_HEX(name, type, field)				\
179170cd6a6SBenjamin Berg 		IEEE80211_IF_FMT(name, type, field, "%#x\n")
180170cd6a6SBenjamin Berg #define IEEE80211_IF_FMT_LHEX(name, type, field)			\
181170cd6a6SBenjamin Berg 		IEEE80211_IF_FMT(name, type, field, "%#lx\n")
182e9f207f0SJiri Benc 
183170cd6a6SBenjamin Berg #define IEEE80211_IF_FMT_HEXARRAY(name, type, field)			\
18419468413SSimon Wunderlich static ssize_t ieee80211_if_fmt_##name(					\
185170cd6a6SBenjamin Berg 	const type *data,						\
18619468413SSimon Wunderlich 	char *buf, int buflen)						\
18719468413SSimon Wunderlich {									\
18819468413SSimon Wunderlich 	char *p = buf;							\
18919468413SSimon Wunderlich 	int i;								\
190170cd6a6SBenjamin Berg 	for (i = 0; i < sizeof(data->field); i++) {			\
19119468413SSimon Wunderlich 		p += scnprintf(p, buflen + buf - p, "%.2x ",		\
192170cd6a6SBenjamin Berg 				 data->field[i]);			\
19319468413SSimon Wunderlich 	}								\
19419468413SSimon Wunderlich 	p += scnprintf(p, buflen + buf - p, "\n");			\
19519468413SSimon Wunderlich 	return p - buf;							\
19619468413SSimon Wunderlich }
19719468413SSimon Wunderlich 
198170cd6a6SBenjamin Berg #define IEEE80211_IF_FMT_ATOMIC(name, type, field)			\
199e9f207f0SJiri Benc static ssize_t ieee80211_if_fmt_##name(					\
200170cd6a6SBenjamin Berg 	const type *data,						\
201e9f207f0SJiri Benc 	char *buf, int buflen)						\
202e9f207f0SJiri Benc {									\
203170cd6a6SBenjamin Berg 	return scnprintf(buf, buflen, "%d\n", atomic_read(&data->field));\
204e9f207f0SJiri Benc }
205e9f207f0SJiri Benc 
206170cd6a6SBenjamin Berg #define IEEE80211_IF_FMT_MAC(name, type, field)				\
207e9f207f0SJiri Benc static ssize_t ieee80211_if_fmt_##name(					\
208170cd6a6SBenjamin Berg 	const type *data, char *buf,					\
209e9f207f0SJiri Benc 	int buflen)							\
210e9f207f0SJiri Benc {									\
211170cd6a6SBenjamin Berg 	return scnprintf(buf, buflen, "%pM\n", data->field);		\
212e9f207f0SJiri Benc }
213e9f207f0SJiri Benc 
214170cd6a6SBenjamin Berg #define IEEE80211_IF_FMT_JIFFIES_TO_MS(name, type, field)		\
21578e443e4SBen Greear static ssize_t ieee80211_if_fmt_##name(					\
216170cd6a6SBenjamin Berg 	const type *data,						\
21778e443e4SBen Greear 	char *buf, int buflen)						\
21878e443e4SBen Greear {									\
21978e443e4SBen Greear 	return scnprintf(buf, buflen, "%d\n",				\
220170cd6a6SBenjamin Berg 			 jiffies_to_msecs(data->field));		\
22178e443e4SBen Greear }
22278e443e4SBen Greear 
2234cd3c4ecSJohannes Berg #define _IEEE80211_IF_FILE_OPS(name, _read, _write)			\
22431cb94f7SJohannes Berg static const struct debugfs_short_fops name##_ops = {				\
2254cd3c4ecSJohannes Berg 	.read = (_read),						\
2264cd3c4ecSJohannes Berg 	.write = (_write),						\
2274cd3c4ecSJohannes Berg 	.llseek = generic_file_llseek,					\
2284cd3c4ecSJohannes Berg }
2294cd3c4ecSJohannes Berg 
2300ab6cba0SJohannes Berg #define _IEEE80211_IF_FILE_R_FN(name)					\
231e9f207f0SJiri Benc static ssize_t ieee80211_if_read_##name(struct file *file,		\
232e9f207f0SJiri Benc 					char __user *userbuf,		\
233e9f207f0SJiri Benc 					size_t count, loff_t *ppos)	\
234e9f207f0SJiri Benc {									\
2354ded3bfeSJohannes Berg 	return ieee80211_if_read_sdata(file,				\
2360ab6cba0SJohannes Berg 				       userbuf, count, ppos,		\
237e9f207f0SJiri Benc 				       ieee80211_if_fmt_##name);	\
238e9f207f0SJiri Benc }
239e9f207f0SJiri Benc 
2400ab6cba0SJohannes Berg #define _IEEE80211_IF_FILE_W_FN(name)					\
2410f78231bSJohannes Berg static ssize_t ieee80211_if_write_##name(struct file *file,		\
2420f78231bSJohannes Berg 					 const char __user *userbuf,	\
2430f78231bSJohannes Berg 					 size_t count, loff_t *ppos)	\
2440f78231bSJohannes Berg {									\
2454ded3bfeSJohannes Berg 	return ieee80211_if_write_sdata(file, userbuf,			\
2460ab6cba0SJohannes Berg 					count, ppos,			\
247170cd6a6SBenjamin Berg 					ieee80211_if_parse_##name);	\
2484cd3c4ecSJohannes Berg }
2490f78231bSJohannes Berg 
2504cd3c4ecSJohannes Berg #define IEEE80211_IF_FILE_R(name)					\
2510ab6cba0SJohannes Berg 	_IEEE80211_IF_FILE_R_FN(name)					\
2524cd3c4ecSJohannes Berg 	_IEEE80211_IF_FILE_OPS(name, ieee80211_if_read_##name, NULL)
2534cd3c4ecSJohannes Berg 
2544cd3c4ecSJohannes Berg #define IEEE80211_IF_FILE_W(name)					\
2550ab6cba0SJohannes Berg 	_IEEE80211_IF_FILE_W_FN(name)					\
2564cd3c4ecSJohannes Berg 	_IEEE80211_IF_FILE_OPS(name, NULL, ieee80211_if_write_##name)
2574cd3c4ecSJohannes Berg 
2584cd3c4ecSJohannes Berg #define IEEE80211_IF_FILE_RW(name)					\
2590ab6cba0SJohannes Berg 	_IEEE80211_IF_FILE_R_FN(name)					\
2600ab6cba0SJohannes Berg 	_IEEE80211_IF_FILE_W_FN(name)					\
2614cd3c4ecSJohannes Berg 	_IEEE80211_IF_FILE_OPS(name, ieee80211_if_read_##name,		\
2624cd3c4ecSJohannes Berg 			       ieee80211_if_write_##name)
2630f78231bSJohannes Berg 
264e9f207f0SJiri Benc #define IEEE80211_IF_FILE(name, field, format)				\
265170cd6a6SBenjamin Berg 	IEEE80211_IF_FMT_##format(name, struct ieee80211_sub_if_data, field) \
2664cd3c4ecSJohannes Berg 	IEEE80211_IF_FILE_R(name)
267e9f207f0SJiri Benc 
2680ab6cba0SJohannes Berg #define _IEEE80211_IF_LINK_R_FN(name)					\
2690ab6cba0SJohannes Berg static ssize_t ieee80211_if_read_##name(struct file *file,		\
2700ab6cba0SJohannes Berg 					char __user *userbuf,		\
2710ab6cba0SJohannes Berg 					size_t count, loff_t *ppos)	\
2720ab6cba0SJohannes Berg {									\
2734ded3bfeSJohannes Berg 	return ieee80211_if_read_link(file,				\
2740ab6cba0SJohannes Berg 				      userbuf, count, ppos,		\
2750ab6cba0SJohannes Berg 				      ieee80211_if_fmt_##name);	\
2760ab6cba0SJohannes Berg }
2770ab6cba0SJohannes Berg 
2780ab6cba0SJohannes Berg #define _IEEE80211_IF_LINK_W_FN(name)					\
2790ab6cba0SJohannes Berg static ssize_t ieee80211_if_write_##name(struct file *file,		\
2800ab6cba0SJohannes Berg 					 const char __user *userbuf,	\
2810ab6cba0SJohannes Berg 					 size_t count, loff_t *ppos)	\
2820ab6cba0SJohannes Berg {									\
2834ded3bfeSJohannes Berg 	return ieee80211_if_write_link(file, userbuf,			\
2840ab6cba0SJohannes Berg 				       count, ppos,			\
2850ab6cba0SJohannes Berg 				       ieee80211_if_parse_##name);	\
2860ab6cba0SJohannes Berg }
2870ab6cba0SJohannes Berg 
288170cd6a6SBenjamin Berg #define IEEE80211_IF_LINK_FILE_R(name)					\
2890ab6cba0SJohannes Berg 	_IEEE80211_IF_LINK_R_FN(name)					\
290170cd6a6SBenjamin Berg 	_IEEE80211_IF_FILE_OPS(link_##name, ieee80211_if_read_##name, NULL)
291170cd6a6SBenjamin Berg 
292170cd6a6SBenjamin Berg #define IEEE80211_IF_LINK_FILE_W(name)					\
2930ab6cba0SJohannes Berg 	_IEEE80211_IF_LINK_W_FN(name)					\
294170cd6a6SBenjamin Berg 	_IEEE80211_IF_FILE_OPS(link_##name, NULL, ieee80211_if_write_##name)
295170cd6a6SBenjamin Berg 
296170cd6a6SBenjamin Berg #define IEEE80211_IF_LINK_FILE_RW(name)					\
2970ab6cba0SJohannes Berg 	_IEEE80211_IF_LINK_R_FN(name)					\
2980ab6cba0SJohannes Berg 	_IEEE80211_IF_LINK_W_FN(name)					\
299170cd6a6SBenjamin Berg 	_IEEE80211_IF_FILE_OPS(link_##name, ieee80211_if_read_##name,	\
300170cd6a6SBenjamin Berg 			       ieee80211_if_write_##name)
301170cd6a6SBenjamin Berg 
302170cd6a6SBenjamin Berg #define IEEE80211_IF_LINK_FILE(name, field, format)				\
303170cd6a6SBenjamin Berg 	IEEE80211_IF_FMT_##format(name, struct ieee80211_link_data, field) \
304170cd6a6SBenjamin Berg 	IEEE80211_IF_LINK_FILE_R(name)
305170cd6a6SBenjamin Berg 
306e9f207f0SJiri Benc /* common attributes */
30757fbcce3SJohannes Berg IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[NL80211_BAND_2GHZ],
30837eb0b16SJouni Malinen 		  HEX);
30957fbcce3SJohannes Berg IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[NL80211_BAND_5GHZ],
31037eb0b16SJouni Malinen 		  HEX);
31119468413SSimon Wunderlich IEEE80211_IF_FILE(rc_rateidx_mcs_mask_2ghz,
31257fbcce3SJohannes Berg 		  rc_rateidx_mcs_mask[NL80211_BAND_2GHZ], HEXARRAY);
31319468413SSimon Wunderlich IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz,
31457fbcce3SJohannes Berg 		  rc_rateidx_mcs_mask[NL80211_BAND_5GHZ], HEXARRAY);
31519468413SSimon Wunderlich 
ieee80211_if_fmt_rc_rateidx_vht_mcs_mask_2ghz(const struct ieee80211_sub_if_data * sdata,char * buf,int buflen)316b119ad6eSLorenzo Bianconi static ssize_t ieee80211_if_fmt_rc_rateidx_vht_mcs_mask_2ghz(
317b119ad6eSLorenzo Bianconi 				const struct ieee80211_sub_if_data *sdata,
318b119ad6eSLorenzo Bianconi 				char *buf, int buflen)
319b119ad6eSLorenzo Bianconi {
320b119ad6eSLorenzo Bianconi 	int i, len = 0;
32157fbcce3SJohannes Berg 	const u16 *mask = sdata->rc_rateidx_vht_mcs_mask[NL80211_BAND_2GHZ];
322b119ad6eSLorenzo Bianconi 
323b119ad6eSLorenzo Bianconi 	for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
324b119ad6eSLorenzo Bianconi 		len += scnprintf(buf + len, buflen - len, "%04x ", mask[i]);
325b119ad6eSLorenzo Bianconi 	len += scnprintf(buf + len, buflen - len, "\n");
326b119ad6eSLorenzo Bianconi 
327b119ad6eSLorenzo Bianconi 	return len;
328b119ad6eSLorenzo Bianconi }
329b119ad6eSLorenzo Bianconi 
330b119ad6eSLorenzo Bianconi IEEE80211_IF_FILE_R(rc_rateidx_vht_mcs_mask_2ghz);
331b119ad6eSLorenzo Bianconi 
ieee80211_if_fmt_rc_rateidx_vht_mcs_mask_5ghz(const struct ieee80211_sub_if_data * sdata,char * buf,int buflen)332b119ad6eSLorenzo Bianconi static ssize_t ieee80211_if_fmt_rc_rateidx_vht_mcs_mask_5ghz(
333b119ad6eSLorenzo Bianconi 				const struct ieee80211_sub_if_data *sdata,
334b119ad6eSLorenzo Bianconi 				char *buf, int buflen)
335b119ad6eSLorenzo Bianconi {
336b119ad6eSLorenzo Bianconi 	int i, len = 0;
33757fbcce3SJohannes Berg 	const u16 *mask = sdata->rc_rateidx_vht_mcs_mask[NL80211_BAND_5GHZ];
338b119ad6eSLorenzo Bianconi 
339b119ad6eSLorenzo Bianconi 	for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
340b119ad6eSLorenzo Bianconi 		len += scnprintf(buf + len, buflen - len, "%04x ", mask[i]);
341b119ad6eSLorenzo Bianconi 	len += scnprintf(buf + len, buflen - len, "\n");
342b119ad6eSLorenzo Bianconi 
343b119ad6eSLorenzo Bianconi 	return len;
344b119ad6eSLorenzo Bianconi }
345b119ad6eSLorenzo Bianconi 
346b119ad6eSLorenzo Bianconi IEEE80211_IF_FILE_R(rc_rateidx_vht_mcs_mask_5ghz);
347b119ad6eSLorenzo Bianconi 
3484914b3bbSBen Greear IEEE80211_IF_FILE(flags, flags, HEX);
3494914b3bbSBen Greear IEEE80211_IF_FILE(state, state, LHEX);
350170cd6a6SBenjamin Berg IEEE80211_IF_LINK_FILE(txpower, conf->txpower, DEC);
351170cd6a6SBenjamin Berg IEEE80211_IF_LINK_FILE(ap_power_level, ap_power_level, DEC);
352170cd6a6SBenjamin Berg IEEE80211_IF_LINK_FILE(user_power_level, user_power_level, DEC);
353e9f207f0SJiri Benc 
35408643315SJohannes Berg static ssize_t
ieee80211_if_fmt_hw_queues(const struct ieee80211_sub_if_data * sdata,char * buf,int buflen)35508643315SJohannes Berg ieee80211_if_fmt_hw_queues(const struct ieee80211_sub_if_data *sdata,
35608643315SJohannes Berg 			   char *buf, int buflen)
35708643315SJohannes Berg {
35808643315SJohannes Berg 	int len;
35908643315SJohannes Berg 
36008643315SJohannes Berg 	len = scnprintf(buf, buflen, "AC queues: VO:%d VI:%d BE:%d BK:%d\n",
36108643315SJohannes Berg 			sdata->vif.hw_queue[IEEE80211_AC_VO],
36208643315SJohannes Berg 			sdata->vif.hw_queue[IEEE80211_AC_VI],
36308643315SJohannes Berg 			sdata->vif.hw_queue[IEEE80211_AC_BE],
36408643315SJohannes Berg 			sdata->vif.hw_queue[IEEE80211_AC_BK]);
36508643315SJohannes Berg 
36608643315SJohannes Berg 	if (sdata->vif.type == NL80211_IFTYPE_AP)
36708643315SJohannes Berg 		len += scnprintf(buf + len, buflen - len, "cab queue: %d\n",
36808643315SJohannes Berg 				 sdata->vif.cab_queue);
36908643315SJohannes Berg 
37008643315SJohannes Berg 	return len;
37108643315SJohannes Berg }
3724cd3c4ecSJohannes Berg IEEE80211_IF_FILE_R(hw_queues);
37308643315SJohannes Berg 
37446900298SJohannes Berg /* STA attributes */
375bfd8403aSJohannes Berg IEEE80211_IF_FILE(bssid, deflink.u.mgd.bssid, MAC);
376f276e20bSJohannes Berg IEEE80211_IF_FILE(aid, vif.cfg.aid, DEC);
37778e443e4SBen Greear IEEE80211_IF_FILE(beacon_timeout, u.mgd.beacon_timeout, JIFFIES_TO_MS);
378e9f207f0SJiri Benc 
ieee80211_set_smps(struct ieee80211_link_data * link,enum ieee80211_smps_mode smps_mode)379170cd6a6SBenjamin Berg static int ieee80211_set_smps(struct ieee80211_link_data *link,
3800f78231bSJohannes Berg 			      enum ieee80211_smps_mode smps_mode)
3810f78231bSJohannes Berg {
382170cd6a6SBenjamin Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
3830f78231bSJohannes Berg 	struct ieee80211_local *local = sdata->local;
3840f78231bSJohannes Berg 
38500f823b6SIlan Peer 	/* The driver indicated that EML is enabled for the interface, thus do
38600f823b6SIlan Peer 	 * not allow to override the SMPS state.
38700f823b6SIlan Peer 	 */
38800f823b6SIlan Peer 	if (sdata->vif.driver_flags & IEEE80211_VIF_EML_ACTIVE)
389bc1be54dSMiri Korenblit 		return -EOPNOTSUPP;
390bc1be54dSMiri Korenblit 
3910d8614b4SEliad Peller 	if (!(local->hw.wiphy->features & NL80211_FEATURE_STATIC_SMPS) &&
3920f78231bSJohannes Berg 	    smps_mode == IEEE80211_SMPS_STATIC)
3930f78231bSJohannes Berg 		return -EINVAL;
3940f78231bSJohannes Berg 
3950f78231bSJohannes Berg 	/* auto should be dynamic if in PS mode */
3960d8614b4SEliad Peller 	if (!(local->hw.wiphy->features & NL80211_FEATURE_DYNAMIC_SMPS) &&
3970f78231bSJohannes Berg 	    (smps_mode == IEEE80211_SMPS_DYNAMIC ||
3980f78231bSJohannes Berg 	     smps_mode == IEEE80211_SMPS_AUTOMATIC))
3990f78231bSJohannes Berg 		return -EINVAL;
4000f78231bSJohannes Berg 
40152b4810bSIlan Peer 	if (sdata->vif.type != NL80211_IFTYPE_STATION)
4020f78231bSJohannes Berg 		return -EOPNOTSUPP;
4030f78231bSJohannes Berg 
404076fc877SJohannes Berg 	return __ieee80211_request_smps_mgd(link->sdata, link, smps_mode);
4050f78231bSJohannes Berg }
4060f78231bSJohannes Berg 
4070f78231bSJohannes Berg static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
4080f78231bSJohannes Berg 	[IEEE80211_SMPS_AUTOMATIC] = "auto",
4090f78231bSJohannes Berg 	[IEEE80211_SMPS_OFF] = "off",
4100f78231bSJohannes Berg 	[IEEE80211_SMPS_STATIC] = "static",
4110f78231bSJohannes Berg 	[IEEE80211_SMPS_DYNAMIC] = "dynamic",
4120f78231bSJohannes Berg };
4130f78231bSJohannes Berg 
ieee80211_if_fmt_smps(const struct ieee80211_link_data * link,char * buf,int buflen)414170cd6a6SBenjamin Berg static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_link_data *link,
4150f78231bSJohannes Berg 				     char *buf, int buflen)
4160f78231bSJohannes Berg {
417170cd6a6SBenjamin Berg 	if (link->sdata->vif.type == NL80211_IFTYPE_STATION)
4180f78231bSJohannes Berg 		return snprintf(buf, buflen, "request: %s\nused: %s\n",
419170cd6a6SBenjamin Berg 				smps_modes[link->u.mgd.req_smps],
420170cd6a6SBenjamin Berg 				smps_modes[link->smps_mode]);
421687da132SEmmanuel Grumbach 	return -EINVAL;
4220f78231bSJohannes Berg }
4230f78231bSJohannes Berg 
ieee80211_if_parse_smps(struct ieee80211_link_data * link,const char * buf,int buflen)424170cd6a6SBenjamin Berg static ssize_t ieee80211_if_parse_smps(struct ieee80211_link_data *link,
4250f78231bSJohannes Berg 				       const char *buf, int buflen)
4260f78231bSJohannes Berg {
4270f78231bSJohannes Berg 	enum ieee80211_smps_mode mode;
4280f78231bSJohannes Berg 
4290f78231bSJohannes Berg 	for (mode = 0; mode < IEEE80211_SMPS_NUM_MODES; mode++) {
4300f78231bSJohannes Berg 		if (strncmp(buf, smps_modes[mode], buflen) == 0) {
431170cd6a6SBenjamin Berg 			int err = ieee80211_set_smps(link, mode);
4320f78231bSJohannes Berg 			if (!err)
4330f78231bSJohannes Berg 				return buflen;
4340f78231bSJohannes Berg 			return err;
4350f78231bSJohannes Berg 		}
4360f78231bSJohannes Berg 	}
4370f78231bSJohannes Berg 
4380f78231bSJohannes Berg 	return -EINVAL;
4390f78231bSJohannes Berg }
440170cd6a6SBenjamin Berg IEEE80211_IF_LINK_FILE_RW(smps);
441681d1190SJouni Malinen 
ieee80211_if_parse_tkip_mic_test(struct ieee80211_sub_if_data * sdata,const char * buf,int buflen)442681d1190SJouni Malinen static ssize_t ieee80211_if_parse_tkip_mic_test(
443681d1190SJouni Malinen 	struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
444681d1190SJouni Malinen {
445681d1190SJouni Malinen 	struct ieee80211_local *local = sdata->local;
446681d1190SJouni Malinen 	u8 addr[ETH_ALEN];
447681d1190SJouni Malinen 	struct sk_buff *skb;
448681d1190SJouni Malinen 	struct ieee80211_hdr *hdr;
449681d1190SJouni Malinen 	__le16 fc;
450681d1190SJouni Malinen 
45128656a11SJohannes Berg 	if (!mac_pton(buf, addr))
452681d1190SJouni Malinen 		return -EINVAL;
453681d1190SJouni Malinen 
454681d1190SJouni Malinen 	if (!ieee80211_sdata_running(sdata))
455681d1190SJouni Malinen 		return -ENOTCONN;
456681d1190SJouni Malinen 
457681d1190SJouni Malinen 	skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 100);
458681d1190SJouni Malinen 	if (!skb)
459681d1190SJouni Malinen 		return -ENOMEM;
460681d1190SJouni Malinen 	skb_reserve(skb, local->hw.extra_tx_headroom);
461681d1190SJouni Malinen 
462b080db58SJohannes Berg 	hdr = skb_put_zero(skb, 24);
463681d1190SJouni Malinen 	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
464681d1190SJouni Malinen 
465681d1190SJouni Malinen 	switch (sdata->vif.type) {
466681d1190SJouni Malinen 	case NL80211_IFTYPE_AP:
467681d1190SJouni Malinen 		fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
468681d1190SJouni Malinen 		/* DA BSSID SA */
469681d1190SJouni Malinen 		memcpy(hdr->addr1, addr, ETH_ALEN);
470681d1190SJouni Malinen 		memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
471681d1190SJouni Malinen 		memcpy(hdr->addr3, sdata->vif.addr, ETH_ALEN);
472681d1190SJouni Malinen 		break;
473681d1190SJouni Malinen 	case NL80211_IFTYPE_STATION:
474681d1190SJouni Malinen 		fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
475681d1190SJouni Malinen 		/* BSSID SA DA */
47641c97a20SJohannes Berg 		if (!sdata->u.mgd.associated) {
477681d1190SJouni Malinen 			dev_kfree_skb(skb);
478681d1190SJouni Malinen 			return -ENOTCONN;
479681d1190SJouni Malinen 		}
480bfd8403aSJohannes Berg 		memcpy(hdr->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN);
481681d1190SJouni Malinen 		memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
482681d1190SJouni Malinen 		memcpy(hdr->addr3, addr, ETH_ALEN);
483681d1190SJouni Malinen 		break;
484681d1190SJouni Malinen 	default:
485681d1190SJouni Malinen 		dev_kfree_skb(skb);
486681d1190SJouni Malinen 		return -EOPNOTSUPP;
487681d1190SJouni Malinen 	}
488681d1190SJouni Malinen 	hdr->frame_control = fc;
489681d1190SJouni Malinen 
490681d1190SJouni Malinen 	/*
491681d1190SJouni Malinen 	 * Add some length to the test frame to make it look bit more valid.
492681d1190SJouni Malinen 	 * The exact contents does not matter since the recipient is required
493681d1190SJouni Malinen 	 * to drop this because of the Michael MIC failure.
494681d1190SJouni Malinen 	 */
495b080db58SJohannes Berg 	skb_put_zero(skb, 50);
496681d1190SJouni Malinen 
497681d1190SJouni Malinen 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_TKIP_MIC_FAILURE;
498681d1190SJouni Malinen 
499681d1190SJouni Malinen 	ieee80211_tx_skb(sdata, skb);
500681d1190SJouni Malinen 
501681d1190SJouni Malinen 	return buflen;
502681d1190SJouni Malinen }
5034cd3c4ecSJohannes Berg IEEE80211_IF_FILE_W(tkip_mic_test);
504681d1190SJouni Malinen 
ieee80211_if_parse_beacon_loss(struct ieee80211_sub_if_data * sdata,const char * buf,int buflen)505802ee9ecSEliad Peller static ssize_t ieee80211_if_parse_beacon_loss(
506802ee9ecSEliad Peller 	struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
507802ee9ecSEliad Peller {
508f276e20bSJohannes Berg 	if (!ieee80211_sdata_running(sdata) || !sdata->vif.cfg.assoc)
509802ee9ecSEliad Peller 		return -ENOTCONN;
510802ee9ecSEliad Peller 
511802ee9ecSEliad Peller 	ieee80211_beacon_loss(&sdata->vif);
512802ee9ecSEliad Peller 
513802ee9ecSEliad Peller 	return buflen;
514802ee9ecSEliad Peller }
515802ee9ecSEliad Peller IEEE80211_IF_FILE_W(beacon_loss);
516802ee9ecSEliad Peller 
ieee80211_if_fmt_uapsd_queues(const struct ieee80211_sub_if_data * sdata,char * buf,int buflen)517dc41e4d4SEliad Peller static ssize_t ieee80211_if_fmt_uapsd_queues(
518dc41e4d4SEliad Peller 	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
519dc41e4d4SEliad Peller {
520dc41e4d4SEliad Peller 	const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
521dc41e4d4SEliad Peller 
522dc41e4d4SEliad Peller 	return snprintf(buf, buflen, "0x%x\n", ifmgd->uapsd_queues);
523dc41e4d4SEliad Peller }
524dc41e4d4SEliad Peller 
ieee80211_if_parse_uapsd_queues(struct ieee80211_sub_if_data * sdata,const char * buf,int buflen)525dc41e4d4SEliad Peller static ssize_t ieee80211_if_parse_uapsd_queues(
526dc41e4d4SEliad Peller 	struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
527dc41e4d4SEliad Peller {
528dc41e4d4SEliad Peller 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
529dc41e4d4SEliad Peller 	u8 val;
530dc41e4d4SEliad Peller 	int ret;
531dc41e4d4SEliad Peller 
532dc41e4d4SEliad Peller 	ret = kstrtou8(buf, 0, &val);
533dc41e4d4SEliad Peller 	if (ret)
534dc41e4d4SEliad Peller 		return ret;
535dc41e4d4SEliad Peller 
536dc41e4d4SEliad Peller 	if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
537dc41e4d4SEliad Peller 		return -ERANGE;
538dc41e4d4SEliad Peller 
539dc41e4d4SEliad Peller 	ifmgd->uapsd_queues = val;
540dc41e4d4SEliad Peller 
541dc41e4d4SEliad Peller 	return buflen;
542dc41e4d4SEliad Peller }
5434cd3c4ecSJohannes Berg IEEE80211_IF_FILE_RW(uapsd_queues);
544dc41e4d4SEliad Peller 
ieee80211_if_fmt_uapsd_max_sp_len(const struct ieee80211_sub_if_data * sdata,char * buf,int buflen)545dc41e4d4SEliad Peller static ssize_t ieee80211_if_fmt_uapsd_max_sp_len(
546dc41e4d4SEliad Peller 	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
547dc41e4d4SEliad Peller {
548dc41e4d4SEliad Peller 	const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
549dc41e4d4SEliad Peller 
550dc41e4d4SEliad Peller 	return snprintf(buf, buflen, "0x%x\n", ifmgd->uapsd_max_sp_len);
551dc41e4d4SEliad Peller }
552dc41e4d4SEliad Peller 
ieee80211_if_parse_uapsd_max_sp_len(struct ieee80211_sub_if_data * sdata,const char * buf,int buflen)553dc41e4d4SEliad Peller static ssize_t ieee80211_if_parse_uapsd_max_sp_len(
554dc41e4d4SEliad Peller 	struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
555dc41e4d4SEliad Peller {
556dc41e4d4SEliad Peller 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
557dc41e4d4SEliad Peller 	unsigned long val;
558dc41e4d4SEliad Peller 	int ret;
559dc41e4d4SEliad Peller 
560dc41e4d4SEliad Peller 	ret = kstrtoul(buf, 0, &val);
561dc41e4d4SEliad Peller 	if (ret)
562dc41e4d4SEliad Peller 		return -EINVAL;
563dc41e4d4SEliad Peller 
564dc41e4d4SEliad Peller 	if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
565dc41e4d4SEliad Peller 		return -ERANGE;
566dc41e4d4SEliad Peller 
567dc41e4d4SEliad Peller 	ifmgd->uapsd_max_sp_len = val;
568dc41e4d4SEliad Peller 
569dc41e4d4SEliad Peller 	return buflen;
570dc41e4d4SEliad Peller }
5714cd3c4ecSJohannes Berg IEEE80211_IF_FILE_RW(uapsd_max_sp_len);
572dc41e4d4SEliad Peller 
ieee80211_if_fmt_tdls_wider_bw(const struct ieee80211_sub_if_data * sdata,char * buf,int buflen)57382c0cc90SArik Nemtsov static ssize_t ieee80211_if_fmt_tdls_wider_bw(
57482c0cc90SArik Nemtsov 	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
57582c0cc90SArik Nemtsov {
57682c0cc90SArik Nemtsov 	const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
57782c0cc90SArik Nemtsov 	bool tdls_wider_bw;
57882c0cc90SArik Nemtsov 
57982c0cc90SArik Nemtsov 	tdls_wider_bw = ieee80211_hw_check(&sdata->local->hw, TDLS_WIDER_BW) &&
58082c0cc90SArik Nemtsov 			!ifmgd->tdls_wider_bw_prohibited;
58182c0cc90SArik Nemtsov 
58282c0cc90SArik Nemtsov 	return snprintf(buf, buflen, "%d\n", tdls_wider_bw);
58382c0cc90SArik Nemtsov }
58482c0cc90SArik Nemtsov 
ieee80211_if_parse_tdls_wider_bw(struct ieee80211_sub_if_data * sdata,const char * buf,int buflen)58582c0cc90SArik Nemtsov static ssize_t ieee80211_if_parse_tdls_wider_bw(
58682c0cc90SArik Nemtsov 	struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
58782c0cc90SArik Nemtsov {
58882c0cc90SArik Nemtsov 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
58982c0cc90SArik Nemtsov 	u8 val;
59082c0cc90SArik Nemtsov 	int ret;
59182c0cc90SArik Nemtsov 
59282c0cc90SArik Nemtsov 	ret = kstrtou8(buf, 0, &val);
59382c0cc90SArik Nemtsov 	if (ret)
59482c0cc90SArik Nemtsov 		return ret;
59582c0cc90SArik Nemtsov 
59682c0cc90SArik Nemtsov 	ifmgd->tdls_wider_bw_prohibited = !val;
59782c0cc90SArik Nemtsov 	return buflen;
59882c0cc90SArik Nemtsov }
59982c0cc90SArik Nemtsov IEEE80211_IF_FILE_RW(tdls_wider_bw);
60082c0cc90SArik Nemtsov 
601e9f207f0SJiri Benc /* AP attributes */
602030ef8f8SFelix Fietkau IEEE80211_IF_FILE(num_mcast_sta, u.ap.num_mcast_sta, ATOMIC);
603d012a605SMarco Porsch IEEE80211_IF_FILE(num_sta_ps, u.ap.ps.num_sta_ps, ATOMIC);
604d012a605SMarco Porsch IEEE80211_IF_FILE(dtim_count, u.ap.ps.dtim_count, DEC);
60572f15d53SMichael Braun IEEE80211_IF_FILE(num_mcast_sta_vlan, u.vlan.num_mcast_sta, ATOMIC);
606e9f207f0SJiri Benc 
ieee80211_if_fmt_num_buffered_multicast(const struct ieee80211_sub_if_data * sdata,char * buf,int buflen)607e9f207f0SJiri Benc static ssize_t ieee80211_if_fmt_num_buffered_multicast(
608e9f207f0SJiri Benc 	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
609e9f207f0SJiri Benc {
610e9f207f0SJiri Benc 	return scnprintf(buf, buflen, "%u\n",
611d012a605SMarco Porsch 			 skb_queue_len(&sdata->u.ap.ps.bc_buf));
612e9f207f0SJiri Benc }
6134cd3c4ecSJohannes Berg IEEE80211_IF_FILE_R(num_buffered_multicast);
614e9f207f0SJiri Benc 
ieee80211_if_fmt_aqm(const struct ieee80211_sub_if_data * sdata,char * buf,int buflen)6158d51dbb8SToke Høiland-Jørgensen static ssize_t ieee80211_if_fmt_aqm(
6168d51dbb8SToke Høiland-Jørgensen 	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
6178d51dbb8SToke Høiland-Jørgensen {
6188d51dbb8SToke Høiland-Jørgensen 	struct ieee80211_local *local = sdata->local;
6198ed31a26SMiaoqing Pan 	struct txq_info *txqi;
6208d51dbb8SToke Høiland-Jørgensen 	int len;
6218d51dbb8SToke Høiland-Jørgensen 
6228ed31a26SMiaoqing Pan 	if (!sdata->vif.txq)
6238ed31a26SMiaoqing Pan 		return 0;
6248ed31a26SMiaoqing Pan 
6258ed31a26SMiaoqing Pan 	txqi = to_txq_info(sdata->vif.txq);
6268ed31a26SMiaoqing Pan 
6278d51dbb8SToke Høiland-Jørgensen 	spin_lock_bh(&local->fq.lock);
6288d51dbb8SToke Høiland-Jørgensen 	rcu_read_lock();
6298d51dbb8SToke Høiland-Jørgensen 
6308d51dbb8SToke Høiland-Jørgensen 	len = scnprintf(buf,
6318d51dbb8SToke Høiland-Jørgensen 			buflen,
6328d51dbb8SToke Høiland-Jørgensen 			"ac backlog-bytes backlog-packets new-flows drops marks overlimit collisions tx-bytes tx-packets\n"
6338d51dbb8SToke Høiland-Jørgensen 			"%u %u %u %u %u %u %u %u %u %u\n",
6348d51dbb8SToke Høiland-Jørgensen 			txqi->txq.ac,
6358d51dbb8SToke Høiland-Jørgensen 			txqi->tin.backlog_bytes,
6368d51dbb8SToke Høiland-Jørgensen 			txqi->tin.backlog_packets,
6378d51dbb8SToke Høiland-Jørgensen 			txqi->tin.flows,
6388d51dbb8SToke Høiland-Jørgensen 			txqi->cstats.drop_count,
6398d51dbb8SToke Høiland-Jørgensen 			txqi->cstats.ecn_mark,
6408d51dbb8SToke Høiland-Jørgensen 			txqi->tin.overlimit,
6418d51dbb8SToke Høiland-Jørgensen 			txqi->tin.collisions,
6428d51dbb8SToke Høiland-Jørgensen 			txqi->tin.tx_bytes,
6438d51dbb8SToke Høiland-Jørgensen 			txqi->tin.tx_packets);
6448d51dbb8SToke Høiland-Jørgensen 
6458d51dbb8SToke Høiland-Jørgensen 	rcu_read_unlock();
6468d51dbb8SToke Høiland-Jørgensen 	spin_unlock_bh(&local->fq.lock);
6478d51dbb8SToke Høiland-Jørgensen 
6488d51dbb8SToke Høiland-Jørgensen 	return len;
6498d51dbb8SToke Høiland-Jørgensen }
6508d51dbb8SToke Høiland-Jørgensen IEEE80211_IF_FILE_R(aqm);
6518d51dbb8SToke Høiland-Jørgensen 
652ebceec86SMichael Braun IEEE80211_IF_FILE(multicast_to_unicast, u.ap.multicast_to_unicast, HEX);
653ebceec86SMichael Braun 
65437a41b4aSEliad Peller /* IBSS attributes */
ieee80211_if_fmt_tsf(const struct ieee80211_sub_if_data * sdata,char * buf,int buflen)65537a41b4aSEliad Peller static ssize_t ieee80211_if_fmt_tsf(
65637a41b4aSEliad Peller 	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
65737a41b4aSEliad Peller {
65837a41b4aSEliad Peller 	struct ieee80211_local *local = sdata->local;
65937a41b4aSEliad Peller 	u64 tsf;
66037a41b4aSEliad Peller 
66137a41b4aSEliad Peller 	tsf = drv_get_tsf(local, (struct ieee80211_sub_if_data *)sdata);
66237a41b4aSEliad Peller 
66337a41b4aSEliad Peller 	return scnprintf(buf, buflen, "0x%016llx\n", (unsigned long long) tsf);
66437a41b4aSEliad Peller }
66537a41b4aSEliad Peller 
ieee80211_if_parse_tsf(struct ieee80211_sub_if_data * sdata,const char * buf,int buflen)66637a41b4aSEliad Peller static ssize_t ieee80211_if_parse_tsf(
66737a41b4aSEliad Peller 	struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
66837a41b4aSEliad Peller {
66937a41b4aSEliad Peller 	struct ieee80211_local *local = sdata->local;
67037a41b4aSEliad Peller 	unsigned long long tsf;
67137a41b4aSEliad Peller 	int ret;
6729bdd3a6bSJavier Cardona 	int tsf_is_delta = 0;
67337a41b4aSEliad Peller 
67437a41b4aSEliad Peller 	if (strncmp(buf, "reset", 5) == 0) {
67537a41b4aSEliad Peller 		if (local->ops->reset_tsf) {
67637a41b4aSEliad Peller 			drv_reset_tsf(local, sdata);
67737a41b4aSEliad Peller 			wiphy_info(local->hw.wiphy, "debugfs reset TSF\n");
67837a41b4aSEliad Peller 		}
67937a41b4aSEliad Peller 	} else {
6809bdd3a6bSJavier Cardona 		if (buflen > 10 && buf[1] == '=') {
6819bdd3a6bSJavier Cardona 			if (buf[0] == '+')
6829bdd3a6bSJavier Cardona 				tsf_is_delta = 1;
6839bdd3a6bSJavier Cardona 			else if (buf[0] == '-')
6849bdd3a6bSJavier Cardona 				tsf_is_delta = -1;
6859bdd3a6bSJavier Cardona 			else
6869bdd3a6bSJavier Cardona 				return -EINVAL;
6879bdd3a6bSJavier Cardona 			buf += 2;
6889bdd3a6bSJavier Cardona 		}
68937a41b4aSEliad Peller 		ret = kstrtoull(buf, 10, &tsf);
69037a41b4aSEliad Peller 		if (ret < 0)
6916fc1da9bSJohannes Berg 			return ret;
692354d381bSPedersen, Thomas 		if (tsf_is_delta && local->ops->offset_tsf) {
693354d381bSPedersen, Thomas 			drv_offset_tsf(local, sdata, tsf_is_delta * tsf);
694354d381bSPedersen, Thomas 			wiphy_info(local->hw.wiphy,
695354d381bSPedersen, Thomas 				   "debugfs offset TSF by %018lld\n",
696354d381bSPedersen, Thomas 				   tsf_is_delta * tsf);
697354d381bSPedersen, Thomas 		} else if (local->ops->set_tsf) {
6989bdd3a6bSJavier Cardona 			if (tsf_is_delta)
699354d381bSPedersen, Thomas 				tsf = drv_get_tsf(local, sdata) +
700354d381bSPedersen, Thomas 				      tsf_is_delta * tsf;
70137a41b4aSEliad Peller 			drv_set_tsf(local, sdata, tsf);
70237a41b4aSEliad Peller 			wiphy_info(local->hw.wiphy,
70337a41b4aSEliad Peller 				   "debugfs set TSF to %#018llx\n", tsf);
70437a41b4aSEliad Peller 		}
70537a41b4aSEliad Peller 	}
70637a41b4aSEliad Peller 
707057d5f4bSThomas Pedersen 	ieee80211_recalc_dtim(local, sdata);
70837a41b4aSEliad Peller 	return buflen;
70937a41b4aSEliad Peller }
7104cd3c4ecSJohannes Berg IEEE80211_IF_FILE_RW(tsf);
71137a41b4aSEliad Peller 
ieee80211_if_fmt_valid_links(const struct ieee80211_sub_if_data * sdata,char * buf,int buflen)7123d901102SJohannes Berg static ssize_t ieee80211_if_fmt_valid_links(const struct ieee80211_sub_if_data *sdata,
7133d901102SJohannes Berg 					    char *buf, int buflen)
7143d901102SJohannes Berg {
7153d901102SJohannes Berg 	return snprintf(buf, buflen, "0x%x\n", sdata->vif.valid_links);
7163d901102SJohannes Berg }
7173d901102SJohannes Berg IEEE80211_IF_FILE_R(valid_links);
7183d901102SJohannes Berg 
ieee80211_if_fmt_active_links(const struct ieee80211_sub_if_data * sdata,char * buf,int buflen)7193d901102SJohannes Berg static ssize_t ieee80211_if_fmt_active_links(const struct ieee80211_sub_if_data *sdata,
7203d901102SJohannes Berg 					     char *buf, int buflen)
7213d901102SJohannes Berg {
7223d901102SJohannes Berg 	return snprintf(buf, buflen, "0x%x\n", sdata->vif.active_links);
7233d901102SJohannes Berg }
7243d901102SJohannes Berg 
ieee80211_if_parse_active_links(struct ieee80211_sub_if_data * sdata,const char * buf,int buflen)7253d901102SJohannes Berg static ssize_t ieee80211_if_parse_active_links(struct ieee80211_sub_if_data *sdata,
7263d901102SJohannes Berg 					       const char *buf, int buflen)
7273d901102SJohannes Berg {
7283d901102SJohannes Berg 	u16 active_links;
7293d901102SJohannes Berg 
7303d901102SJohannes Berg 	if (kstrtou16(buf, 0, &active_links) || !active_links)
7313d901102SJohannes Berg 		return -EINVAL;
7323d901102SJohannes Berg 
7333d901102SJohannes Berg 	return ieee80211_set_active_links(&sdata->vif, active_links) ?: buflen;
7343d901102SJohannes Berg }
7353d901102SJohannes Berg IEEE80211_IF_FILE_RW(active_links);
73637a41b4aSEliad Peller 
737170cd6a6SBenjamin Berg IEEE80211_IF_LINK_FILE(addr, conf->addr, MAC);
738170cd6a6SBenjamin Berg 
7399f42f607SLuis Carlos Cobo #ifdef CONFIG_MAC80211_MESH
740d4a5a489SAshok Nagarajan IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC);
741d4a5a489SAshok Nagarajan 
7429f42f607SLuis Carlos Cobo /* Mesh stats attributes */
743c8a61a7dSDaniel Walker IEEE80211_IF_FILE(fwded_mcast, u.mesh.mshstats.fwded_mcast, DEC);
744c8a61a7dSDaniel Walker IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC);
745472dbc45SJohannes Berg IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC);
746472dbc45SJohannes Berg IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC);
7479f42f607SLuis Carlos Cobo IEEE80211_IF_FILE(dropped_frames_no_route,
748472dbc45SJohannes Berg 		  u.mesh.mshstats.dropped_frames_no_route, DEC);
7499f42f607SLuis Carlos Cobo 
7509f42f607SLuis Carlos Cobo /* Mesh parameters */
75136ff382dSJohannes Berg IEEE80211_IF_FILE(dot11MeshMaxRetries,
75236ff382dSJohannes Berg 		  u.mesh.mshcfg.dot11MeshMaxRetries, DEC);
75336ff382dSJohannes Berg IEEE80211_IF_FILE(dot11MeshRetryTimeout,
75436ff382dSJohannes Berg 		  u.mesh.mshcfg.dot11MeshRetryTimeout, DEC);
75536ff382dSJohannes Berg IEEE80211_IF_FILE(dot11MeshConfirmTimeout,
75636ff382dSJohannes Berg 		  u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC);
75736ff382dSJohannes Berg IEEE80211_IF_FILE(dot11MeshHoldingTimeout,
75836ff382dSJohannes Berg 		  u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC);
75936ff382dSJohannes Berg IEEE80211_IF_FILE(dot11MeshTTL, u.mesh.mshcfg.dot11MeshTTL, DEC);
76045904f21SJavier Cardona IEEE80211_IF_FILE(element_ttl, u.mesh.mshcfg.element_ttl, DEC);
76136ff382dSJohannes Berg IEEE80211_IF_FILE(auto_open_plinks, u.mesh.mshcfg.auto_open_plinks, DEC);
76236ff382dSJohannes Berg IEEE80211_IF_FILE(dot11MeshMaxPeerLinks,
76336ff382dSJohannes Berg 		  u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC);
76436ff382dSJohannes Berg IEEE80211_IF_FILE(dot11MeshHWMPactivePathTimeout,
76536ff382dSJohannes Berg 		  u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC);
76636ff382dSJohannes Berg IEEE80211_IF_FILE(dot11MeshHWMPpreqMinInterval,
76736ff382dSJohannes Berg 		  u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC);
768dca7e943SThomas Pedersen IEEE80211_IF_FILE(dot11MeshHWMPperrMinInterval,
769dca7e943SThomas Pedersen 		  u.mesh.mshcfg.dot11MeshHWMPperrMinInterval, DEC);
77036ff382dSJohannes Berg IEEE80211_IF_FILE(dot11MeshHWMPnetDiameterTraversalTime,
77136ff382dSJohannes Berg 		  u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC);
77236ff382dSJohannes Berg IEEE80211_IF_FILE(dot11MeshHWMPmaxPREQretries,
77336ff382dSJohannes Berg 		  u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC);
77436ff382dSJohannes Berg IEEE80211_IF_FILE(path_refresh_time,
77536ff382dSJohannes Berg 		  u.mesh.mshcfg.path_refresh_time, DEC);
77636ff382dSJohannes Berg IEEE80211_IF_FILE(min_discovery_timeout,
77736ff382dSJohannes Berg 		  u.mesh.mshcfg.min_discovery_timeout, DEC);
77863c5723bSRui Paulo IEEE80211_IF_FILE(dot11MeshHWMPRootMode,
77963c5723bSRui Paulo 		  u.mesh.mshcfg.dot11MeshHWMPRootMode, DEC);
78016dd7267SJavier Cardona IEEE80211_IF_FILE(dot11MeshGateAnnouncementProtocol,
78116dd7267SJavier Cardona 		  u.mesh.mshcfg.dot11MeshGateAnnouncementProtocol, DEC);
7820507e159SJavier Cardona IEEE80211_IF_FILE(dot11MeshHWMPRannInterval,
7830507e159SJavier Cardona 		  u.mesh.mshcfg.dot11MeshHWMPRannInterval, DEC);
78494f90656SChun-Yeow Yeoh IEEE80211_IF_FILE(dot11MeshForwarding, u.mesh.mshcfg.dot11MeshForwarding, DEC);
78555335137SAshok Nagarajan IEEE80211_IF_FILE(rssi_threshold, u.mesh.mshcfg.rssi_threshold, DEC);
7864416f5d2SAshok Nagarajan IEEE80211_IF_FILE(ht_opmode, u.mesh.mshcfg.ht_opmode, DEC);
787ac1073a6SChun-Yeow Yeoh IEEE80211_IF_FILE(dot11MeshHWMPactivePathToRootTimeout,
788ac1073a6SChun-Yeow Yeoh 		  u.mesh.mshcfg.dot11MeshHWMPactivePathToRootTimeout, DEC);
789ac1073a6SChun-Yeow Yeoh IEEE80211_IF_FILE(dot11MeshHWMProotInterval,
790ac1073a6SChun-Yeow Yeoh 		  u.mesh.mshcfg.dot11MeshHWMProotInterval, DEC);
791728b19e5SChun-Yeow Yeoh IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval,
792728b19e5SChun-Yeow Yeoh 		  u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval, DEC);
7933f52b7e3SMarco Porsch IEEE80211_IF_FILE(power_mode, u.mesh.mshcfg.power_mode, DEC);
7943f52b7e3SMarco Porsch IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration,
7953f52b7e3SMarco Porsch 		  u.mesh.mshcfg.dot11MeshAwakeWindowDuration, DEC);
79601d66fbdSBob Copeland IEEE80211_IF_FILE(dot11MeshConnectedToMeshGate,
79701d66fbdSBob Copeland 		  u.mesh.mshcfg.dot11MeshConnectedToMeshGate, DEC);
798e3718a61SLinus Lüssing IEEE80211_IF_FILE(dot11MeshNolearn, u.mesh.mshcfg.dot11MeshNolearn, DEC);
799184eebe6SMarkus Theil IEEE80211_IF_FILE(dot11MeshConnectedToAuthServer,
800184eebe6SMarkus Theil 		  u.mesh.mshcfg.dot11MeshConnectedToAuthServer, DEC);
8019f42f607SLuis Carlos Cobo #endif
8029f42f607SLuis Carlos Cobo 
8030f78231bSJohannes Berg #define DEBUGFS_ADD_MODE(name, mode) \
804ddbfe860SStanislaw Gruszka 	debugfs_create_file(#name, mode, sdata->vif.debugfs_dir, \
80584674ef4STom Rix 			    sdata, &name##_ops)
8060f78231bSJohannes Berg 
807a8df1f58SIlan Peer #define DEBUGFS_ADD_X(_bits, _name, _mode) \
808a8df1f58SIlan Peer 	debugfs_create_x##_bits(#_name, _mode, sdata->vif.debugfs_dir, \
809a8df1f58SIlan Peer 				&sdata->vif._name)
810a8df1f58SIlan Peer 
811a8df1f58SIlan Peer #define DEBUGFS_ADD_X8(_name, _mode) \
812a8df1f58SIlan Peer 	DEBUGFS_ADD_X(8, _name, _mode)
813a8df1f58SIlan Peer 
814a8df1f58SIlan Peer #define DEBUGFS_ADD_X16(_name, _mode) \
815a8df1f58SIlan Peer 	DEBUGFS_ADD_X(16, _name, _mode)
816a8df1f58SIlan Peer 
817a8df1f58SIlan Peer #define DEBUGFS_ADD_X32(_name, _mode) \
818a8df1f58SIlan Peer 	DEBUGFS_ADD_X(32, _name, _mode)
819a8df1f58SIlan Peer 
820fcb2c9e1SFelix Fietkau #define DEBUGFS_ADD(name) DEBUGFS_ADD_MODE(name, 0400)
821fcb2c9e1SFelix Fietkau 
add_common_files(struct ieee80211_sub_if_data * sdata)822fcb2c9e1SFelix Fietkau static void add_common_files(struct ieee80211_sub_if_data *sdata)
823e9f207f0SJiri Benc {
8242d46d7c1SJohannes Berg 	DEBUGFS_ADD(rc_rateidx_mask_2ghz);
8252d46d7c1SJohannes Berg 	DEBUGFS_ADD(rc_rateidx_mask_5ghz);
82619468413SSimon Wunderlich 	DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
82719468413SSimon Wunderlich 	DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
828b119ad6eSLorenzo Bianconi 	DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_2ghz);
829b119ad6eSLorenzo Bianconi 	DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_5ghz);
83008643315SJohannes Berg 	DEBUGFS_ADD(hw_queues);
8318d51dbb8SToke Høiland-Jørgensen 
832107395f9SAlexander Wetzel 	if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
833942741daSFelix Fietkau 	    sdata->vif.type != NL80211_IFTYPE_NAN)
8348d51dbb8SToke Høiland-Jørgensen 		DEBUGFS_ADD(aqm);
835fcb2c9e1SFelix Fietkau }
8363e122be0SJohannes Berg 
add_sta_files(struct ieee80211_sub_if_data * sdata)837fcb2c9e1SFelix Fietkau static void add_sta_files(struct ieee80211_sub_if_data *sdata)
838fcb2c9e1SFelix Fietkau {
8392d46d7c1SJohannes Berg 	DEBUGFS_ADD(bssid);
8402d46d7c1SJohannes Berg 	DEBUGFS_ADD(aid);
84178e443e4SBen Greear 	DEBUGFS_ADD(beacon_timeout);
842681d1190SJouni Malinen 	DEBUGFS_ADD_MODE(tkip_mic_test, 0200);
843802ee9ecSEliad Peller 	DEBUGFS_ADD_MODE(beacon_loss, 0200);
844dc41e4d4SEliad Peller 	DEBUGFS_ADD_MODE(uapsd_queues, 0600);
845dc41e4d4SEliad Peller 	DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600);
84682c0cc90SArik Nemtsov 	DEBUGFS_ADD_MODE(tdls_wider_bw, 0600);
8474cacadc0SIlan Peer 	DEBUGFS_ADD_MODE(valid_links, 0400);
8483d901102SJohannes Berg 	DEBUGFS_ADD_MODE(active_links, 0600);
849a8df1f58SIlan Peer 	DEBUGFS_ADD_X16(dormant_links, 0400);
850e9f207f0SJiri Benc }
851e9f207f0SJiri Benc 
add_ap_files(struct ieee80211_sub_if_data * sdata)852e9f207f0SJiri Benc static void add_ap_files(struct ieee80211_sub_if_data *sdata)
853e9f207f0SJiri Benc {
854030ef8f8SFelix Fietkau 	DEBUGFS_ADD(num_mcast_sta);
8552d46d7c1SJohannes Berg 	DEBUGFS_ADD(num_sta_ps);
8562d46d7c1SJohannes Berg 	DEBUGFS_ADD(dtim_count);
8572d46d7c1SJohannes Berg 	DEBUGFS_ADD(num_buffered_multicast);
858681d1190SJouni Malinen 	DEBUGFS_ADD_MODE(tkip_mic_test, 0200);
859ebceec86SMichael Braun 	DEBUGFS_ADD_MODE(multicast_to_unicast, 0600);
860e9f207f0SJiri Benc }
861e9f207f0SJiri Benc 
add_vlan_files(struct ieee80211_sub_if_data * sdata)86272f15d53SMichael Braun static void add_vlan_files(struct ieee80211_sub_if_data *sdata)
86372f15d53SMichael Braun {
86472f15d53SMichael Braun 	/* add num_mcast_sta_vlan using name num_mcast_sta */
86572f15d53SMichael Braun 	debugfs_create_file("num_mcast_sta", 0400, sdata->vif.debugfs_dir,
86672f15d53SMichael Braun 			    sdata, &num_mcast_sta_vlan_ops);
86772f15d53SMichael Braun }
86872f15d53SMichael Braun 
add_ibss_files(struct ieee80211_sub_if_data * sdata)86937a41b4aSEliad Peller static void add_ibss_files(struct ieee80211_sub_if_data *sdata)
87037a41b4aSEliad Peller {
87137a41b4aSEliad Peller 	DEBUGFS_ADD_MODE(tsf, 0600);
87237a41b4aSEliad Peller }
87337a41b4aSEliad Peller 
8749f42f607SLuis Carlos Cobo #ifdef CONFIG_MAC80211_MESH
8759f42f607SLuis Carlos Cobo 
add_mesh_files(struct ieee80211_sub_if_data * sdata)87612ce8ba3SJavier Cardona static void add_mesh_files(struct ieee80211_sub_if_data *sdata)
87712ce8ba3SJavier Cardona {
87812ce8ba3SJavier Cardona 	DEBUGFS_ADD_MODE(tsf, 0600);
879d4a5a489SAshok Nagarajan 	DEBUGFS_ADD_MODE(estab_plinks, 0400);
88012ce8ba3SJavier Cardona }
88112ce8ba3SJavier Cardona 
add_mesh_stats(struct ieee80211_sub_if_data * sdata)8829f42f607SLuis Carlos Cobo static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
8839f42f607SLuis Carlos Cobo {
8847bcfaf2fSJohannes Berg 	struct dentry *dir = debugfs_create_dir("mesh_stats",
885ddbfe860SStanislaw Gruszka 						sdata->vif.debugfs_dir);
8867bcfaf2fSJohannes Berg #define MESHSTATS_ADD(name)\
88784674ef4STom Rix 	debugfs_create_file(#name, 0400, dir, sdata, &name##_ops)
8887bcfaf2fSJohannes Berg 
889c8a61a7dSDaniel Walker 	MESHSTATS_ADD(fwded_mcast);
890c8a61a7dSDaniel Walker 	MESHSTATS_ADD(fwded_unicast);
8919f42f607SLuis Carlos Cobo 	MESHSTATS_ADD(fwded_frames);
8929f42f607SLuis Carlos Cobo 	MESHSTATS_ADD(dropped_frames_ttl);
8939f42f607SLuis Carlos Cobo 	MESHSTATS_ADD(dropped_frames_no_route);
8947bcfaf2fSJohannes Berg #undef MESHSTATS_ADD
8959f42f607SLuis Carlos Cobo }
8969f42f607SLuis Carlos Cobo 
add_mesh_config(struct ieee80211_sub_if_data * sdata)8979f42f607SLuis Carlos Cobo static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
8989f42f607SLuis Carlos Cobo {
8997bcfaf2fSJohannes Berg 	struct dentry *dir = debugfs_create_dir("mesh_config",
900ddbfe860SStanislaw Gruszka 						sdata->vif.debugfs_dir);
9017bcfaf2fSJohannes Berg 
9027bcfaf2fSJohannes Berg #define MESHPARAMS_ADD(name) \
90384674ef4STom Rix 	debugfs_create_file(#name, 0600, dir, sdata, &name##_ops)
9047bcfaf2fSJohannes Berg 
9059f42f607SLuis Carlos Cobo 	MESHPARAMS_ADD(dot11MeshMaxRetries);
9069f42f607SLuis Carlos Cobo 	MESHPARAMS_ADD(dot11MeshRetryTimeout);
9079f42f607SLuis Carlos Cobo 	MESHPARAMS_ADD(dot11MeshConfirmTimeout);
9089f42f607SLuis Carlos Cobo 	MESHPARAMS_ADD(dot11MeshHoldingTimeout);
9099f42f607SLuis Carlos Cobo 	MESHPARAMS_ADD(dot11MeshTTL);
91045904f21SJavier Cardona 	MESHPARAMS_ADD(element_ttl);
9119f42f607SLuis Carlos Cobo 	MESHPARAMS_ADD(auto_open_plinks);
9129f42f607SLuis Carlos Cobo 	MESHPARAMS_ADD(dot11MeshMaxPeerLinks);
9139f42f607SLuis Carlos Cobo 	MESHPARAMS_ADD(dot11MeshHWMPactivePathTimeout);
9149f42f607SLuis Carlos Cobo 	MESHPARAMS_ADD(dot11MeshHWMPpreqMinInterval);
915dca7e943SThomas Pedersen 	MESHPARAMS_ADD(dot11MeshHWMPperrMinInterval);
9169f42f607SLuis Carlos Cobo 	MESHPARAMS_ADD(dot11MeshHWMPnetDiameterTraversalTime);
9179f42f607SLuis Carlos Cobo 	MESHPARAMS_ADD(dot11MeshHWMPmaxPREQretries);
9189f42f607SLuis Carlos Cobo 	MESHPARAMS_ADD(path_refresh_time);
9199f42f607SLuis Carlos Cobo 	MESHPARAMS_ADD(min_discovery_timeout);
920699403dbSJavier Cardona 	MESHPARAMS_ADD(dot11MeshHWMPRootMode);
9210507e159SJavier Cardona 	MESHPARAMS_ADD(dot11MeshHWMPRannInterval);
9228c06e8c0SChun-Yeow Yeoh 	MESHPARAMS_ADD(dot11MeshForwarding);
92316dd7267SJavier Cardona 	MESHPARAMS_ADD(dot11MeshGateAnnouncementProtocol);
92455335137SAshok Nagarajan 	MESHPARAMS_ADD(rssi_threshold);
9254416f5d2SAshok Nagarajan 	MESHPARAMS_ADD(ht_opmode);
926ac1073a6SChun-Yeow Yeoh 	MESHPARAMS_ADD(dot11MeshHWMPactivePathToRootTimeout);
927ac1073a6SChun-Yeow Yeoh 	MESHPARAMS_ADD(dot11MeshHWMProotInterval);
928728b19e5SChun-Yeow Yeoh 	MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval);
9293f52b7e3SMarco Porsch 	MESHPARAMS_ADD(power_mode);
9303f52b7e3SMarco Porsch 	MESHPARAMS_ADD(dot11MeshAwakeWindowDuration);
93101d66fbdSBob Copeland 	MESHPARAMS_ADD(dot11MeshConnectedToMeshGate);
932e3718a61SLinus Lüssing 	MESHPARAMS_ADD(dot11MeshNolearn);
933184eebe6SMarkus Theil 	MESHPARAMS_ADD(dot11MeshConnectedToAuthServer);
9347bcfaf2fSJohannes Berg #undef MESHPARAMS_ADD
9359f42f607SLuis Carlos Cobo }
9369f42f607SLuis Carlos Cobo #endif
9379f42f607SLuis Carlos Cobo 
add_files(struct ieee80211_sub_if_data * sdata)938e9f207f0SJiri Benc static void add_files(struct ieee80211_sub_if_data *sdata)
939e9f207f0SJiri Benc {
940ddbfe860SStanislaw Gruszka 	if (!sdata->vif.debugfs_dir)
941e9f207f0SJiri Benc 		return;
942e9f207f0SJiri Benc 
943fcb2c9e1SFelix Fietkau 	DEBUGFS_ADD(flags);
944fcb2c9e1SFelix Fietkau 	DEBUGFS_ADD(state);
945fcb2c9e1SFelix Fietkau 
946fcb2c9e1SFelix Fietkau 	if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
947fcb2c9e1SFelix Fietkau 		add_common_files(sdata);
948fcb2c9e1SFelix Fietkau 
94951fb61e7SJohannes Berg 	switch (sdata->vif.type) {
95005c914feSJohannes Berg 	case NL80211_IFTYPE_MESH_POINT:
9519f42f607SLuis Carlos Cobo #ifdef CONFIG_MAC80211_MESH
95212ce8ba3SJavier Cardona 		add_mesh_files(sdata);
9539f42f607SLuis Carlos Cobo 		add_mesh_stats(sdata);
9549f42f607SLuis Carlos Cobo 		add_mesh_config(sdata);
9559f42f607SLuis Carlos Cobo #endif
956472dbc45SJohannes Berg 		break;
95705c914feSJohannes Berg 	case NL80211_IFTYPE_STATION:
958e9f207f0SJiri Benc 		add_sta_files(sdata);
959e9f207f0SJiri Benc 		break;
96046900298SJohannes Berg 	case NL80211_IFTYPE_ADHOC:
96137a41b4aSEliad Peller 		add_ibss_files(sdata);
96246900298SJohannes Berg 		break;
96305c914feSJohannes Berg 	case NL80211_IFTYPE_AP:
964e9f207f0SJiri Benc 		add_ap_files(sdata);
965e9f207f0SJiri Benc 		break;
96672f15d53SMichael Braun 	case NL80211_IFTYPE_AP_VLAN:
96772f15d53SMichael Braun 		add_vlan_files(sdata);
96872f15d53SMichael Braun 		break;
969e9f207f0SJiri Benc 	default:
970e9f207f0SJiri Benc 		break;
971e9f207f0SJiri Benc 	}
972e9f207f0SJiri Benc }
973e9f207f0SJiri Benc 
974170cd6a6SBenjamin Berg #undef DEBUGFS_ADD_MODE
975170cd6a6SBenjamin Berg #undef DEBUGFS_ADD
976170cd6a6SBenjamin Berg 
977170cd6a6SBenjamin Berg #define DEBUGFS_ADD_MODE(dentry, name, mode) \
978170cd6a6SBenjamin Berg 	debugfs_create_file(#name, mode, dentry, \
979170cd6a6SBenjamin Berg 			    link, &link_##name##_ops)
980170cd6a6SBenjamin Berg 
981170cd6a6SBenjamin Berg #define DEBUGFS_ADD(dentry, name) DEBUGFS_ADD_MODE(dentry, name, 0400)
982170cd6a6SBenjamin Berg 
add_link_files(struct ieee80211_link_data * link,struct dentry * dentry)983170cd6a6SBenjamin Berg static void add_link_files(struct ieee80211_link_data *link,
984170cd6a6SBenjamin Berg 			   struct dentry *dentry)
985170cd6a6SBenjamin Berg {
986170cd6a6SBenjamin Berg 	DEBUGFS_ADD(dentry, txpower);
987170cd6a6SBenjamin Berg 	DEBUGFS_ADD(dentry, user_power_level);
988170cd6a6SBenjamin Berg 	DEBUGFS_ADD(dentry, ap_power_level);
989170cd6a6SBenjamin Berg 
990170cd6a6SBenjamin Berg 	switch (link->sdata->vif.type) {
991170cd6a6SBenjamin Berg 	case NL80211_IFTYPE_STATION:
992170cd6a6SBenjamin Berg 		DEBUGFS_ADD_MODE(dentry, smps, 0600);
993170cd6a6SBenjamin Berg 		break;
994170cd6a6SBenjamin Berg 	default:
995170cd6a6SBenjamin Berg 		break;
996170cd6a6SBenjamin Berg 	}
997170cd6a6SBenjamin Berg }
998170cd6a6SBenjamin Berg 
ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data * sdata,bool mld_vif)999733c498aSJohannes Berg static void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata,
1000c942398fSMiri Korenblit 					 bool mld_vif)
1001e9f207f0SJiri Benc {
1002e9f207f0SJiri Benc 	char buf[10+IFNAMSIZ];
1003e9f207f0SJiri Benc 
100447846c9bSJohannes Berg 	sprintf(buf, "netdev:%s", sdata->name);
1005ddbfe860SStanislaw Gruszka 	sdata->vif.debugfs_dir = debugfs_create_dir(buf,
1006e9f207f0SJiri Benc 		sdata->local->hw.wiphy->debugfsdir);
1007c942398fSMiri Korenblit 	/* deflink also has this */
1008c942398fSMiri Korenblit 	sdata->deflink.debugfs_dir = sdata->vif.debugfs_dir;
1009295bafb4SBen Greear 	sdata->debugfs.subdir_stations = debugfs_create_dir("stations",
1010ddbfe860SStanislaw Gruszka 							sdata->vif.debugfs_dir);
101175636525SJohannes Berg 	add_files(sdata);
1012c942398fSMiri Korenblit 	if (!mld_vif)
1013170cd6a6SBenjamin Berg 		add_link_files(&sdata->deflink, sdata->vif.debugfs_dir);
1014e9f207f0SJiri Benc }
1015e9f207f0SJiri Benc 
ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data * sdata)1016e9f207f0SJiri Benc void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata)
1017e9f207f0SJiri Benc {
1018ddbfe860SStanislaw Gruszka 	if (!sdata->vif.debugfs_dir)
10197bcfaf2fSJohannes Berg 		return;
10207bcfaf2fSJohannes Berg 
1021ddbfe860SStanislaw Gruszka 	debugfs_remove_recursive(sdata->vif.debugfs_dir);
1022ddbfe860SStanislaw Gruszka 	sdata->vif.debugfs_dir = NULL;
10234479004eSTom Hughes 	sdata->debugfs.subdir_stations = NULL;
1024e9f207f0SJiri Benc }
1025e9f207f0SJiri Benc 
ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data * sdata)102647846c9bSJohannes Berg void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata)
1027e9f207f0SJiri Benc {
1028*f7862dfeSAl Viro 	debugfs_change_name(sdata->vif.debugfs_dir, "netdev:%s", sdata->name);
1029e9f207f0SJiri Benc }
1030170cd6a6SBenjamin Berg 
ieee80211_debugfs_recreate_netdev(struct ieee80211_sub_if_data * sdata,bool mld_vif)1031c942398fSMiri Korenblit void ieee80211_debugfs_recreate_netdev(struct ieee80211_sub_if_data *sdata,
1032c942398fSMiri Korenblit 				       bool mld_vif)
1033c942398fSMiri Korenblit {
1034c942398fSMiri Korenblit 	ieee80211_debugfs_remove_netdev(sdata);
1035c942398fSMiri Korenblit 	ieee80211_debugfs_add_netdev(sdata, mld_vif);
10360a3d898eSBenjamin Berg 
10370a3d898eSBenjamin Berg 	if (sdata->flags & IEEE80211_SDATA_IN_DRIVER) {
1038c942398fSMiri Korenblit 		drv_vif_add_debugfs(sdata->local, sdata);
1039c942398fSMiri Korenblit 		if (!mld_vif)
1040c942398fSMiri Korenblit 			ieee80211_link_debugfs_drv_add(&sdata->deflink);
1041c942398fSMiri Korenblit 	}
10420a3d898eSBenjamin Berg }
1043c942398fSMiri Korenblit 
ieee80211_link_debugfs_add(struct ieee80211_link_data * link)1044170cd6a6SBenjamin Berg void ieee80211_link_debugfs_add(struct ieee80211_link_data *link)
1045170cd6a6SBenjamin Berg {
1046170cd6a6SBenjamin Berg 	char link_dir_name[10];
1047170cd6a6SBenjamin Berg 
1048c942398fSMiri Korenblit 	if (WARN_ON(!link->sdata->vif.debugfs_dir || link->debugfs_dir))
1049170cd6a6SBenjamin Berg 		return;
1050170cd6a6SBenjamin Berg 
1051170cd6a6SBenjamin Berg 	/* For now, this should not be called for non-MLO capable drivers */
1052170cd6a6SBenjamin Berg 	if (WARN_ON(!(link->sdata->local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO)))
1053170cd6a6SBenjamin Berg 		return;
1054170cd6a6SBenjamin Berg 
1055170cd6a6SBenjamin Berg 	snprintf(link_dir_name, sizeof(link_dir_name),
1056170cd6a6SBenjamin Berg 		 "link-%d", link->link_id);
1057170cd6a6SBenjamin Berg 
1058170cd6a6SBenjamin Berg 	link->debugfs_dir =
1059170cd6a6SBenjamin Berg 		debugfs_create_dir(link_dir_name,
1060170cd6a6SBenjamin Berg 				   link->sdata->vif.debugfs_dir);
1061170cd6a6SBenjamin Berg 
1062170cd6a6SBenjamin Berg 	DEBUGFS_ADD(link->debugfs_dir, addr);
1063170cd6a6SBenjamin Berg 	add_link_files(link, link->debugfs_dir);
1064170cd6a6SBenjamin Berg }
1065170cd6a6SBenjamin Berg 
ieee80211_link_debugfs_remove(struct ieee80211_link_data * link)1066170cd6a6SBenjamin Berg void ieee80211_link_debugfs_remove(struct ieee80211_link_data *link)
1067170cd6a6SBenjamin Berg {
1068170cd6a6SBenjamin Berg 	if (!link->sdata->vif.debugfs_dir || !link->debugfs_dir) {
1069170cd6a6SBenjamin Berg 		link->debugfs_dir = NULL;
1070170cd6a6SBenjamin Berg 		return;
1071170cd6a6SBenjamin Berg 	}
1072170cd6a6SBenjamin Berg 
1073170cd6a6SBenjamin Berg 	if (link->debugfs_dir == link->sdata->vif.debugfs_dir) {
1074170cd6a6SBenjamin Berg 		WARN_ON(link != &link->sdata->deflink);
1075170cd6a6SBenjamin Berg 		link->debugfs_dir = NULL;
1076170cd6a6SBenjamin Berg 		return;
1077170cd6a6SBenjamin Berg 	}
1078170cd6a6SBenjamin Berg 
1079170cd6a6SBenjamin Berg 	debugfs_remove_recursive(link->debugfs_dir);
1080170cd6a6SBenjamin Berg 	link->debugfs_dir = NULL;
1081170cd6a6SBenjamin Berg }
1082170cd6a6SBenjamin Berg 
ieee80211_link_debugfs_drv_add(struct ieee80211_link_data * link)1083170cd6a6SBenjamin Berg void ieee80211_link_debugfs_drv_add(struct ieee80211_link_data *link)
1084170cd6a6SBenjamin Berg {
1085c942398fSMiri Korenblit 	if (link->sdata->vif.type == NL80211_IFTYPE_MONITOR ||
1086c942398fSMiri Korenblit 	    WARN_ON(!link->debugfs_dir))
1087170cd6a6SBenjamin Berg 		return;
1088170cd6a6SBenjamin Berg 
1089170cd6a6SBenjamin Berg 	drv_link_add_debugfs(link->sdata->local, link->sdata,
1090170cd6a6SBenjamin Berg 			     link->conf, link->debugfs_dir);
1091170cd6a6SBenjamin Berg }
1092170cd6a6SBenjamin Berg 
ieee80211_link_debugfs_drv_remove(struct ieee80211_link_data * link)1093170cd6a6SBenjamin Berg void ieee80211_link_debugfs_drv_remove(struct ieee80211_link_data *link)
1094170cd6a6SBenjamin Berg {
1095170cd6a6SBenjamin Berg 	if (!link || !link->debugfs_dir)
1096170cd6a6SBenjamin Berg 		return;
1097170cd6a6SBenjamin Berg 
1098170cd6a6SBenjamin Berg 	if (WARN_ON(link->debugfs_dir == link->sdata->vif.debugfs_dir))
1099170cd6a6SBenjamin Berg 		return;
1100170cd6a6SBenjamin Berg 
1101170cd6a6SBenjamin Berg 	/* Recreate the directory excluding the driver data */
1102170cd6a6SBenjamin Berg 	debugfs_remove_recursive(link->debugfs_dir);
1103170cd6a6SBenjamin Berg 	link->debugfs_dir = NULL;
1104170cd6a6SBenjamin Berg 
1105170cd6a6SBenjamin Berg 	ieee80211_link_debugfs_add(link);
1106170cd6a6SBenjamin Berg }
1107