brintos

brintos / linux-shallow public Read only

0
0
Text · 17.2 KiB · efa9844 Raw
772 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 4  Broadcom B43 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 "b43.h"21#include "main.h"22#include "debugfs.h"23#include "dma.h"24#include "xmit.h"25 26 27/* The root directory. */28static struct dentry *rootdir;29 30struct b43_debugfs_fops {31	ssize_t (*read)(struct b43_wldev *dev, char *buf, size_t bufsize);32	int (*write)(struct b43_wldev *dev, const char *buf, size_t count);33	struct file_operations fops;34	/* Offset of struct b43_dfs_file in struct b43_dfsentry */35	size_t file_struct_offset;36};37 38static inline39struct b43_dfs_file *fops_to_dfs_file(struct b43_wldev *dev,40				      const struct b43_debugfs_fops *dfops)41{42	void *p;43 44	p = dev->dfsentry;45	p += dfops->file_struct_offset;46 47	return p;48}49 50 51#define fappend(fmt, x...)	\52	do {							\53		if (bufsize - count)				\54			count += scnprintf(buf + count,		\55					  bufsize - count,	\56					  fmt , ##x);		\57		else						\58			printk(KERN_ERR "b43: fappend overflow\n"); \59	} while (0)60 61 62/* The biggest address values for SHM access from the debugfs files. */63#define B43_MAX_SHM_ROUTING	464#define B43_MAX_SHM_ADDR	0xFFFF65 66static ssize_t shm16read__read_file(struct b43_wldev *dev,67				    char *buf, size_t bufsize)68{69	ssize_t count = 0;70	unsigned int routing, addr;71	u16 val;72 73	routing = dev->dfsentry->shm16read_routing_next;74	addr = dev->dfsentry->shm16read_addr_next;75	if ((routing > B43_MAX_SHM_ROUTING) ||76	    (addr > B43_MAX_SHM_ADDR))77		return -EDESTADDRREQ;78 79	val = b43_shm_read16(dev, routing, addr);80	fappend("0x%04X\n", val);81 82	return count;83}84 85static int shm16read__write_file(struct b43_wldev *dev,86				 const char *buf, size_t count)87{88	unsigned int routing, addr;89	int res;90 91	res = sscanf(buf, "0x%X 0x%X", &routing, &addr);92	if (res != 2)93		return -EINVAL;94	if (routing > B43_MAX_SHM_ROUTING)95		return -EADDRNOTAVAIL;96	if (addr > B43_MAX_SHM_ADDR)97		return -EADDRNOTAVAIL;98	if (routing == B43_SHM_SHARED) {99		if ((addr % 2) != 0)100			return -EADDRNOTAVAIL;101	}102 103	dev->dfsentry->shm16read_routing_next = routing;104	dev->dfsentry->shm16read_addr_next = addr;105 106	return 0;107}108 109static int shm16write__write_file(struct b43_wldev *dev,110				  const char *buf, size_t count)111{112	unsigned int routing, addr, mask, set;113	u16 val;114	int res;115 116	res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X",117		     &routing, &addr, &mask, &set);118	if (res != 4)119		return -EINVAL;120	if (routing > B43_MAX_SHM_ROUTING)121		return -EADDRNOTAVAIL;122	if (addr > B43_MAX_SHM_ADDR)123		return -EADDRNOTAVAIL;124	if (routing == B43_SHM_SHARED) {125		if ((addr % 2) != 0)126			return -EADDRNOTAVAIL;127	}128	if ((mask > 0xFFFF) || (set > 0xFFFF))129		return -E2BIG;130 131	if (mask == 0)132		val = 0;133	else134		val = b43_shm_read16(dev, routing, addr);135	val &= mask;136	val |= set;137	b43_shm_write16(dev, routing, addr, val);138 139	return 0;140}141 142static ssize_t shm32read__read_file(struct b43_wldev *dev,143				    char *buf, size_t bufsize)144{145	ssize_t count = 0;146	unsigned int routing, addr;147	u32 val;148 149	routing = dev->dfsentry->shm32read_routing_next;150	addr = dev->dfsentry->shm32read_addr_next;151	if ((routing > B43_MAX_SHM_ROUTING) ||152	    (addr > B43_MAX_SHM_ADDR))153		return -EDESTADDRREQ;154 155	val = b43_shm_read32(dev, routing, addr);156	fappend("0x%08X\n", val);157 158	return count;159}160 161static int shm32read__write_file(struct b43_wldev *dev,162				 const char *buf, size_t count)163{164	unsigned int routing, addr;165	int res;166 167	res = sscanf(buf, "0x%X 0x%X", &routing, &addr);168	if (res != 2)169		return -EINVAL;170	if (routing > B43_MAX_SHM_ROUTING)171		return -EADDRNOTAVAIL;172	if (addr > B43_MAX_SHM_ADDR)173		return -EADDRNOTAVAIL;174	if (routing == B43_SHM_SHARED) {175		if ((addr % 2) != 0)176			return -EADDRNOTAVAIL;177	}178 179	dev->dfsentry->shm32read_routing_next = routing;180	dev->dfsentry->shm32read_addr_next = addr;181 182	return 0;183}184 185static int shm32write__write_file(struct b43_wldev *dev,186				  const char *buf, size_t count)187{188	unsigned int routing, addr, mask, set;189	u32 val;190	int res;191 192	res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X",193		     &routing, &addr, &mask, &set);194	if (res != 4)195		return -EINVAL;196	if (routing > B43_MAX_SHM_ROUTING)197		return -EADDRNOTAVAIL;198	if (addr > B43_MAX_SHM_ADDR)199		return -EADDRNOTAVAIL;200	if (routing == B43_SHM_SHARED) {201		if ((addr % 2) != 0)202			return -EADDRNOTAVAIL;203	}204	if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF))205		return -E2BIG;206 207	if (mask == 0)208		val = 0;209	else210		val = b43_shm_read32(dev, routing, addr);211	val &= mask;212	val |= set;213	b43_shm_write32(dev, routing, addr, val);214 215	return 0;216}217 218/* The biggest MMIO address that we allow access to from the debugfs files. */219#define B43_MAX_MMIO_ACCESS	(0xF00 - 1)220 221static ssize_t mmio16read__read_file(struct b43_wldev *dev,222				     char *buf, size_t bufsize)223{224	ssize_t count = 0;225	unsigned int addr;226	u16 val;227 228	addr = dev->dfsentry->mmio16read_next;229	if (addr > B43_MAX_MMIO_ACCESS)230		return -EDESTADDRREQ;231 232	val = b43_read16(dev, addr);233	fappend("0x%04X\n", val);234 235	return count;236}237 238static int mmio16read__write_file(struct b43_wldev *dev,239				  const char *buf, size_t count)240{241	unsigned int addr;242	int res;243 244	res = sscanf(buf, "0x%X", &addr);245	if (res != 1)246		return -EINVAL;247	if (addr > B43_MAX_MMIO_ACCESS)248		return -EADDRNOTAVAIL;249	if ((addr % 2) != 0)250		return -EINVAL;251 252	dev->dfsentry->mmio16read_next = addr;253 254	return 0;255}256 257static int mmio16write__write_file(struct b43_wldev *dev,258				   const char *buf, size_t count)259{260	unsigned int addr, mask, set;261	int res;262	u16 val;263 264	res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set);265	if (res != 3)266		return -EINVAL;267	if (addr > B43_MAX_MMIO_ACCESS)268		return -EADDRNOTAVAIL;269	if ((mask > 0xFFFF) || (set > 0xFFFF))270		return -E2BIG;271	if ((addr % 2) != 0)272		return -EINVAL;273 274	if (mask == 0)275		val = 0;276	else277		val = b43_read16(dev, addr);278	val &= mask;279	val |= set;280	b43_write16(dev, addr, val);281 282	return 0;283}284 285static ssize_t mmio32read__read_file(struct b43_wldev *dev,286				     char *buf, size_t bufsize)287{288	ssize_t count = 0;289	unsigned int addr;290	u32 val;291 292	addr = dev->dfsentry->mmio32read_next;293	if (addr > B43_MAX_MMIO_ACCESS)294		return -EDESTADDRREQ;295 296	val = b43_read32(dev, addr);297	fappend("0x%08X\n", val);298 299	return count;300}301 302static int mmio32read__write_file(struct b43_wldev *dev,303				  const char *buf, size_t count)304{305	unsigned int addr;306	int res;307 308	res = sscanf(buf, "0x%X", &addr);309	if (res != 1)310		return -EINVAL;311	if (addr > B43_MAX_MMIO_ACCESS)312		return -EADDRNOTAVAIL;313	if ((addr % 4) != 0)314		return -EINVAL;315 316	dev->dfsentry->mmio32read_next = addr;317 318	return 0;319}320 321static int mmio32write__write_file(struct b43_wldev *dev,322				   const char *buf, size_t count)323{324	unsigned int addr, mask, set;325	int res;326	u32 val;327 328	res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set);329	if (res != 3)330		return -EINVAL;331	if (addr > B43_MAX_MMIO_ACCESS)332		return -EADDRNOTAVAIL;333	if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF))334		return -E2BIG;335	if ((addr % 4) != 0)336		return -EINVAL;337 338	if (mask == 0)339		val = 0;340	else341		val = b43_read32(dev, addr);342	val &= mask;343	val |= set;344	b43_write32(dev, addr, val);345 346	return 0;347}348 349static ssize_t txstat_read_file(struct b43_wldev *dev,350				char *buf, size_t bufsize)351{352	struct b43_txstatus_log *log = &dev->dfsentry->txstatlog;353	ssize_t count = 0;354	int i, idx;355	struct b43_txstatus *stat;356 357	if (log->end < 0) {358		fappend("Nothing transmitted, yet\n");359		goto out;360	}361	fappend("b43 TX status reports:\n\n"362		"index | cookie | seq | phy_stat | frame_count | "363		"rts_count | supp_reason | pm_indicated | "364		"intermediate | for_ampdu | acked\n" "---\n");365	i = log->end + 1;366	idx = 0;367	while (1) {368		if (i == B43_NR_LOGGED_TXSTATUS)369			i = 0;370		stat = &(log->log[i]);371		if (stat->cookie) {372			fappend("%03d | "373				"0x%04X | 0x%04X | 0x%02X | "374				"0x%X | 0x%X | "375				"%u | %u | "376				"%u | %u | %u\n",377				idx,378				stat->cookie, stat->seq, stat->phy_stat,379				stat->frame_count, stat->rts_count,380				stat->supp_reason, stat->pm_indicated,381				stat->intermediate, stat->for_ampdu,382				stat->acked);383			idx++;384		}385		if (i == log->end)386			break;387		i++;388	}389out:390 391	return count;392}393 394static int restart_write_file(struct b43_wldev *dev,395			      const char *buf, size_t count)396{397	int err = 0;398 399	if (count > 0 && buf[0] == '1') {400		b43_controller_restart(dev, "manually restarted");401	} else402		err = -EINVAL;403 404	return err;405}406 407static unsigned long calc_expire_secs(unsigned long now,408				      unsigned long time,409				      unsigned long expire)410{411	expire = time + expire;412 413	if (time_after(now, expire))414		return 0; /* expired */415	if (expire < now) {416		/* jiffies wrapped */417		expire -= MAX_JIFFY_OFFSET;418		now -= MAX_JIFFY_OFFSET;419	}420	B43_WARN_ON(expire < now);421 422	return (expire - now) / HZ;423}424 425static ssize_t loctls_read_file(struct b43_wldev *dev,426				char *buf, size_t bufsize)427{428	ssize_t count = 0;429	struct b43_txpower_lo_control *lo;430	int i, err = 0;431	struct b43_lo_calib *cal;432	unsigned long now = jiffies;433	struct b43_phy *phy = &dev->phy;434 435	if (phy->type != B43_PHYTYPE_G) {436		fappend("Device is not a G-PHY\n");437		err = -ENODEV;438		goto out;439	}440	lo = phy->g->lo_control;441	fappend("-- Local Oscillator calibration data --\n\n");442	fappend("HW-power-control enabled: %d\n",443		dev->phy.hardware_power_control);444	fappend("TX Bias: 0x%02X,  TX Magn: 0x%02X  (expire in %lu sec)\n",445		lo->tx_bias, lo->tx_magn,446		calc_expire_secs(now, lo->txctl_measured_time,447				 B43_LO_TXCTL_EXPIRE));448	fappend("Power Vector: 0x%08X%08X  (expires in %lu sec)\n",449		(unsigned int)((lo->power_vector & 0xFFFFFFFF00000000ULL) >> 32),450		(unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL),451		calc_expire_secs(now, lo->pwr_vec_read_time,452				 B43_LO_PWRVEC_EXPIRE));453 454	fappend("\nCalibrated settings:\n");455	list_for_each_entry(cal, &lo->calib_list, list) {456		bool active;457 458		active = (b43_compare_bbatt(&cal->bbatt, &phy->g->bbatt) &&459			  b43_compare_rfatt(&cal->rfatt, &phy->g->rfatt));460		fappend("BB(%d), RF(%d,%d)  ->  I=%d, Q=%d  "461			"(expires in %lu sec)%s\n",462			cal->bbatt.att,463			cal->rfatt.att, cal->rfatt.with_padmix,464			cal->ctl.i, cal->ctl.q,465			calc_expire_secs(now, cal->calib_time,466					 B43_LO_CALIB_EXPIRE),467			active ? "  ACTIVE" : "");468	}469 470	fappend("\nUsed RF attenuation values:  Value(WithPadmix flag)\n");471	for (i = 0; i < lo->rfatt_list.len; i++) {472		fappend("%u(%d), ",473			lo->rfatt_list.list[i].att,474			lo->rfatt_list.list[i].with_padmix);475	}476	fappend("\n");477	fappend("\nUsed Baseband attenuation values:\n");478	for (i = 0; i < lo->bbatt_list.len; i++) {479		fappend("%u, ",480			lo->bbatt_list.list[i].att);481	}482	fappend("\n");483 484out:485	return err ? err : count;486}487 488#undef fappend489 490static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,491				size_t count, loff_t *ppos)492{493	struct b43_wldev *dev;494	struct b43_debugfs_fops *dfops;495	struct b43_dfs_file *dfile;496	ssize_t ret;497	char *buf;498	const size_t bufsize = 1024 * 16; /* 16 kiB buffer */499	const size_t buforder = get_order(bufsize);500	int err = 0;501 502	if (!count)503		return 0;504	dev = file->private_data;505	if (!dev)506		return -ENODEV;507 508	mutex_lock(&dev->wl->mutex);509	if (b43_status(dev) < B43_STAT_INITIALIZED) {510		err = -ENODEV;511		goto out_unlock;512	}513 514	dfops = container_of(debugfs_real_fops(file),515			     struct b43_debugfs_fops, fops);516	if (!dfops->read) {517		err = -ENOSYS;518		goto out_unlock;519	}520	dfile = fops_to_dfs_file(dev, dfops);521 522	if (!dfile->buffer) {523		buf = (char *)__get_free_pages(GFP_KERNEL, buforder);524		if (!buf) {525			err = -ENOMEM;526			goto out_unlock;527		}528		memset(buf, 0, bufsize);529		ret = dfops->read(dev, buf, bufsize);530		if (ret <= 0) {531			free_pages((unsigned long)buf, buforder);532			err = ret;533			goto out_unlock;534		}535		dfile->data_len = ret;536		dfile->buffer = buf;537	}538 539	ret = simple_read_from_buffer(userbuf, count, ppos,540				      dfile->buffer,541				      dfile->data_len);542	if (*ppos >= dfile->data_len) {543		free_pages((unsigned long)dfile->buffer, buforder);544		dfile->buffer = NULL;545		dfile->data_len = 0;546	}547out_unlock:548	mutex_unlock(&dev->wl->mutex);549 550	return err ? err : ret;551}552 553static ssize_t b43_debugfs_write(struct file *file,554				 const char __user *userbuf,555				 size_t count, loff_t *ppos)556{557	struct b43_wldev *dev;558	struct b43_debugfs_fops *dfops;559	char *buf;560	int err = 0;561 562	if (!count)563		return 0;564	if (count > PAGE_SIZE)565		return -E2BIG;566	dev = file->private_data;567	if (!dev)568		return -ENODEV;569 570	mutex_lock(&dev->wl->mutex);571	if (b43_status(dev) < B43_STAT_INITIALIZED) {572		err = -ENODEV;573		goto out_unlock;574	}575 576	dfops = container_of(debugfs_real_fops(file),577			     struct b43_debugfs_fops, fops);578	if (!dfops->write) {579		err = -ENOSYS;580		goto out_unlock;581	}582 583	buf = (char *)get_zeroed_page(GFP_KERNEL);584	if (!buf) {585		err = -ENOMEM;586		goto out_unlock;587	}588	if (copy_from_user(buf, userbuf, count)) {589		err = -EFAULT;590		goto out_freepage;591	}592	err = dfops->write(dev, buf, count);593	if (err)594		goto out_freepage;595 596out_freepage:597	free_page((unsigned long)buf);598out_unlock:599	mutex_unlock(&dev->wl->mutex);600 601	return err ? err : count;602}603 604 605#define B43_DEBUGFS_FOPS(name, _read, _write)			\606	static struct b43_debugfs_fops fops_##name = {		\607		.read	= _read,				\608		.write	= _write,				\609		.fops	= {					\610			.open	= simple_open,			\611			.read	= b43_debugfs_read,		\612			.write	= b43_debugfs_write,		\613			.llseek = generic_file_llseek,		\614		},						\615		.file_struct_offset = offsetof(struct b43_dfsentry, \616					       file_##name),	\617	}618 619B43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file);620B43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file);621B43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file);622B43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file);623B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file);624B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file);625B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file);626B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file);627B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL);628B43_DEBUGFS_FOPS(restart, NULL, restart_write_file);629B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL);630 631 632bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature)633{634	bool enabled;635 636	enabled = (dev->dfsentry && dev->dfsentry->dyn_debug[feature]);637	if (unlikely(enabled)) {638		/* Force full debugging messages, if the user enabled639		 * some dynamic debugging feature. */640		b43_modparam_verbose = B43_VERBOSITY_MAX;641	}642 643	return enabled;644}645 646static void b43_add_dynamic_debug(struct b43_wldev *dev)647{648	struct b43_dfsentry *e = dev->dfsentry;649 650#define add_dyn_dbg(name, id, initstate) do {			\651	e->dyn_debug[id] = (initstate);				\652	debugfs_create_bool(name, 0600, e->subdir,		\653			    &(e->dyn_debug[id]));		\654	} while (0)655 656	add_dyn_dbg("debug_xmitpower", B43_DBG_XMITPOWER, false);657	add_dyn_dbg("debug_dmaoverflow", B43_DBG_DMAOVERFLOW, false);658	add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, false);659	add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, false);660	add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, false);661	add_dyn_dbg("debug_lo", B43_DBG_LO, false);662	add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, false);663	add_dyn_dbg("debug_keys", B43_DBG_KEYS, false);664	add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, false);665 666#undef add_dyn_dbg667}668 669void b43_debugfs_add_device(struct b43_wldev *dev)670{671	struct b43_dfsentry *e;672	struct b43_txstatus_log *log;673	char devdir[16];674 675	B43_WARN_ON(!dev);676	e = kzalloc(sizeof(*e), GFP_KERNEL);677	if (!e) {678		b43err(dev->wl, "debugfs: add device OOM\n");679		return;680	}681	e->dev = dev;682	log = &e->txstatlog;683	log->log = kcalloc(B43_NR_LOGGED_TXSTATUS,684			   sizeof(struct b43_txstatus), GFP_KERNEL);685	if (!log->log) {686		b43err(dev->wl, "debugfs: add device txstatus OOM\n");687		kfree(e);688		return;689	}690	log->end = -1;691 692	dev->dfsentry = e;693 694	snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));695	e->subdir = debugfs_create_dir(devdir, rootdir);696 697	e->mmio16read_next = 0xFFFF; /* invalid address */698	e->mmio32read_next = 0xFFFF; /* invalid address */699	e->shm16read_routing_next = 0xFFFFFFFF; /* invalid routing */700	e->shm16read_addr_next = 0xFFFFFFFF; /* invalid address */701	e->shm32read_routing_next = 0xFFFFFFFF; /* invalid routing */702	e->shm32read_addr_next = 0xFFFFFFFF; /* invalid address */703 704#define ADD_FILE(name, mode)	\705	do {							\706		debugfs_create_file(__stringify(name),		\707				mode, e->subdir, dev,		\708				&fops_##name.fops);		\709	} while (0)710 711 712	ADD_FILE(shm16read, 0600);713	ADD_FILE(shm16write, 0200);714	ADD_FILE(shm32read, 0600);715	ADD_FILE(shm32write, 0200);716	ADD_FILE(mmio16read, 0600);717	ADD_FILE(mmio16write, 0200);718	ADD_FILE(mmio32read, 0600);719	ADD_FILE(mmio32write, 0200);720	ADD_FILE(txstat, 0400);721	ADD_FILE(restart, 0200);722	ADD_FILE(loctls, 0400);723 724#undef ADD_FILE725 726	b43_add_dynamic_debug(dev);727}728 729void b43_debugfs_remove_device(struct b43_wldev *dev)730{731	struct b43_dfsentry *e;732 733	if (!dev)734		return;735	e = dev->dfsentry;736	if (!e)737		return;738 739	debugfs_remove(e->subdir);740	kfree(e->txstatlog.log);741	kfree(e);742}743 744void b43_debugfs_log_txstat(struct b43_wldev *dev,745			    const struct b43_txstatus *status)746{747	struct b43_dfsentry *e = dev->dfsentry;748	struct b43_txstatus_log *log;749	struct b43_txstatus *cur;750	int i;751 752	if (!e)753		return;754	log = &e->txstatlog;755	i = log->end + 1;756	if (i == B43_NR_LOGGED_TXSTATUS)757		i = 0;758	log->end = i;759	cur = &(log->log[i]);760	memcpy(cur, status, sizeof(*cur));761}762 763void b43_debugfs_init(void)764{765	rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);766}767 768void b43_debugfs_exit(void)769{770	debugfs_remove(rootdir);771}772