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 WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE); 174 175 sprintf(buf, "%d", id); 176 177 root = debugfs_create_dir(buf, ctx); 178 179 debugfs_create_file("active", 0644, root, &ldev->ctx.table[id], 180 &lowpan_ctx_flag_active_fops); 181 182 debugfs_create_file("compression", 0644, root, &ldev->ctx.table[id], 183 &lowpan_ctx_flag_c_fops); 184 185 debugfs_create_file("prefix", 0644, root, &ldev->ctx.table[id], 186 &lowpan_ctx_pfx_fops); 187 188 debugfs_create_file("prefix_len", 0644, root, &ldev->ctx.table[id], 189 &lowpan_ctx_plen_fops); 190 } 191 192 static int lowpan_context_show(struct seq_file *file, void *offset) 193 { 194 struct lowpan_iphc_ctx_table *t = file->private; 195 int i; 196 197 seq_printf(file, "%3s|%-43s|%c\n", "cid", "prefix", 'C'); 198 seq_puts(file, "-------------------------------------------------\n"); 199 200 spin_lock_bh(&t->lock); 201 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) { 202 if (!lowpan_iphc_ctx_is_active(&t->table[i])) 203 continue; 204 205 seq_printf(file, "%3d|%39pI6c/%-3d|%d\n", t->table[i].id, 206 &t->table[i].pfx, t->table[i].plen, 207 lowpan_iphc_ctx_is_compression(&t->table[i])); 208 } 209 spin_unlock_bh(&t->lock); 210 211 return 0; 212 } 213 DEFINE_SHOW_ATTRIBUTE(lowpan_context); 214 215 static int lowpan_short_addr_get(void *data, u64 *val) 216 { 217 struct wpan_dev *wdev = data; 218 219 rtnl_lock(); 220 *val = le16_to_cpu(wdev->short_addr); 221 rtnl_unlock(); 222 223 return 0; 224 } 225 226 DEFINE_DEBUGFS_ATTRIBUTE(lowpan_short_addr_fops, lowpan_short_addr_get, NULL, 227 "0x%04llx\n"); 228 229 static void lowpan_dev_debugfs_802154_init(const struct net_device *dev, 230 struct lowpan_dev *ldev) 231 { 232 struct dentry *root; 233 234 if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) 235 return; 236 237 root = debugfs_create_dir("ieee802154", ldev->iface_debugfs); 238 239 debugfs_create_file("short_addr", 0444, root, 240 lowpan_802154_dev(dev)->wdev->ieee802154_ptr, 241 &lowpan_short_addr_fops); 242 } 243 244 void lowpan_dev_debugfs_init(struct net_device *dev) 245 { 246 struct lowpan_dev *ldev = lowpan_dev(dev); 247 struct dentry *contexts; 248 int i; 249 250 /* creating the root */ 251 ldev->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs); 252 253 contexts = debugfs_create_dir("contexts", ldev->iface_debugfs); 254 255 debugfs_create_file("show", 0644, contexts, &lowpan_dev(dev)->ctx, 256 &lowpan_context_fops); 257 258 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) 259 lowpan_dev_debugfs_ctx_init(dev, contexts, i); 260 261 lowpan_dev_debugfs_802154_init(dev, ldev); 262 } 263 264 void lowpan_dev_debugfs_exit(struct net_device *dev) 265 { 266 debugfs_remove_recursive(lowpan_dev(dev)->iface_debugfs); 267 } 268 269 void __init lowpan_debugfs_init(void) 270 { 271 lowpan_debugfs = debugfs_create_dir("6lowpan", NULL); 272 } 273 274 void lowpan_debugfs_exit(void) 275 { 276 debugfs_remove_recursive(lowpan_debugfs); 277 } 278