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        if (!priv->debugfs_dir)
 712                goto exit;
 713
 714        for (i=0; i<ARRAY_SIZE(debugfs_files); i++) {
 715                files = &debugfs_files[i];
 716                priv->debugfs_files[i] = debugfs_create_file(files->name,
 717                                                             files->perm,
 718                                                             priv->debugfs_dir,
 719                                                             priv,
 720                                                             &files->fops);
 721        }
 722
 723        priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir);
 724        if (!priv->events_dir)
 725                goto exit;
 726
 727        for (i=0; i<ARRAY_SIZE(debugfs_events_files); i++) {
 728                files = &debugfs_events_files[i];
 729                priv->debugfs_events_files[i] = debugfs_create_file(files->name,
 730                                                             files->perm,
 731                                                             priv->events_dir,
 732                                                             priv,
 733                                                             &files->fops);
 734        }
 735
 736        priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir);
 737        if (!priv->regs_dir)
 738                goto exit;
 739
 740        for (i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) {
 741                files = &debugfs_regs_files[i];
 742                priv->debugfs_regs_files[i] = debugfs_create_file(files->name,
 743                                                             files->perm,
 744                                                             priv->regs_dir,
 745                                                             priv,
 746                                                             &files->fops);
 747        }
 748
 749#ifdef PROC_DEBUG
 750        lbs_debug_init(priv);
 751#endif
 752exit:
 753        return;
 754}
 755
 756void lbs_debugfs_remove_one(struct lbs_private *priv)
 757{
 758        int i;
 759
 760        for(i=0; i<ARRAY_SIZE(debugfs_regs_files); i++)
 761                debugfs_remove(priv->debugfs_regs_files[i]);
 762
 763        debugfs_remove(priv->regs_dir);
 764
 765        for(i=0; i<ARRAY_SIZE(debugfs_events_files); i++)
 766                debugfs_remove(priv->debugfs_events_files[i]);
 767
 768        debugfs_remove(priv->events_dir);
 769#ifdef PROC_DEBUG
 770        debugfs_remove(priv->debugfs_debug);
 771#endif
 772        for(i=0; i<ARRAY_SIZE(debugfs_files); i++)
 773                debugfs_remove(priv->debugfs_files[i]);
 774        debugfs_remove(priv->debugfs_dir);
 775}
 776
 777
 778
 779/* debug entry */
 780
 781#ifdef PROC_DEBUG
 782
 783#define item_size(n)    (FIELD_SIZEOF(struct lbs_private, n))
 784#define item_addr(n)    (offsetof(struct lbs_private, n))
 785
 786
 787struct debug_data {
 788        char name[32];
 789        u32 size;
 790        size_t addr;
 791};
 792
 793/* To debug any member of struct lbs_private, simply add one line here.
 794 */
 795static struct debug_data items[] = {
 796        {"psmode", item_size(psmode), item_addr(psmode)},
 797        {"psstate", item_size(psstate), item_addr(psstate)},
 798};
 799
 800static int num_of_items = ARRAY_SIZE(items);
 801
 802/**
 803 * lbs_debugfs_read - proc read function
 804 *
 805 * @file:       file to read
 806 * @userbuf:    pointer to buffer
 807 * @count:      number of bytes to read
 808 * @ppos:       read data starting position
 809 *
 810 * returns:     amount of data read or negative error code
 811 */
 812static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf,
 813                        size_t count, loff_t *ppos)
 814{
 815        int val = 0;
 816        size_t pos = 0;
 817        ssize_t res;
 818        char *p;
 819        int i;
 820        struct debug_data *d;
 821        unsigned long addr = get_zeroed_page(GFP_KERNEL);
 822        char *buf = (char *)addr;
 823        if (!buf)
 824                return -ENOMEM;
 825
 826        p = buf;
 827
 828        d = file->private_data;
 829
 830        for (i = 0; i < num_of_items; i++) {
 831                if (d[i].size == 1)
 832                        val = *((u8 *) d[i].addr);
 833                else if (d[i].size == 2)
 834                        val = *((u16 *) d[i].addr);
 835                else if (d[i].size == 4)
 836                        val = *((u32 *) d[i].addr);
 837                else if (d[i].size == 8)
 838                        val = *((u64 *) d[i].addr);
 839
 840                pos += sprintf(p + pos, "%s=%d\n", d[i].name, val);
 841        }
 842
 843        res = simple_read_from_buffer(userbuf, count, ppos, p, pos);
 844
 845        free_page(addr);
 846        return res;
 847}
 848
 849/**
 850 * lbs_debugfs_write - proc write function
 851 *
 852 * @f:          file pointer
 853 * @buf:        pointer to data buffer
 854 * @cnt:        data number to write
 855 * @ppos:       file position
 856 *
 857 * returns:     amount of data written
 858 */
 859static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
 860                            size_t cnt, loff_t *ppos)
 861{
 862        int r, i;
 863        char *pdata;
 864        char *p;
 865        char *p0;
 866        char *p1;
 867        char *p2;
 868        struct debug_data *d = f->private_data;
 869
 870        if (cnt == 0)
 871                return 0;
 872
 873        pdata = memdup_user_nul(buf, cnt);
 874        if (IS_ERR(pdata))
 875                return PTR_ERR(pdata);
 876
 877        p0 = pdata;
 878        for (i = 0; i < num_of_items; i++) {
 879                do {
 880                        p = strstr(p0, d[i].name);
 881                        if (p == NULL)
 882                                break;
 883                        p1 = strchr(p, '\n');
 884                        if (p1 == NULL)
 885                                break;
 886                        p0 = p1++;
 887                        p2 = strchr(p, '=');
 888                        if (!p2)
 889                                break;
 890                        p2++;
 891                        r = simple_strtoul(p2, NULL, 0);
 892                        if (d[i].size == 1)
 893                                *((u8 *) d[i].addr) = (u8) r;
 894                        else if (d[i].size == 2)
 895                                *((u16 *) d[i].addr) = (u16) r;
 896                        else if (d[i].size == 4)
 897                                *((u32 *) d[i].addr) = (u32) r;
 898                        else if (d[i].size == 8)
 899                                *((u64 *) d[i].addr) = (u64) r;
 900                        break;
 901                } while (1);
 902        }
 903        kfree(pdata);
 904
 905        return (ssize_t)cnt;
 906}
 907
 908static const struct file_operations lbs_debug_fops = {
 909        .owner = THIS_MODULE,
 910        .open = simple_open,
 911        .write = lbs_debugfs_write,
 912        .read = lbs_debugfs_read,
 913        .llseek = default_llseek,
 914};
 915
 916/**
 917 * lbs_debug_init - create debug proc file
 918 *
 919 * @priv:       pointer to &struct lbs_private
 920 *
 921 * returns:     N/A
 922 */
 923static void lbs_debug_init(struct lbs_private *priv)
 924{
 925        int i;
 926
 927        if (!priv->debugfs_dir)
 928                return;
 929
 930        for (i = 0; i < num_of_items; i++)
 931                items[i].addr += (size_t) priv;
 932
 933        priv->debugfs_debug = debugfs_create_file("debug", 0644,
 934                                                  priv->debugfs_dir, &items[0],
 935                                                  &lbs_debug_fops);
 936}
 937#endif
 938