---help---
          Enable creation of debugfs files for the iwlwifi drivers.
 
+config IWLWIFI_DEVICE_TRACING
+       bool "iwlwifi device access tracing"
+       depends on IWLWIFI
+       depends on EVENT_TRACING
+       help
+         Say Y here to trace all commands, including TX frames and IO
+         accesses, sent to the device. If you say yes, iwlwifi will
+         register with the ftrace framework for event tracing and dump
+         all this information to the ringbuffer, you may need to
+         increase the ringbuffer size. See the ftrace documentation
+         for more information.
+
+         When tracing is not enabled, this option still has some
+         (though rather small) overhead.
+
+         If unsure, say Y so we can help you better when problems
+         occur.
+
 config IWLAGN
        tristate "Intel Wireless WiFi Next Gen AGN (iwlagn)"
        depends on IWLWIFI
 
 iwlcore-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o
 iwlcore-$(CONFIG_IWLWIFI_LEDS) += iwl-led.o
 iwlcore-$(CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT) += iwl-spectrum.o
+iwlcore-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
 
+CFLAGS_iwl-devtrace.o := -I$(src)
+
+# AGN
 obj-$(CONFIG_IWLAGN)   += iwlagn.o
 iwlagn-objs            := iwl-agn.o iwl-agn-rs.o
 
 iwlagn-$(CONFIG_IWL5000) += iwl-6000.o
 iwlagn-$(CONFIG_IWL5000) += iwl-1000.o
 
+# 3945
 obj-$(CONFIG_IWL3945)  += iwl3945.o
 iwl3945-objs           := iwl3945-base.o iwl-3945.o iwl-3945-rs.o iwl-3945-led.o
-
-
 
                                 PCI_DMA_FROMDEVICE);
                pkt = (struct iwl_rx_packet *)rxb->skb->data;
 
+               trace_iwlwifi_dev_rx(priv, pkt,
+                       le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
+
                /* Reclaim a command buffer only if this packet is a response
                 *   to a (driver-originated) command.
                 * If the packet (e.g. Rx frame) originated from uCode,
        line = iwl_read_targ_mem(priv, base + 9 * sizeof(u32));
        time = iwl_read_targ_mem(priv, base + 11 * sizeof(u32));
 
+       trace_iwlwifi_dev_ucode_error(priv, desc, time, data1, data2, line,
+                                     blink1, blink2, ilink1, ilink2);
+
        IWL_ERR(priv, "Desc                               Time       "
                "data1      data2      line\n");
        IWL_ERR(priv, "%-28s (#%02d) %010u 0x%08X 0x%08X %u\n",
                ptr += sizeof(u32);
                if (mode == 0) {
                        /* data, ev */
+                       trace_iwlwifi_dev_ucode_event(priv, 0, time, ev);
                        IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev);
                } else {
                        data = iwl_read_targ_mem(priv, ptr);
                        ptr += sizeof(u32);
                        IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
                                        time, data, ev);
+                       trace_iwlwifi_dev_ucode_event(priv, time, data, ev);
                }
        }
 }
 
--- /dev/null
+#include <linux/module.h>
+
+/* sparse doesn't like tracepoint macros */
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "iwl-devtrace.h"
+
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ioread32);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite32);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_rx);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_error);
+#endif
 
