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