xref: /linux/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c (revision c95baf12f5077419db01313ab61c2aac007d40cd)
11ccea77eSThomas 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
55e403fa31SStanislaw Gruszka 	 *   - hardware restart file
5633aca94dSKalle Valo 	 *   - register folder
5733aca94dSKalle Valo 	 *     - csr offset/value files
5833aca94dSKalle Valo 	 *     - eeprom offset/value files
5933aca94dSKalle Valo 	 *     - bbp offset/value files
6033aca94dSKalle Valo 	 *     - rf offset/value files
6133aca94dSKalle Valo 	 *     - rfcsr offset/value files
6233aca94dSKalle Valo 	 *   - queue folder
6333aca94dSKalle Valo 	 *     - frame dump file
6433aca94dSKalle Valo 	 *     - queue stats file
6533aca94dSKalle Valo 	 *     - crypto stats file
6633aca94dSKalle Valo 	 */
6733aca94dSKalle Valo 	struct dentry *driver_folder;
6833aca94dSKalle Valo 
6933aca94dSKalle Valo 	/*
7033aca94dSKalle Valo 	 * The frame dump file only allows a single reader,
7133aca94dSKalle Valo 	 * so we need to store the current state here.
7233aca94dSKalle Valo 	 */
7333aca94dSKalle Valo 	unsigned long frame_dump_flags;
7433aca94dSKalle Valo #define FRAME_DUMP_FILE_OPEN	1
7533aca94dSKalle Valo 
7633aca94dSKalle Valo 	/*
7733aca94dSKalle Valo 	 * We queue each frame before dumping it to the user,
7833aca94dSKalle Valo 	 * per read command we will pass a single skb structure
7933aca94dSKalle Valo 	 * so we should be prepared to queue multiple sk buffers
8033aca94dSKalle Valo 	 * before sending it to userspace.
8133aca94dSKalle Valo 	 */
8233aca94dSKalle Valo 	struct sk_buff_head frame_dump_skbqueue;
8333aca94dSKalle Valo 	wait_queue_head_t frame_dump_waitqueue;
8433aca94dSKalle Valo 
8533aca94dSKalle Valo 	/*
8633aca94dSKalle Valo 	 * HW crypto statistics.
8733aca94dSKalle Valo 	 * All statistics are stored separately per cipher type.
8833aca94dSKalle Valo 	 */
8933aca94dSKalle Valo 	struct rt2x00debug_crypto crypto_stats[CIPHER_MAX];
9033aca94dSKalle Valo 
9133aca94dSKalle Valo 	/*
9233aca94dSKalle Valo 	 * Driver and chipset files will use a data buffer
9333aca94dSKalle Valo 	 * that has been created in advance. This will simplify
9433aca94dSKalle Valo 	 * the code since we can use the debugfs functions.
9533aca94dSKalle Valo 	 */
9633aca94dSKalle Valo 	struct debugfs_blob_wrapper driver_blob;
9733aca94dSKalle Valo 	struct debugfs_blob_wrapper chipset_blob;
9833aca94dSKalle Valo 
9933aca94dSKalle Valo 	/*
10033aca94dSKalle Valo 	 * Requested offset for each register type.
10133aca94dSKalle Valo 	 */
10233aca94dSKalle Valo 	unsigned int offset_csr;
10333aca94dSKalle Valo 	unsigned int offset_eeprom;
10433aca94dSKalle Valo 	unsigned int offset_bbp;
10533aca94dSKalle Valo 	unsigned int offset_rf;
10633aca94dSKalle Valo 	unsigned int offset_rfcsr;
10733aca94dSKalle Valo };
10833aca94dSKalle Valo 
rt2x00debug_update_crypto(struct rt2x00_dev * rt2x00dev,struct rxdone_entry_desc * rxdesc)10933aca94dSKalle Valo void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev,
11033aca94dSKalle Valo 			       struct rxdone_entry_desc *rxdesc)
11133aca94dSKalle Valo {
11233aca94dSKalle Valo 	struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf;
11333aca94dSKalle Valo 	enum cipher cipher = rxdesc->cipher;
11433aca94dSKalle Valo 	enum rx_crypto status = rxdesc->cipher_status;
11533aca94dSKalle Valo 
11633aca94dSKalle Valo 	if (cipher == CIPHER_TKIP_NO_MIC)
11733aca94dSKalle Valo 		cipher = CIPHER_TKIP;
11833aca94dSKalle Valo 	if (cipher == CIPHER_NONE || cipher >= CIPHER_MAX)
11933aca94dSKalle Valo 		return;
12033aca94dSKalle Valo 
12133aca94dSKalle Valo 	/* Remove CIPHER_NONE index */
12233aca94dSKalle Valo 	cipher--;
12333aca94dSKalle Valo 
12433aca94dSKalle Valo 	intf->crypto_stats[cipher].success += (status == RX_CRYPTO_SUCCESS);
12533aca94dSKalle Valo 	intf->crypto_stats[cipher].icv_error += (status == RX_CRYPTO_FAIL_ICV);
12633aca94dSKalle Valo 	intf->crypto_stats[cipher].mic_error += (status == RX_CRYPTO_FAIL_MIC);
12733aca94dSKalle Valo 	intf->crypto_stats[cipher].key_error += (status == RX_CRYPTO_FAIL_KEY);
12833aca94dSKalle Valo }
12933aca94dSKalle Valo 
rt2x00debug_dump_frame(struct rt2x00_dev * rt2x00dev,enum rt2x00_dump_type type,struct queue_entry * entry)13033aca94dSKalle Valo void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev,
1312ceb8137SStanislaw Gruszka 			    enum rt2x00_dump_type type, struct queue_entry *entry)
13233aca94dSKalle Valo {
13333aca94dSKalle Valo 	struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf;
1342ceb8137SStanislaw Gruszka 	struct sk_buff *skb = entry->skb;
13533aca94dSKalle Valo 	struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
13633aca94dSKalle Valo 	struct sk_buff *skbcopy;
13733aca94dSKalle Valo 	struct rt2x00dump_hdr *dump_hdr;
138f87eba99SArnd Bergmann 	struct timespec64 timestamp;
13933aca94dSKalle Valo 	u32 data_len;
14033aca94dSKalle Valo 
14133aca94dSKalle Valo 	if (likely(!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)))
14233aca94dSKalle Valo 		return;
14333aca94dSKalle Valo 
144f87eba99SArnd Bergmann 	ktime_get_ts64(&timestamp);
14533aca94dSKalle Valo 
14633aca94dSKalle Valo 	if (skb_queue_len(&intf->frame_dump_skbqueue) > 20) {
14733aca94dSKalle Valo 		rt2x00_dbg(rt2x00dev, "txrx dump queue length exceeded\n");
14833aca94dSKalle Valo 		return;
14933aca94dSKalle Valo 	}
15033aca94dSKalle Valo 
15133aca94dSKalle Valo 	data_len = skb->len;
15233aca94dSKalle Valo 	if (skbdesc->flags & SKBDESC_DESC_IN_SKB)
15333aca94dSKalle Valo 		data_len -= skbdesc->desc_len;
15433aca94dSKalle Valo 
15533aca94dSKalle Valo 	skbcopy = alloc_skb(sizeof(*dump_hdr) + skbdesc->desc_len + data_len,
15633aca94dSKalle Valo 			    GFP_ATOMIC);
15733aca94dSKalle Valo 	if (!skbcopy) {
15833aca94dSKalle Valo 		rt2x00_dbg(rt2x00dev, "Failed to copy skb for dump\n");
15933aca94dSKalle Valo 		return;
16033aca94dSKalle Valo 	}
16133aca94dSKalle Valo 
1624df864c1SJohannes Berg 	dump_hdr = skb_put(skbcopy, sizeof(*dump_hdr));
16333aca94dSKalle Valo 	dump_hdr->version = cpu_to_le32(DUMP_HEADER_VERSION);
16433aca94dSKalle Valo 	dump_hdr->header_length = cpu_to_le32(sizeof(*dump_hdr));
16533aca94dSKalle Valo 	dump_hdr->desc_length = cpu_to_le32(skbdesc->desc_len);
16633aca94dSKalle Valo 	dump_hdr->data_length = cpu_to_le32(data_len);
16733aca94dSKalle Valo 	dump_hdr->chip_rt = cpu_to_le16(rt2x00dev->chip.rt);
16833aca94dSKalle Valo 	dump_hdr->chip_rf = cpu_to_le16(rt2x00dev->chip.rf);
16933aca94dSKalle Valo 	dump_hdr->chip_rev = cpu_to_le16(rt2x00dev->chip.rev);
17033aca94dSKalle Valo 	dump_hdr->type = cpu_to_le16(type);
1712ceb8137SStanislaw Gruszka 	dump_hdr->queue_index = entry->queue->qid;
1722ceb8137SStanislaw Gruszka 	dump_hdr->entry_index = entry->entry_idx;
17333aca94dSKalle Valo 	dump_hdr->timestamp_sec = cpu_to_le32(timestamp.tv_sec);
174f87eba99SArnd Bergmann 	dump_hdr->timestamp_usec = cpu_to_le32(timestamp.tv_nsec /
175f87eba99SArnd Bergmann 					       NSEC_PER_USEC);
17633aca94dSKalle Valo 
17733aca94dSKalle Valo 	if (!(skbdesc->flags & SKBDESC_DESC_IN_SKB))
17859ae1d12SJohannes Berg 		skb_put_data(skbcopy, skbdesc->desc, skbdesc->desc_len);
17959ae1d12SJohannes Berg 	skb_put_data(skbcopy, skb->data, skb->len);
18033aca94dSKalle Valo 
18133aca94dSKalle Valo 	skb_queue_tail(&intf->frame_dump_skbqueue, skbcopy);
18233aca94dSKalle Valo 	wake_up_interruptible(&intf->frame_dump_waitqueue);
18333aca94dSKalle Valo 
18433aca94dSKalle Valo 	/*
18533aca94dSKalle Valo 	 * Verify that the file has not been closed while we were working.
18633aca94dSKalle Valo 	 */
18733aca94dSKalle Valo 	if (!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags))
18833aca94dSKalle Valo 		skb_queue_purge(&intf->frame_dump_skbqueue);
18933aca94dSKalle Valo }
19033aca94dSKalle Valo EXPORT_SYMBOL_GPL(rt2x00debug_dump_frame);
19133aca94dSKalle Valo 
rt2x00debug_file_open(struct inode * inode,struct file * file)19233aca94dSKalle Valo static int rt2x00debug_file_open(struct inode *inode, struct file *file)
19333aca94dSKalle Valo {
19433aca94dSKalle Valo 	struct rt2x00debug_intf *intf = inode->i_private;
19533aca94dSKalle Valo 
19633aca94dSKalle Valo 	file->private_data = inode->i_private;
19733aca94dSKalle Valo 
19833aca94dSKalle Valo 	if (!try_module_get(intf->debug->owner))
19933aca94dSKalle Valo 		return -EBUSY;
20033aca94dSKalle Valo 
20133aca94dSKalle Valo 	return 0;
20233aca94dSKalle Valo }
20333aca94dSKalle Valo 
rt2x00debug_file_release(struct inode * inode,struct file * file)20433aca94dSKalle Valo static int rt2x00debug_file_release(struct inode *inode, struct file *file)
20533aca94dSKalle Valo {
20633aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;
20733aca94dSKalle Valo 
20833aca94dSKalle Valo 	module_put(intf->debug->owner);
20933aca94dSKalle Valo 
21033aca94dSKalle Valo 	return 0;
21133aca94dSKalle Valo }
21233aca94dSKalle Valo 
rt2x00debug_open_queue_dump(struct inode * inode,struct file * file)21333aca94dSKalle Valo static int rt2x00debug_open_queue_dump(struct inode *inode, struct file *file)
21433aca94dSKalle Valo {
21533aca94dSKalle Valo 	struct rt2x00debug_intf *intf = inode->i_private;
21633aca94dSKalle Valo 	int retval;
21733aca94dSKalle Valo 
21833aca94dSKalle Valo 	retval = rt2x00debug_file_open(inode, file);
21933aca94dSKalle Valo 	if (retval)
22033aca94dSKalle Valo 		return retval;
22133aca94dSKalle Valo 
22233aca94dSKalle Valo 	if (test_and_set_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)) {
22333aca94dSKalle Valo 		rt2x00debug_file_release(inode, file);
22433aca94dSKalle Valo 		return -EBUSY;
22533aca94dSKalle Valo 	}
22633aca94dSKalle Valo 
22733aca94dSKalle Valo 	return 0;
22833aca94dSKalle Valo }
22933aca94dSKalle Valo 
rt2x00debug_release_queue_dump(struct inode * inode,struct file * file)23033aca94dSKalle Valo static int rt2x00debug_release_queue_dump(struct inode *inode, struct file *file)
23133aca94dSKalle Valo {
23233aca94dSKalle Valo 	struct rt2x00debug_intf *intf = inode->i_private;
23333aca94dSKalle Valo 
23433aca94dSKalle Valo 	skb_queue_purge(&intf->frame_dump_skbqueue);
23533aca94dSKalle Valo 
23633aca94dSKalle Valo 	clear_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags);
23733aca94dSKalle Valo 
23833aca94dSKalle Valo 	return rt2x00debug_file_release(inode, file);
23933aca94dSKalle Valo }
24033aca94dSKalle Valo 
rt2x00debug_read_queue_dump(struct file * file,char __user * buf,size_t length,loff_t * offset)24133aca94dSKalle Valo static ssize_t rt2x00debug_read_queue_dump(struct file *file,
24233aca94dSKalle Valo 					   char __user *buf,
24333aca94dSKalle Valo 					   size_t length,
24433aca94dSKalle Valo 					   loff_t *offset)
24533aca94dSKalle Valo {
24633aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;
24733aca94dSKalle Valo 	struct sk_buff *skb;
24833aca94dSKalle Valo 	size_t status;
24933aca94dSKalle Valo 	int retval;
25033aca94dSKalle Valo 
25133aca94dSKalle Valo 	if (file->f_flags & O_NONBLOCK)
25233aca94dSKalle Valo 		return -EAGAIN;
25333aca94dSKalle Valo 
25433aca94dSKalle Valo 	retval =
25533aca94dSKalle Valo 	    wait_event_interruptible(intf->frame_dump_waitqueue,
25633aca94dSKalle Valo 				     (skb =
25733aca94dSKalle Valo 				     skb_dequeue(&intf->frame_dump_skbqueue)));
25833aca94dSKalle Valo 	if (retval)
25933aca94dSKalle Valo 		return retval;
26033aca94dSKalle Valo 
26133aca94dSKalle Valo 	status = min_t(size_t, skb->len, length);
26233aca94dSKalle Valo 	if (copy_to_user(buf, skb->data, status)) {
26333aca94dSKalle Valo 		status = -EFAULT;
26433aca94dSKalle Valo 		goto exit;
26533aca94dSKalle Valo 	}
26633aca94dSKalle Valo 
26733aca94dSKalle Valo 	*offset += status;
26833aca94dSKalle Valo 
26933aca94dSKalle Valo exit:
27033aca94dSKalle Valo 	kfree_skb(skb);
27133aca94dSKalle Valo 
27233aca94dSKalle Valo 	return status;
27333aca94dSKalle Valo }
27433aca94dSKalle Valo 
rt2x00debug_poll_queue_dump(struct file * file,poll_table * wait)275afc9a42bSAl Viro static __poll_t rt2x00debug_poll_queue_dump(struct file *file,
27633aca94dSKalle Valo 						poll_table *wait)
27733aca94dSKalle Valo {
27833aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;
27933aca94dSKalle Valo 
28033aca94dSKalle Valo 	poll_wait(file, &intf->frame_dump_waitqueue, wait);
28133aca94dSKalle Valo 
28233aca94dSKalle Valo 	if (!skb_queue_empty(&intf->frame_dump_skbqueue))
283a9a08845SLinus Torvalds 		return EPOLLOUT | EPOLLWRNORM;
28433aca94dSKalle Valo 
28533aca94dSKalle Valo 	return 0;
28633aca94dSKalle Valo }
28733aca94dSKalle Valo 
28833aca94dSKalle Valo static const struct file_operations rt2x00debug_fop_queue_dump = {
28933aca94dSKalle Valo 	.owner		= THIS_MODULE,
29033aca94dSKalle Valo 	.read		= rt2x00debug_read_queue_dump,
29133aca94dSKalle Valo 	.poll		= rt2x00debug_poll_queue_dump,
29233aca94dSKalle Valo 	.open		= rt2x00debug_open_queue_dump,
29333aca94dSKalle Valo 	.release	= rt2x00debug_release_queue_dump,
29433aca94dSKalle Valo 	.llseek		= default_llseek,
29533aca94dSKalle Valo };
29633aca94dSKalle Valo 
rt2x00debug_read_queue_stats(struct file * file,char __user * buf,size_t length,loff_t * offset)29733aca94dSKalle Valo static ssize_t rt2x00debug_read_queue_stats(struct file *file,
29833aca94dSKalle Valo 					    char __user *buf,
29933aca94dSKalle Valo 					    size_t length,
30033aca94dSKalle Valo 					    loff_t *offset)
30133aca94dSKalle Valo {
30233aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;
30333aca94dSKalle Valo 	struct data_queue *queue;
30433aca94dSKalle Valo 	unsigned long irqflags;
30533aca94dSKalle Valo 	unsigned int lines = 1 + intf->rt2x00dev->data_queues;
30633aca94dSKalle Valo 	size_t size;
30733aca94dSKalle Valo 	char *data;
30833aca94dSKalle Valo 	char *temp;
30933aca94dSKalle Valo 
31033aca94dSKalle Valo 	if (*offset)
31133aca94dSKalle Valo 		return 0;
31233aca94dSKalle Valo 
31333aca94dSKalle Valo 	data = kcalloc(lines, MAX_LINE_LENGTH, GFP_KERNEL);
31433aca94dSKalle Valo 	if (!data)
31533aca94dSKalle Valo 		return -ENOMEM;
31633aca94dSKalle Valo 
31733aca94dSKalle Valo 	temp = data +
31833aca94dSKalle Valo 	    sprintf(data, "qid\tflags\t\tcount\tlimit\tlength\tindex\tdma done\tdone\n");
31933aca94dSKalle Valo 
32033aca94dSKalle Valo 	queue_for_each(intf->rt2x00dev, queue) {
32133aca94dSKalle Valo 		spin_lock_irqsave(&queue->index_lock, irqflags);
32233aca94dSKalle Valo 
32333aca94dSKalle Valo 		temp += sprintf(temp, "%d\t0x%.8x\t%d\t%d\t%d\t%d\t%d\t\t%d\n",
32433aca94dSKalle Valo 				queue->qid, (unsigned int)queue->flags,
32533aca94dSKalle Valo 				queue->count, queue->limit, queue->length,
32633aca94dSKalle Valo 				queue->index[Q_INDEX],
32733aca94dSKalle Valo 				queue->index[Q_INDEX_DMA_DONE],
32833aca94dSKalle Valo 				queue->index[Q_INDEX_DONE]);
32933aca94dSKalle Valo 
33033aca94dSKalle Valo 		spin_unlock_irqrestore(&queue->index_lock, irqflags);
33133aca94dSKalle Valo 	}
33233aca94dSKalle Valo 
33333aca94dSKalle Valo 	size = strlen(data);
33433aca94dSKalle Valo 	size = min(size, length);
33533aca94dSKalle Valo 
33633aca94dSKalle Valo 	if (copy_to_user(buf, data, size)) {
33733aca94dSKalle Valo 		kfree(data);
33833aca94dSKalle Valo 		return -EFAULT;
33933aca94dSKalle Valo 	}
34033aca94dSKalle Valo 
34133aca94dSKalle Valo 	kfree(data);
34233aca94dSKalle Valo 
34333aca94dSKalle Valo 	*offset += size;
34433aca94dSKalle Valo 	return size;
34533aca94dSKalle Valo }
34633aca94dSKalle Valo 
34733aca94dSKalle Valo static const struct file_operations rt2x00debug_fop_queue_stats = {
34833aca94dSKalle Valo 	.owner		= THIS_MODULE,
34933aca94dSKalle Valo 	.read		= rt2x00debug_read_queue_stats,
35033aca94dSKalle Valo 	.open		= rt2x00debug_file_open,
35133aca94dSKalle Valo 	.release	= rt2x00debug_file_release,
35233aca94dSKalle Valo 	.llseek		= default_llseek,
35333aca94dSKalle Valo };
35433aca94dSKalle Valo 
35533aca94dSKalle Valo #ifdef CONFIG_RT2X00_LIB_CRYPTO
rt2x00debug_read_crypto_stats(struct file * file,char __user * buf,size_t length,loff_t * offset)35633aca94dSKalle Valo static ssize_t rt2x00debug_read_crypto_stats(struct file *file,
35733aca94dSKalle Valo 					     char __user *buf,
35833aca94dSKalle Valo 					     size_t length,
35933aca94dSKalle Valo 					     loff_t *offset)
36033aca94dSKalle Valo {
36133aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;
36233aca94dSKalle Valo 	static const char * const name[] = { "WEP64", "WEP128", "TKIP", "AES" };
36333aca94dSKalle Valo 	char *data;
36433aca94dSKalle Valo 	char *temp;
36533aca94dSKalle Valo 	size_t size;
36633aca94dSKalle Valo 	unsigned int i;
36733aca94dSKalle Valo 
36833aca94dSKalle Valo 	if (*offset)
36933aca94dSKalle Valo 		return 0;
37033aca94dSKalle Valo 
3716396bb22SKees Cook 	data = kcalloc(1 + CIPHER_MAX, MAX_LINE_LENGTH, GFP_KERNEL);
37233aca94dSKalle Valo 	if (!data)
37333aca94dSKalle Valo 		return -ENOMEM;
37433aca94dSKalle Valo 
37533aca94dSKalle Valo 	temp = data;
37633aca94dSKalle Valo 	temp += sprintf(data, "cipher\tsuccess\ticv err\tmic err\tkey err\n");
37733aca94dSKalle Valo 
37833aca94dSKalle Valo 	for (i = 0; i < CIPHER_MAX; i++) {
37933aca94dSKalle Valo 		temp += sprintf(temp, "%s\t%lu\t%lu\t%lu\t%lu\n", name[i],
38033aca94dSKalle Valo 				intf->crypto_stats[i].success,
38133aca94dSKalle Valo 				intf->crypto_stats[i].icv_error,
38233aca94dSKalle Valo 				intf->crypto_stats[i].mic_error,
38333aca94dSKalle Valo 				intf->crypto_stats[i].key_error);
38433aca94dSKalle Valo 	}
38533aca94dSKalle Valo 
38633aca94dSKalle Valo 	size = strlen(data);
38733aca94dSKalle Valo 	size = min(size, length);
38833aca94dSKalle Valo 
38933aca94dSKalle Valo 	if (copy_to_user(buf, data, size)) {
39033aca94dSKalle Valo 		kfree(data);
39133aca94dSKalle Valo 		return -EFAULT;
39233aca94dSKalle Valo 	}
39333aca94dSKalle Valo 
39433aca94dSKalle Valo 	kfree(data);
39533aca94dSKalle Valo 
39633aca94dSKalle Valo 	*offset += size;
39733aca94dSKalle Valo 	return size;
39833aca94dSKalle Valo }
39933aca94dSKalle Valo 
40033aca94dSKalle Valo static const struct file_operations rt2x00debug_fop_crypto_stats = {
40133aca94dSKalle Valo 	.owner		= THIS_MODULE,
40233aca94dSKalle Valo 	.read		= rt2x00debug_read_crypto_stats,
40333aca94dSKalle Valo 	.open		= rt2x00debug_file_open,
40433aca94dSKalle Valo 	.release	= rt2x00debug_file_release,
40533aca94dSKalle Valo 	.llseek		= default_llseek,
40633aca94dSKalle Valo };
40733aca94dSKalle Valo #endif
40833aca94dSKalle Valo 
40933aca94dSKalle Valo #define RT2X00DEBUGFS_OPS_READ(__name, __format, __type)	\
41033aca94dSKalle Valo static ssize_t rt2x00debug_read_##__name(struct file *file,	\
41133aca94dSKalle Valo 					 char __user *buf,	\
41233aca94dSKalle Valo 					 size_t length,		\
41333aca94dSKalle Valo 					 loff_t *offset)	\
41433aca94dSKalle Valo {								\
41533aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;	\
41633aca94dSKalle Valo 	const struct rt2x00debug *debug = intf->debug;		\
41733aca94dSKalle Valo 	char line[16];						\
41833aca94dSKalle Valo 	size_t size;						\
41933aca94dSKalle Valo 	unsigned int index = intf->offset_##__name;		\
42033aca94dSKalle Valo 	__type value;						\
42133aca94dSKalle Valo 								\
42233aca94dSKalle Valo 	if (*offset)						\
42333aca94dSKalle Valo 		return 0;					\
42433aca94dSKalle Valo 								\
42533aca94dSKalle Valo 	if (index >= debug->__name.word_count)			\
42633aca94dSKalle Valo 		return -EINVAL;					\
42733aca94dSKalle Valo 								\
42833aca94dSKalle Valo 	index += (debug->__name.word_base /			\
42933aca94dSKalle Valo 		  debug->__name.word_size);			\
43033aca94dSKalle Valo 								\
43133aca94dSKalle Valo 	if (debug->__name.flags & RT2X00DEBUGFS_OFFSET)		\
43233aca94dSKalle Valo 		index *= debug->__name.word_size;		\
43333aca94dSKalle Valo 								\
4346b81745eSArnd Bergmann 	value = debug->__name.read(intf->rt2x00dev, index);	\
43533aca94dSKalle Valo 								\
43633aca94dSKalle Valo 	size = sprintf(line, __format, value);			\
43733aca94dSKalle Valo 								\
438f483039cSDan Carpenter 	return simple_read_from_buffer(buf, length, offset, line, size); \
43933aca94dSKalle Valo }
44033aca94dSKalle Valo 
44133aca94dSKalle Valo #define RT2X00DEBUGFS_OPS_WRITE(__name, __type)			\
44233aca94dSKalle Valo static ssize_t rt2x00debug_write_##__name(struct file *file,	\
44333aca94dSKalle Valo 					  const char __user *buf,\
44433aca94dSKalle Valo 					  size_t length,	\
44533aca94dSKalle Valo 					  loff_t *offset)	\
44633aca94dSKalle Valo {								\
44733aca94dSKalle Valo 	struct rt2x00debug_intf *intf = file->private_data;	\
44833aca94dSKalle Valo 	const struct rt2x00debug *debug = intf->debug;		\
4499cc3fdc8SOne Thousand Gnomes 	char line[17];						\
45033aca94dSKalle Valo 	size_t size;						\
45133aca94dSKalle Valo 	unsigned int index = intf->offset_##__name;		\
45233aca94dSKalle Valo 	__type value;						\
45333aca94dSKalle Valo 								\
45433aca94dSKalle Valo 	if (*offset)						\
45533aca94dSKalle Valo 		return 0;					\
45633aca94dSKalle Valo 								\
45733aca94dSKalle Valo 	if (index >= debug->__name.word_count)			\
45833aca94dSKalle Valo 		return -EINVAL;					\
45933aca94dSKalle Valo 								\
46033aca94dSKalle Valo 	if (length > sizeof(line))				\
46133aca94dSKalle Valo 		return -EINVAL;					\
46233aca94dSKalle Valo 								\
46333aca94dSKalle Valo 	if (copy_from_user(line, buf, length))			\
46433aca94dSKalle Valo 		return -EFAULT;					\
4659cc3fdc8SOne Thousand Gnomes 	line[16] = 0;						\
46633aca94dSKalle Valo 						\
46733aca94dSKalle Valo 	size = strlen(line);					\
46833aca94dSKalle Valo 	value = simple_strtoul(line, NULL, 0);			\
46933aca94dSKalle Valo 								\
47033aca94dSKalle Valo 	index += (debug->__name.word_base /			\
47133aca94dSKalle Valo 		  debug->__name.word_size);			\
47233aca94dSKalle Valo 								\
47333aca94dSKalle Valo 	if (debug->__name.flags & RT2X00DEBUGFS_OFFSET)		\
47433aca94dSKalle Valo 		index *= debug->__name.word_size;		\
47533aca94dSKalle Valo 								\
47633aca94dSKalle Valo 	debug->__name.write(intf->rt2x00dev, index, value);	\
47733aca94dSKalle Valo 								\
47833aca94dSKalle Valo 	*offset += size;					\
47933aca94dSKalle Valo 	return size;						\
48033aca94dSKalle Valo }
48133aca94dSKalle Valo 
48233aca94dSKalle Valo #define RT2X00DEBUGFS_OPS(__name, __format, __type)		\
48333aca94dSKalle Valo RT2X00DEBUGFS_OPS_READ(__name, __format, __type);		\
48433aca94dSKalle Valo RT2X00DEBUGFS_OPS_WRITE(__name, __type);			\
48533aca94dSKalle Valo 								\
48633aca94dSKalle Valo static const struct file_operations rt2x00debug_fop_##__name = {\
48733aca94dSKalle Valo 	.owner		= THIS_MODULE,				\
48833aca94dSKalle Valo 	.read		= rt2x00debug_read_##__name,		\
48933aca94dSKalle Valo 	.write		= rt2x00debug_write_##__name,		\
49033aca94dSKalle Valo 	.open		= rt2x00debug_file_open,		\
49133aca94dSKalle Valo 	.release	= rt2x00debug_file_release,		\
49233aca94dSKalle Valo 	.llseek		= generic_file_llseek,			\
49333aca94dSKalle Valo };
49433aca94dSKalle Valo 
49533aca94dSKalle Valo RT2X00DEBUGFS_OPS(csr, "0x%.8x\n", u32);
49633aca94dSKalle Valo RT2X00DEBUGFS_OPS(eeprom, "0x%.4x\n", u16);
49733aca94dSKalle Valo RT2X00DEBUGFS_OPS(bbp, "0x%.2x\n", u8);
49833aca94dSKalle Valo RT2X00DEBUGFS_OPS(rf, "0x%.8x\n", u32);
49933aca94dSKalle Valo RT2X00DEBUGFS_OPS(rfcsr, "0x%.2x\n", u8);
50033aca94dSKalle Valo 
rt2x00debug_read_dev_flags(struct file * file,char __user * buf,size_t length,loff_t * offset)50133aca94dSKalle Valo static ssize_t rt2x00debug_read_dev_flags(struct file *file,
50233aca94dSKalle Valo 					  char __user *buf,
50333aca94dSKalle Valo 					  size_t length,
50433aca94dSKalle Valo 					  loff_t *offset)
50533aca94dSKalle Valo {
50633aca94dSKalle Valo 	struct rt2x00debug_intf *intf =	file->private_data;
50733aca94dSKalle Valo 	char line[16];
50833aca94dSKalle Valo 	size_t size;
50933aca94dSKalle Valo 
51033aca94dSKalle Valo 	if (*offset)
51133aca94dSKalle Valo 		return 0;
51233aca94dSKalle Valo 
51333aca94dSKalle Valo 	size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->flags);
51433aca94dSKalle Valo 
515f483039cSDan Carpenter 	return simple_read_from_buffer(buf, length, offset, line, size);
51633aca94dSKalle Valo }
51733aca94dSKalle Valo 
51833aca94dSKalle Valo static const struct file_operations rt2x00debug_fop_dev_flags = {
51933aca94dSKalle Valo 	.owner		= THIS_MODULE,
52033aca94dSKalle Valo 	.read		= rt2x00debug_read_dev_flags,
52133aca94dSKalle Valo 	.open		= rt2x00debug_file_open,
52233aca94dSKalle Valo 	.release	= rt2x00debug_file_release,
52333aca94dSKalle Valo 	.llseek		= default_llseek,
52433aca94dSKalle Valo };
52533aca94dSKalle Valo 
rt2x00debug_read_cap_flags(struct file * file,char __user * buf,size_t length,loff_t * offset)52633aca94dSKalle Valo static ssize_t rt2x00debug_read_cap_flags(struct file *file,
52733aca94dSKalle Valo 					  char __user *buf,
52833aca94dSKalle Valo 					  size_t length,
52933aca94dSKalle Valo 					  loff_t *offset)
53033aca94dSKalle Valo {
53133aca94dSKalle Valo 	struct rt2x00debug_intf *intf =	file->private_data;
53233aca94dSKalle Valo 	char line[16];
53333aca94dSKalle Valo 	size_t size;
53433aca94dSKalle Valo 
53533aca94dSKalle Valo 	if (*offset)
53633aca94dSKalle Valo 		return 0;
53733aca94dSKalle Valo 
53833aca94dSKalle Valo 	size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->cap_flags);
53933aca94dSKalle Valo 
540f483039cSDan Carpenter 	return simple_read_from_buffer(buf, length, offset, line, size);
54133aca94dSKalle Valo }
54233aca94dSKalle Valo 
54333aca94dSKalle Valo static const struct file_operations rt2x00debug_fop_cap_flags = {
54433aca94dSKalle Valo 	.owner		= THIS_MODULE,
54533aca94dSKalle Valo 	.read		= rt2x00debug_read_cap_flags,
54633aca94dSKalle Valo 	.open		= rt2x00debug_file_open,
54733aca94dSKalle Valo 	.release	= rt2x00debug_file_release,
54833aca94dSKalle Valo 	.llseek		= default_llseek,
54933aca94dSKalle Valo };
55033aca94dSKalle Valo 
rt2x00debug_write_restart_hw(struct file * file,const char __user * buf,size_t length,loff_t * offset)551e403fa31SStanislaw Gruszka static ssize_t rt2x00debug_write_restart_hw(struct file *file,
552e403fa31SStanislaw Gruszka 					    const char __user *buf,
553e403fa31SStanislaw Gruszka 					    size_t length,
554e403fa31SStanislaw Gruszka 					    loff_t *offset)
555e403fa31SStanislaw Gruszka {
556e403fa31SStanislaw Gruszka 	struct rt2x00debug_intf *intf =	file->private_data;
557e403fa31SStanislaw Gruszka 	struct rt2x00_dev *rt2x00dev = intf->rt2x00dev;
558*c91a9cfeSStanislaw Gruszka 	static unsigned long last_reset = INITIAL_JIFFIES;
559e403fa31SStanislaw Gruszka 
560e403fa31SStanislaw Gruszka 	if (!rt2x00_has_cap_restart_hw(rt2x00dev))
561e403fa31SStanislaw Gruszka 		return -EOPNOTSUPP;
562e403fa31SStanislaw Gruszka 
563e403fa31SStanislaw Gruszka 	if (time_before(jiffies, last_reset + msecs_to_jiffies(2000)))
564e403fa31SStanislaw Gruszka 		return -EBUSY;
565e403fa31SStanislaw Gruszka 
566e403fa31SStanislaw Gruszka 	last_reset = jiffies;
567e403fa31SStanislaw Gruszka 
568e403fa31SStanislaw Gruszka 	ieee80211_restart_hw(rt2x00dev->hw);
569e403fa31SStanislaw Gruszka 	return length;
570e403fa31SStanislaw Gruszka }
571e403fa31SStanislaw Gruszka 
572e403fa31SStanislaw Gruszka static const struct file_operations rt2x00debug_restart_hw = {
573e403fa31SStanislaw Gruszka 	.owner = THIS_MODULE,
574e403fa31SStanislaw Gruszka 	.write = rt2x00debug_write_restart_hw,
575e403fa31SStanislaw Gruszka 	.open = simple_open,
576e403fa31SStanislaw Gruszka 	.llseek = generic_file_llseek,
577e403fa31SStanislaw Gruszka };
578e403fa31SStanislaw Gruszka 
rt2x00debug_create_file_driver(const char * name,struct rt2x00debug_intf * intf,struct debugfs_blob_wrapper * blob)5791dc24406SGreg Kroah-Hartman static void rt2x00debug_create_file_driver(const char *name,
5801dc24406SGreg Kroah-Hartman 					   struct rt2x00debug_intf *intf,
5811dc24406SGreg Kroah-Hartman 					   struct debugfs_blob_wrapper *blob)
58233aca94dSKalle Valo {
58333aca94dSKalle Valo 	char *data;
58433aca94dSKalle Valo 
58533aca94dSKalle Valo 	data = kzalloc(3 * MAX_LINE_LENGTH, GFP_KERNEL);
58633aca94dSKalle Valo 	if (!data)
5871dc24406SGreg Kroah-Hartman 		return;
58833aca94dSKalle Valo 
58933aca94dSKalle Valo 	blob->data = data;
59033aca94dSKalle Valo 	data += sprintf(data, "driver:\t%s\n", intf->rt2x00dev->ops->name);
59133aca94dSKalle Valo 	data += sprintf(data, "version:\t%s\n", DRV_VERSION);
59233aca94dSKalle Valo 	blob->size = strlen(blob->data);
59333aca94dSKalle Valo 
5941dc24406SGreg Kroah-Hartman 	debugfs_create_blob(name, 0400, intf->driver_folder, blob);
59533aca94dSKalle Valo }
59633aca94dSKalle Valo 
rt2x00debug_create_file_chipset(const char * name,struct rt2x00debug_intf * intf,struct debugfs_blob_wrapper * blob)5971dc24406SGreg Kroah-Hartman static void rt2x00debug_create_file_chipset(const char *name,
5981dc24406SGreg Kroah-Hartman 					    struct rt2x00debug_intf *intf,
5991dc24406SGreg Kroah-Hartman 					    struct debugfs_blob_wrapper *blob)
60033aca94dSKalle Valo {
60133aca94dSKalle Valo 	const struct rt2x00debug *debug = intf->debug;
60233aca94dSKalle Valo 	char *data;
60333aca94dSKalle Valo 
60433aca94dSKalle Valo 	data = kzalloc(9 * MAX_LINE_LENGTH, GFP_KERNEL);
60533aca94dSKalle Valo 	if (!data)
6061dc24406SGreg Kroah-Hartman 		return;
60733aca94dSKalle Valo 
60833aca94dSKalle Valo 	blob->data = data;
60933aca94dSKalle Valo 	data += sprintf(data, "rt chip:\t%04x\n", intf->rt2x00dev->chip.rt);
61033aca94dSKalle Valo 	data += sprintf(data, "rf chip:\t%04x\n", intf->rt2x00dev->chip.rf);
61133aca94dSKalle Valo 	data += sprintf(data, "revision:\t%04x\n", intf->rt2x00dev->chip.rev);
61233aca94dSKalle Valo 	data += sprintf(data, "\n");
61333aca94dSKalle Valo 	data += sprintf(data, "register\tbase\twords\twordsize\n");
61433aca94dSKalle Valo #define RT2X00DEBUGFS_SPRINTF_REGISTER(__name)			\
61533aca94dSKalle Valo {								\
61633aca94dSKalle Valo 	if (debug->__name.read)					\
61733aca94dSKalle Valo 		data += sprintf(data, __stringify(__name)	\
61833aca94dSKalle Valo 				"\t%d\t%d\t%d\n",		\
61933aca94dSKalle Valo 				debug->__name.word_base,	\
62033aca94dSKalle Valo 				debug->__name.word_count,	\
62133aca94dSKalle Valo 				debug->__name.word_size);	\
62233aca94dSKalle Valo }
62333aca94dSKalle Valo 	RT2X00DEBUGFS_SPRINTF_REGISTER(csr);
62433aca94dSKalle Valo 	RT2X00DEBUGFS_SPRINTF_REGISTER(eeprom);
62533aca94dSKalle Valo 	RT2X00DEBUGFS_SPRINTF_REGISTER(bbp);
62633aca94dSKalle Valo 	RT2X00DEBUGFS_SPRINTF_REGISTER(rf);
62733aca94dSKalle Valo 	RT2X00DEBUGFS_SPRINTF_REGISTER(rfcsr);
62833aca94dSKalle Valo #undef RT2X00DEBUGFS_SPRINTF_REGISTER
62933aca94dSKalle Valo 
63033aca94dSKalle Valo 	blob->size = strlen(blob->data);
63133aca94dSKalle Valo 
6321dc24406SGreg Kroah-Hartman 	debugfs_create_blob(name, 0400, intf->driver_folder, blob);
63333aca94dSKalle Valo }
63433aca94dSKalle Valo 
rt2x00debug_register(struct rt2x00_dev * rt2x00dev)63533aca94dSKalle Valo void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
63633aca94dSKalle Valo {
63733aca94dSKalle Valo 	const struct rt2x00debug *debug = rt2x00dev->ops->debugfs;
63833aca94dSKalle Valo 	struct rt2x00debug_intf *intf;
6391dc24406SGreg Kroah-Hartman 	struct dentry *queue_folder;
6401dc24406SGreg Kroah-Hartman 	struct dentry *register_folder;
64133aca94dSKalle Valo 
64233aca94dSKalle Valo 	intf = kzalloc(sizeof(struct rt2x00debug_intf), GFP_KERNEL);
64333aca94dSKalle Valo 	if (!intf) {
64433aca94dSKalle Valo 		rt2x00_err(rt2x00dev, "Failed to allocate debug handler\n");
64533aca94dSKalle Valo 		return;
64633aca94dSKalle Valo 	}
64733aca94dSKalle Valo 
64833aca94dSKalle Valo 	intf->debug = debug;
64933aca94dSKalle Valo 	intf->rt2x00dev = rt2x00dev;
65033aca94dSKalle Valo 	rt2x00dev->debugfs_intf = intf;
65133aca94dSKalle Valo 
65233aca94dSKalle Valo 	intf->driver_folder =
65333aca94dSKalle Valo 	    debugfs_create_dir(intf->rt2x00dev->ops->name,
65433aca94dSKalle Valo 			       rt2x00dev->hw->wiphy->debugfsdir);
65533aca94dSKalle Valo 
65633aca94dSKalle Valo 	rt2x00debug_create_file_driver("driver", intf, &intf->driver_blob);
6571dc24406SGreg Kroah-Hartman 	rt2x00debug_create_file_chipset("chipset", intf, &intf->chipset_blob);
6581dc24406SGreg Kroah-Hartman 	debugfs_create_file("dev_flags", 0400, intf->driver_folder, intf,
65933aca94dSKalle Valo 			    &rt2x00debug_fop_dev_flags);
6601dc24406SGreg Kroah-Hartman 	debugfs_create_file("cap_flags", 0400, intf->driver_folder, intf,
66133aca94dSKalle Valo 			    &rt2x00debug_fop_cap_flags);
6621dc24406SGreg Kroah-Hartman 	debugfs_create_file("restart_hw", 0200, intf->driver_folder, intf,
663e403fa31SStanislaw Gruszka 			    &rt2x00debug_restart_hw);
664e403fa31SStanislaw Gruszka 
6651dc24406SGreg Kroah-Hartman 	register_folder = 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) {					\
6701dc24406SGreg Kroah-Hartman 		debugfs_create_u32(__stringify(__name) "_offset", 0600,	\
6711dc24406SGreg Kroah-Hartman 				   register_folder,			\
67233aca94dSKalle Valo 				   &(__intf)->offset_##__name);		\
67333aca94dSKalle Valo 									\
6741dc24406SGreg Kroah-Hartman 		debugfs_create_file(__stringify(__name) "_value", 0600,	\
6751dc24406SGreg Kroah-Hartman 				    register_folder, (__intf),		\
6762ef00c53SJoe Perches 				    &rt2x00debug_fop_##__name);		\
67733aca94dSKalle Valo 	}								\
67833aca94dSKalle Valo })
67933aca94dSKalle Valo 
68033aca94dSKalle Valo 	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, csr);
68133aca94dSKalle Valo 	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, eeprom);
68233aca94dSKalle Valo 	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, bbp);
68333aca94dSKalle Valo 	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rf);
68433aca94dSKalle Valo 	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rfcsr);
68533aca94dSKalle Valo 
68633aca94dSKalle Valo #undef RT2X00DEBUGFS_CREATE_REGISTER_ENTRY
68733aca94dSKalle Valo 
6881dc24406SGreg Kroah-Hartman 	queue_folder = debugfs_create_dir("queue", intf->driver_folder);
68933aca94dSKalle Valo 
6901dc24406SGreg Kroah-Hartman 	debugfs_create_file("dump", 0400, queue_folder, intf,
6911dc24406SGreg Kroah-Hartman 			    &rt2x00debug_fop_queue_dump);
69233aca94dSKalle Valo 
69333aca94dSKalle Valo 	skb_queue_head_init(&intf->frame_dump_skbqueue);
69433aca94dSKalle Valo 	init_waitqueue_head(&intf->frame_dump_waitqueue);
69533aca94dSKalle Valo 
6961dc24406SGreg Kroah-Hartman 	debugfs_create_file("queue", 0400, queue_folder, intf,
6971dc24406SGreg Kroah-Hartman 			    &rt2x00debug_fop_queue_stats);
69833aca94dSKalle Valo 
69933aca94dSKalle Valo #ifdef CONFIG_RT2X00_LIB_CRYPTO
70033aca94dSKalle Valo 	if (rt2x00_has_cap_hw_crypto(rt2x00dev))
7011dc24406SGreg Kroah-Hartman 		debugfs_create_file("crypto", 0444, queue_folder, intf,
7022ef00c53SJoe Perches 				    &rt2x00debug_fop_crypto_stats);
70333aca94dSKalle Valo #endif
70433aca94dSKalle Valo 
70533aca94dSKalle Valo 	return;
70633aca94dSKalle Valo }
70733aca94dSKalle Valo 
rt2x00debug_deregister(struct rt2x00_dev * rt2x00dev)70833aca94dSKalle Valo void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev)
70933aca94dSKalle Valo {
71033aca94dSKalle Valo 	struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf;
71133aca94dSKalle Valo 
71233aca94dSKalle Valo 	if (unlikely(!intf))
71333aca94dSKalle Valo 		return;
71433aca94dSKalle Valo 
71533aca94dSKalle Valo 	skb_queue_purge(&intf->frame_dump_skbqueue);
71633aca94dSKalle Valo 
7171dc24406SGreg Kroah-Hartman 	debugfs_remove_recursive(intf->driver_folder);
71833aca94dSKalle Valo 	kfree(intf->chipset_blob.data);
71933aca94dSKalle Valo 	kfree(intf->driver_blob.data);
72033aca94dSKalle Valo 	kfree(intf);
72133aca94dSKalle Valo 
72233aca94dSKalle Valo 	rt2x00dev->debugfs_intf = NULL;
72333aca94dSKalle Valo }
724