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