]>
Commit | Line | Data |
---|---|---|
b1815fd9 AA |
1 | /* This program is free software; you can redistribute it and/or modify |
2 | * it under the terms of the GNU General Public License version 2 | |
3 | * as published by the Free Software Foundation. | |
4 | * | |
5 | * This program is distributed in the hope that it will be useful, | |
6 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
7 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
8 | * GNU General Public License for more details. | |
9 | * | |
10 | * Authors: | |
11 | * (C) 2015 Pengutronix, Alexander Aring <[email protected]> | |
12 | * Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. | |
13 | */ | |
14 | ||
15 | #include <net/6lowpan.h> | |
16 | ||
17 | #include "6lowpan_i.h" | |
18 | ||
5609c185 AA |
19 | #define LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS 8 |
20 | ||
b1815fd9 AA |
21 | static struct dentry *lowpan_debugfs; |
22 | ||
5609c185 AA |
23 | static int lowpan_ctx_flag_active_set(void *data, u64 val) |
24 | { | |
25 | struct lowpan_iphc_ctx *ctx = data; | |
26 | ||
27 | if (val != 0 && val != 1) | |
28 | return -EINVAL; | |
29 | ||
30 | if (val) | |
31 | set_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags); | |
32 | else | |
33 | clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags); | |
34 | ||
35 | return 0; | |
36 | } | |
37 | ||
38 | static int lowpan_ctx_flag_active_get(void *data, u64 *val) | |
39 | { | |
40 | *val = lowpan_iphc_ctx_is_active(data); | |
41 | return 0; | |
42 | } | |
43 | ||
44 | DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_active_fops, | |
45 | lowpan_ctx_flag_active_get, | |
46 | lowpan_ctx_flag_active_set, "%llu\n"); | |
47 | ||
48 | static int lowpan_ctx_flag_c_set(void *data, u64 val) | |
49 | { | |
50 | struct lowpan_iphc_ctx *ctx = data; | |
51 | ||
52 | if (val != 0 && val != 1) | |
53 | return -EINVAL; | |
54 | ||
55 | if (val) | |
56 | set_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags); | |
57 | else | |
58 | clear_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags); | |
59 | ||
60 | return 0; | |
61 | } | |
62 | ||
63 | static int lowpan_ctx_flag_c_get(void *data, u64 *val) | |
64 | { | |
65 | *val = lowpan_iphc_ctx_is_compression(data); | |
66 | return 0; | |
67 | } | |
68 | ||
69 | DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_c_fops, lowpan_ctx_flag_c_get, | |
70 | lowpan_ctx_flag_c_set, "%llu\n"); | |
71 | ||
72 | static int lowpan_ctx_plen_set(void *data, u64 val) | |
73 | { | |
74 | struct lowpan_iphc_ctx *ctx = data; | |
75 | struct lowpan_iphc_ctx_table *t = | |
76 | container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]); | |
77 | ||
78 | if (val > 128) | |
79 | return -EINVAL; | |
80 | ||
81 | spin_lock_bh(&t->lock); | |
82 | ctx->plen = val; | |
83 | spin_unlock_bh(&t->lock); | |
84 | ||
85 | return 0; | |
86 | } | |
87 | ||
88 | static int lowpan_ctx_plen_get(void *data, u64 *val) | |
89 | { | |
90 | struct lowpan_iphc_ctx *ctx = data; | |
91 | struct lowpan_iphc_ctx_table *t = | |
92 | container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]); | |
93 | ||
94 | spin_lock_bh(&t->lock); | |
95 | *val = ctx->plen; | |
96 | spin_unlock_bh(&t->lock); | |
97 | return 0; | |
98 | } | |
99 | ||
100 | DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_plen_fops, lowpan_ctx_plen_get, | |
101 | lowpan_ctx_plen_set, "%llu\n"); | |
102 | ||
103 | static int lowpan_ctx_pfx_show(struct seq_file *file, void *offset) | |
104 | { | |
105 | struct lowpan_iphc_ctx *ctx = file->private; | |
106 | struct lowpan_iphc_ctx_table *t = | |
107 | container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]); | |
108 | ||
109 | spin_lock_bh(&t->lock); | |
110 | seq_printf(file, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", | |
111 | be16_to_cpu(ctx->pfx.s6_addr16[0]), | |
112 | be16_to_cpu(ctx->pfx.s6_addr16[1]), | |
113 | be16_to_cpu(ctx->pfx.s6_addr16[2]), | |
114 | be16_to_cpu(ctx->pfx.s6_addr16[3]), | |
115 | be16_to_cpu(ctx->pfx.s6_addr16[4]), | |
116 | be16_to_cpu(ctx->pfx.s6_addr16[5]), | |
117 | be16_to_cpu(ctx->pfx.s6_addr16[6]), | |
118 | be16_to_cpu(ctx->pfx.s6_addr16[7])); | |
119 | spin_unlock_bh(&t->lock); | |
120 | ||
121 | return 0; | |
122 | } | |
123 | ||
124 | static int lowpan_ctx_pfx_open(struct inode *inode, struct file *file) | |
125 | { | |
126 | return single_open(file, lowpan_ctx_pfx_show, inode->i_private); | |
127 | } | |
128 | ||
129 | static ssize_t lowpan_ctx_pfx_write(struct file *fp, | |
130 | const char __user *user_buf, size_t count, | |
131 | loff_t *ppos) | |
132 | { | |
133 | char buf[128] = {}; | |
134 | struct seq_file *file = fp->private_data; | |
135 | struct lowpan_iphc_ctx *ctx = file->private; | |
136 | struct lowpan_iphc_ctx_table *t = | |
137 | container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]); | |
138 | int status = count, n, i; | |
139 | unsigned int addr[8]; | |
140 | ||
141 | if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1, | |
142 | count))) { | |
143 | status = -EFAULT; | |
144 | goto out; | |
145 | } | |
146 | ||
147 | n = sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", | |
148 | &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], | |
149 | &addr[5], &addr[6], &addr[7]); | |
150 | if (n != LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS) { | |
151 | status = -EINVAL; | |
152 | goto out; | |
153 | } | |
154 | ||
155 | spin_lock_bh(&t->lock); | |
156 | for (i = 0; i < 8; i++) | |
157 | ctx->pfx.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff); | |
158 | spin_unlock_bh(&t->lock); | |
159 | ||
160 | out: | |
161 | return status; | |
162 | } | |
163 | ||
6aaf37b4 | 164 | static const struct file_operations lowpan_ctx_pfx_fops = { |
5609c185 AA |
165 | .open = lowpan_ctx_pfx_open, |
166 | .read = seq_read, | |
167 | .write = lowpan_ctx_pfx_write, | |
168 | .llseek = seq_lseek, | |
169 | .release = single_release, | |
170 | }; | |
171 | ||
172 | static int lowpan_dev_debugfs_ctx_init(struct net_device *dev, | |
173 | struct dentry *ctx, u8 id) | |
174 | { | |
2e4d60cb | 175 | struct lowpan_dev *ldev = lowpan_dev(dev); |
5609c185 AA |
176 | struct dentry *dentry, *root; |
177 | char buf[32]; | |
178 | ||
179 | WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE); | |
180 | ||
181 | sprintf(buf, "%d", id); | |
182 | ||
183 | root = debugfs_create_dir(buf, ctx); | |
184 | if (!root) | |
185 | return -EINVAL; | |
186 | ||
187 | dentry = debugfs_create_file("active", 0644, root, | |
2e4d60cb | 188 | &ldev->ctx.table[id], |
5609c185 AA |
189 | &lowpan_ctx_flag_active_fops); |
190 | if (!dentry) | |
191 | return -EINVAL; | |
192 | ||
193 | dentry = debugfs_create_file("compression", 0644, root, | |
2e4d60cb | 194 | &ldev->ctx.table[id], |
5609c185 AA |
195 | &lowpan_ctx_flag_c_fops); |
196 | if (!dentry) | |
197 | return -EINVAL; | |
198 | ||
199 | dentry = debugfs_create_file("prefix", 0644, root, | |
2e4d60cb | 200 | &ldev->ctx.table[id], |
5609c185 AA |
201 | &lowpan_ctx_pfx_fops); |
202 | if (!dentry) | |
203 | return -EINVAL; | |
204 | ||
205 | dentry = debugfs_create_file("prefix_len", 0644, root, | |
2e4d60cb | 206 | &ldev->ctx.table[id], |
5609c185 AA |
207 | &lowpan_ctx_plen_fops); |
208 | if (!dentry) | |
209 | return -EINVAL; | |
210 | ||
211 | return 0; | |
212 | } | |
213 | ||
214 | static int lowpan_context_show(struct seq_file *file, void *offset) | |
215 | { | |
216 | struct lowpan_iphc_ctx_table *t = file->private; | |
217 | int i; | |
218 | ||
219 | seq_printf(file, "%3s|%-43s|%c\n", "cid", "prefix", 'C'); | |
220 | seq_puts(file, "-------------------------------------------------\n"); | |
221 | ||
222 | spin_lock_bh(&t->lock); | |
223 | for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) { | |
224 | if (!lowpan_iphc_ctx_is_active(&t->table[i])) | |
225 | continue; | |
226 | ||
227 | seq_printf(file, "%3d|%39pI6c/%-3d|%d\n", t->table[i].id, | |
228 | &t->table[i].pfx, t->table[i].plen, | |
229 | lowpan_iphc_ctx_is_compression(&t->table[i])); | |
230 | } | |
231 | spin_unlock_bh(&t->lock); | |
232 | ||
233 | return 0; | |
234 | } | |
235 | ||
236 | static int lowpan_context_open(struct inode *inode, struct file *file) | |
237 | { | |
238 | return single_open(file, lowpan_context_show, inode->i_private); | |
239 | } | |
240 | ||
6aaf37b4 | 241 | static const struct file_operations lowpan_context_fops = { |
5609c185 AA |
242 | .open = lowpan_context_open, |
243 | .read = seq_read, | |
244 | .llseek = seq_lseek, | |
245 | .release = single_release, | |
246 | }; | |
247 | ||
cfce9465 AA |
248 | static int lowpan_short_addr_get(void *data, u64 *val) |
249 | { | |
250 | struct wpan_dev *wdev = data; | |
251 | ||
252 | rtnl_lock(); | |
253 | *val = le16_to_cpu(wdev->short_addr); | |
254 | rtnl_unlock(); | |
255 | ||
256 | return 0; | |
257 | } | |
258 | ||
259 | DEFINE_SIMPLE_ATTRIBUTE(lowpan_short_addr_fops, lowpan_short_addr_get, | |
260 | NULL, "0x%04llx\n"); | |
261 | ||
262 | static int lowpan_dev_debugfs_802154_init(const struct net_device *dev, | |
263 | struct lowpan_dev *ldev) | |
264 | { | |
265 | struct dentry *dentry, *root; | |
266 | ||
267 | if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) | |
268 | return 0; | |
269 | ||
270 | root = debugfs_create_dir("ieee802154", ldev->iface_debugfs); | |
271 | if (!root) | |
272 | return -EINVAL; | |
273 | ||
274 | dentry = debugfs_create_file("short_addr", 0444, root, | |
275 | lowpan_802154_dev(dev)->wdev->ieee802154_ptr, | |
276 | &lowpan_short_addr_fops); | |
277 | if (!dentry) | |
278 | return -EINVAL; | |
279 | ||
280 | return 0; | |
281 | } | |
282 | ||
b1815fd9 AA |
283 | int lowpan_dev_debugfs_init(struct net_device *dev) |
284 | { | |
2e4d60cb | 285 | struct lowpan_dev *ldev = lowpan_dev(dev); |
5609c185 AA |
286 | struct dentry *contexts, *dentry; |
287 | int ret, i; | |
b1815fd9 AA |
288 | |
289 | /* creating the root */ | |
2e4d60cb AA |
290 | ldev->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs); |
291 | if (!ldev->iface_debugfs) | |
b1815fd9 AA |
292 | goto fail; |
293 | ||
2e4d60cb | 294 | contexts = debugfs_create_dir("contexts", ldev->iface_debugfs); |
5609c185 AA |
295 | if (!contexts) |
296 | goto remove_root; | |
297 | ||
298 | dentry = debugfs_create_file("show", 0644, contexts, | |
2e4d60cb | 299 | &lowpan_dev(dev)->ctx, |
5609c185 AA |
300 | &lowpan_context_fops); |
301 | if (!dentry) | |
302 | goto remove_root; | |
303 | ||
304 | for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) { | |
305 | ret = lowpan_dev_debugfs_ctx_init(dev, contexts, i); | |
306 | if (ret < 0) | |
307 | goto remove_root; | |
308 | } | |
309 | ||
cfce9465 AA |
310 | ret = lowpan_dev_debugfs_802154_init(dev, ldev); |
311 | if (ret < 0) | |
312 | goto remove_root; | |
313 | ||
b1815fd9 AA |
314 | return 0; |
315 | ||
5609c185 AA |
316 | remove_root: |
317 | lowpan_dev_debugfs_exit(dev); | |
b1815fd9 AA |
318 | fail: |
319 | return -EINVAL; | |
320 | } | |
321 | ||
322 | void lowpan_dev_debugfs_exit(struct net_device *dev) | |
323 | { | |
2e4d60cb | 324 | debugfs_remove_recursive(lowpan_dev(dev)->iface_debugfs); |
b1815fd9 AA |
325 | } |
326 | ||
327 | int __init lowpan_debugfs_init(void) | |
328 | { | |
329 | lowpan_debugfs = debugfs_create_dir("6lowpan", NULL); | |
330 | if (!lowpan_debugfs) | |
331 | return -EINVAL; | |
332 | ||
333 | return 0; | |
334 | } | |
335 | ||
336 | void lowpan_debugfs_exit(void) | |
337 | { | |
338 | debugfs_remove_recursive(lowpan_debugfs); | |
339 | } |