--- /dev/null
+#if !defined(__IWLWIFI_DEVICE_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#define __IWLWIFI_DEVICE_TRACE
+
+#include <linux/tracepoint.h>
+#include "iwl-dev.h"
+
+#if !defined(CONFIG_IWLWIFI_DEVICE_TRACING) || defined(__CHECKER__)
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif
+
+#define PRIV_ENTRY     __field(struct iwl_priv *, priv)
+#define PRIV_ASSIGN    __entry->priv = priv
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi
+
+TRACE_EVENT(iwlwifi_dev_ioread32,
+       TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val),
+       TP_ARGS(priv, offs, val),
+       TP_STRUCT__entry(
+               PRIV_ENTRY
+               __field(u32, offs)
+               __field(u32, val)
+       ),
+       TP_fast_assign(
+               PRIV_ASSIGN;
+               __entry->offs = offs;
+               __entry->val = val;
+       ),
+       TP_printk("[%p] read io[%#x] = %#x", __entry->priv, __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_iowrite32,
+       TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val),
+       TP_ARGS(priv, offs, val),
+       TP_STRUCT__entry(
+               PRIV_ENTRY
+               __field(u32, offs)
+               __field(u32, val)
+       ),
+       TP_fast_assign(
+               PRIV_ASSIGN;
+               __entry->offs = offs;
+               __entry->val = val;
+       ),
+       TP_printk("[%p] write io[%#x] = %#x)", __entry->priv, __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_hcmd,
+       TP_PROTO(struct iwl_priv *priv, void *hcmd, size_t len, u32 flags),
+       TP_ARGS(priv, hcmd, len, flags),
+       TP_STRUCT__entry(
+               PRIV_ENTRY
+               __dynamic_array(u8, hcmd, len)
+               __field(u32, flags)
+       ),
+       TP_fast_assign(
+               PRIV_ASSIGN;
+               memcpy(__get_dynamic_array(hcmd), hcmd, len);
+               __entry->flags = flags;
+       ),
+       TP_printk("[%p] hcmd %#.2x (%ssync)",
+                 __entry->priv, ((u8 *)__get_dynamic_array(hcmd))[0],
+                 __entry->flags & CMD_ASYNC ? "a" : "")
+);
+
+TRACE_EVENT(iwlwifi_dev_rx,
+       TP_PROTO(struct iwl_priv *priv, void *rxbuf, size_t len),
+       TP_ARGS(priv, rxbuf, len),
+       TP_STRUCT__entry(
+               PRIV_ENTRY
+               __dynamic_array(u8, rxbuf, len)
+       ),
+       TP_fast_assign(
+               PRIV_ASSIGN;
+               memcpy(__get_dynamic_array(rxbuf), rxbuf, len);
+       ),
+       TP_printk("[%p] RX cmd %#.2x",
+                 __entry->priv, ((u8 *)__get_dynamic_array(rxbuf))[4])
+);
+
+TRACE_EVENT(iwlwifi_dev_tx,
+       TP_PROTO(struct iwl_priv *priv, void *tfd, size_t tfdlen,
+                void *buf0, size_t buf0_len,
+                void *buf1, size_t buf1_len),
+       TP_ARGS(priv, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len),
+       TP_STRUCT__entry(
+               PRIV_ENTRY
+
+               __field(size_t, framelen)
+               __dynamic_array(u8, tfd, tfdlen)
+
+               /*
+                * Do not insert between or below these items,
+                * we want to keep the frame together (except
+                * for the possible padding).
+                */
+               __dynamic_array(u8, buf0, buf0_len)
+               __dynamic_array(u8, buf1, buf1_len)
+       ),
+       TP_fast_assign(
+               PRIV_ASSIGN;
+               __entry->framelen = buf0_len + buf1_len;
+               memcpy(__get_dynamic_array(tfd), tfd, tfdlen);
+               memcpy(__get_dynamic_array(buf0), buf0, buf0_len);
+               memcpy(__get_dynamic_array(buf1), buf1, buf0_len);
+       ),
+       TP_printk("[%p] TX %.2x (%zu bytes)",
+                 __entry->priv,
+                 ((u8 *)__get_dynamic_array(buf0))[0],
+                 __entry->framelen)
+);
+
+TRACE_EVENT(iwlwifi_dev_ucode_error,
+       TP_PROTO(struct iwl_priv *priv, u32 desc, u32 time,
+                u32 data1, u32 data2, u32 line, u32 blink1,
+                u32 blink2, u32 ilink1, u32 ilink2),
+       TP_ARGS(priv, desc, time, data1, data2, line,
+               blink1, blink2, ilink1, ilink2),
+       TP_STRUCT__entry(
+               PRIV_ENTRY
+               __field(u32, desc)
+               __field(u32, time)
+               __field(u32, data1)
+               __field(u32, data2)
+               __field(u32, line)
+               __field(u32, blink1)
+               __field(u32, blink2)
+               __field(u32, ilink1)
+               __field(u32, ilink2)
+       ),
+       TP_fast_assign(
+               PRIV_ASSIGN;
+               __entry->desc = desc;
+               __entry->time = time;
+               __entry->data1 = data1;
+               __entry->data2 = data2;
+               __entry->line = line;
+               __entry->blink1 = blink1;
+               __entry->blink2 = blink2;
+               __entry->ilink1 = ilink1;
+               __entry->ilink2 = ilink2;
+       ),
+       TP_printk("[%p] #%02d %010u data 0x%08X 0x%08X line %u, "
+                 "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X",
+                 __entry->priv, __entry->desc, __entry->time, __entry->data1,
+                 __entry->data2, __entry->line, __entry->blink1,
+                 __entry->blink2, __entry->ilink1, __entry->ilink2)
+);
+
+TRACE_EVENT(iwlwifi_dev_ucode_event,
+       TP_PROTO(struct iwl_priv *priv, u32 time, u32 data, u32 ev),
+       TP_ARGS(priv, time, data, ev),
+       TP_STRUCT__entry(
+               PRIV_ENTRY
+
+               __field(u32, time)
+               __field(u32, data)
+               __field(u32, ev)
+       ),
+       TP_fast_assign(
+               PRIV_ASSIGN;
+               __entry->time = time;
+               __entry->data = data;
+               __entry->ev = ev;
+       ),
+       TP_printk("[%p] EVT_LOGT:%010u:0x%08x:%04u",
+                 __entry->priv, __entry->time, __entry->data, __entry->ev)
+);
+#endif /* __IWLWIFI_DEVICE_TRACE */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE iwl-devtrace
+#include <trace/define_trace.h>
 
 #ifndef __iwl_eeprom_h__
 #define __iwl_eeprom_h__
 
