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