brintos

brintos / linux-shallow public Read only

0
0
Text · 10.1 KiB · 6b0e8d1 Raw
452 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 4  Broadcom B43legacy wireless driver5 6  debugfs driver debugging code7 8  Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>9 10 11*/12 13#include <linux/fs.h>14#include <linux/debugfs.h>15#include <linux/slab.h>16#include <linux/netdevice.h>17#include <linux/pci.h>18#include <linux/mutex.h>19 20#include "b43legacy.h"21#include "main.h"22#include "debugfs.h"23#include "dma.h"24#include "pio.h"25#include "xmit.h"26 27 28/* The root directory. */29static struct dentry *rootdir;30 31struct b43legacy_debugfs_fops {32	ssize_t (*read)(struct b43legacy_wldev *dev, char *buf, size_t bufsize);33	int (*write)(struct b43legacy_wldev *dev, const char *buf, size_t count);34	struct file_operations fops;35	/* Offset of struct b43legacy_dfs_file in struct b43legacy_dfsentry */36	size_t file_struct_offset;37	/* Take wl->irq_lock before calling read/write? */38	bool take_irqlock;39};40 41static inline42struct b43legacy_dfs_file * fops_to_dfs_file(struct b43legacy_wldev *dev,43				       const struct b43legacy_debugfs_fops *dfops)44{45	void *p;46 47	p = dev->dfsentry;48	p += dfops->file_struct_offset;49 50	return p;51}52 53 54#define fappend(fmt, x...)	\55	do {							\56		if (bufsize - count)				\57			count += scnprintf(buf + count,		\58					  bufsize - count,	\59					  fmt , ##x);		\60		else						\61			printk(KERN_ERR "b43legacy: fappend overflow\n"); \62	} while (0)63 64 65/* wl->irq_lock is locked */66static ssize_t tsf_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)67{68	ssize_t count = 0;69	u64 tsf;70 71	b43legacy_tsf_read(dev, &tsf);72	fappend("0x%08x%08x\n",73		(unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32),74		(unsigned int)(tsf & 0xFFFFFFFFULL));75 76	return count;77}78 79/* wl->irq_lock is locked */80static int tsf_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count)81{82	u64 tsf;83 84	if (sscanf(buf, "%llu", (unsigned long long *)(&tsf)) != 1)85		return -EINVAL;86	b43legacy_tsf_write(dev, tsf);87 88	return 0;89}90 91/* wl->irq_lock is locked */92static ssize_t ucode_regs_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)93{94	ssize_t count = 0;95	int i;96 97	for (i = 0; i < 64; i++) {98		fappend("r%d = 0x%04x\n", i,99			b43legacy_shm_read16(dev, B43legacy_SHM_WIRELESS, i));100	}101 102	return count;103}104 105/* wl->irq_lock is locked */106static ssize_t shm_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)107{108	ssize_t count = 0;109	int i;110	u16 tmp;111	__le16 *le16buf = (__le16 *)buf;112 113	for (i = 0; i < 0x1000; i++) {114		if (bufsize < sizeof(tmp))115			break;116		tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 2 * i);117		le16buf[i] = cpu_to_le16(tmp);118		count += sizeof(tmp);119		bufsize -= sizeof(tmp);120	}121 122	return count;123}124 125static ssize_t txstat_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)126{127	struct b43legacy_txstatus_log *log = &dev->dfsentry->txstatlog;128	ssize_t count = 0;129	unsigned long flags;130	int i, idx;131	struct b43legacy_txstatus *stat;132 133	spin_lock_irqsave(&log->lock, flags);134	if (log->end < 0) {135		fappend("Nothing transmitted, yet\n");136		goto out_unlock;137	}138	fappend("b43legacy TX status reports:\n\n"139		"index | cookie | seq | phy_stat | frame_count | "140		"rts_count | supp_reason | pm_indicated | "141		"intermediate | for_ampdu | acked\n" "---\n");142	i = log->end + 1;143	idx = 0;144	while (1) {145		if (i == B43legacy_NR_LOGGED_TXSTATUS)146			i = 0;147		stat = &(log->log[i]);148		if (stat->cookie) {149			fappend("%03d | "150				"0x%04X | 0x%04X | 0x%02X | "151				"0x%X | 0x%X | "152				"%u | %u | "153				"%u | %u | %u\n",154				idx,155				stat->cookie, stat->seq, stat->phy_stat,156				stat->frame_count, stat->rts_count,157				stat->supp_reason, stat->pm_indicated,158				stat->intermediate, stat->for_ampdu,159				stat->acked);160			idx++;161		}162		if (i == log->end)163			break;164		i++;165	}166out_unlock:167	spin_unlock_irqrestore(&log->lock, flags);168 169	return count;170}171 172/* wl->irq_lock is locked */173static int restart_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count)174{175	int err = 0;176 177	if (count > 0 && buf[0] == '1') {178		b43legacy_controller_restart(dev, "manually restarted");179	} else180		err = -EINVAL;181 182	return err;183}184 185#undef fappend186 187static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf,188				size_t count, loff_t *ppos)189{190	struct b43legacy_wldev *dev;191	struct b43legacy_debugfs_fops *dfops;192	struct b43legacy_dfs_file *dfile;193	ssize_t ret;194	char *buf;195	const size_t bufsize = 1024 * 16; /* 16 KiB buffer */196	const size_t buforder = get_order(bufsize);197	int err = 0;198 199	if (!count)200		return 0;201	dev = file->private_data;202	if (!dev)203		return -ENODEV;204 205	mutex_lock(&dev->wl->mutex);206	if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {207		err = -ENODEV;208		goto out_unlock;209	}210 211	dfops = container_of(debugfs_real_fops(file),212			     struct b43legacy_debugfs_fops, fops);213	if (!dfops->read) {214		err = -ENOSYS;215		goto out_unlock;216	}217	dfile = fops_to_dfs_file(dev, dfops);218 219	if (!dfile->buffer) {220		buf = (char *)__get_free_pages(GFP_KERNEL, buforder);221		if (!buf) {222			err = -ENOMEM;223			goto out_unlock;224		}225		memset(buf, 0, bufsize);226		if (dfops->take_irqlock) {227			spin_lock_irq(&dev->wl->irq_lock);228			ret = dfops->read(dev, buf, bufsize);229			spin_unlock_irq(&dev->wl->irq_lock);230		} else231			ret = dfops->read(dev, buf, bufsize);232		if (ret <= 0) {233			free_pages((unsigned long)buf, buforder);234			err = ret;235			goto out_unlock;236		}237		dfile->data_len = ret;238		dfile->buffer = buf;239	}240 241	ret = simple_read_from_buffer(userbuf, count, ppos,242				      dfile->buffer,243				      dfile->data_len);244	if (*ppos >= dfile->data_len) {245		free_pages((unsigned long)dfile->buffer, buforder);246		dfile->buffer = NULL;247		dfile->data_len = 0;248	}249out_unlock:250	mutex_unlock(&dev->wl->mutex);251 252	return err ? err : ret;253}254 255static ssize_t b43legacy_debugfs_write(struct file *file,256				 const char __user *userbuf,257				 size_t count, loff_t *ppos)258{259	struct b43legacy_wldev *dev;260	struct b43legacy_debugfs_fops *dfops;261	char *buf;262	int err = 0;263 264	if (!count)265		return 0;266	if (count > PAGE_SIZE)267		return -E2BIG;268	dev = file->private_data;269	if (!dev)270		return -ENODEV;271 272	mutex_lock(&dev->wl->mutex);273	if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {274		err = -ENODEV;275		goto out_unlock;276	}277 278	dfops = container_of(debugfs_real_fops(file),279			     struct b43legacy_debugfs_fops, fops);280	if (!dfops->write) {281		err = -ENOSYS;282		goto out_unlock;283	}284 285	buf = (char *)get_zeroed_page(GFP_KERNEL);286	if (!buf) {287		err = -ENOMEM;288		goto out_unlock;289	}290	if (copy_from_user(buf, userbuf, count)) {291		err = -EFAULT;292		goto out_freepage;293	}294	if (dfops->take_irqlock) {295		spin_lock_irq(&dev->wl->irq_lock);296		err = dfops->write(dev, buf, count);297		spin_unlock_irq(&dev->wl->irq_lock);298	} else299		err = dfops->write(dev, buf, count);300	if (err)301		goto out_freepage;302 303out_freepage:304	free_page((unsigned long)buf);305out_unlock:306	mutex_unlock(&dev->wl->mutex);307 308	return err ? err : count;309}310 311 312#define B43legacy_DEBUGFS_FOPS(name, _read, _write, _take_irqlock)	\313	static struct b43legacy_debugfs_fops fops_##name = {		\314		.read	= _read,				\315		.write	= _write,				\316		.fops	= {					\317			.open	= simple_open,				\318			.read	= b43legacy_debugfs_read,		\319			.write	= b43legacy_debugfs_write,		\320			.llseek = generic_file_llseek,			\321		},						\322		.file_struct_offset = offsetof(struct b43legacy_dfsentry, \323					       file_##name),	\324		.take_irqlock	= _take_irqlock,		\325	}326 327B43legacy_DEBUGFS_FOPS(tsf, tsf_read_file, tsf_write_file, 1);328B43legacy_DEBUGFS_FOPS(ucode_regs, ucode_regs_read_file, NULL, 1);329B43legacy_DEBUGFS_FOPS(shm, shm_read_file, NULL, 1);330B43legacy_DEBUGFS_FOPS(txstat, txstat_read_file, NULL, 0);331B43legacy_DEBUGFS_FOPS(restart, NULL, restart_write_file, 1);332 333 334int b43legacy_debug(struct b43legacy_wldev *dev, enum b43legacy_dyndbg feature)335{336	return !!(dev->dfsentry && dev->dfsentry->dyn_debug[feature]);337}338 339static void b43legacy_add_dynamic_debug(struct b43legacy_wldev *dev)340{341	struct b43legacy_dfsentry *e = dev->dfsentry;342 343#define add_dyn_dbg(name, id, initstate) do {			\344	e->dyn_debug[id] = (initstate);				\345	debugfs_create_bool(name, 0600, e->subdir,		\346			    &(e->dyn_debug[id]));		\347	} while (0)348 349	add_dyn_dbg("debug_xmitpower", B43legacy_DBG_XMITPOWER, false);350	add_dyn_dbg("debug_dmaoverflow", B43legacy_DBG_DMAOVERFLOW, false);351	add_dyn_dbg("debug_dmaverbose", B43legacy_DBG_DMAVERBOSE, false);352	add_dyn_dbg("debug_pwork_fast", B43legacy_DBG_PWORK_FAST, false);353	add_dyn_dbg("debug_pwork_stop", B43legacy_DBG_PWORK_STOP, false);354 355#undef add_dyn_dbg356}357 358void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev)359{360	struct b43legacy_dfsentry *e;361	struct b43legacy_txstatus_log *log;362	char devdir[16];363 364	B43legacy_WARN_ON(!dev);365	e = kzalloc(sizeof(*e), GFP_KERNEL);366	if (!e) {367		b43legacyerr(dev->wl, "debugfs: add device OOM\n");368		return;369	}370	e->dev = dev;371	log = &e->txstatlog;372	log->log = kcalloc(B43legacy_NR_LOGGED_TXSTATUS,373			   sizeof(struct b43legacy_txstatus), GFP_KERNEL);374	if (!log->log) {375		b43legacyerr(dev->wl, "debugfs: add device txstatus OOM\n");376		kfree(e);377		return;378	}379	log->end = -1;380	spin_lock_init(&log->lock);381 382	dev->dfsentry = e;383 384	snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));385	e->subdir = debugfs_create_dir(devdir, rootdir);386 387#define ADD_FILE(name, mode)	\388	do {							\389		debugfs_create_file(__stringify(name), mode,	\390				    e->subdir, dev,		\391				    &fops_##name.fops);		\392	} while (0)393 394 395	ADD_FILE(tsf, 0600);396	ADD_FILE(ucode_regs, 0400);397	ADD_FILE(shm, 0400);398	ADD_FILE(txstat, 0400);399	ADD_FILE(restart, 0200);400 401#undef ADD_FILE402 403	b43legacy_add_dynamic_debug(dev);404}405 406void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev)407{408	struct b43legacy_dfsentry *e;409 410	if (!dev)411		return;412	e = dev->dfsentry;413	if (!e)414		return;415 416	debugfs_remove(e->subdir);417	kfree(e->txstatlog.log);418	kfree(e);419}420 421void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev,422			    const struct b43legacy_txstatus *status)423{424	struct b43legacy_dfsentry *e = dev->dfsentry;425	struct b43legacy_txstatus_log *log;426	struct b43legacy_txstatus *cur;427	int i;428 429	if (!e)430		return;431	log = &e->txstatlog;432	B43legacy_WARN_ON(!irqs_disabled());433	spin_lock(&log->lock);434	i = log->end + 1;435	if (i == B43legacy_NR_LOGGED_TXSTATUS)436		i = 0;437	log->end = i;438	cur = &(log->log[i]);439	memcpy(cur, status, sizeof(*cur));440	spin_unlock(&log->lock);441}442 443void b43legacy_debugfs_init(void)444{445	rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);446}447 448void b43legacy_debugfs_exit(void)449{450	debugfs_remove(rootdir);451}452