+#include <net/mac80211.h>
+
 struct iwl_priv;
 
 /*
 
 #include <linux/io.h>
 
 #include "iwl-debug.h"
+#include "iwl-devtrace.h"
 
 /*
  * IO, register, and NIC memory access functions
  *
  */
 
-#define _iwl_write32(priv, ofs, val) iowrite32((val), (priv)->hw_base + (ofs))
+static inline void _iwl_write32(struct iwl_priv *priv, u32 ofs, u32 val)
+{
+       trace_iwlwifi_dev_iowrite32(priv, ofs, val);
+       iowrite32(val, priv->hw_base + ofs);
+}
+
 #ifdef CONFIG_IWLWIFI_DEBUG
 static inline void __iwl_write32(const char *f, u32 l, struct iwl_priv *priv,
                                 u32 ofs, u32 val)
 #define iwl_write32(priv, ofs, val) _iwl_write32(priv, ofs, val)
 #endif
 
-#define _iwl_read32(priv, ofs) ioread32((priv)->hw_base + (ofs))
+static inline u32 _iwl_read32(struct iwl_priv *priv, u32 ofs)
+{
+       u32 val = ioread32(priv->hw_base + ofs);
+       trace_iwlwifi_dev_ioread32(priv, ofs, val);
+       return val;
+}
+
 #ifdef CONFIG_IWLWIFI_DEBUG
 static inline u32 __iwl_read32(char *f, u32 l, struct iwl_priv *priv, u32 ofs)
 {
 
        dma_addr_t phys_addr;
        dma_addr_t txcmd_phys;
        dma_addr_t scratch_phys;
-       u16 len, len_org;
+       u16 len, len_org, firstlen, secondlen;
        u16 seq_number = 0;
        __le16 fc;
        u8 hdr_len;
                sizeof(struct iwl_cmd_header) + hdr_len;
 
        len_org = len;
-       len = (len + 3) & ~3;
+       firstlen = len = (len + 3) & ~3;
 
        if (len_org != len)
                len_org = 1;
 
        /* Set up TFD's 2nd entry to point directly to remainder of skb,
         * if any (802.11 null frames have no payload). */
-       len = skb->len - hdr_len;
+       secondlen = len = skb->len - hdr_len;
        if (len) {
                phys_addr = pci_map_single(priv->pci_dev, skb->data + hdr_len,
                                           len, PCI_DMA_TODEVICE);
        pci_dma_sync_single_for_device(priv->pci_dev, txcmd_phys,
                                       len, PCI_DMA_BIDIRECTIONAL);
 
+       trace_iwlwifi_dev_tx(priv,
+                            &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr],
+                            sizeof(struct iwl_tfd),
+                            &out_cmd->hdr, firstlen,
+                            skb->data + hdr_len, secondlen);
+
        /* Tell device the write index *just past* this latest filled TFD */
        q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
        ret = iwl_txq_update_write_ptr(priv, txq);
        pci_unmap_addr_set(out_meta, mapping, phys_addr);
        pci_unmap_len_set(out_meta, len, fix_size);
 
+       trace_iwlwifi_dev_hcmd(priv, &out_cmd->hdr, fix_size, cmd->flags);
+
        priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq,
                                                   phys_addr, fix_size, 1,
                                                   U32_PAD(cmd->len));
 
                                PCI_DMA_FROMDEVICE);
                pkt = (struct iwl_rx_packet *)rxb->skb->data;
 
+               trace_iwlwifi_dev_rx(priv, pkt,
+                       le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
+
                /* Reclaim a command buffer only if this packet is a response
                 *   to a (driver-originated) command.
                 * If the packet (e.g. Rx frame) originated from uCode,
                        "%-13s (#%d) %010u 0x%05X 0x%05X 0x%05X 0x%05X %u\n\n",
                        desc_lookup(desc), desc, time, blink1, blink2,
                        ilink1, ilink2, data1);
+               trace_iwlwifi_dev_ucode_error(priv, desc, time, data1, 0,
+                                       0, blink1, blink2, ilink1, ilink2);
        }
-
 }
 
 #define EVENT_START_OFFSET  (6 * sizeof(u32))
                if (mode == 0) {
                        /* data, ev */
                        IWL_ERR(priv, "0x%08x\t%04u\n", time, ev);
+                       trace_iwlwifi_dev_ucode_event(priv, 0, time, ev);
                } else {
                        data = iwl_read_targ_mem(priv, ptr);
                        ptr += sizeof(u32);
                        IWL_ERR(priv, "%010u\t0x%08x\t%04u\n", time, data, ev);
+                       trace_iwlwifi_dev_ucode_event(priv, time, data, ev);
                }
        }
 }