18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci	Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
48c2ecf20Sopenharmony_ci	<http://rt2x00.serialmonkey.com>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci	Module: rt2x00lib
108c2ecf20Sopenharmony_ci	Abstract: rt2x00 debugfs specific routines.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/poll.h>
178c2ecf20Sopenharmony_ci#include <linux/sched.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "rt2x00.h"
228c2ecf20Sopenharmony_ci#include "rt2x00lib.h"
238c2ecf20Sopenharmony_ci#include "rt2x00dump.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define MAX_LINE_LENGTH 64
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct rt2x00debug_crypto {
288c2ecf20Sopenharmony_ci	unsigned long success;
298c2ecf20Sopenharmony_ci	unsigned long icv_error;
308c2ecf20Sopenharmony_ci	unsigned long mic_error;
318c2ecf20Sopenharmony_ci	unsigned long key_error;
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistruct rt2x00debug_intf {
358c2ecf20Sopenharmony_ci	/*
368c2ecf20Sopenharmony_ci	 * Pointer to driver structure where
378c2ecf20Sopenharmony_ci	 * this debugfs entry belongs to.
388c2ecf20Sopenharmony_ci	 */
398c2ecf20Sopenharmony_ci	struct rt2x00_dev *rt2x00dev;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/*
428c2ecf20Sopenharmony_ci	 * Reference to the rt2x00debug structure
438c2ecf20Sopenharmony_ci	 * which can be used to communicate with
448c2ecf20Sopenharmony_ci	 * the registers.
458c2ecf20Sopenharmony_ci	 */
468c2ecf20Sopenharmony_ci	const struct rt2x00debug *debug;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	/*
498c2ecf20Sopenharmony_ci	 * Debugfs entries for:
508c2ecf20Sopenharmony_ci	 * - driver folder
518c2ecf20Sopenharmony_ci	 *   - driver file
528c2ecf20Sopenharmony_ci	 *   - chipset file
538c2ecf20Sopenharmony_ci	 *   - device state flags file
548c2ecf20Sopenharmony_ci	 *   - device capability flags file
558c2ecf20Sopenharmony_ci	 *   - hardware restart file
568c2ecf20Sopenharmony_ci	 *   - register folder
578c2ecf20Sopenharmony_ci	 *     - csr offset/value files
588c2ecf20Sopenharmony_ci	 *     - eeprom offset/value files
598c2ecf20Sopenharmony_ci	 *     - bbp offset/value files
608c2ecf20Sopenharmony_ci	 *     - rf offset/value files
618c2ecf20Sopenharmony_ci	 *     - rfcsr offset/value files
628c2ecf20Sopenharmony_ci	 *   - queue folder
638c2ecf20Sopenharmony_ci	 *     - frame dump file
648c2ecf20Sopenharmony_ci	 *     - queue stats file
658c2ecf20Sopenharmony_ci	 *     - crypto stats file
668c2ecf20Sopenharmony_ci	 */
678c2ecf20Sopenharmony_ci	struct dentry *driver_folder;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/*
708c2ecf20Sopenharmony_ci	 * The frame dump file only allows a single reader,
718c2ecf20Sopenharmony_ci	 * so we need to store the current state here.
728c2ecf20Sopenharmony_ci	 */
738c2ecf20Sopenharmony_ci	unsigned long frame_dump_flags;
748c2ecf20Sopenharmony_ci#define FRAME_DUMP_FILE_OPEN	1
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/*
778c2ecf20Sopenharmony_ci	 * We queue each frame before dumping it to the user,
788c2ecf20Sopenharmony_ci	 * per read command we will pass a single skb structure
798c2ecf20Sopenharmony_ci	 * so we should be prepared to queue multiple sk buffers
808c2ecf20Sopenharmony_ci	 * before sending it to userspace.
818c2ecf20Sopenharmony_ci	 */
828c2ecf20Sopenharmony_ci	struct sk_buff_head frame_dump_skbqueue;
838c2ecf20Sopenharmony_ci	wait_queue_head_t frame_dump_waitqueue;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/*
868c2ecf20Sopenharmony_ci	 * HW crypto statistics.
878c2ecf20Sopenharmony_ci	 * All statistics are stored separately per cipher type.
888c2ecf20Sopenharmony_ci	 */
898c2ecf20Sopenharmony_ci	struct rt2x00debug_crypto crypto_stats[CIPHER_MAX];
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/*
928c2ecf20Sopenharmony_ci	 * Driver and chipset files will use a data buffer
938c2ecf20Sopenharmony_ci	 * that has been created in advance. This will simplify
948c2ecf20Sopenharmony_ci	 * the code since we can use the debugfs functions.
958c2ecf20Sopenharmony_ci	 */
968c2ecf20Sopenharmony_ci	struct debugfs_blob_wrapper driver_blob;
978c2ecf20Sopenharmony_ci	struct debugfs_blob_wrapper chipset_blob;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/*
1008c2ecf20Sopenharmony_ci	 * Requested offset for each register type.
1018c2ecf20Sopenharmony_ci	 */
1028c2ecf20Sopenharmony_ci	unsigned int offset_csr;
1038c2ecf20Sopenharmony_ci	unsigned int offset_eeprom;
1048c2ecf20Sopenharmony_ci	unsigned int offset_bbp;
1058c2ecf20Sopenharmony_ci	unsigned int offset_rf;
1068c2ecf20Sopenharmony_ci	unsigned int offset_rfcsr;
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_civoid rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev,
1108c2ecf20Sopenharmony_ci			       struct rxdone_entry_desc *rxdesc)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf;
1138c2ecf20Sopenharmony_ci	enum cipher cipher = rxdesc->cipher;
1148c2ecf20Sopenharmony_ci	enum rx_crypto status = rxdesc->cipher_status;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (cipher == CIPHER_TKIP_NO_MIC)
1178c2ecf20Sopenharmony_ci		cipher = CIPHER_TKIP;
1188c2ecf20Sopenharmony_ci	if (cipher == CIPHER_NONE || cipher >= CIPHER_MAX)
1198c2ecf20Sopenharmony_ci		return;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* Remove CIPHER_NONE index */
1228c2ecf20Sopenharmony_ci	cipher--;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	intf->crypto_stats[cipher].success += (status == RX_CRYPTO_SUCCESS);
1258c2ecf20Sopenharmony_ci	intf->crypto_stats[cipher].icv_error += (status == RX_CRYPTO_FAIL_ICV);
1268c2ecf20Sopenharmony_ci	intf->crypto_stats[cipher].mic_error += (status == RX_CRYPTO_FAIL_MIC);
1278c2ecf20Sopenharmony_ci	intf->crypto_stats[cipher].key_error += (status == RX_CRYPTO_FAIL_KEY);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_civoid rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev,
1318c2ecf20Sopenharmony_ci			    enum rt2x00_dump_type type, struct queue_entry *entry)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf;
1348c2ecf20Sopenharmony_ci	struct sk_buff *skb = entry->skb;
1358c2ecf20Sopenharmony_ci	struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
1368c2ecf20Sopenharmony_ci	struct sk_buff *skbcopy;
1378c2ecf20Sopenharmony_ci	struct rt2x00dump_hdr *dump_hdr;
1388c2ecf20Sopenharmony_ci	struct timespec64 timestamp;
1398c2ecf20Sopenharmony_ci	u32 data_len;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (likely(!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)))
1428c2ecf20Sopenharmony_ci		return;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	ktime_get_ts64(&timestamp);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (skb_queue_len(&intf->frame_dump_skbqueue) > 20) {
1478c2ecf20Sopenharmony_ci		rt2x00_dbg(rt2x00dev, "txrx dump queue length exceeded\n");
1488c2ecf20Sopenharmony_ci		return;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	data_len = skb->len;
1528c2ecf20Sopenharmony_ci	if (skbdesc->flags & SKBDESC_DESC_IN_SKB)
1538c2ecf20Sopenharmony_ci		data_len -= skbdesc->desc_len;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	skbcopy = alloc_skb(sizeof(*dump_hdr) + skbdesc->desc_len + data_len,
1568c2ecf20Sopenharmony_ci			    GFP_ATOMIC);
1578c2ecf20Sopenharmony_ci	if (!skbcopy) {
1588c2ecf20Sopenharmony_ci		rt2x00_dbg(rt2x00dev, "Failed to copy skb for dump\n");
1598c2ecf20Sopenharmony_ci		return;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	dump_hdr = skb_put(skbcopy, sizeof(*dump_hdr));
1638c2ecf20Sopenharmony_ci	dump_hdr->version = cpu_to_le32(DUMP_HEADER_VERSION);
1648c2ecf20Sopenharmony_ci	dump_hdr->header_length = cpu_to_le32(sizeof(*dump_hdr));
1658c2ecf20Sopenharmony_ci	dump_hdr->desc_length = cpu_to_le32(skbdesc->desc_len);
1668c2ecf20Sopenharmony_ci	dump_hdr->data_length = cpu_to_le32(data_len);
1678c2ecf20Sopenharmony_ci	dump_hdr->chip_rt = cpu_to_le16(rt2x00dev->chip.rt);
1688c2ecf20Sopenharmony_ci	dump_hdr->chip_rf = cpu_to_le16(rt2x00dev->chip.rf);
1698c2ecf20Sopenharmony_ci	dump_hdr->chip_rev = cpu_to_le16(rt2x00dev->chip.rev);
1708c2ecf20Sopenharmony_ci	dump_hdr->type = cpu_to_le16(type);
1718c2ecf20Sopenharmony_ci	dump_hdr->queue_index = entry->queue->qid;
1728c2ecf20Sopenharmony_ci	dump_hdr->entry_index = entry->entry_idx;
1738c2ecf20Sopenharmony_ci	dump_hdr->timestamp_sec = cpu_to_le32(timestamp.tv_sec);
1748c2ecf20Sopenharmony_ci	dump_hdr->timestamp_usec = cpu_to_le32(timestamp.tv_nsec /
1758c2ecf20Sopenharmony_ci					       NSEC_PER_USEC);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (!(skbdesc->flags & SKBDESC_DESC_IN_SKB))
1788c2ecf20Sopenharmony_ci		skb_put_data(skbcopy, skbdesc->desc, skbdesc->desc_len);
1798c2ecf20Sopenharmony_ci	skb_put_data(skbcopy, skb->data, skb->len);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	skb_queue_tail(&intf->frame_dump_skbqueue, skbcopy);
1828c2ecf20Sopenharmony_ci	wake_up_interruptible(&intf->frame_dump_waitqueue);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/*
1858c2ecf20Sopenharmony_ci	 * Verify that the file has not been closed while we were working.
1868c2ecf20Sopenharmony_ci	 */
1878c2ecf20Sopenharmony_ci	if (!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags))
1888c2ecf20Sopenharmony_ci		skb_queue_purge(&intf->frame_dump_skbqueue);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00debug_dump_frame);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int rt2x00debug_file_open(struct inode *inode, struct file *file)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf = inode->i_private;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	file->private_data = inode->i_private;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (!try_module_get(intf->debug->owner))
1998c2ecf20Sopenharmony_ci		return -EBUSY;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return 0;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int rt2x00debug_file_release(struct inode *inode, struct file *file)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf = file->private_data;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	module_put(intf->debug->owner);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return 0;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic int rt2x00debug_open_queue_dump(struct inode *inode, struct file *file)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf = inode->i_private;
2168c2ecf20Sopenharmony_ci	int retval;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	retval = rt2x00debug_file_open(inode, file);
2198c2ecf20Sopenharmony_ci	if (retval)
2208c2ecf20Sopenharmony_ci		return retval;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (test_and_set_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)) {
2238c2ecf20Sopenharmony_ci		rt2x00debug_file_release(inode, file);
2248c2ecf20Sopenharmony_ci		return -EBUSY;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return 0;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic int rt2x00debug_release_queue_dump(struct inode *inode, struct file *file)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf = inode->i_private;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	skb_queue_purge(&intf->frame_dump_skbqueue);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	clear_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return rt2x00debug_file_release(inode, file);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic ssize_t rt2x00debug_read_queue_dump(struct file *file,
2428c2ecf20Sopenharmony_ci					   char __user *buf,
2438c2ecf20Sopenharmony_ci					   size_t length,
2448c2ecf20Sopenharmony_ci					   loff_t *offset)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf = file->private_data;
2478c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2488c2ecf20Sopenharmony_ci	size_t status;
2498c2ecf20Sopenharmony_ci	int retval;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (file->f_flags & O_NONBLOCK)
2528c2ecf20Sopenharmony_ci		return -EAGAIN;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	retval =
2558c2ecf20Sopenharmony_ci	    wait_event_interruptible(intf->frame_dump_waitqueue,
2568c2ecf20Sopenharmony_ci				     (skb =
2578c2ecf20Sopenharmony_ci				     skb_dequeue(&intf->frame_dump_skbqueue)));
2588c2ecf20Sopenharmony_ci	if (retval)
2598c2ecf20Sopenharmony_ci		return retval;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	status = min_t(size_t, skb->len, length);
2628c2ecf20Sopenharmony_ci	if (copy_to_user(buf, skb->data, status)) {
2638c2ecf20Sopenharmony_ci		status = -EFAULT;
2648c2ecf20Sopenharmony_ci		goto exit;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	*offset += status;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ciexit:
2708c2ecf20Sopenharmony_ci	kfree_skb(skb);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return status;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic __poll_t rt2x00debug_poll_queue_dump(struct file *file,
2768c2ecf20Sopenharmony_ci						poll_table *wait)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf = file->private_data;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	poll_wait(file, &intf->frame_dump_waitqueue, wait);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (!skb_queue_empty(&intf->frame_dump_skbqueue))
2838c2ecf20Sopenharmony_ci		return EPOLLOUT | EPOLLWRNORM;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic const struct file_operations rt2x00debug_fop_queue_dump = {
2898c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2908c2ecf20Sopenharmony_ci	.read		= rt2x00debug_read_queue_dump,
2918c2ecf20Sopenharmony_ci	.poll		= rt2x00debug_poll_queue_dump,
2928c2ecf20Sopenharmony_ci	.open		= rt2x00debug_open_queue_dump,
2938c2ecf20Sopenharmony_ci	.release	= rt2x00debug_release_queue_dump,
2948c2ecf20Sopenharmony_ci	.llseek		= default_llseek,
2958c2ecf20Sopenharmony_ci};
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic ssize_t rt2x00debug_read_queue_stats(struct file *file,
2988c2ecf20Sopenharmony_ci					    char __user *buf,
2998c2ecf20Sopenharmony_ci					    size_t length,
3008c2ecf20Sopenharmony_ci					    loff_t *offset)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf = file->private_data;
3038c2ecf20Sopenharmony_ci	struct data_queue *queue;
3048c2ecf20Sopenharmony_ci	unsigned long irqflags;
3058c2ecf20Sopenharmony_ci	unsigned int lines = 1 + intf->rt2x00dev->data_queues;
3068c2ecf20Sopenharmony_ci	size_t size;
3078c2ecf20Sopenharmony_ci	char *data;
3088c2ecf20Sopenharmony_ci	char *temp;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (*offset)
3118c2ecf20Sopenharmony_ci		return 0;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	data = kcalloc(lines, MAX_LINE_LENGTH, GFP_KERNEL);
3148c2ecf20Sopenharmony_ci	if (!data)
3158c2ecf20Sopenharmony_ci		return -ENOMEM;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	temp = data +
3188c2ecf20Sopenharmony_ci	    sprintf(data, "qid\tflags\t\tcount\tlimit\tlength\tindex\tdma done\tdone\n");
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	queue_for_each(intf->rt2x00dev, queue) {
3218c2ecf20Sopenharmony_ci		spin_lock_irqsave(&queue->index_lock, irqflags);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		temp += sprintf(temp, "%d\t0x%.8x\t%d\t%d\t%d\t%d\t%d\t\t%d\n",
3248c2ecf20Sopenharmony_ci				queue->qid, (unsigned int)queue->flags,
3258c2ecf20Sopenharmony_ci				queue->count, queue->limit, queue->length,
3268c2ecf20Sopenharmony_ci				queue->index[Q_INDEX],
3278c2ecf20Sopenharmony_ci				queue->index[Q_INDEX_DMA_DONE],
3288c2ecf20Sopenharmony_ci				queue->index[Q_INDEX_DONE]);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&queue->index_lock, irqflags);
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	size = strlen(data);
3348c2ecf20Sopenharmony_ci	size = min(size, length);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (copy_to_user(buf, data, size)) {
3378c2ecf20Sopenharmony_ci		kfree(data);
3388c2ecf20Sopenharmony_ci		return -EFAULT;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	kfree(data);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	*offset += size;
3448c2ecf20Sopenharmony_ci	return size;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic const struct file_operations rt2x00debug_fop_queue_stats = {
3488c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
3498c2ecf20Sopenharmony_ci	.read		= rt2x00debug_read_queue_stats,
3508c2ecf20Sopenharmony_ci	.open		= rt2x00debug_file_open,
3518c2ecf20Sopenharmony_ci	.release	= rt2x00debug_file_release,
3528c2ecf20Sopenharmony_ci	.llseek		= default_llseek,
3538c2ecf20Sopenharmony_ci};
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci#ifdef CONFIG_RT2X00_LIB_CRYPTO
3568c2ecf20Sopenharmony_cistatic ssize_t rt2x00debug_read_crypto_stats(struct file *file,
3578c2ecf20Sopenharmony_ci					     char __user *buf,
3588c2ecf20Sopenharmony_ci					     size_t length,
3598c2ecf20Sopenharmony_ci					     loff_t *offset)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf = file->private_data;
3628c2ecf20Sopenharmony_ci	static const char * const name[] = { "WEP64", "WEP128", "TKIP", "AES" };
3638c2ecf20Sopenharmony_ci	char *data;
3648c2ecf20Sopenharmony_ci	char *temp;
3658c2ecf20Sopenharmony_ci	size_t size;
3668c2ecf20Sopenharmony_ci	unsigned int i;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (*offset)
3698c2ecf20Sopenharmony_ci		return 0;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	data = kcalloc(1 + CIPHER_MAX, MAX_LINE_LENGTH, GFP_KERNEL);
3728c2ecf20Sopenharmony_ci	if (!data)
3738c2ecf20Sopenharmony_ci		return -ENOMEM;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	temp = data;
3768c2ecf20Sopenharmony_ci	temp += sprintf(data, "cipher\tsuccess\ticv err\tmic err\tkey err\n");
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	for (i = 0; i < CIPHER_MAX; i++) {
3798c2ecf20Sopenharmony_ci		temp += sprintf(temp, "%s\t%lu\t%lu\t%lu\t%lu\n", name[i],
3808c2ecf20Sopenharmony_ci				intf->crypto_stats[i].success,
3818c2ecf20Sopenharmony_ci				intf->crypto_stats[i].icv_error,
3828c2ecf20Sopenharmony_ci				intf->crypto_stats[i].mic_error,
3838c2ecf20Sopenharmony_ci				intf->crypto_stats[i].key_error);
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	size = strlen(data);
3878c2ecf20Sopenharmony_ci	size = min(size, length);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if (copy_to_user(buf, data, size)) {
3908c2ecf20Sopenharmony_ci		kfree(data);
3918c2ecf20Sopenharmony_ci		return -EFAULT;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	kfree(data);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	*offset += size;
3978c2ecf20Sopenharmony_ci	return size;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic const struct file_operations rt2x00debug_fop_crypto_stats = {
4018c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
4028c2ecf20Sopenharmony_ci	.read		= rt2x00debug_read_crypto_stats,
4038c2ecf20Sopenharmony_ci	.open		= rt2x00debug_file_open,
4048c2ecf20Sopenharmony_ci	.release	= rt2x00debug_file_release,
4058c2ecf20Sopenharmony_ci	.llseek		= default_llseek,
4068c2ecf20Sopenharmony_ci};
4078c2ecf20Sopenharmony_ci#endif
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci#define RT2X00DEBUGFS_OPS_READ(__name, __format, __type)	\
4108c2ecf20Sopenharmony_cistatic ssize_t rt2x00debug_read_##__name(struct file *file,	\
4118c2ecf20Sopenharmony_ci					 char __user *buf,	\
4128c2ecf20Sopenharmony_ci					 size_t length,		\
4138c2ecf20Sopenharmony_ci					 loff_t *offset)	\
4148c2ecf20Sopenharmony_ci{								\
4158c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf = file->private_data;	\
4168c2ecf20Sopenharmony_ci	const struct rt2x00debug *debug = intf->debug;		\
4178c2ecf20Sopenharmony_ci	char line[16];						\
4188c2ecf20Sopenharmony_ci	size_t size;						\
4198c2ecf20Sopenharmony_ci	unsigned int index = intf->offset_##__name;		\
4208c2ecf20Sopenharmony_ci	__type value;						\
4218c2ecf20Sopenharmony_ci								\
4228c2ecf20Sopenharmony_ci	if (*offset)						\
4238c2ecf20Sopenharmony_ci		return 0;					\
4248c2ecf20Sopenharmony_ci								\
4258c2ecf20Sopenharmony_ci	if (index >= debug->__name.word_count)			\
4268c2ecf20Sopenharmony_ci		return -EINVAL;					\
4278c2ecf20Sopenharmony_ci								\
4288c2ecf20Sopenharmony_ci	index += (debug->__name.word_base /			\
4298c2ecf20Sopenharmony_ci		  debug->__name.word_size);			\
4308c2ecf20Sopenharmony_ci								\
4318c2ecf20Sopenharmony_ci	if (debug->__name.flags & RT2X00DEBUGFS_OFFSET)		\
4328c2ecf20Sopenharmony_ci		index *= debug->__name.word_size;		\
4338c2ecf20Sopenharmony_ci								\
4348c2ecf20Sopenharmony_ci	value = debug->__name.read(intf->rt2x00dev, index);	\
4358c2ecf20Sopenharmony_ci								\
4368c2ecf20Sopenharmony_ci	size = sprintf(line, __format, value);			\
4378c2ecf20Sopenharmony_ci								\
4388c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, length, offset, line, size); \
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci#define RT2X00DEBUGFS_OPS_WRITE(__name, __type)			\
4428c2ecf20Sopenharmony_cistatic ssize_t rt2x00debug_write_##__name(struct file *file,	\
4438c2ecf20Sopenharmony_ci					  const char __user *buf,\
4448c2ecf20Sopenharmony_ci					  size_t length,	\
4458c2ecf20Sopenharmony_ci					  loff_t *offset)	\
4468c2ecf20Sopenharmony_ci{								\
4478c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf = file->private_data;	\
4488c2ecf20Sopenharmony_ci	const struct rt2x00debug *debug = intf->debug;		\
4498c2ecf20Sopenharmony_ci	char line[17];						\
4508c2ecf20Sopenharmony_ci	size_t size;						\
4518c2ecf20Sopenharmony_ci	unsigned int index = intf->offset_##__name;		\
4528c2ecf20Sopenharmony_ci	__type value;						\
4538c2ecf20Sopenharmony_ci								\
4548c2ecf20Sopenharmony_ci	if (*offset)						\
4558c2ecf20Sopenharmony_ci		return 0;					\
4568c2ecf20Sopenharmony_ci								\
4578c2ecf20Sopenharmony_ci	if (index >= debug->__name.word_count)			\
4588c2ecf20Sopenharmony_ci		return -EINVAL;					\
4598c2ecf20Sopenharmony_ci								\
4608c2ecf20Sopenharmony_ci	if (length > sizeof(line))				\
4618c2ecf20Sopenharmony_ci		return -EINVAL;					\
4628c2ecf20Sopenharmony_ci								\
4638c2ecf20Sopenharmony_ci	if (copy_from_user(line, buf, length))			\
4648c2ecf20Sopenharmony_ci		return -EFAULT;					\
4658c2ecf20Sopenharmony_ci	line[16] = 0;						\
4668c2ecf20Sopenharmony_ci						\
4678c2ecf20Sopenharmony_ci	size = strlen(line);					\
4688c2ecf20Sopenharmony_ci	value = simple_strtoul(line, NULL, 0);			\
4698c2ecf20Sopenharmony_ci								\
4708c2ecf20Sopenharmony_ci	index += (debug->__name.word_base /			\
4718c2ecf20Sopenharmony_ci		  debug->__name.word_size);			\
4728c2ecf20Sopenharmony_ci								\
4738c2ecf20Sopenharmony_ci	if (debug->__name.flags & RT2X00DEBUGFS_OFFSET)		\
4748c2ecf20Sopenharmony_ci		index *= debug->__name.word_size;		\
4758c2ecf20Sopenharmony_ci								\
4768c2ecf20Sopenharmony_ci	debug->__name.write(intf->rt2x00dev, index, value);	\
4778c2ecf20Sopenharmony_ci								\
4788c2ecf20Sopenharmony_ci	*offset += size;					\
4798c2ecf20Sopenharmony_ci	return size;						\
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci#define RT2X00DEBUGFS_OPS(__name, __format, __type)		\
4838c2ecf20Sopenharmony_ciRT2X00DEBUGFS_OPS_READ(__name, __format, __type);		\
4848c2ecf20Sopenharmony_ciRT2X00DEBUGFS_OPS_WRITE(__name, __type);			\
4858c2ecf20Sopenharmony_ci								\
4868c2ecf20Sopenharmony_cistatic const struct file_operations rt2x00debug_fop_##__name = {\
4878c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,				\
4888c2ecf20Sopenharmony_ci	.read		= rt2x00debug_read_##__name,		\
4898c2ecf20Sopenharmony_ci	.write		= rt2x00debug_write_##__name,		\
4908c2ecf20Sopenharmony_ci	.open		= rt2x00debug_file_open,		\
4918c2ecf20Sopenharmony_ci	.release	= rt2x00debug_file_release,		\
4928c2ecf20Sopenharmony_ci	.llseek		= generic_file_llseek,			\
4938c2ecf20Sopenharmony_ci};
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ciRT2X00DEBUGFS_OPS(csr, "0x%.8x\n", u32);
4968c2ecf20Sopenharmony_ciRT2X00DEBUGFS_OPS(eeprom, "0x%.4x\n", u16);
4978c2ecf20Sopenharmony_ciRT2X00DEBUGFS_OPS(bbp, "0x%.2x\n", u8);
4988c2ecf20Sopenharmony_ciRT2X00DEBUGFS_OPS(rf, "0x%.8x\n", u32);
4998c2ecf20Sopenharmony_ciRT2X00DEBUGFS_OPS(rfcsr, "0x%.2x\n", u8);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic ssize_t rt2x00debug_read_dev_flags(struct file *file,
5028c2ecf20Sopenharmony_ci					  char __user *buf,
5038c2ecf20Sopenharmony_ci					  size_t length,
5048c2ecf20Sopenharmony_ci					  loff_t *offset)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf =	file->private_data;
5078c2ecf20Sopenharmony_ci	char line[16];
5088c2ecf20Sopenharmony_ci	size_t size;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	if (*offset)
5118c2ecf20Sopenharmony_ci		return 0;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->flags);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, length, offset, line, size);
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_cistatic const struct file_operations rt2x00debug_fop_dev_flags = {
5198c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
5208c2ecf20Sopenharmony_ci	.read		= rt2x00debug_read_dev_flags,
5218c2ecf20Sopenharmony_ci	.open		= rt2x00debug_file_open,
5228c2ecf20Sopenharmony_ci	.release	= rt2x00debug_file_release,
5238c2ecf20Sopenharmony_ci	.llseek		= default_llseek,
5248c2ecf20Sopenharmony_ci};
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic ssize_t rt2x00debug_read_cap_flags(struct file *file,
5278c2ecf20Sopenharmony_ci					  char __user *buf,
5288c2ecf20Sopenharmony_ci					  size_t length,
5298c2ecf20Sopenharmony_ci					  loff_t *offset)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf =	file->private_data;
5328c2ecf20Sopenharmony_ci	char line[16];
5338c2ecf20Sopenharmony_ci	size_t size;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	if (*offset)
5368c2ecf20Sopenharmony_ci		return 0;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->cap_flags);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, length, offset, line, size);
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic const struct file_operations rt2x00debug_fop_cap_flags = {
5448c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
5458c2ecf20Sopenharmony_ci	.read		= rt2x00debug_read_cap_flags,
5468c2ecf20Sopenharmony_ci	.open		= rt2x00debug_file_open,
5478c2ecf20Sopenharmony_ci	.release	= rt2x00debug_file_release,
5488c2ecf20Sopenharmony_ci	.llseek		= default_llseek,
5498c2ecf20Sopenharmony_ci};
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_cistatic ssize_t rt2x00debug_write_restart_hw(struct file *file,
5528c2ecf20Sopenharmony_ci					    const char __user *buf,
5538c2ecf20Sopenharmony_ci					    size_t length,
5548c2ecf20Sopenharmony_ci					    loff_t *offset)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf =	file->private_data;
5578c2ecf20Sopenharmony_ci	struct rt2x00_dev *rt2x00dev = intf->rt2x00dev;
5588c2ecf20Sopenharmony_ci	static unsigned long last_reset = INITIAL_JIFFIES;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	if (!rt2x00_has_cap_restart_hw(rt2x00dev))
5618c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (time_before(jiffies, last_reset + msecs_to_jiffies(2000)))
5648c2ecf20Sopenharmony_ci		return -EBUSY;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	last_reset = jiffies;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	ieee80211_restart_hw(rt2x00dev->hw);
5698c2ecf20Sopenharmony_ci	return length;
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic const struct file_operations rt2x00debug_restart_hw = {
5738c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
5748c2ecf20Sopenharmony_ci	.write = rt2x00debug_write_restart_hw,
5758c2ecf20Sopenharmony_ci	.open = simple_open,
5768c2ecf20Sopenharmony_ci	.llseek = generic_file_llseek,
5778c2ecf20Sopenharmony_ci};
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic void rt2x00debug_create_file_driver(const char *name,
5808c2ecf20Sopenharmony_ci					   struct rt2x00debug_intf *intf,
5818c2ecf20Sopenharmony_ci					   struct debugfs_blob_wrapper *blob)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	char *data;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	data = kzalloc(3 * MAX_LINE_LENGTH, GFP_KERNEL);
5868c2ecf20Sopenharmony_ci	if (!data)
5878c2ecf20Sopenharmony_ci		return;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	blob->data = data;
5908c2ecf20Sopenharmony_ci	data += sprintf(data, "driver:\t%s\n", intf->rt2x00dev->ops->name);
5918c2ecf20Sopenharmony_ci	data += sprintf(data, "version:\t%s\n", DRV_VERSION);
5928c2ecf20Sopenharmony_ci	blob->size = strlen(blob->data);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	debugfs_create_blob(name, 0400, intf->driver_folder, blob);
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cistatic void rt2x00debug_create_file_chipset(const char *name,
5988c2ecf20Sopenharmony_ci					    struct rt2x00debug_intf *intf,
5998c2ecf20Sopenharmony_ci					    struct debugfs_blob_wrapper *blob)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	const struct rt2x00debug *debug = intf->debug;
6028c2ecf20Sopenharmony_ci	char *data;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	data = kzalloc(9 * MAX_LINE_LENGTH, GFP_KERNEL);
6058c2ecf20Sopenharmony_ci	if (!data)
6068c2ecf20Sopenharmony_ci		return;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	blob->data = data;
6098c2ecf20Sopenharmony_ci	data += sprintf(data, "rt chip:\t%04x\n", intf->rt2x00dev->chip.rt);
6108c2ecf20Sopenharmony_ci	data += sprintf(data, "rf chip:\t%04x\n", intf->rt2x00dev->chip.rf);
6118c2ecf20Sopenharmony_ci	data += sprintf(data, "revision:\t%04x\n", intf->rt2x00dev->chip.rev);
6128c2ecf20Sopenharmony_ci	data += sprintf(data, "\n");
6138c2ecf20Sopenharmony_ci	data += sprintf(data, "register\tbase\twords\twordsize\n");
6148c2ecf20Sopenharmony_ci#define RT2X00DEBUGFS_SPRINTF_REGISTER(__name)			\
6158c2ecf20Sopenharmony_ci{								\
6168c2ecf20Sopenharmony_ci	if (debug->__name.read)					\
6178c2ecf20Sopenharmony_ci		data += sprintf(data, __stringify(__name)	\
6188c2ecf20Sopenharmony_ci				"\t%d\t%d\t%d\n",		\
6198c2ecf20Sopenharmony_ci				debug->__name.word_base,	\
6208c2ecf20Sopenharmony_ci				debug->__name.word_count,	\
6218c2ecf20Sopenharmony_ci				debug->__name.word_size);	\
6228c2ecf20Sopenharmony_ci}
6238c2ecf20Sopenharmony_ci	RT2X00DEBUGFS_SPRINTF_REGISTER(csr);
6248c2ecf20Sopenharmony_ci	RT2X00DEBUGFS_SPRINTF_REGISTER(eeprom);
6258c2ecf20Sopenharmony_ci	RT2X00DEBUGFS_SPRINTF_REGISTER(bbp);
6268c2ecf20Sopenharmony_ci	RT2X00DEBUGFS_SPRINTF_REGISTER(rf);
6278c2ecf20Sopenharmony_ci	RT2X00DEBUGFS_SPRINTF_REGISTER(rfcsr);
6288c2ecf20Sopenharmony_ci#undef RT2X00DEBUGFS_SPRINTF_REGISTER
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	blob->size = strlen(blob->data);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	debugfs_create_blob(name, 0400, intf->driver_folder, blob);
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_civoid rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	const struct rt2x00debug *debug = rt2x00dev->ops->debugfs;
6388c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf;
6398c2ecf20Sopenharmony_ci	struct dentry *queue_folder;
6408c2ecf20Sopenharmony_ci	struct dentry *register_folder;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	intf = kzalloc(sizeof(struct rt2x00debug_intf), GFP_KERNEL);
6438c2ecf20Sopenharmony_ci	if (!intf) {
6448c2ecf20Sopenharmony_ci		rt2x00_err(rt2x00dev, "Failed to allocate debug handler\n");
6458c2ecf20Sopenharmony_ci		return;
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	intf->debug = debug;
6498c2ecf20Sopenharmony_ci	intf->rt2x00dev = rt2x00dev;
6508c2ecf20Sopenharmony_ci	rt2x00dev->debugfs_intf = intf;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	intf->driver_folder =
6538c2ecf20Sopenharmony_ci	    debugfs_create_dir(intf->rt2x00dev->ops->name,
6548c2ecf20Sopenharmony_ci			       rt2x00dev->hw->wiphy->debugfsdir);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	rt2x00debug_create_file_driver("driver", intf, &intf->driver_blob);
6578c2ecf20Sopenharmony_ci	rt2x00debug_create_file_chipset("chipset", intf, &intf->chipset_blob);
6588c2ecf20Sopenharmony_ci	debugfs_create_file("dev_flags", 0400, intf->driver_folder, intf,
6598c2ecf20Sopenharmony_ci			    &rt2x00debug_fop_dev_flags);
6608c2ecf20Sopenharmony_ci	debugfs_create_file("cap_flags", 0400, intf->driver_folder, intf,
6618c2ecf20Sopenharmony_ci			    &rt2x00debug_fop_cap_flags);
6628c2ecf20Sopenharmony_ci	debugfs_create_file("restart_hw", 0200, intf->driver_folder, intf,
6638c2ecf20Sopenharmony_ci			    &rt2x00debug_restart_hw);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	register_folder = debugfs_create_dir("register", intf->driver_folder);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci#define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name)		\
6688c2ecf20Sopenharmony_ci({									\
6698c2ecf20Sopenharmony_ci	if (debug->__name.read) {					\
6708c2ecf20Sopenharmony_ci		debugfs_create_u32(__stringify(__name) "_offset", 0600,	\
6718c2ecf20Sopenharmony_ci				   register_folder,			\
6728c2ecf20Sopenharmony_ci				   &(__intf)->offset_##__name);		\
6738c2ecf20Sopenharmony_ci									\
6748c2ecf20Sopenharmony_ci		debugfs_create_file(__stringify(__name) "_value", 0600,	\
6758c2ecf20Sopenharmony_ci				    register_folder, (__intf),		\
6768c2ecf20Sopenharmony_ci				    &rt2x00debug_fop_##__name);		\
6778c2ecf20Sopenharmony_ci	}								\
6788c2ecf20Sopenharmony_ci})
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, csr);
6818c2ecf20Sopenharmony_ci	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, eeprom);
6828c2ecf20Sopenharmony_ci	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, bbp);
6838c2ecf20Sopenharmony_ci	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rf);
6848c2ecf20Sopenharmony_ci	RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rfcsr);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci#undef RT2X00DEBUGFS_CREATE_REGISTER_ENTRY
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	queue_folder = debugfs_create_dir("queue", intf->driver_folder);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	debugfs_create_file("dump", 0400, queue_folder, intf,
6918c2ecf20Sopenharmony_ci			    &rt2x00debug_fop_queue_dump);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	skb_queue_head_init(&intf->frame_dump_skbqueue);
6948c2ecf20Sopenharmony_ci	init_waitqueue_head(&intf->frame_dump_waitqueue);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	debugfs_create_file("queue", 0400, queue_folder, intf,
6978c2ecf20Sopenharmony_ci			    &rt2x00debug_fop_queue_stats);
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci#ifdef CONFIG_RT2X00_LIB_CRYPTO
7008c2ecf20Sopenharmony_ci	if (rt2x00_has_cap_hw_crypto(rt2x00dev))
7018c2ecf20Sopenharmony_ci		debugfs_create_file("crypto", 0444, queue_folder, intf,
7028c2ecf20Sopenharmony_ci				    &rt2x00debug_fop_crypto_stats);
7038c2ecf20Sopenharmony_ci#endif
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	return;
7068c2ecf20Sopenharmony_ci}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_civoid rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev)
7098c2ecf20Sopenharmony_ci{
7108c2ecf20Sopenharmony_ci	struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	if (unlikely(!intf))
7138c2ecf20Sopenharmony_ci		return;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	skb_queue_purge(&intf->frame_dump_skbqueue);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	debugfs_remove_recursive(intf->driver_folder);
7188c2ecf20Sopenharmony_ci	kfree(intf->chipset_blob.data);
7198c2ecf20Sopenharmony_ci	kfree(intf->driver_blob.data);
7208c2ecf20Sopenharmony_ci	kfree(intf);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	rt2x00dev->debugfs_intf = NULL;
7238c2ecf20Sopenharmony_ci}
724