xref: /linux/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c (revision 1ccea77e2a2687cae171b7987eb44730ec8c6d5f)
1*1ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
233aca94dSKalle Valo /*
333aca94dSKalle Valo 	Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
433aca94dSKalle Valo 	<http://rt2x00.serialmonkey.com>
533aca94dSKalle Valo 
633aca94dSKalle Valo  */
733aca94dSKalle Valo 
833aca94dSKalle Valo /*
933aca94dSKalle Valo 	Module: rt2x00lib
1033aca94dSKalle Valo 	Abstract: rt2x00 debugfs specific routines.
1133aca94dSKalle Valo  */
1233aca94dSKalle Valo 
1333aca94dSKalle Valo #include <linux/debugfs.h>
1433aca94dSKalle Valo #include <linux/kernel.h>
1533aca94dSKalle Valo #include <linux/module.h>
1633aca94dSKalle Valo #include <linux/poll.h>
1733aca94dSKalle Valo #include <linux/sched.h>
1833aca94dSKalle Valo #include <linux/slab.h>
1933aca94dSKalle Valo #include <linux/uaccess.h>
2033aca94dSKalle Valo 
2133aca94dSKalle Valo #include "rt2x00.h"
2233aca94dSKalle Valo #include "rt2x00lib.h"
2333aca94dSKalle Valo #include "rt2x00dump.h"
2433aca94dSKalle Valo 
2533aca94dSKalle Valo #define MAX_LINE_LENGTH 64
2633aca94dSKalle Valo 
2733aca94dSKalle Valo struct rt2x00debug_crypto {
2833aca94dSKalle Valo 	unsigned long success;
2933aca94dSKalle Valo 	unsigned long icv_error;
3033aca94dSKalle Valo 	unsigned long mic_error;
3133aca94dSKalle Valo 	unsigned long key_error;
3233aca94dSKalle Valo };
3333aca94dSKalle Valo 
3433aca94dSKalle Valo struct rt2x00debug_intf {
3533aca94dSKalle Valo 	/*
3633aca94dSKalle Valo 	 * Pointer to driver structure where
3733aca94dSKalle Valo 	 * this debugfs entry belongs to.
3833aca94dSKalle Valo 	 */
3933aca94dSKalle Valo 	struct rt2x00_dev *rt2x00dev;
4033aca94dSKalle Valo 
4133aca94dSKalle Valo 	/*
4233aca94dSKalle Valo 	 * Reference to the rt2x00debug structure
4333aca94dSKalle Valo 	 * which can be used to communicate with
4433aca94dSKalle Valo 	 * the registers.
4533aca94dSKalle Valo 	 */
4633aca94dSKalle Valo 	const struct rt2x00debug *debug;
4733aca94dSKalle Valo 
4833aca94dSKalle Valo 	/*
4933aca94dSKalle Valo 	 * Debugfs entries for:
5033aca94dSKalle Valo 	 * - driver folder
5133aca94dSKalle Valo 	 *   - driver file
5233aca94dSKalle Valo 	 *   - chipset file
5333aca94dSKalle Valo 	 *   - device state flags file
5433aca94dSKalle Valo 	 *   - device capability flags file
5533aca94dSKalle Valo 	 *   - register folder
5633aca94dSKalle Valo 	 *     - csr offset/value files
5733aca94dSKalle Valo 	 *     - eeprom offset/value files
5833aca94dSKalle Valo 	 *     - bbp offset/value files
5933aca94dSKalle Valo 	 *     - rf offset/value files
6033aca94dSKalle Valo 	 *     - rfcsr offset/value files
6133aca94dSKalle Valo 	 *   - queue folder
6233aca94dSKalle Valo 	 *     - frame dump file
6333aca94dSKalle Valo 	 *     - queue stats file
6433aca94dSKalle Valo 	 *     - crypto stats file
6533aca94dSKalle Valo 	 */
6633aca94dSKalle Valo 	struct dentry *driver_folder;
6733aca94dSKalle Valo 	struct dentry *driver_entry;
6833aca94dSKalle Valo 	struct dentry *chipset_entry;
6933aca94dSKalle Valo 	struct dentry *dev_flags;
7033aca94dSKalle Valo 	struct dentry *cap_flags;
7133aca94dSKalle Valo 	struct dentry *register_folder;
7233aca94dSKalle Valo 	struct dentry *csr_off_entry;
7333aca94dSKalle Valo 	struct dentry *csr_val_entry;
7433aca94dSKalle Valo 	struct dentry *eeprom_off_entry;
7533aca94dSKalle Valo 	struct dentry *eeprom_val_entry;
7633aca94dSKalle Valo 	struct dentry *bbp_off_entry;
7733aca94dSKalle Valo 	struct dentry *bbp_val_entry;
7833aca94dSKalle Valo 	struct dentry *rf_off_entry;
7933aca94dSKalle Valo 	struct dentry *rf_val_entry;
8033aca94dSKalle Valo 	struct dentry *rfcsr_off_entry;
8133aca94dSKalle Valo 	struct dentry *rfcsr_val_entry;
8233aca94dSKalle Valo 	struct dentry *queue_folder;
8333aca94dSKalle Valo 	struct dentry *queue_frame_dump_entry;
8433aca94dSKalle Valo 	struct dentry *queue_stats_entry;
8533aca94dSKalle Valo 	struct dentry *crypto_stats_entry;
8633aca94dSKalle Valo 
8733aca94dSKalle Valo 	/*
8833aca94dSKalle Valo 	 * The frame dump file only allows a single reader,
8933aca94dSKalle Valo 	 * so we need to store the current state here.
9033aca94dSKalle Valo 	 */
9133aca94dSKalle Valo 	unsigned long frame_dump_flags;
9233aca94dSKalle Valo #define FRAME_DUMP_FILE_OPEN	1
9333aca94dSKalle Valo 
9433aca94dSKalle Valo 	/*
9533aca94dSKalle Valo 	 * We queue each frame before dumping it to the user,
9633aca94dSKalle Valo 	 * per read command we will pass a single skb structure
9733aca94dSKalle Valo 	 * so we should be prepared to queue multiple sk buffers
9833aca94dSKalle Valo 	 * before sending it to userspace.
9933aca94dSKalle Valo 	 */
10033aca94dSKalle Valo 	struct sk_buff_head frame_dump_skbqueue;
10133aca94dSKalle Valo 	wait_queue_head_t frame_dump_waitqueue;
10233aca94dSKalle Valo 
10333aca94dSKalle Valo 	/*
10433aca94dSKalle Valo 	 * HW crypto statistics.
10533aca94dSKalle Valo 	 * All statistics are stored separately per cipher type.
10633aca94dSKalle Valo 	 */
10733aca94dSKalle Valo 	struct rt2x00debug_crypto crypto_stats[CIPHER_MAX];
10833aca94dSKalle Valo 
10933aca94dSKalle Valo 	/*
11033aca94dSKalle Valo 	 * Driver and chipset files will use a data buffer
11133aca94dSKalle Valo 	 * that has been created in advance. This will simplify
11233aca94dSKalle Valo 	 * the code since we can use the debugfs functions.
11333aca94dSKalle Valo 	 */
11433aca94dSKalle Valo 	struct debugfs_blob_wrapper driver_blob;
11533aca94dSKalle Valo 	struct debugfs_blob_wrapper chipset_blob;
11633aca94dSKalle Valo 
11733aca94dSKalle Valo 	/*
11833aca94dSKalle Valo 	 * Requested offset for each register type.
11933aca94dSKalle Valo 	 */
12033aca94dSKalle Valo 	unsigned int offset_csr;
12133aca94dSKalle Valo 	unsigned int offset_eeprom;
12233aca94dSKalle Valo 	unsigned int offset_bbp;
12333aca94dSKalle Valo 	unsigned int offset_rf;
12433aca94dSKalle Valo 	unsigned int offset_rfcsr;
12533aca94dSKalle Valo };
12633aca94dSKalle Valo 
12733aca94dSKalle Valo void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev,
12833aca94dSKalle Valo 			       struct rxdone_entry_desc *rxdesc)
12933aca94dSKalle Valo {
13033aca94dSKalle Valo 	struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf;
13133aca94dSKalle Valo 	enum cipher cipher = rxdesc->cipher;
13233aca94dSKalle Valo 	enum rx_crypto status = rxdesc->cipher_status;
13333aca94dSKalle Valo 
13433aca94dSKalle Valo 	if (cipher == CIPHER_TKIP_NO_MIC)
13533aca94dSKalle Valo 		cipher = CIPHER_TKIP;
13633aca94dSKalle Valo 	if (cipher == CIPHER_NONE || cipher >= CIPHER_MAX)
13733aca94dSKalle Valo 		return;
13833aca94dSKalle Valo 
13933aca94dSKalle Valo 	/* Remove CIPHER_NONE index */
14033aca94dSKalle Valo 	cipher--;
14133aca94dSKalle Valo 
14233aca94dSKalle Valo 	intf->crypto_stats[cipher].success += (status == RX_CRYPTO_SUCCESS);
14333aca94dSKalle Valo 	intf->crypto_stats[cipher].icv_error += (status == RX_CRYPTO_FAIL_ICV);
14433aca94dSKalle Valo 	intf->crypto_stats[cipher].mic_error += (status == RX_CRYPTO_FAIL_MIC);
14533aca94dSKalle Valo 	intf->crypto_stats[cipher].key_error += (status == RX_CRYPTO_FAIL_KEY);
14633aca94dSKalle Valo }
14733aca94dSKalle Valo 
14833aca94dSKalle Valo void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev,
1492ceb8137SStanislaw Gruszka 			    enum rt2x00_dump_type type, struct queue_entry *entry)
15033aca94dSKalle Valo {
15133aca94dSKalle Valo 	struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf;
1522ceb8137SStanislaw Gruszka 	struct sk_buff *skb = entry->skb;
15333aca94dSKalle Valo 	struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
15433aca94dSKalle Valo 	struct sk_buff *skbcopy;
15533aca94dSKalle Valo 	struct rt2x00dump_hdr *dump_hdr;
156f87eba99SArnd Bergmann 	struct timespec64 timestamp;
15733aca94dSKalle Valo 	u32 data_len;
15833aca94dSKalle Valo 
15933aca94dSKalle Valo 	if (likely(!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)))
16033aca94dSKalle Valo 		return;
16133aca94dSKalle Valo 
162f87eba99SArnd Bergmann 	ktime_get_ts64(&timestamp);
16333aca94dSKalle Valo 
16433aca94dSKalle Valo 	if (skb_queue_len(&intf->frame_dump_skbqueue) > 20) {
16533aca94dSKalle Valo 		rt2x00_dbg(rt2x00dev, "txrx dump queue length exceeded\n");
16633aca94dSKalle Valo 		return;
16733aca94dSKalle Valo 	}
16833aca94dSKalle Valo 
16933aca94dSKalle Valo 	data_len = skb->len;
17033aca94dSKalle Valo 	if (skbdesc->flags & SKBDESC_DESC_IN_SKB)
17133aca94dSKalle Valo 		data_len -= skbdesc->desc_len;
17233aca94dSKalle Valo 
17333aca94dSKalle Valo 	skbcopy = alloc_skb(sizeof(*dump_hdr) + skbdesc->desc_len + data_len,
17433aca94dSKalle Valo 			    GFP_ATOMIC);
17533aca94dSKalle Valo 	if (!skbcopy) {
17633aca94dSKalle Valo 		rt2x00_dbg(rt2x00dev, "Failed to copy skb for dump\n");
17733aca94dSKalle Valo 		return;
17833aca94dSKalle Valo 	}
17933aca94dSKalle Valo 
1804df864c1SJohannes Berg 	dump_hdr = skb_put(skbcopy, sizeof(*dump_hdr));
18133aca94dSKalle Valo 	dump_hdr->version = cpu_to_le32(DUMP_HEADER_VERSION);
18233aca94dSKalle Valo 	dump_hdr->header_length = cpu_to_le32(sizeof(*dump_hdr));
18333aca94dSKalle Valo 	dump_hdr->desc_length = cpu_to_le32(skbdesc->desc_len);
18433aca94dSKalle Valo 	dump_hdr->data_length = cpu_to_le32(data_len);
18533aca94dSKalle Valo 	dump_hdr->chip_rt = cpu_to_le16(rt2x00dev->chip.rt);
18633aca94dSKalle Valo 	dump_hdr->chip_rf = cpu_to_le16(rt2x00dev->chip.rf);
18733aca94dSKalle Valo 	dump_hdr->chip_rev = cpu_to_le16(rt2x00dev->chip.rev);
18833aca94dSKalle Valo 	dump_hdr->type = cpu_to_le16(type);
1892ceb8137SStanislaw Gruszka 	dump_hdr->queue_index = entry->queue->qid;
1902ceb8137SStanislaw Gruszka 	dump_hdr->entry_index = entry->entry_idx;
19133aca94dSKalle Valo 	dump_hdr->timestamp_sec = cpu_to_le32(timestamp.tv_sec);
192f87eba99SArnd Bergmann 	dump_hdr->timestamp_usec = cpu_to_le32(timestamp.tv_nsec /
193f87eba99SArnd Bergmann 					       NSEC_PER_USEC);
19433aca94dSKalle Valo 
19533aca94dSKalle Valo 	if (!(skbdesc->flags & SKBDESC_DESC_IN_SKB))
19659ae1d12SJohannes Berg 		skb_put_data(skbcopy, skbdesc->desc, skbdesc->desc_len);
19759ae1d12SJohannes Berg 	skb_put_data(skbcopy, skb->data, skb->len);
19833aca94dSKalle Valo 
19933aca94dSKalle Valo 	skb_queue_tail(&intf->frame_dump_skbqueue, skbcopy);
20033aca94dSKalle Valo 	wake_up_interruptible(&intf->frame_dump_waitqueue);
20133aca94dSKalle Valo 
20233aca94dSKalle Valo 	/*
20333aca94dSKalle Valo 	 * Verify that the file has not been closed while we were working.
20433aca94dSKalle Valo 	 */
20533aca94dSKalle Valo 	if (!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags))
20633aca94dSKalle Valo 		skb_queue_purge(&intf->frame_dump_skbqueue);
20733aca94dSKalle Valo }
20833aca94dSKalle Valo EXPORT_SYMBOL_GPL(rt2x00debug_dump_frame);
20933aca94dSKalle Valo 
21033aca94dSKalle Valo static int rt2x00debug_file_open(struct inode *inode, struct file *file)
21133aca94dSKalle Valo {
21233aca94dSKalle Valo 	struct rt2x00debug_intf *intf = inode->i_private;
21333aca94dSKalle Valo 
21433aca94dSKalle Valo 	file->private_data = inode->i_private;
21533aca94dSKalle Valo 
21633aca94dSKalle Valo 	if (!try_module_get(intf->debug->owner))
21733aca94dSKalle Valo 		return -EBUSY;
21833aca94dSKalle Valo 
21933aca94dSKalle Valo 	return 0;
22033aca94dSKalle Valo }
22133aca94dSKalle Valo 
22233aca94dSKalle Valo static int rt2x00debug_file_release(struct inode *inode, struct file *file)
22333aca94dSKalle Valo {
22433aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;
22533aca94dSKalle Valo 
22633aca94dSKalle Valo 	module_put(intf->debug->owner);
22733aca94dSKalle Valo 
22833aca94dSKalle Valo 	return 0;
22933aca94dSKalle Valo }
23033aca94dSKalle Valo 
23133aca94dSKalle Valo static int rt2x00debug_open_queue_dump(struct inode *inode, struct file *file)
23233aca94dSKalle Valo {
23333aca94dSKalle Valo 	struct rt2x00debug_intf *intf = inode->i_private;
23433aca94dSKalle Valo 	int retval;
23533aca94dSKalle Valo 
23633aca94dSKalle Valo 	retval = rt2x00debug_file_open(inode, file);
23733aca94dSKalle Valo 	if (retval)
23833aca94dSKalle Valo 		return retval;
23933aca94dSKalle Valo 
24033aca94dSKalle Valo 	if (test_and_set_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)) {
24133aca94dSKalle Valo 		rt2x00debug_file_release(inode, file);
24233aca94dSKalle Valo 		return -EBUSY;
24333aca94dSKalle Valo 	}
24433aca94dSKalle Valo 
24533aca94dSKalle Valo 	return 0;
24633aca94dSKalle Valo }
24733aca94dSKalle Valo 
24833aca94dSKalle Valo static int rt2x00debug_release_queue_dump(struct inode *inode, struct file *file)
24933aca94dSKalle Valo {
25033aca94dSKalle Valo 	struct rt2x00debug_intf *intf = inode->i_private;
25133aca94dSKalle Valo 
25233aca94dSKalle Valo 	skb_queue_purge(&intf->frame_dump_skbqueue);
25333aca94dSKalle Valo 
25433aca94dSKalle Valo 	clear_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags);
25533aca94dSKalle Valo 
25633aca94dSKalle Valo 	return rt2x00debug_file_release(inode, file);
25733aca94dSKalle Valo }
25833aca94dSKalle Valo 
25933aca94dSKalle Valo static ssize_t rt2x00debug_read_queue_dump(struct file *file,
26033aca94dSKalle Valo 					   char __user *buf,
26133aca94dSKalle Valo 					   size_t length,
26233aca94dSKalle Valo 					   loff_t *offset)
26333aca94dSKalle Valo {
26433aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;
26533aca94dSKalle Valo 	struct sk_buff *skb;
26633aca94dSKalle Valo 	size_t status;
26733aca94dSKalle Valo 	int retval;
26833aca94dSKalle Valo 
26933aca94dSKalle Valo 	if (file->f_flags & O_NONBLOCK)
27033aca94dSKalle Valo 		return -EAGAIN;
27133aca94dSKalle Valo 
27233aca94dSKalle Valo 	retval =
27333aca94dSKalle Valo 	    wait_event_interruptible(intf->frame_dump_waitqueue,
27433aca94dSKalle Valo 				     (skb =
27533aca94dSKalle Valo 				     skb_dequeue(&intf->frame_dump_skbqueue)));
27633aca94dSKalle Valo 	if (retval)
27733aca94dSKalle Valo 		return retval;
27833aca94dSKalle Valo 
27933aca94dSKalle Valo 	status = min_t(size_t, skb->len, length);
28033aca94dSKalle Valo 	if (copy_to_user(buf, skb->data, status)) {
28133aca94dSKalle Valo 		status = -EFAULT;
28233aca94dSKalle Valo 		goto exit;
28333aca94dSKalle Valo 	}
28433aca94dSKalle Valo 
28533aca94dSKalle Valo 	*offset += status;
28633aca94dSKalle Valo 
28733aca94dSKalle Valo exit:
28833aca94dSKalle Valo 	kfree_skb(skb);
28933aca94dSKalle Valo 
29033aca94dSKalle Valo 	return status;
29133aca94dSKalle Valo }
29233aca94dSKalle Valo 
293afc9a42bSAl Viro static __poll_t rt2x00debug_poll_queue_dump(struct file *file,
29433aca94dSKalle Valo 						poll_table *wait)
29533aca94dSKalle Valo {
29633aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;
29733aca94dSKalle Valo 
29833aca94dSKalle Valo 	poll_wait(file, &intf->frame_dump_waitqueue, wait);
29933aca94dSKalle Valo 
30033aca94dSKalle Valo 	if (!skb_queue_empty(&intf->frame_dump_skbqueue))
301a9a08845SLinus Torvalds 		return EPOLLOUT | EPOLLWRNORM;
30233aca94dSKalle Valo 
30333aca94dSKalle Valo 	return 0;
30433aca94dSKalle Valo }
30533aca94dSKalle Valo 
30633aca94dSKalle Valo static const struct file_operations rt2x00debug_fop_queue_dump = {
30733aca94dSKalle Valo 	.owner		= THIS_MODULE,
30833aca94dSKalle Valo 	.read		= rt2x00debug_read_queue_dump,
30933aca94dSKalle Valo 	.poll		= rt2x00debug_poll_queue_dump,
31033aca94dSKalle Valo 	.open		= rt2x00debug_open_queue_dump,
31133aca94dSKalle Valo 	.release	= rt2x00debug_release_queue_dump,
31233aca94dSKalle Valo 	.llseek		= default_llseek,
31333aca94dSKalle Valo };
31433aca94dSKalle Valo 
31533aca94dSKalle Valo static ssize_t rt2x00debug_read_queue_stats(struct file *file,
31633aca94dSKalle Valo 					    char __user *buf,
31733aca94dSKalle Valo 					    size_t length,
31833aca94dSKalle Valo 					    loff_t *offset)
31933aca94dSKalle Valo {
32033aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;
32133aca94dSKalle Valo 	struct data_queue *queue;
32233aca94dSKalle Valo 	unsigned long irqflags;
32333aca94dSKalle Valo 	unsigned int lines = 1 + intf->rt2x00dev->data_queues;
32433aca94dSKalle Valo 	size_t size;
32533aca94dSKalle Valo 	char *data;
32633aca94dSKalle Valo 	char *temp;
32733aca94dSKalle Valo 
32833aca94dSKalle Valo 	if (*offset)
32933aca94dSKalle Valo 		return 0;
33033aca94dSKalle Valo 
33133aca94dSKalle Valo 	data = kcalloc(lines, MAX_LINE_LENGTH, GFP_KERNEL);
33233aca94dSKalle Valo 	if (!data)
33333aca94dSKalle Valo 		return -ENOMEM;
33433aca94dSKalle Valo 
33533aca94dSKalle Valo 	temp = data +
33633aca94dSKalle Valo 	    sprintf(data, "qid\tflags\t\tcount\tlimit\tlength\tindex\tdma done\tdone\n");
33733aca94dSKalle Valo 
33833aca94dSKalle Valo 	queue_for_each(intf->rt2x00dev, queue) {
33933aca94dSKalle Valo 		spin_lock_irqsave(&queue->index_lock, irqflags);
34033aca94dSKalle Valo 
34133aca94dSKalle Valo 		temp += sprintf(temp, "%d\t0x%.8x\t%d\t%d\t%d\t%d\t%d\t\t%d\n",
34233aca94dSKalle Valo 				queue->qid, (unsigned int)queue->flags,
34333aca94dSKalle Valo 				queue->count, queue->limit, queue->length,
34433aca94dSKalle Valo 				queue->index[Q_INDEX],
34533aca94dSKalle Valo 				queue->index[Q_INDEX_DMA_DONE],
34633aca94dSKalle Valo 				queue->index[Q_INDEX_DONE]);
34733aca94dSKalle Valo 
34833aca94dSKalle Valo 		spin_unlock_irqrestore(&queue->index_lock, irqflags);
34933aca94dSKalle Valo 	}
35033aca94dSKalle Valo 
35133aca94dSKalle Valo 	size = strlen(data);
35233aca94dSKalle Valo 	size = min(size, length);
35333aca94dSKalle Valo 
35433aca94dSKalle Valo 	if (copy_to_user(buf, data, size)) {
35533aca94dSKalle Valo 		kfree(data);
35633aca94dSKalle Valo 		return -EFAULT;
35733aca94dSKalle Valo 	}
35833aca94dSKalle Valo 
35933aca94dSKalle Valo 	kfree(data);
36033aca94dSKalle Valo 
36133aca94dSKalle Valo 	*offset += size;
36233aca94dSKalle Valo 	return size;
36333aca94dSKalle Valo }
36433aca94dSKalle Valo 
36533aca94dSKalle Valo static const struct file_operations rt2x00debug_fop_queue_stats = {
36633aca94dSKalle Valo 	.owner		= THIS_MODULE,
36733aca94dSKalle Valo 	.read		= rt2x00debug_read_queue_stats,
36833aca94dSKalle Valo 	.open		= rt2x00debug_file_open,
36933aca94dSKalle Valo 	.release	= rt2x00debug_file_release,
37033aca94dSKalle Valo 	.llseek		= default_llseek,
37133aca94dSKalle Valo };
37233aca94dSKalle Valo 
37333aca94dSKalle Valo #ifdef CONFIG_RT2X00_LIB_CRYPTO
37433aca94dSKalle Valo static ssize_t rt2x00debug_read_crypto_stats(struct file *file,
37533aca94dSKalle Valo 					     char __user *buf,
37633aca94dSKalle Valo 					     size_t length,
37733aca94dSKalle Valo 					     loff_t *offset)
37833aca94dSKalle Valo {
37933aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;
38033aca94dSKalle Valo 	static const char * const name[] = { "WEP64", "WEP128", "TKIP", "AES" };
38133aca94dSKalle Valo 	char *data;
38233aca94dSKalle Valo 	char *temp;
38333aca94dSKalle Valo 	size_t size;
38433aca94dSKalle Valo 	unsigned int i;
38533aca94dSKalle Valo 
38633aca94dSKalle Valo 	if (*offset)
38733aca94dSKalle Valo 		return 0;
38833aca94dSKalle Valo 
3896396bb22SKees Cook 	data = kcalloc(1 + CIPHER_MAX, MAX_LINE_LENGTH, GFP_KERNEL);
39033aca94dSKalle Valo 	if (!data)
39133aca94dSKalle Valo 		return -ENOMEM;
39233aca94dSKalle Valo 
39333aca94dSKalle Valo 	temp = data;
39433aca94dSKalle Valo 	temp += sprintf(data, "cipher\tsuccess\ticv err\tmic err\tkey err\n");
39533aca94dSKalle Valo 
39633aca94dSKalle Valo 	for (i = 0; i < CIPHER_MAX; i++) {
39733aca94dSKalle Valo 		temp += sprintf(temp, "%s\t%lu\t%lu\t%lu\t%lu\n", name[i],
39833aca94dSKalle Valo 				intf->crypto_stats[i].success,
39933aca94dSKalle Valo 				intf->crypto_stats[i].icv_error,
40033aca94dSKalle Valo 				intf->crypto_stats[i].mic_error,
40133aca94dSKalle Valo 				intf->crypto_stats[i].key_error);
40233aca94dSKalle Valo 	}
40333aca94dSKalle Valo 
40433aca94dSKalle Valo 	size = strlen(data);
40533aca94dSKalle Valo 	size = min(size, length);
40633aca94dSKalle Valo 
40733aca94dSKalle Valo 	if (copy_to_user(buf, data, size)) {
40833aca94dSKalle Valo 		kfree(data);
40933aca94dSKalle Valo 		return -EFAULT;
41033aca94dSKalle Valo 	}
41133aca94dSKalle Valo 
41233aca94dSKalle Valo 	kfree(data);
41333aca94dSKalle Valo 
41433aca94dSKalle Valo 	*offset += size;
41533aca94dSKalle Valo 	return size;
41633aca94dSKalle Valo }
41733aca94dSKalle Valo 
41833aca94dSKalle Valo static const struct file_operations rt2x00debug_fop_crypto_stats = {
41933aca94dSKalle Valo 	.owner		= THIS_MODULE,
42033aca94dSKalle Valo 	.read		= rt2x00debug_read_crypto_stats,
42133aca94dSKalle Valo 	.open		= rt2x00debug_file_open,
42233aca94dSKalle Valo 	.release	= rt2x00debug_file_release,
42333aca94dSKalle Valo 	.llseek		= default_llseek,
42433aca94dSKalle Valo };
42533aca94dSKalle Valo #endif
42633aca94dSKalle Valo 
42733aca94dSKalle Valo #define RT2X00DEBUGFS_OPS_READ(__name, __format, __type)	\
42833aca94dSKalle Valo static ssize_t rt2x00debug_read_##__name(struct file *file,	\
42933aca94dSKalle Valo 					 char __user *buf,	\
43033aca94dSKalle Valo 					 size_t length,		\
43133aca94dSKalle Valo 					 loff_t *offset)	\
43233aca94dSKalle Valo {								\
43333aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;	\
43433aca94dSKalle Valo 	const struct rt2x00debug *debug = intf->debug;		\
43533aca94dSKalle Valo 	char line[16];						\
43633aca94dSKalle Valo 	size_t size;						\
43733aca94dSKalle Valo 	unsigned int index = intf->offset_##__name;		\
43833aca94dSKalle Valo 	__type value;						\
43933aca94dSKalle Valo 								\
44033aca94dSKalle Valo 	if (*offset)						\
44133aca94dSKalle Valo 		return 0;					\
44233aca94dSKalle Valo 								\
44333aca94dSKalle Valo 	if (index >= debug->__name.word_count)			\
44433aca94dSKalle Valo 		return -EINVAL;					\
44533aca94dSKalle Valo 								\
44633aca94dSKalle Valo 	index += (debug->__name.word_base /			\
44733aca94dSKalle Valo 		  debug->__name.word_size);			\
44833aca94dSKalle Valo 								\
44933aca94dSKalle Valo 	if (debug->__name.flags & RT2X00DEBUGFS_OFFSET)		\
45033aca94dSKalle Valo 		index *= debug->__name.word_size;		\
45133aca94dSKalle Valo 								\
4526b81745eSArnd Bergmann 	value = debug->__name.read(intf->rt2x00dev, index);	\
45333aca94dSKalle Valo 								\
45433aca94dSKalle Valo 	size = sprintf(line, __format, value);			\
45533aca94dSKalle Valo 								\
456f483039cSDan Carpenter 	return simple_read_from_buffer(buf, length, offset, line, size); \
45733aca94dSKalle Valo }
45833aca94dSKalle Valo 
45933aca94dSKalle Valo #define RT2X00DEBUGFS_OPS_WRITE(__name, __type)			\
46033aca94dSKalle Valo static ssize_t rt2x00debug_write_##__name(struct file *file,	\
46133aca94dSKalle Valo 					  const char __user *buf,\
46233aca94dSKalle Valo 					  size_t length,	\
46333aca94dSKalle Valo 					  loff_t *offset)	\
46433aca94dSKalle Valo {								\
46533aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;	\
46633aca94dSKalle Valo 	const struct rt2x00debug *debug = intf->debug;		\
4679cc3fdc8SOne Thousand Gnomes 	char line[17];						\
46833aca94dSKalle Valo 	size_t size;						\
46933aca94dSKalle Valo 	unsigned int index = intf->offset_##__name;		\
47033aca94dSKalle Valo 	__type value;						\
47133aca94dSKalle Valo 								\
47233aca94dSKalle Valo 	if (*offset)						\
47333aca94dSKalle Valo 		return 0;					\
47433aca94dSKalle Valo 								\
47533aca94dSKalle Valo 	if (index >= debug->__name.word_count)			\
47633aca94dSKalle Valo 		return -EINVAL;					\
47733aca94dSKalle Valo 								\
47833aca94dSKalle Valo 	if (length > sizeof(line))				\
47933aca94dSKalle Valo 		return -EINVAL;					\
48033aca94dSKalle Valo 								\
48133aca94dSKalle Valo 	if (copy_from_user(line, buf, length))			\
48233aca94dSKalle Valo 		return -EFAULT;					\
4839cc3fdc8SOne Thousand Gnomes 	line[16] = 0;						\
48433aca94dSKalle Valo 						\
48533aca94dSKalle Valo 	size = strlen(line);					\
48633aca94dSKalle Valo 	value = simple_strtoul(line, NULL, 0);			\
48733aca94dSKalle Valo 								\
48833aca94dSKalle Valo 	index += (debug->__name.word_base /			\
48933aca94dSKalle Valo 		  debug->__name.word_size);			\
49033aca94dSKalle Valo 								\
49133aca94dSKalle Valo 	if (debug->__name.flags & RT2X00DEBUGFS_OFFSET)		\
49233aca94dSKalle Valo 		index *= debug->__name.word_size;		\
49333aca94dSKalle Valo 								\
49433aca94dSKalle Valo 	debug->__name.write(intf->rt2x00dev, index, value);	\
49533aca94dSKalle Valo 								\
49633aca94dSKalle Valo 	*offset += size;					\
49733aca94dSKalle Valo 	return size;						\
49833aca94dSKalle Valo }
49933aca94dSKalle Valo 
50033aca94dSKalle Valo #define RT2X00DEBUGFS_OPS(__name, __format, __type)		\
50133aca94dSKalle Valo RT2X00DEBUGFS_OPS_READ(__name, __format, __type);		\
50233aca94dSKalle Valo RT2X00DEBUGFS_OPS_WRITE(__name, __type);			\
50333aca94dSKalle Valo 								\
50433aca94dSKalle Valo static const struct file_operations rt2x00debug_fop_##__name = {\
50533aca94dSKalle Valo 	.owner		= THIS_MODULE,				\
50633aca94dSKalle Valo 	.read		= rt2x00debug_read_##__name,		\
50733aca94dSKalle Valo 	.write		= rt2x00debug_write_##__name,		\
50833aca94dSKalle Valo 	.open		= rt2x00debug_file_open,		\
50933aca94dSKalle Valo 	.release	= rt2x00debug_file_release,		\
51033aca94dSKalle Valo 	.llseek		= generic_file_llseek,			\
51133aca94dSKalle Valo };
51233aca94dSKalle Valo 
51333aca94dSKalle Valo RT2X00DEBUGFS_OPS(csr, "0x%.8x\n", u32);
51433aca94dSKalle Valo RT2X00DEBUGFS_OPS(eeprom, "0x%.4x\n", u16);
51533aca94dSKalle Valo RT2X00DEBUGFS_OPS(bbp, "0x%.2x\n", u8);
51633aca94dSKalle Valo RT2X00DEBUGFS_OPS(rf, "0x%.8x\n", u32);
51733aca94dSKalle Valo RT2X00DEBUGFS_OPS(rfcsr, "0x%.2x\n", u8);
51833aca94dSKalle Valo 
51933aca94dSKalle Valo static ssize_t rt2x00debug_read_dev_flags(struct file *file,
52033aca94dSKalle Valo 					  char __user *buf,
52133aca94dSKalle Valo 					  size_t length,
52233aca94dSKalle Valo 					  loff_t *offset)
52333aca94dSKalle Valo {
52433aca94dSKalle Valo 	struct rt2x00debug_intf *intf =	file->private_data;
52533aca94dSKalle Valo 	char line[16];
52633aca94dSKalle Valo 	size_t size;
52733aca94dSKalle Valo 
52833aca94dSKalle Valo 	if (*offset)
52933aca94dSKalle Valo 		return 0;
53033aca94dSKalle Valo 
53133aca94dSKalle Valo 	size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->flags);
53233aca94dSKalle Valo 
533f483039cSDan Carpenter 	return simple_read_from_buffer(buf, length, offset, line, size);
53433aca94dSKalle Valo }
53533aca94dSKalle Valo 
53633aca94dSKalle Valo static const struct file_operations rt2x00debug_fop_dev_flags = {
53733aca94dSKalle Valo 	.owner		= THIS_MODULE,
53833aca94dSKalle Valo 	.read		= rt2x00debug_read_dev_flags,
53933aca94dSKalle Valo 	.open		= rt2x00debug_file_open,
54033aca94dSKalle Valo 	.release	= rt2x00debug_file_release,
54133aca94dSKalle Valo 	.llseek		= default_llseek,
54233aca94dSKalle Valo };
54333aca94dSKalle Valo 
54433aca94dSKalle Valo static ssize_t rt2x00debug_read_cap_flags(struct file *file,
54533aca94dSKalle Valo 					  char __user *buf,
54633aca94dSKalle Valo 					  size_t length,
54733aca94dSKalle Valo 					  loff_t *offset)
54833aca94dSKalle Valo {
54933aca94dSKalle Valo 	struct rt2x00debug_intf *intf =	file->private_data;
55033aca94dSKalle Valo 	char line[16];
55133aca94dSKalle Valo 	size_t size;
55233aca94dSKalle Valo 
55333aca94dSKalle Valo 	if (*offset)
55433aca94dSKalle Valo 		return 0;
55533aca94dSKalle Valo 
55633aca94dSKalle Valo 	size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->cap_flags);
55733aca94dSKalle Valo 
558f483039cSDan Carpenter 	return simple_read_from_buffer(buf, length, offset, line, size);
55933aca94dSKalle Valo }
56033aca94dSKalle Valo 
56133aca94dSKalle Valo static const struct file_operations rt2x00debug_fop_cap_flags = {
56233aca94dSKalle Valo 	.owner		= THIS_MODULE,
56333aca94dSKalle Valo 	.read		= rt2x00debug_read_cap_flags,
56433aca94dSKalle Valo 	.open		= rt2x00debug_file_open,
56533aca94dSKalle Valo 	.release	= rt2x00debug_file_release,
56633aca94dSKalle Valo 	.llseek		= default_llseek,
56733aca94dSKalle Valo };
56833aca94dSKalle Valo 
56933aca94dSKalle Valo static struct dentry *rt2x00debug_create_file_driver(const char *name,
57033aca94dSKalle Valo 						     struct rt2x00debug_intf
57133aca94dSKalle Valo 						     *intf,
57233aca94dSKalle Valo 						     struct debugfs_blob_wrapper
57333aca94dSKalle Valo 						     *blob)
57433aca94dSKalle Valo {
57533aca94dSKalle Valo 	char *data;
57633aca94dSKalle Valo 
57733aca94dSKalle Valo 	data = kzalloc(3 * MAX_LINE_LENGTH, GFP_KERNEL);
57833aca94dSKalle Valo 	if (!data)
57933aca94dSKalle Valo 		return NULL;
58033aca94dSKalle Valo 
58133aca94dSKalle Valo 	blob->data = data;
58233aca94dSKalle Valo 	data += sprintf(data, "driver:\t%s\n", intf->rt2x00dev->ops->name);
58333aca94dSKalle Valo 	data += sprintf(data, "version:\t%s\n", DRV_VERSION);
58433aca94dSKalle Valo 	blob->size = strlen(blob->data);
58533aca94dSKalle Valo 
5862ef00c53SJoe Perches 	return debugfs_create_blob(name, 0400, intf->driver_folder, blob);
58733aca94dSKalle Valo }
58833aca94dSKalle Valo 
58933aca94dSKalle Valo static struct dentry *rt2x00debug_create_file_chipset(const char *name,
59033aca94dSKalle Valo 						      struct rt2x00debug_intf
59133aca94dSKalle Valo 						      *intf,
59233aca94dSKalle Valo 						      struct
59333aca94dSKalle Valo 						      debugfs_blob_wrapper
59433aca94dSKalle Valo 						      *blob)
59533aca94dSKalle Valo {
59633aca94dSKalle Valo 	const struct rt2x00debug *debug = intf->debug;
59733aca94dSKalle Valo 	char *data;
59833aca94dSKalle Valo 
59933aca94dSKalle Valo 	data = kzalloc(9 * MAX_LINE_LENGTH, GFP_KERNEL);
60033aca94dSKalle Valo 	if (!data)
60133aca94dSKalle Valo 		return NULL;
60233aca94dSKalle Valo 
60333aca94dSKalle Valo 	blob->data = data;
60433aca94dSKalle Valo 	data += sprintf(data, "rt chip:\t%04x\n", intf->rt2x00dev->chip.rt);
60533aca94dSKalle Valo 	data += sprintf(data, "rf chip:\t%04x\n", intf->rt2x00dev->chip.rf);
60633aca94dSKalle Valo 	data += sprintf(data, "revision:\t%04x\n", intf->rt2x00dev->chip.rev);
60733aca94dSKalle Valo 	data += sprintf(data, "\n");
60833aca94dSKalle Valo 	data += sprintf(data, "register\tbase\twords\twordsize\n");
60933aca94dSKalle Valo #define RT2X00DEBUGFS_SPRINTF_REGISTER(__name)			\
61033aca94dSKalle Valo {								\
61133aca94dSKalle Valo 	if (debug->__name.read)					\
61233aca94dSKalle Valo 		data += sprintf(data, __stringify(__name)	\
61333aca94dSKalle Valo 				"\t%d\t%d\t%d\n",		\
61433aca94dSKalle Valo 				debug->__name.word_base,	\
61533aca94dSKalle Valo 				debug->__name.word_count,	\
61633aca94dSKalle Valo 				debug->__name.word_size);	\
61733aca94dSKalle Valo }
61833aca94dSKalle Valo 	RT2X00DEBUGFS_SPRINTF_REGISTER(csr);
61933aca94dSKalle Valo 	RT2X00DEBUGFS_SPRINTF_REGISTER(eeprom);
62033aca94dSKalle Valo 	RT2X00DEBUGFS_SPRINTF_REGISTER(bbp);
62133aca94dSKalle Valo 	RT2X00DEBUGFS_SPRINTF_REGISTER(rf);
62233aca94dSKalle Valo 	RT2X00DEBUGFS_SPRINTF_REGISTER(rfcsr);
62333aca94dSKalle Valo #undef RT2X00DEBUGFS_SPRINTF_REGISTER
62433aca94dSKalle Valo 
62533aca94dSKalle Valo 	blob->size = strlen(blob->data);
62633aca94dSKalle Valo 
6272ef00c53SJoe Perches 	return debugfs_create_blob(name, 0400, intf->driver_folder, blob);
62833aca94dSKalle Valo }
62933aca94dSKalle Valo 
63033aca94dSKalle Valo void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
63133aca94dSKalle Valo {
63233aca94dSKalle Valo 	const struct rt2x00debug *debug = rt2x00dev->ops->debugfs;
63333aca94dSKalle Valo 	struct rt2x00debug_intf *intf;
63433aca94dSKalle Valo 
63533aca94dSKalle Valo 	intf = kzalloc(sizeof(struct rt2x00debug_intf), GFP_KERNEL);
63633aca94dSKalle Valo 	if (!intf) {
63733aca94dSKalle Valo 		rt2x00_err(rt2x00dev, "Failed to allocate debug handler\n");
63833aca94dSKalle Valo 		return;
63933aca94dSKalle Valo 	}
64033aca94dSKalle Valo 
64133aca94dSKalle Valo 	intf->debug = debug;
64233aca94dSKalle Valo 	intf->rt2x00dev = rt2x00dev;
64333aca94dSKalle Valo 	rt2x00dev->debugfs_intf = intf;
64433aca94dSKalle Valo 
64533aca94dSKalle Valo 	intf->driver_folder =
64633aca94dSKalle Valo 	    debugfs_create_dir(intf->rt2x00dev->ops->name,
64733aca94dSKalle Valo 			       rt2x00dev->hw->wiphy->debugfsdir);
64833aca94dSKalle Valo 
64933aca94dSKalle Valo 	intf->driver_entry =
65033aca94dSKalle Valo 	    rt2x00debug_create_file_driver("driver", intf, &intf->driver_blob);
65133aca94dSKalle Valo 
65233aca94dSKalle Valo 	intf->chipset_entry =
65333aca94dSKalle Valo 	    rt2x00debug_create_file_chipset("chipset",
65433aca94dSKalle Valo 					    intf, &intf->chipset_blob);
65533aca94dSKalle Valo 
6562ef00c53SJoe Perches 	intf->dev_flags = debugfs_create_file("dev_flags", 0400,
65733aca94dSKalle Valo 					      intf->driver_folder, intf,
65833aca94dSKalle Valo 					      &rt2x00debug_fop_dev_flags);
65933aca94dSKalle Valo 
6602ef00c53SJoe Perches 	intf->cap_flags = debugfs_create_file("cap_flags", 0400,
66133aca94dSKalle Valo 					      intf->driver_folder, intf,
66233aca94dSKalle Valo 					      &rt2x00debug_fop_cap_flags);
66333aca94dSKalle Valo 
66433aca94dSKalle Valo 	intf->register_folder =
66533aca94dSKalle Valo 	    debugfs_create_dir("register", intf->driver_folder);
66633aca94dSKalle Valo 
66733aca94dSKalle Valo #define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name)		\
66833aca94dSKalle Valo ({									\
66933aca94dSKalle Valo 	if (debug->__name.read) {					\
67033aca94dSKalle Valo 		(__intf)->__name##_off_entry =				\
67133aca94dSKalle Valo 			debugfs_create_u32(__stringify(__name) "_offset", \
6722ef00c53SJoe Perches 					   0600,			\
67333aca94dSKalle Valo 					   (__intf)->register_folder,	\
67433aca94dSKalle Valo 					   &(__intf)->offset_##__name);	\
67533aca94dSKalle Valo 									\
67633aca94dSKalle Valo 		(__intf)->__name##_val_entry =				\
67733aca94dSKalle Valo 			debugfs_create_file(__stringify(__name) "_value", \
6782ef00c53SJoe Perches 					    0600,			\
67933aca94dSKalle Valo 					    (__intf)->register_folder,	\
6802ef00c53SJoe Perches 					    (__intf),			\
6812ef00c53SJoe Perches 					    &rt2x00debug_fop_##__name); \
68233aca94dSKalle Valo 	}								\
68333aca94dSKalle Valo })
68433aca94dSKalle Valo 
68533aca94dSKalle Valo 	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, csr);
68633aca94dSKalle Valo 	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, eeprom);
68733aca94dSKalle Valo 	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, bbp);
68833aca94dSKalle Valo 	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rf);
68933aca94dSKalle Valo 	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rfcsr);
69033aca94dSKalle Valo 
69133aca94dSKalle Valo #undef RT2X00DEBUGFS_CREATE_REGISTER_ENTRY
69233aca94dSKalle Valo 
69333aca94dSKalle Valo 	intf->queue_folder =
69433aca94dSKalle Valo 	    debugfs_create_dir("queue", intf->driver_folder);
69533aca94dSKalle Valo 
69633aca94dSKalle Valo 	intf->queue_frame_dump_entry =
6972ef00c53SJoe Perches 		debugfs_create_file("dump", 0400, intf->queue_folder,
69833aca94dSKalle Valo 				    intf, &rt2x00debug_fop_queue_dump);
69933aca94dSKalle Valo 
70033aca94dSKalle Valo 	skb_queue_head_init(&intf->frame_dump_skbqueue);
70133aca94dSKalle Valo 	init_waitqueue_head(&intf->frame_dump_waitqueue);
70233aca94dSKalle Valo 
70333aca94dSKalle Valo 	intf->queue_stats_entry =
7042ef00c53SJoe Perches 		debugfs_create_file("queue", 0400, intf->queue_folder,
70533aca94dSKalle Valo 				    intf, &rt2x00debug_fop_queue_stats);
70633aca94dSKalle Valo 
70733aca94dSKalle Valo #ifdef CONFIG_RT2X00_LIB_CRYPTO
70833aca94dSKalle Valo 	if (rt2x00_has_cap_hw_crypto(rt2x00dev))
70933aca94dSKalle Valo 		intf->crypto_stats_entry =
7102ef00c53SJoe Perches 			debugfs_create_file("crypto", 0444, intf->queue_folder,
7112ef00c53SJoe Perches 					    intf,
7122ef00c53SJoe Perches 					    &rt2x00debug_fop_crypto_stats);
71333aca94dSKalle Valo #endif
71433aca94dSKalle Valo 
71533aca94dSKalle Valo 	return;
71633aca94dSKalle Valo }
71733aca94dSKalle Valo 
71833aca94dSKalle Valo void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev)
71933aca94dSKalle Valo {
72033aca94dSKalle Valo 	struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf;
72133aca94dSKalle Valo 
72233aca94dSKalle Valo 	if (unlikely(!intf))
72333aca94dSKalle Valo 		return;
72433aca94dSKalle Valo 
72533aca94dSKalle Valo 	skb_queue_purge(&intf->frame_dump_skbqueue);
72633aca94dSKalle Valo 
72733aca94dSKalle Valo #ifdef CONFIG_RT2X00_LIB_CRYPTO
72833aca94dSKalle Valo 	debugfs_remove(intf->crypto_stats_entry);
72933aca94dSKalle Valo #endif
73033aca94dSKalle Valo 	debugfs_remove(intf->queue_stats_entry);
73133aca94dSKalle Valo 	debugfs_remove(intf->queue_frame_dump_entry);
73233aca94dSKalle Valo 	debugfs_remove(intf->queue_folder);
73333aca94dSKalle Valo 	debugfs_remove(intf->rfcsr_val_entry);
73433aca94dSKalle Valo 	debugfs_remove(intf->rfcsr_off_entry);
73533aca94dSKalle Valo 	debugfs_remove(intf->rf_val_entry);
73633aca94dSKalle Valo 	debugfs_remove(intf->rf_off_entry);
73733aca94dSKalle Valo 	debugfs_remove(intf->bbp_val_entry);
73833aca94dSKalle Valo 	debugfs_remove(intf->bbp_off_entry);
73933aca94dSKalle Valo 	debugfs_remove(intf->eeprom_val_entry);
74033aca94dSKalle Valo 	debugfs_remove(intf->eeprom_off_entry);
74133aca94dSKalle Valo 	debugfs_remove(intf->csr_val_entry);
74233aca94dSKalle Valo 	debugfs_remove(intf->csr_off_entry);
74333aca94dSKalle Valo 	debugfs_remove(intf->register_folder);
74433aca94dSKalle Valo 	debugfs_remove(intf->dev_flags);
74533aca94dSKalle Valo 	debugfs_remove(intf->cap_flags);
74633aca94dSKalle Valo 	debugfs_remove(intf->chipset_entry);
74733aca94dSKalle Valo 	debugfs_remove(intf->driver_entry);
74833aca94dSKalle Valo 	debugfs_remove(intf->driver_folder);
74933aca94dSKalle Valo 	kfree(intf->chipset_blob.data);
75033aca94dSKalle Valo 	kfree(intf->driver_blob.data);
75133aca94dSKalle Valo 	kfree(intf);
75233aca94dSKalle Valo 
75333aca94dSKalle Valo 	rt2x00dev->debugfs_intf = NULL;
75433aca94dSKalle Valo }
755