1 /* This program is free software; you can redistribute it and/or modify 2 * it under the terms of the GNU General Public License version 2 3 * as published by the Free Software Foundation. 4 * 5 * This program is distributed in the hope that it will be useful, 6 * but WITHOUT ANY WARRANTY; without even the implied warranty of 7 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 * GNU General Public License for more details. 9 * 10 * Authors: 11 * (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de> 12 * Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. 13 */ 14 15 #include <net/6lowpan.h> 16 17 #include "6lowpan_i.h" 18 19 #define LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS 8 20 21 static struct dentry *lowpan_debugfs; 22 23 static int lowpan_ctx_flag_active_set(void *data, u64 val) 24 { 25 struct lowpan_iphc_ctx *ctx = data; 26 27 if (val != 0 && val != 1) 28 return -EINVAL; 29 30 if (val) 31 set_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags); 32 else 33 clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags); 34 35 return 0; 36 } 37 38 static int lowpan_ctx_flag_active_get(void *data, u64 *val) 39 { 40 *val = lowpan_iphc_ctx_is_active(data); 41 return 0; 42 } 43 44 DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_active_fops, 45 lowpan_ctx_flag_active_get, 46 lowpan_ctx_flag_active_set, "%llu\n"); 47 48 static int lowpan_ctx_flag_c_set(void *data, u64 val) 49 { 50 struct lowpan_iphc_ctx *ctx = data; 51 52 if (val != 0 && val != 1) 53 return -EINVAL; 54 55 if (val) 56 set_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags); 57 else 58 clear_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags); 59 60 return 0; 61 } 62 63 static int lowpan_ctx_flag_c_get(void *data, u64 *val) 64 { 65 *val = lowpan_iphc_ctx_is_compression(data); 66 return 0; 67 } 68 69 DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_c_fops, lowpan_ctx_flag_c_get, 70 lowpan_ctx_flag_c_set, "%llu\n"); 71 72 static int lowpan_ctx_plen_set(void *data, u64 val) 73 { 74 struct lowpan_iphc_ctx *ctx = data; 75 struct lowpan_iphc_ctx_table *t = 76 container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]); 77 78 if (val > 128) 79 return -EINVAL; 80 81 spin_lock_bh(&t->lock); 82 ctx->plen = val; 83 spin_unlock_bh(&t->lock); 84 85 return 0; 86 } 87 88 static int lowpan_ctx_plen_get(void *data, u64 *val) 89 { 90 struct lowpan_iphc_ctx *ctx = data; 91 struct lowpan_iphc_ctx_table *t = 92 container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]); 93 94 spin_lock_bh(&t->lock); 95 *val = ctx->plen; 96 spin_unlock_bh(&t->lock); 97 return 0; 98 } 99 100 DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_plen_fops, lowpan_ctx_plen_get, 101 lowpan_ctx_plen_set, "%llu\n"); 102 103 static int lowpan_ctx_pfx_show(struct seq_file *file, void *offset) 104 { 105 struct lowpan_iphc_ctx *ctx = file->private; 106 struct lowpan_iphc_ctx_table *t = 107 container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]); 108 109 spin_lock_bh(&t->lock); 110 seq_printf(file, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", 111 be16_to_cpu(ctx->pfx.s6_addr16[0]), 112 be16_to_cpu(ctx->pfx.s6_addr16[1]), 113 be16_to_cpu(ctx->pfx.s6_addr16[2]), 114 be16_to_cpu(ctx->pfx.s6_addr16[3]), 115 be16_to_cpu(ctx->pfx.s6_addr16[4]), 116 be16_to_cpu(ctx->pfx.s6_addr16[5]), 117 be16_to_cpu(ctx->pfx.s6_addr16[6]), 118 be16_to_cpu(ctx->pfx.s6_addr16[7])); 119 spin_unlock_bh(&t->lock); 120 121 return 0; 122 } 123 124 static int lowpan_ctx_pfx_open(struct inode *inode, struct file *file) 125 { 126 return single_open(file, lowpan_ctx_pfx_show, inode->i_private); 127 } 128 129 static ssize_t lowpan_ctx_pfx_write(struct file *fp, 130 const char __user *user_buf, size_t count, 131 loff_t *ppos) 132 { 133 char buf[128] = {}; 134 struct seq_file *file = fp->private_data; 135 struct lowpan_iphc_ctx *ctx = file->private; 136 struct lowpan_iphc_ctx_table *t = 137 container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]); 138 int status = count, n, i; 139 unsigned int addr[8]; 140 141 if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1, 142 count))) { 143 status = -EFAULT; 144 goto out; 145 } 146 147 n = sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", 148 &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], 149 &addr[5], &addr[6], &addr[7]); 150 if (n != LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS) { 151 status = -EINVAL; 152 goto out; 153 } 154 155 spin_lock_bh(&t->lock); 156 for (i = 0; i < 8; i++) 157 ctx->pfx.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff); 158 spin_unlock_bh(&t->lock); 159 160 out: 161 return status; 162 } 163 164 static const struct file_operations lowpan_ctx_pfx_fops = { 165 .open = lowpan_ctx_pfx_open, 166 .read = seq_read, 167 .write = lowpan_ctx_pfx_write, 168 .llseek = seq_lseek, 169 .release = single_release, 170 }; 171 172 static int lowpan_dev_debugfs_ctx_init(struct net_device *dev, 173 struct dentry *ctx, u8 id) 174 { 175 struct lowpan_priv *lpriv = lowpan_priv(dev); 176 struct dentry *dentry, *root; 177 char buf[32]; 178 179 WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE); 180 181 sprintf(buf, "%d", id); 182 183 root = debugfs_create_dir(buf, ctx); 184 if (!root) 185 return -EINVAL; 186 187 dentry = debugfs_create_file("active", 0644, root, 188 &lpriv->ctx.table[id], 189 &lowpan_ctx_flag_active_fops); 190 if (!dentry) 191 return -EINVAL; 192 193 dentry = debugfs_create_file("compression", 0644, root, 194 &lpriv->ctx.table[id], 195 &lowpan_ctx_flag_c_fops); 196 if (!dentry) 197 return -EINVAL; 198 199 dentry = debugfs_create_file("prefix", 0644, root, 200 &lpriv->ctx.table[id], 201 &lowpan_ctx_pfx_fops); 202 if (!dentry) 203 return -EINVAL; 204 205 dentry = debugfs_create_file("prefix_len", 0644, root, 206 &lpriv->ctx.table[id], 207 &lowpan_ctx_plen_fops); 208 if (!dentry) 209 return -EINVAL; 210 211 return 0; 212 } 213 214 static int lowpan_context_show(struct seq_file *file, void *offset) 215 { 216 struct lowpan_iphc_ctx_table *t = file->private; 217 int i; 218 219 seq_printf(file, "%3s|%-43s|%c\n", "cid", "prefix", 'C'); 220 seq_puts(file, "-------------------------------------------------\n"); 221 222 spin_lock_bh(&t->lock); 223 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) { 224 if (!lowpan_iphc_ctx_is_active(&t->table[i])) 225 continue; 226 227 seq_printf(file, "%3d|%39pI6c/%-3d|%d\n", t->table[i].id, 228 &t->table[i].pfx, t->table[i].plen, 229 lowpan_iphc_ctx_is_compression(&t->table[i])); 230 } 231 spin_unlock_bh(&t->lock); 232 233 return 0; 234 } 235 236 static int lowpan_context_open(struct inode *inode, struct file *file) 237 { 238 return single_open(file, lowpan_context_show, inode->i_private); 239 } 240 241 static const struct file_operations lowpan_context_fops = { 242 .open = lowpan_context_open, 243 .read = seq_read, 244 .llseek = seq_lseek, 245 .release = single_release, 246 }; 247 248 int lowpan_dev_debugfs_init(struct net_device *dev) 249 { 250 struct lowpan_priv *lpriv = lowpan_priv(dev); 251 struct dentry *contexts, *dentry; 252 int ret, i; 253 254 /* creating the root */ 255 lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs); 256 if (!lpriv->iface_debugfs) 257 goto fail; 258 259 contexts = debugfs_create_dir("contexts", lpriv->iface_debugfs); 260 if (!contexts) 261 goto remove_root; 262 263 dentry = debugfs_create_file("show", 0644, contexts, 264 &lowpan_priv(dev)->ctx, 265 &lowpan_context_fops); 266 if (!dentry) 267 goto remove_root; 268 269 for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) { 270 ret = lowpan_dev_debugfs_ctx_init(dev, contexts, i); 271 if (ret < 0) 272 goto remove_root; 273 } 274 275 return 0; 276 277 remove_root: 278 lowpan_dev_debugfs_exit(dev); 279 fail: 280 return -EINVAL; 281 } 282 283 void lowpan_dev_debugfs_exit(struct net_device *dev) 284 { 285 debugfs_remove_recursive(lowpan_priv(dev)->iface_debugfs); 286 } 287 288 int __init lowpan_debugfs_init(void) 289 { 290 lowpan_debugfs = debugfs_create_dir("6lowpan", NULL); 291 if (!lowpan_debugfs) 292 return -EINVAL; 293 294 return 0; 295 } 296 297 void lowpan_debugfs_exit(void) 298 { 299 debugfs_remove_recursive(lowpan_debugfs); 300 } 301