brintos

brintos / linux-shallow public Read only

0
0
Text · 21.2 KiB · c604613 Raw
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