932 lines · c
1// SPDX-License-Identifier: GPL-2.02#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_DEBUG22static void lbs_debug_init(struct lbs_private *priv);23#endif24 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 might183 * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the184 * firmware. Here's an example:185 * 04 01 02 00 00 00 05 01 02 00 00 00 06 01 02 00186 * 00 00 07 01 02 00 3c 00 00 00 00 00 00 00 03 03187 * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00188 *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 is191 * defined in mrvlietypes_thresholds192 *193 * This function searches in this TLV data chunk for a given TLV type194 * and returns a pointer to the first data byte of the TLV, or to NULL195 * 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 else309 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_DEBUG744 lbs_debug_init(priv);745#endif746exit: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_DEBUG764 debugfs_remove(priv->debugfs_debug);765#endif766 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_DEBUG776 777#define item_size(n) (sizeof_field(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 function798 *799 * @file: file to read800 * @userbuf: pointer to buffer801 * @count: number of bytes to read802 * @ppos: read data starting position803 *804 * returns: amount of data read or negative error code805 */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 function845 *846 * @f: file pointer847 * @buf: pointer to data buffer848 * @cnt: data number to write849 * @ppos: file position850 *851 * returns: amount of data written852 */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 file912 *913 * @priv: pointer to &struct lbs_private914 *915 * returns: N/A916 */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#endif932