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(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags); 26 else 27 clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &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(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(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags); 51 else 52 clear_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &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(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(&t->lock); 76 ctx->plen = val; 77 spin_unlock_bh(&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(&t->lock); 89 *val = ctx->plen; 90 spin_unlock_bh(&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(&t->lock); 104 seq_printf(file, "%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(&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(&buf, 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(&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(&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 int 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 *dentry, *root; 171 char buf[32]; 172 173 WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE); 174 175 sprintf(buf, "%d", id); 176 177 root = debugfs_create_dir(buf, ctx); 178 if (!root) 179 return -EINVAL; 180 181 dentry = debugfs_create_file_unsafe("active", 0644, root, 182 &ldev->ctx.table[id], 183 &lowpan_ctx_flag_active_fops); 184 if (!dentry) 185 return -EINVAL; 186 187 dentry = debugfs_create_file_unsafe("compression", 0644, root, 188 &ldev->ctx.table[id], 189 &lowpan_ctx_flag_c_fops); 190 if (!dentry) 191 return -EINVAL; 192 193 dentry = debugfs_create_file("prefix", 0644, root, 194 &ldev->ctx.table[id], 195 &lowpan_ctx_pfx_fops); 196 if (!dentry) 197 return -EINVAL; 198 199 dentry = debugfs_create_file_unsafe("prefix_len", 0644, root, 200 &ldev->ctx.table[id], 201 &lowpan_ctx_plen_fops); 202 if (!dentry) 203 return -EINVAL; 204 205 return 0; 206 } 207 208 static int lowpan_context_show(struct seq_file *file, void *offset) 209 { 210 struct lowpan_iphc_ctx_table *t = file->private; 211 int i; 212 213 seq_printf(file, "%3s|%-43s|%c\n", "cid", "prefix", 'C'); 214 seq_puts(file, "-------------------------------------------------\n"); 215 216 spin_lock_bh(&t->lock); 217 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) { 218 if (!lowpan_iphc_ctx_is_active(&t->table[i])) 219 continue; 220 221 seq_printf(file, "%3d|%39pI6c/%-3d|%d\n", t->table[i].id, 222 &t->table[i].pfx, t->table[i].plen, 223 lowpan_iphc_ctx_is_compression(&t->table[i])); 224 } 225 spin_unlock_bh(&t->lock); 226 227 return 0; 228 } 229 DEFINE_SHOW_ATTRIBUTE(lowpan_context); 230 231 static int lowpan_short_addr_get(void *data, u64 *val) 232 { 233 struct wpan_dev *wdev = data; 234 235 rtnl_lock(); 236 *val = le16_to_cpu(wdev->short_addr); 237 rtnl_unlock(); 238 239 return 0; 240 } 241 242 DEFINE_DEBUGFS_ATTRIBUTE(lowpan_short_addr_fops, lowpan_short_addr_get, NULL, 243 "0x%04llx\n"); 244 245 static int lowpan_dev_debugfs_802154_init(const struct net_device *dev, 246 struct lowpan_dev *ldev) 247 { 248 struct dentry *dentry, *root; 249 250 if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) 251 return 0; 252 253 root = debugfs_create_dir("ieee802154", ldev->iface_debugfs); 254 if (!root) 255 return -EINVAL; 256 257 dentry = debugfs_create_file_unsafe("short_addr", 0444, root, 258 lowpan_802154_dev(dev)->wdev->ieee802154_ptr, 259 &lowpan_short_addr_fops); 260 if (!dentry) 261 return -EINVAL; 262 263 return 0; 264 } 265 266 int lowpan_dev_debugfs_init(struct net_device *dev) 267 { 268 struct lowpan_dev *ldev = lowpan_dev(dev); 269 struct dentry *contexts, *dentry; 270 int ret, i; 271 272 /* creating the root */ 273 ldev->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs); 274 if (!ldev->iface_debugfs) 275 goto fail; 276 277 contexts = debugfs_create_dir("contexts", ldev->iface_debugfs); 278 if (!contexts) 279 goto remove_root; 280 281 dentry = debugfs_create_file("show", 0644, contexts, 282 &lowpan_dev(dev)->ctx, 283 &lowpan_context_fops); 284 if (!dentry) 285 goto remove_root; 286 287 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) { 288 ret = lowpan_dev_debugfs_ctx_init(dev, contexts, i); 289 if (ret < 0) 290 goto remove_root; 291 } 292 293 ret = lowpan_dev_debugfs_802154_init(dev, ldev); 294 if (ret < 0) 295 goto remove_root; 296 297 return 0; 298 299 remove_root: 300 lowpan_dev_debugfs_exit(dev); 301 fail: 302 return -EINVAL; 303 } 304 305 void lowpan_dev_debugfs_exit(struct net_device *dev) 306 { 307 debugfs_remove_recursive(lowpan_dev(dev)->iface_debugfs); 308 } 309 310 int __init lowpan_debugfs_init(void) 311 { 312 lowpan_debugfs = debugfs_create_dir("6lowpan", NULL); 313 if (!lowpan_debugfs) 314 return -EINVAL; 315 316 return 0; 317 } 318 319 void lowpan_debugfs_exit(void) 320 { 321 debugfs_remove_recursive(lowpan_debugfs); 322 } 323