linux/drivers/net/wireless/marvell/libertas/debugfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/dcache.h>
   3#include <linux/debugfs.h>
   4#include <linux/delay.h>
   5#include <linux/hardirq.h>
   6#include <linux/mm.h>
   7#include <linux/string.h>
   8#include <linux/slab.h>
   9#include <linux/export.h>
  10
  11#include "decl.h"
  12#include "cmd.h"
  13#include "debugfs.h"
  14
  15static struct dentry *lbs_dir;
  16static char *szStates[] = {
  17        "Connected",
  18        "Disconnected"
  19};
  20
  21#ifdef PROC_DEBUG
  22static void lbs_debug_init(struct lbs_private *priv);
  23#endif
  24
  25static ssize_t write_file_dummy(struct file *file, const char __user *buf,
  26                                size_t count, loff_t *ppos)
  27{
  28        return -EINVAL;
  29}
  30
  31static const size_t len = PAGE_SIZE;
  32
  33static ssize_t lbs_dev_info(struct file *file, char __user *userbuf,
  34                                  size_t count, loff_t *ppos)
  35{
  36        struct lbs_private *priv = file->private_data;
  37        size_t pos = 0;
  38        unsigned long addr = get_zeroed_page(GFP_KERNEL);
  39        char *buf = (char *)addr;
  40        ssize_t res;
  41        if (!buf)
  42                return -ENOMEM;
  43
  44        pos += snprintf(buf+pos, len-pos, "state = %s\n",
  45                                szStates[priv->connect_status]);
  46        pos += snprintf(buf+pos, len-pos, "region_code = %02x\n",
  47                                (u32) priv->regioncode);
  48
  49        res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
  50
  51        free_page(addr);
  52        return res;
  53}
  54
  55static ssize_t lbs_sleepparams_write(struct file *file,
  56                                const char __user *user_buf, size_t count,
  57                                loff_t *ppos)
  58{
  59        struct lbs_private *priv = file->private_data;
  60        ssize_t ret;
  61        struct sleep_params sp;
  62        int p1, p2, p3, p4, p5, p6;
  63        char *buf;
  64
  65        buf = memdup_user_nul(user_buf, min(count, len - 1));
  66        if (IS_ERR(buf))
  67                return PTR_ERR(buf);
  68
  69        ret = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6);
  70        if (ret != 6) {
  71                ret = -EINVAL;
  72                goto out_unlock;
  73        }
  74        sp.sp_error = p1;
  75        sp.sp_offset = p2;
  76        sp.sp_stabletime = p3;
  77        sp.sp_calcontrol = p4;
  78        sp.sp_extsleepclk = p5;
  79        sp.sp_reserved = p6;
  80
  81        ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_SET, &sp);
  82        if (!ret)
  83                ret = count;
  84        else if (ret > 0)
  85                ret = -EINVAL;
  86
  87out_unlock:
  88        kfree(buf);
  89        return ret;
  90}
  91
  92static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
  93                                  size_t count, loff_t *ppos)
  94{
  95        struct lbs_private *priv = file->private_data;
  96        ssize_t ret;
  97        size_t pos = 0;
  98        struct sleep_params sp;
  99        unsigned long addr = get_zeroed_page(GFP_KERNEL);
 100        char *buf = (char *)addr;
 101        if (!buf)
 102                return -ENOMEM;
 103
 104        ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp);
 105        if (ret)
 106                goto out_unlock;
 107
 108        pos += snprintf(buf, len, "%d %d %d %d %d %d\n", sp.sp_error,
 109                        sp.sp_offset, sp.sp_stabletime,
 110                        sp.sp_calcontrol, sp.sp_extsleepclk,
 111                        sp.sp_reserved);
 112
 113        ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
 114
 115out_unlock:
 116        free_page(addr);
 117        return ret;
 118}
 119
 120static ssize_t lbs_host_sleep_write(struct file *file,
 121                                const char __user *user_buf, size_t count,
 122                                loff_t *ppos)
 123{
 124        struct lbs_private *priv = file->private_data;
 125        ssize_t ret;
 126        int host_sleep;
 127        char *buf;
 128
 129        buf = memdup_user_nul(user_buf, min(count, len - 1));
 130        if (IS_ERR(buf))
 131                return PTR_ERR(buf);
 132
 133        ret = sscanf(buf, "%d", &host_sleep);
 134        if (ret != 1) {
 135                ret = -EINVAL;
 136                goto out_unlock;
 137        }
 138
 139        if (host_sleep == 0)
 140                ret = lbs_set_host_sleep(priv, 0);
 141        else if (host_sleep == 1) {
 142                if (priv->wol_criteria == EHS_REMOVE_WAKEUP) {
 143                        netdev_info(priv->dev,
 144                                    "wake parameters not configured\n");
 145                        ret = -EINVAL;
 146                        goto out_unlock;
 147                }
 148                ret = lbs_set_host_sleep(priv, 1);
 149        } else {
 150                netdev_err(priv->dev, "invalid option\n");
 151                ret = -EINVAL;
 152        }
 153
 154        if (!ret)
 155                ret = count;
 156
 157out_unlock:
 158        kfree(buf);
 159        return ret;
 160}
 161
 162static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf,
 163                                  size_t count, loff_t *ppos)
 164{
 165        struct lbs_private *priv = file->private_data;
 166        ssize_t ret;
 167        size_t pos = 0;
 168        unsigned long addr = get_zeroed_page(GFP_KERNEL);
 169        char *buf = (char *)addr;
 170        if (!buf)
 171                return -ENOMEM;
 172
 173        pos += snprintf(buf, len, "%d\n", priv->is_host_sleep_activated);
 174
 175        ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
 176
 177        free_page(addr);
 178        return ret;
 179}
 180
 181/*
 182 * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might
 183 * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the
 184 * firmware. Here's an example:
 185 *      04 01 02 00 00 00 05 01 02 00 00 00 06 01 02 00
 186 *      00 00 07 01 02 00 3c 00 00 00 00 00 00 00 03 03
 187 *      00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 188 *
 189 * The 04 01 is the TLV type (here TLV_TYPE_RSSI_LOW), 02 00 is the length,
 190 * 00 00 are the data bytes of this TLV. For this TLV, their meaning is
 191 * defined in mrvlietypes_thresholds
 192 *
 193 * This function searches in this TLV data chunk for a given TLV type
 194 * and returns a pointer to the first data byte of the TLV, or to NULL
 195 * if the TLV hasn't been found.
 196 */
 197static void *lbs_tlv_find(uint16_t tlv_type, const uint8_t *tlv, uint16_t size)
 198{
 199        struct mrvl_ie_header *tlv_h;
 200        uint16_t length;
 201        ssize_t pos = 0;
 202
 203        while (pos < size) {
 204                tlv_h = (struct mrvl_ie_header *) tlv;
 205                if (!tlv_h->len)
 206                        return NULL;
 207                if (tlv_h->type == cpu_to_le16(tlv_type))
 208                        return tlv_h;
 209                length = le16_to_cpu(tlv_h->len) + sizeof(*tlv_h);
 210                pos += length;
 211                tlv += length;
 212        }
 213        return NULL;
 214}
 215
 216
 217static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask,
 218                                  struct file *file, char __user *userbuf,
 219                                  size_t count, loff_t *ppos)
 220{
 221        struct cmd_ds_802_11_subscribe_event *subscribed;
 222        struct mrvl_ie_thresholds *got;
 223        struct lbs_private *priv = file->private_data;
 224        ssize_t ret = 0;
 225        size_t pos = 0;
 226        char *buf;
 227        u8 value;
 228        u8 freq;
 229        int events = 0;
 230
 231        buf = (char *)get_zeroed_page(GFP_KERNEL);
 232        if (!buf)
 233                return -ENOMEM;
 234
 235        subscribed = kzalloc(sizeof(*subscribed), GFP_KERNEL);
 236        if (!subscribed) {
 237                ret = -ENOMEM;
 238                goto out_page;
 239        }
 240
 241        subscribed->hdr.size = cpu_to_le16(sizeof(*subscribed));
 242        subscribed->action = cpu_to_le16(CMD_ACT_GET);
 243
 244        ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, subscribed);
 245        if (ret)
 246                goto out_cmd;
 247
 248        got = lbs_tlv_find(tlv_type, subscribed->tlv, sizeof(subscribed->tlv));
 249        if (got) {
 250                value = got->value;
 251                freq  = got->freq;
 252                events = le16_to_cpu(subscribed->events);
 253
 254                pos += snprintf(buf, len, "%d %d %d\n", value, freq,
 255                                !!(events & event_mask));
 256        }
 257
 258        ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
 259
 260 out_cmd:
 261        kfree(subscribed);
 262
 263 out_page:
 264        free_page((unsigned long)buf);
 265        return ret;
 266}
 267
 268
 269static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask,
 270                                   struct file *file,
 271                                   const char __user *userbuf, size_t count,
 272                                   loff_t *ppos)
 273{
 274        struct cmd_ds_802_11_subscribe_event *events;
 275        struct mrvl_ie_thresholds *tlv;
 276        struct lbs_private *priv = file->private_data;
 277        int value, freq, new_mask;
 278        uint16_t curr_mask;
 279        char *buf;
 280        int ret;
 281
 282        buf = memdup_user_nul(userbuf, min(count, len - 1));
 283        if (IS_ERR(buf))
 284                return PTR_ERR(buf);
 285
 286        ret = sscanf(buf, "%d %d %d", &value, &freq, &new_mask);
 287        if (ret != 3) {
 288                ret = -EINVAL;
 289                goto out_page;
 290        }
 291        events = kzalloc(sizeof(*events), GFP_KERNEL);
 292        if (!events) {
 293                ret = -ENOMEM;
 294                goto out_page;
 295        }
 296
 297        events->hdr.size = cpu_to_le16(sizeof(*events));
 298        events->action = cpu_to_le16(CMD_ACT_GET);
 299
 300        ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events);
 301        if (ret)
 302                goto out_events;
 303
 304        curr_mask = le16_to_cpu(events->events);
 305
 306        if (new_mask)
 307                new_mask = curr_mask | event_mask;
 308        else
 309                new_mask = curr_mask & ~event_mask;
 310
 311        /* Now everything is set and we can send stuff down to the firmware */
 312
 313        tlv = (void *)events->tlv;
 314
 315        events->action = cpu_to_le16(CMD_ACT_SET);
 316        events->events = cpu_to_le16(new_mask);
 317        tlv->header.type = cpu_to_le16(tlv_type);
 318        tlv->header.len = cpu_to_le16(sizeof(*tlv) - sizeof(tlv->header));
 319        tlv->value = value;
 320        if (tlv_type != TLV_TYPE_BCNMISS)
 321                tlv->freq = freq;
 322
 323        /* The command header, the action, the event mask, and one TLV */
 324        events->hdr.size = cpu_to_le16(sizeof(events->hdr) + 4 + sizeof(*tlv));
 325
 326        ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events);
 327
 328        if (!ret)
 329                ret = count;
 330 out_events:
 331        kfree(events);
 332 out_page:
 333        kfree(buf);
 334        return ret;
 335}
 336
 337
 338static ssize_t lbs_lowrssi_read(struct file *file, char __user *userbuf,
 339                                size_t count, loff_t *ppos)
 340{
 341        return lbs_threshold_read(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW,
 342                                  file, userbuf, count, ppos);
 343}
 344
 345
 346static ssize_t lbs_lowrssi_write(struct file *file, const char __user *userbuf,
 347                                 size_t count, loff_t *ppos)
 348{
 349        return lbs_threshold_write(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW,
 350                                   file, userbuf, count, ppos);
 351}
 352
 353
 354static ssize_t lbs_lowsnr_read(struct file *file, char __user *userbuf,
 355                               size_t count, loff_t *ppos)
 356{
 357        return lbs_threshold_read(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW,
 358                                  file, userbuf, count, ppos);
 359}
 360
 361
 362static ssize_t lbs_lowsnr_write(struct file *file, const char __user *userbuf,
 363                                size_t count, loff_t *ppos)
 364{
 365        return lbs_threshold_write(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW,
 366                                   file, userbuf, count, ppos);
 367}
 368
 369
 370static ssize_t lbs_failcount_read(struct file *file, char __user *userbuf,
 371                                  size_t count, loff_t *ppos)
 372{
 373        return lbs_threshold_read(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT,
 374                                  file, userbuf, count, ppos);
 375}
 376
 377
 378static ssize_t lbs_failcount_write(struct file *file, const char __user *userbuf,
 379                                   size_t count, loff_t *ppos)
 380{
 381        return lbs_threshold_write(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT,
 382                                   file, userbuf, count, ppos);
 383}
 384
 385
 386static ssize_t lbs_highrssi_read(struct file *file, char __user *userbuf,
 387                                 size_t count, loff_t *ppos)
 388{
 389        return lbs_threshold_read(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH,
 390                                  file, userbuf, count, ppos);
 391}
 392
 393
 394static ssize_t lbs_highrssi_write(struct file *file, const char __user *userbuf,
 395                                  size_t count, loff_t *ppos)
 396{
 397        return lbs_threshold_write(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH,
 398                                   file, userbuf, count, ppos);
 399}
 400
 401
 402static ssize_t lbs_highsnr_read(struct file *file, char __user *userbuf,
 403                                size_t count, loff_t *ppos)
 404{
 405        return lbs_threshold_read(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH,
 406                                  file, userbuf, count, ppos);
 407}
 408
 409
 410static ssize_t lbs_highsnr_write(struct file *file, const char __user *userbuf,
 411                                 size_t count, loff_t *ppos)
 412{
 413        return lbs_threshold_write(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH,
 414                                   file, userbuf, count, ppos);
 415}
 416
 417static ssize_t lbs_bcnmiss_read(struct file *file, char __user *userbuf,
 418                                size_t count, loff_t *ppos)
 419{
 420        return lbs_threshold_read(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS,
 421                                  file, userbuf, count, ppos);
 422}
 423
 424
 425static ssize_t lbs_bcnmiss_write(struct file *file, const char __user *userbuf,
 426                                 size_t count, loff_t *ppos)
 427{
 428        return lbs_threshold_write(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS,
 429                                   file, userbuf, count, ppos);
 430}
 431
 432
 433static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf,
 434                                  size_t count, loff_t *ppos)
 435{
 436        struct lbs_private *priv = file->private_data;
 437        ssize_t pos = 0;
 438        int ret;
 439        unsigned long addr = get_zeroed_page(GFP_KERNEL);
 440        char *buf = (char *)addr;
 441        u32 val = 0;
 442
 443        if (!buf)
 444                return -ENOMEM;
 445
 446        ret = lbs_get_reg(priv, CMD_MAC_REG_ACCESS, priv->mac_offset, &val);
 447        mdelay(10);
 448        if (!ret) {
 449                pos = snprintf(buf, len, "MAC[0x%x] = 0x%08x\n",
 450                                priv->mac_offset, val);
 451                ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
 452        }
 453        free_page(addr);
 454        return ret;
 455}
 456
 457static ssize_t lbs_rdmac_write(struct file *file,
 458                                    const char __user *userbuf,
 459                                    size_t count, loff_t *ppos)
 460{
 461        struct lbs_private *priv = file->private_data;
 462        char *buf;
 463
 464        buf = memdup_user_nul(userbuf, min(count, len - 1));
 465        if (IS_ERR(buf))
 466                return PTR_ERR(buf);
 467
 468        priv->mac_offset = simple_strtoul(buf, NULL, 16);
 469        kfree(buf);
 470        return count;
 471}
 472
 473static ssize_t lbs_wrmac_write(struct file *file,
 474                                    const char __user *userbuf,
 475                                    size_t count, loff_t *ppos)
 476{
 477
 478        struct lbs_private *priv = file->private_data;
 479        ssize_t res;
 480        u32 offset, value;
 481        char *buf;
 482
 483        buf = memdup_user_nul(userbuf, min(count, len - 1));
 484        if (IS_ERR(buf))
 485                return PTR_ERR(buf);
 486
 487        res = sscanf(buf, "%x %x", &offset, &value);
 488        if (res != 2) {
 489                res = -EFAULT;
 490                goto out_unlock;
 491        }
 492
 493        res = lbs_set_reg(priv, CMD_MAC_REG_ACCESS, offset, value);
 494        mdelay(10);
 495
 496        if (!res)
 497                res = count;
 498out_unlock:
 499        kfree(buf);
 500        return res;
 501}
 502
 503static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf,
 504                                  size_t count, loff_t *ppos)
 505{
 506        struct lbs_private *priv = file->private_data;
 507        ssize_t pos = 0;
 508        int ret;
 509        unsigned long addr = get_zeroed_page(GFP_KERNEL);
 510        char *buf = (char *)addr;
 511        u32 val;
 512
 513        if (!buf)
 514                return -ENOMEM;
 515
 516        ret = lbs_get_reg(priv, CMD_BBP_REG_ACCESS, priv->bbp_offset, &val);
 517        mdelay(10);
 518        if (!ret) {
 519                pos = snprintf(buf, len, "BBP[0x%x] = 0x%08x\n",
 520                                priv->bbp_offset, val);
 521                ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
 522        }
 523        free_page(addr);
 524
 525        return ret;
 526}
 527
 528static ssize_t lbs_rdbbp_write(struct file *file,
 529                                    const char __user *userbuf,
 530                                    size_t count, loff_t *ppos)
 531{
 532        struct lbs_private *priv = file->private_data;
 533        char *buf;
 534
 535        buf = memdup_user_nul(userbuf, min(count, len - 1));
 536        if (IS_ERR(buf))
 537                return PTR_ERR(buf);
 538
 539        priv->bbp_offset = simple_strtoul(buf, NULL, 16);
 540        kfree(buf);
 541
 542        return count;
 543}
 544
 545static ssize_t lbs_wrbbp_write(struct file *file,
 546                                    const char __user *userbuf,
 547                                    size_t count, loff_t *ppos)
 548{
 549
 550        struct lbs_private *priv = file->private_data;
 551        ssize_t res;
 552        u32 offset, value;
 553        char *buf;
 554
 555        buf = memdup_user_nul(userbuf, min(count, len - 1));
 556        if (IS_ERR(buf))
 557                return PTR_ERR(buf);
 558
 559        res = sscanf(buf, "%x %x", &offset, &value);
 560        if (res != 2) {
 561                res = -EFAULT;
 562                goto out_unlock;
 563        }
 564
 565        res = lbs_set_reg(priv, CMD_BBP_REG_ACCESS, offset, value);
 566        mdelay(10);
 567
 568        if (!res)
 569                res = count;
 570out_unlock:
 571        kfree(buf);
 572        return res;
 573}
 574
 575static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf,
 576                                  size_t count, loff_t *ppos)
 577{
 578        struct lbs_private *priv = file->private_data;
 579        ssize_t pos = 0;
 580        int ret;
 581        unsigned long addr = get_zeroed_page(GFP_KERNEL);
 582        char *buf = (char *)addr;
 583        u32 val;
 584
 585        if (!buf)
 586                return -ENOMEM;
 587
 588        ret = lbs_get_reg(priv, CMD_RF_REG_ACCESS, priv->rf_offset, &val);
 589        mdelay(10);
 590        if (!ret) {
 591                pos = snprintf(buf, len, "RF[0x%x] = 0x%08x\n",
 592                                priv->rf_offset, val);
 593                ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
 594        }
 595        free_page(addr);
 596
 597        return ret;
 598}
 599
 600static ssize_t lbs_rdrf_write(struct file *file,
 601                                    const char __user *userbuf,
 602                                    size_t count, loff_t *ppos)
 603{
 604        struct lbs_private *priv = file->private_data;
 605        char *buf;
 606
 607        buf = memdup_user_nul(userbuf, min(count, len - 1));
 608        if (IS_ERR(buf))
 609                return PTR_ERR(buf);
 610
 611        priv->rf_offset = simple_strtoul(buf, NULL, 16);
 612        kfree(buf);
 613        return count;
 614}
 615
 616static ssize_t lbs_wrrf_write(struct file *file,
 617                                    const char __user *userbuf,
 618                                    size_t count, loff_t *ppos)
 619{
 620
 621        struct lbs_private *priv = file->private_data;
 622        ssize_t res;
 623        u32 offset, value;
 624        char *buf;
 625
 626        buf = memdup_user_nul(userbuf, min(count, len - 1));
 627        if (IS_ERR(buf))
 628                return PTR_ERR(buf);
 629
 630        res = sscanf(buf, "%x %x", &offset, &value);
 631        if (res != 2) {
 632                res = -EFAULT;
 633                goto out_unlock;
 634        }
 635
 636        res = lbs_set_reg(priv, CMD_RF_REG_ACCESS, offset, value);
 637        mdelay(10);
 638
 639        if (!res)
 640                res = count;
 641out_unlock:
 642        kfree(buf);
 643        return res;
 644}
 645
 646#define FOPS(fread, fwrite) { \
 647        .owner = THIS_MODULE, \
 648        .open = simple_open, \
 649        .read = (fread), \
 650        .write = (fwrite), \
 651        .llseek = generic_file_llseek, \
 652}
 653
 654struct lbs_debugfs_files {
 655        const char *name;
 656        umode_t perm;
 657        struct file_operations fops;
 658};
 659
 660static const struct lbs_debugfs_files debugfs_files[] = {
 661        { "info", 0444, FOPS(lbs_dev_info, write_file_dummy), },
 662        { "sleepparams", 0644, FOPS(lbs_sleepparams_read,
 663                                lbs_sleepparams_write), },
 664        { "hostsleep", 0644, FOPS(lbs_host_sleep_read,
 665                                lbs_host_sleep_write), },
 666};
 667
 668static const struct lbs_debugfs_files debugfs_events_files[] = {
 669        {"low_rssi", 0644, FOPS(lbs_lowrssi_read,
 670                                lbs_lowrssi_write), },
 671        {"low_snr", 0644, FOPS(lbs_lowsnr_read,
 672                                lbs_lowsnr_write), },
 673        {"failure_count", 0644, FOPS(lbs_failcount_read,
 674                                lbs_failcount_write), },
 675        {"beacon_missed", 0644, FOPS(lbs_bcnmiss_read,
 676                                lbs_bcnmiss_write), },
 677        {"high_rssi", 0644, FOPS(lbs_highrssi_read,
 678                                lbs_highrssi_write), },
 679        {"high_snr", 0644, FOPS(lbs_highsnr_read,
 680                                lbs_highsnr_write), },
 681};
 682
 683static const struct lbs_debugfs_files debugfs_regs_files[] = {
 684        {"rdmac", 0644, FOPS(lbs_rdmac_read, lbs_rdmac_write), },
 685        {"wrmac", 0600, FOPS(NULL, lbs_wrmac_write), },
 686        {"rdbbp", 0644, FOPS(lbs_rdbbp_read, lbs_rdbbp_write), },
 687        {"wrbbp", 0600, FOPS(NULL, lbs_wrbbp_write), },
 688        {"rdrf", 0644, FOPS(lbs_rdrf_read, lbs_rdrf_write), },
 689        {"wrrf", 0600, FOPS(NULL, lbs_wrrf_write), },
 690};
 691
 692void lbs_debugfs_init(void)
 693{
 694        if (!lbs_dir)
 695                lbs_dir = debugfs_create_dir("lbs_wireless", NULL);
 696}
 697
 698void lbs_debugfs_remove(void)
 699{
 700        debugfs_remove(lbs_dir);
 701}
 702
 703void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev)
 704{
 705        int i;
 706        const struct lbs_debugfs_files *files;
 707        if (!lbs_dir)
 708                goto exit;
 709
 710        priv->debugfs_dir = debugfs_create_dir(dev->name, lbs_dir);
 711
 712        for (i=0; i<ARRAY_SIZE(debugfs_files); i++) {
 713                files = &debugfs_files[i];
 714                priv->debugfs_files[i] = debugfs_create_file(files->name,
 715                                                             files->perm,
 716                                                             priv->debugfs_dir,
 717                                                             priv,
 718                                                             &files->fops);
 719        }
 720
 721        priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir);
 722
 723        for (i=0; i<ARRAY_SIZE(debugfs_events_files); i++) {
 724                files = &debugfs_events_files[i];
 725                priv->debugfs_events_files[i] = debugfs_create_file(files->name,
 726                                                             files->perm,
 727                                                             priv->events_dir,
 728                                                             priv,
 729                                                             &files->fops);
 730        }
 731
 732        priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir);
 733
 734        for (i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) {
 735                files = &debugfs_regs_files[i];
 736                priv->debugfs_regs_files[i] = debugfs_create_file(files->name,
 737                                                             files->perm,
 738                                                             priv->regs_dir,
 739                                                             priv,
 740                                                             &files->fops);
 741        }
 742
 743#ifdef PROC_DEBUG
 744        lbs_debug_init(priv);
 745#endif
 746exit:
 747        return;
 748}
 749
 750void lbs_debugfs_remove_one(struct lbs_private *priv)
 751{
 752        int i;
 753
 754        for(i=0; i<ARRAY_SIZE(debugfs_regs_files); i++)
 755                debugfs_remove(priv->debugfs_regs_files[i]);
 756
 757        debugfs_remove(priv->regs_dir);
 758
 759        for(i=0; i<ARRAY_SIZE(debugfs_events_files); i++)
 760                debugfs_remove(priv->debugfs_events_files[i]);
 761
 762        debugfs_remove(priv->events_dir);
 763#ifdef PROC_DEBUG
 764        debugfs_remove(priv->debugfs_debug);
 765#endif
 766        for(i=0; i<ARRAY_SIZE(debugfs_files); i++)
 767                debugfs_remove(priv->debugfs_files[i]);
 768        debugfs_remove(priv->debugfs_dir);
 769}
 770
 771
 772
 773/* debug entry */
 774
 775#ifdef PROC_DEBUG
 776
 777#define item_size(n)    (FIELD_SIZEOF(struct lbs_private, n))
 778#define item_addr(n)    (offsetof(struct lbs_private, n))
 779
 780
 781struct debug_data {
 782        char name[32];
 783        u32 size;
 784        size_t addr;
 785};
 786
 787/* To debug any member of struct lbs_private, simply add one line here.
 788 */
 789static struct debug_data items[] = {
 790        {"psmode", item_size(psmode), item_addr(psmode)},
 791        {"psstate", item_size(psstate), item_addr(psstate)},
 792};
 793
 794static int num_of_items = ARRAY_SIZE(items);
 795
 796/**
 797 * lbs_debugfs_read - proc read function
 798 *
 799 * @file:       file to read
 800 * @userbuf:    pointer to buffer
 801 * @count:      number of bytes to read
 802 * @ppos:       read data starting position
 803 *
 804 * returns:     amount of data read or negative error code
 805 */
 806static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf,
 807                        size_t count, loff_t *ppos)
 808{
 809        int val = 0;
 810        size_t pos = 0;
 811        ssize_t res;
 812        char *p;
 813        int i;
 814        struct debug_data *d;
 815        unsigned long addr = get_zeroed_page(GFP_KERNEL);
 816        char *buf = (char *)addr;
 817        if (!buf)
 818                return -ENOMEM;
 819
 820        p = buf;
 821
 822        d = file->private_data;
 823
 824        for (i = 0; i < num_of_items; i++) {
 825                if (d[i].size == 1)
 826                        val = *((u8 *) d[i].addr);
 827                else if (d[i].size == 2)
 828                        val = *((u16 *) d[i].addr);
 829                else if (d[i].size == 4)
 830                        val = *((u32 *) d[i].addr);
 831                else if (d[i].size == 8)
 832                        val = *((u64 *) d[i].addr);
 833
 834                pos += sprintf(p + pos, "%s=%d\n", d[i].name, val);
 835        }
 836
 837        res = simple_read_from_buffer(userbuf, count, ppos, p, pos);
 838
 839        free_page(addr);
 840        return res;
 841}
 842
 843/**
 844 * lbs_debugfs_write - proc write function
 845 *
 846 * @f:          file pointer
 847 * @buf:        pointer to data buffer
 848 * @cnt:        data number to write
 849 * @ppos:       file position
 850 *
 851 * returns:     amount of data written
 852 */
 853static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
 854                            size_t cnt, loff_t *ppos)
 855{
 856        int r, i;
 857        char *pdata;
 858        char *p;
 859        char *p0;
 860        char *p1;
 861        char *p2;
 862        struct debug_data *d = f->private_data;
 863
 864        if (cnt == 0)
 865                return 0;
 866
 867        pdata = memdup_user_nul(buf, cnt);
 868        if (IS_ERR(pdata))
 869                return PTR_ERR(pdata);
 870
 871        p0 = pdata;
 872        for (i = 0; i < num_of_items; i++) {
 873                do {
 874                        p = strstr(p0, d[i].name);
 875                        if (p == NULL)
 876                                break;
 877                        p1 = strchr(p, '\n');
 878                        if (p1 == NULL)
 879                                break;
 880                        p0 = p1++;
 881                        p2 = strchr(p, '=');
 882                        if (!p2)
 883                                break;
 884                        p2++;
 885                        r = simple_strtoul(p2, NULL, 0);
 886                        if (d[i].size == 1)
 887                                *((u8 *) d[i].addr) = (u8) r;
 888                        else if (d[i].size == 2)
 889                                *((u16 *) d[i].addr) = (u16) r;
 890                        else if (d[i].size == 4)
 891                                *((u32 *) d[i].addr) = (u32) r;
 892                        else if (d[i].size == 8)
 893                                *((u64 *) d[i].addr) = (u64) r;
 894                        break;
 895                } while (1);
 896        }
 897        kfree(pdata);
 898
 899        return (ssize_t)cnt;
 900}
 901
 902static const struct file_operations lbs_debug_fops = {
 903        .owner = THIS_MODULE,
 904        .open = simple_open,
 905        .write = lbs_debugfs_write,
 906        .read = lbs_debugfs_read,
 907        .llseek = default_llseek,
 908};
 909
 910/**
 911 * lbs_debug_init - create debug proc file
 912 *
 913 * @priv:       pointer to &struct lbs_private
 914 *
 915 * returns:     N/A
 916 */
 917static void lbs_debug_init(struct lbs_private *priv)
 918{
 919        int i;
 920
 921        if (!priv->debugfs_dir)
 922                return;
 923
 924        for (i = 0; i < num_of_items; i++)
 925                items[i].addr += (size_t) priv;
 926
 927        priv->debugfs_debug = debugfs_create_file("debug", 0644,
 928                                                  priv->debugfs_dir, &items[0],
 929                                                  &lbs_debug_fops);
 930}
 931#endif
 932