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 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, "%d", id); 177 178 root = debugfs_create_dir(buf, ctx); 179 180 debugfs_create_file("active", 0644, root, &ldev->ctx.table[id], 181 &lowpan_ctx_flag_active_fops); 182 183 debugfs_create_file("compression", 0644, root, &ldev->ctx.table[id], 184 &lowpan_ctx_flag_c_fops); 185 186 debugfs_create_file("prefix", 0644, root, &ldev->ctx.table[id], 187 &lowpan_ctx_pfx_fops); 188 189 debugfs_create_file("prefix_len", 0644, root, &ldev->ctx.table[id], 190 &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(file, "%3s|%-43s|%c\n", "cid", "prefix", 'C'); 199 seq_puts(file, "-------------------------------------------------\n"); 200 201 spin_lock_bh(&t->lock); 202 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) { 203 if (!lowpan_iphc_ctx_is_active(&t->table[i])) 204 continue; 205 206 seq_printf(file, "%3d|%39pI6c/%-3d|%d\n", t->table[i].id, 207 &t->table[i].pfx, t->table[i].plen, 208 lowpan_iphc_ctx_is_compression(&t->table[i])); 209 } 210 spin_unlock_bh(&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, LOWPAN_LLTYPE_IEEE802154)) 236 return; 237 238 root = debugfs_create_dir("ieee802154", ldev->iface_debugfs); 239 240 debugfs_create_file("short_addr", 0444, root, 241 lowpan_802154_dev(dev)->wdev->ieee802154_ptr, 242 &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(dev->name, lowpan_debugfs); 253 254 contexts = debugfs_create_dir("contexts", ldev->iface_debugfs); 255 256 debugfs_create_file("show", 0644, contexts, &lowpan_dev(dev)->ctx, 257 &lowpan_context_fops); 258 259 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) 260 lowpan_dev_debugfs_ctx_init(dev, contexts, 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(lowpan_dev(dev)->iface_debugfs); 268 } 269 270 void __init lowpan_debugfs_init(void) 271 { 272 lowpan_debugfs = debugfs_create_dir("6lowpan", NULL); 273 } 274 275 void lowpan_debugfs_exit(void) 276 { 277 debugfs_remove_recursive(lowpan_debugfs); 278 } 279