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(×tamp); 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