xref: /linux/net/6lowpan/debugfs.c (revision 42a9fb3382fc2573e92f41d203b095d9a372cfc9)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *
4  * Authors:
5  * (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de>
6  * Copyright (c)  2015 Nordic Semiconductor. All Rights Reserved.
7  */
8 
9 #include <net/6lowpan.h>
10 
11 #include "6lowpan_i.h"
12 
13 #define LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS	8
14 
15 static struct dentry *lowpan_debugfs;
16 
17 static int lowpan_ctx_flag_active_set(void *data, u64 val)
18 {
19 	struct lowpan_iphc_ctx *ctx = data;
20 
21 	if (val != 0 && val != 1)
22 		return -EINVAL;
23 
24 	assign_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags, val);
25 
26 	return 0;
27 }
28 
29 static int lowpan_ctx_flag_active_get(void *data, u64 *val)
30 {
31 	*val = lowpan_iphc_ctx_is_active(data);
32 	return 0;
33 }
34 
35 DEFINE_DEBUGFS_ATTRIBUTE(lowpan_ctx_flag_active_fops,
36 			 lowpan_ctx_flag_active_get,
37 			 lowpan_ctx_flag_active_set, "%llu\n");
38 
39 static int lowpan_ctx_flag_c_set(void *data, u64 val)
40 {
41 	struct lowpan_iphc_ctx *ctx = data;
42 
43 	if (val != 0 && val != 1)
44 		return -EINVAL;
45 
46 	assign_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags, val);
47 
48 	return 0;
49 }
50 
51 static int lowpan_ctx_flag_c_get(void *data, u64 *val)
52 {
53 	*val = lowpan_iphc_ctx_is_compression(data);
54 	return 0;
55 }
56 
57 DEFINE_DEBUGFS_ATTRIBUTE(lowpan_ctx_flag_c_fops, lowpan_ctx_flag_c_get,
58 			 lowpan_ctx_flag_c_set, "%llu\n");
59 
60 static int lowpan_ctx_plen_set(void *data, u64 val)
61 {
62 	struct lowpan_iphc_ctx *ctx = data;
63 	struct lowpan_iphc_ctx_table *t =
64 		container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
65 
66 	if (val > 128)
67 		return -EINVAL;
68 
69 	spin_lock_bh(&t->lock);
70 	ctx->plen = val;
71 	spin_unlock_bh(&t->lock);
72 
73 	return 0;
74 }
75 
76 static int lowpan_ctx_plen_get(void *data, u64 *val)
77 {
78 	struct lowpan_iphc_ctx *ctx = data;
79 	struct lowpan_iphc_ctx_table *t =
80 		container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
81 
82 	spin_lock_bh(&t->lock);
83 	*val = ctx->plen;
84 	spin_unlock_bh(&t->lock);
85 	return 0;
86 }
87 
88 DEFINE_DEBUGFS_ATTRIBUTE(lowpan_ctx_plen_fops, lowpan_ctx_plen_get,
89 			 lowpan_ctx_plen_set, "%llu\n");
90 
91 static int lowpan_ctx_pfx_show(struct seq_file *file, void *offset)
92 {
93 	struct lowpan_iphc_ctx *ctx = file->private;
94 	struct lowpan_iphc_ctx_table *t =
95 		container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
96 
97 	spin_lock_bh(&t->lock);
98 	seq_printf(file, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
99 		   be16_to_cpu(ctx->pfx.s6_addr16[0]),
100 		   be16_to_cpu(ctx->pfx.s6_addr16[1]),
101 		   be16_to_cpu(ctx->pfx.s6_addr16[2]),
102 		   be16_to_cpu(ctx->pfx.s6_addr16[3]),
103 		   be16_to_cpu(ctx->pfx.s6_addr16[4]),
104 		   be16_to_cpu(ctx->pfx.s6_addr16[5]),
105 		   be16_to_cpu(ctx->pfx.s6_addr16[6]),
106 		   be16_to_cpu(ctx->pfx.s6_addr16[7]));
107 	spin_unlock_bh(&t->lock);
108 
109 	return 0;
110 }
111 
112 static int lowpan_ctx_pfx_open(struct inode *inode, struct file *file)
113 {
114 	return single_open(file, lowpan_ctx_pfx_show, inode->i_private);
115 }
116 
117 static ssize_t lowpan_ctx_pfx_write(struct file *fp,
118 				    const char __user *user_buf, size_t count,
119 				    loff_t *ppos)
120 {
121 	char buf[128] = {};
122 	struct seq_file *file = fp->private_data;
123 	struct lowpan_iphc_ctx *ctx = file->private;
124 	struct lowpan_iphc_ctx_table *t =
125 		container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
126 	int status = count, n, i;
127 	unsigned int addr[8];
128 
129 	if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1,
130 						 count))) {
131 		status = -EFAULT;
132 		goto out;
133 	}
134 
135 	n = sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
136 		   &addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
137 		   &addr[5], &addr[6], &addr[7]);
138 	if (n != LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS) {
139 		status = -EINVAL;
140 		goto out;
141 	}
142 
143 	spin_lock_bh(&t->lock);
144 	for (i = 0; i < 8; i++)
145 		ctx->pfx.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
146 	spin_unlock_bh(&t->lock);
147 
148 out:
149 	return status;
150 }
151 
152 static const struct file_operations lowpan_ctx_pfx_fops = {
153 	.open		= lowpan_ctx_pfx_open,
154 	.read		= seq_read,
155 	.write		= lowpan_ctx_pfx_write,
156 	.llseek		= seq_lseek,
157 	.release	= single_release,
158 };
159 
160 static void lowpan_dev_debugfs_ctx_init(struct net_device *dev,
161 					struct dentry *ctx, u8 id)
162 {
163 	struct lowpan_dev *ldev = lowpan_dev(dev);
164 	struct dentry *root;
165 	char buf[32];
166 
167 	if (WARN_ON_ONCE(id >= LOWPAN_IPHC_CTX_TABLE_SIZE))
168 		return;
169 
170 	sprintf(buf, "%d", id);
171 
172 	root = debugfs_create_dir(buf, ctx);
173 
174 	debugfs_create_file("active", 0644, root, &ldev->ctx.table[id],
175 			    &lowpan_ctx_flag_active_fops);
176 
177 	debugfs_create_file("compression", 0644, root, &ldev->ctx.table[id],
178 			    &lowpan_ctx_flag_c_fops);
179 
180 	debugfs_create_file("prefix", 0644, root, &ldev->ctx.table[id],
181 			    &lowpan_ctx_pfx_fops);
182 
183 	debugfs_create_file("prefix_len", 0644, root, &ldev->ctx.table[id],
184 			    &lowpan_ctx_plen_fops);
185 }
186 
187 static int lowpan_context_show(struct seq_file *file, void *offset)
188 {
189 	struct lowpan_iphc_ctx_table *t = file->private;
190 	int i;
191 
192 	seq_printf(file, "%3s|%-43s|%c\n", "cid", "prefix", 'C');
193 	seq_puts(file, "-------------------------------------------------\n");
194 
195 	spin_lock_bh(&t->lock);
196 	for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
197 		if (!lowpan_iphc_ctx_is_active(&t->table[i]))
198 			continue;
199 
200 		seq_printf(file, "%3d|%39pI6c/%-3d|%d\n", t->table[i].id,
201 			   &t->table[i].pfx, t->table[i].plen,
202 			   lowpan_iphc_ctx_is_compression(&t->table[i]));
203 	}
204 	spin_unlock_bh(&t->lock);
205 
206 	return 0;
207 }
208 DEFINE_SHOW_ATTRIBUTE(lowpan_context);
209 
210 static int lowpan_short_addr_get(void *data, u64 *val)
211 {
212 	struct wpan_dev *wdev = data;
213 
214 	rtnl_lock();
215 	*val = le16_to_cpu(wdev->short_addr);
216 	rtnl_unlock();
217 
218 	return 0;
219 }
220 
221 DEFINE_DEBUGFS_ATTRIBUTE(lowpan_short_addr_fops, lowpan_short_addr_get, NULL,
222 			 "0x%04llx\n");
223 
224 static void lowpan_dev_debugfs_802154_init(const struct net_device *dev,
225 					  struct lowpan_dev *ldev)
226 {
227 	struct dentry *root;
228 
229 	if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
230 		return;
231 
232 	root = debugfs_create_dir("ieee802154", ldev->iface_debugfs);
233 
234 	debugfs_create_file("short_addr", 0444, root,
235 			    lowpan_802154_dev(dev)->wdev->ieee802154_ptr,
236 			    &lowpan_short_addr_fops);
237 }
238 
239 void lowpan_dev_debugfs_init(struct net_device *dev)
240 {
241 	struct lowpan_dev *ldev = lowpan_dev(dev);
242 	struct dentry *contexts;
243 	int i;
244 
245 	/* creating the root */
246 	ldev->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
247 
248 	contexts = debugfs_create_dir("contexts", ldev->iface_debugfs);
249 
250 	debugfs_create_file("show", 0644, contexts, &lowpan_dev(dev)->ctx,
251 			    &lowpan_context_fops);
252 
253 	for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
254 		lowpan_dev_debugfs_ctx_init(dev, contexts, i);
255 
256 	lowpan_dev_debugfs_802154_init(dev, ldev);
257 }
258 
259 void lowpan_dev_debugfs_exit(struct net_device *dev)
260 {
261 	debugfs_remove_recursive(lowpan_dev(dev)->iface_debugfs);
262 }
263 
264 void __init lowpan_debugfs_init(void)
265 {
266 	lowpan_debugfs = debugfs_create_dir("6lowpan", NULL);
267 }
268 
269 void lowpan_debugfs_exit(void)
270 {
271 	debugfs_remove_recursive(lowpan_debugfs);
272 }
273