iwmc3200wifi: Add a last_fw_err debugfs entry
authorSamuel Ortiz <sameo@linux.intel.com>
Tue, 1 Sep 2009 13:14:06 +0000 (15:14 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 1 Sep 2009 16:48:28 +0000 (12:48 -0400)
In order to check what was the last fw error we got accross resets, we add
this debugfs entry. It displays the complete ASSERT information.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/iwmc3200wifi/debug.h
drivers/net/wireless/iwmc3200wifi/debugfs.c
drivers/net/wireless/iwmc3200wifi/iwm.h
drivers/net/wireless/iwmc3200wifi/main.c
drivers/net/wireless/iwmc3200wifi/rx.c

index 8fbb42d9c21f71c66df11ac1afcfe2afa77a31b6..e35c9b693d1fde1a06a5dcf4eaa3aa7c7ac016b2 100644 (file)
@@ -108,6 +108,8 @@ struct iwm_debugfs {
        struct dentry *txq_dentry;
        struct dentry *tx_credit_dentry;
        struct dentry *rx_ticket_dentry;
+
+       struct dentry *fw_err_dentry;
 };
 
 #ifdef CONFIG_IWM_DEBUG
index 0fa7b9150d5839bc1411ecbc3a74e85298a7c662..1465379f900a1e46334c333ceae118b87500fb60 100644 (file)
@@ -98,7 +98,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_modules,
                        iwm_debugfs_u32_read, iwm_debugfs_dbg_modules_write,
                        "%llu\n");
 
-static int iwm_txrx_open(struct inode *inode, struct file *filp)
+static int iwm_generic_open(struct inode *inode, struct file *filp)
 {
        filp->private_data = inode->i_private;
        return 0;
@@ -289,25 +289,111 @@ static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp,
        return ret;
 }
 
+static ssize_t iwm_debugfs_fw_err_read(struct file *filp,
+                                      char __user *buffer,
+                                      size_t count, loff_t *ppos)
+{
+
+       struct iwm_priv *iwm = filp->private_data;
+       char buf[512];
+       int buf_len = 512;
+       size_t len = 0;
+
+       if (*ppos != 0)
+               return 0;
+       if (count < sizeof(buf))
+               return -ENOSPC;
+
+       if (!iwm->last_fw_err)
+               return -ENOMEM;
+
+       if (iwm->last_fw_err->line_num == 0)
+               goto out;
+
+       len += snprintf(buf + len, buf_len - len, "%cMAC FW ERROR:\n",
+            (le32_to_cpu(iwm->last_fw_err->category) == UMAC_SYS_ERR_CAT_LMAC)
+                       ? 'L' : 'U');
+       len += snprintf(buf + len, buf_len - len,
+                       "\tCategory:    %d\n",
+                       le32_to_cpu(iwm->last_fw_err->category));
+
+       len += snprintf(buf + len, buf_len - len,
+                       "\tStatus:      0x%x\n",
+                       le32_to_cpu(iwm->last_fw_err->status));
+
+       len += snprintf(buf + len, buf_len - len,
+                       "\tPC:          0x%x\n",
+                       le32_to_cpu(iwm->last_fw_err->pc));
+
+       len += snprintf(buf + len, buf_len - len,
+                       "\tblink1:      %d\n",
+                       le32_to_cpu(iwm->last_fw_err->blink1));
+
+       len += snprintf(buf + len, buf_len - len,
+                       "\tblink2:      %d\n",
+                       le32_to_cpu(iwm->last_fw_err->blink2));
+
+       len += snprintf(buf + len, buf_len - len,
+                       "\tilink1:      %d\n",
+                       le32_to_cpu(iwm->last_fw_err->ilink1));
+
+       len += snprintf(buf + len, buf_len - len,
+                       "\tilink2:      %d\n",
+                       le32_to_cpu(iwm->last_fw_err->ilink2));
+
+       len += snprintf(buf + len, buf_len - len,
+                       "\tData1:       0x%x\n",
+                       le32_to_cpu(iwm->last_fw_err->data1));
+
+       len += snprintf(buf + len, buf_len - len,
+                       "\tData2:       0x%x\n",
+                       le32_to_cpu(iwm->last_fw_err->data2));
+
+       len += snprintf(buf + len, buf_len - len,
+                       "\tLine number: %d\n",
+                       le32_to_cpu(iwm->last_fw_err->line_num));
+
+       len += snprintf(buf + len, buf_len - len,
+                       "\tUMAC status: 0x%x\n",
+                       le32_to_cpu(iwm->last_fw_err->umac_status));
+
+       len += snprintf(buf + len, buf_len - len,
+                       "\tLMAC status: 0x%x\n",
+                       le32_to_cpu(iwm->last_fw_err->lmac_status));
+
+       len += snprintf(buf + len, buf_len - len,
+                       "\tSDIO status: 0x%x\n",
+                       le32_to_cpu(iwm->last_fw_err->sdio_status));
+
+out:
+
+       return simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
+}
 
 static const struct file_operations iwm_debugfs_txq_fops = {
        .owner =        THIS_MODULE,
-       .open =         iwm_txrx_open,
+       .open =         iwm_generic_open,
        .read =         iwm_debugfs_txq_read,
 };
 
 static const struct file_operations iwm_debugfs_tx_credit_fops = {
        .owner =        THIS_MODULE,
-       .open =         iwm_txrx_open,
+       .open =         iwm_generic_open,
        .read =         iwm_debugfs_tx_credit_read,
 };
 
 static const struct file_operations iwm_debugfs_rx_ticket_fops = {
        .owner =        THIS_MODULE,
-       .open =         iwm_txrx_open,
+       .open =         iwm_generic_open,
        .read =         iwm_debugfs_rx_ticket_read,
 };
 
+static const struct file_operations iwm_debugfs_fw_err_fops = {
+       .owner =        THIS_MODULE,
+       .open =         iwm_generic_open,
+       .read =         iwm_debugfs_fw_err_read,
+};
+
 int iwm_debugfs_init(struct iwm_priv *iwm)
 {
        int i, result;
@@ -423,6 +509,16 @@ int iwm_debugfs_init(struct iwm_priv *iwm)
                goto error;
        }
 
+       iwm->dbg.fw_err_dentry = debugfs_create_file("last_fw_err", 0200,
+                                                    iwm->dbg.dbgdir, iwm,
+                                                    &iwm_debugfs_fw_err_fops);
+       result = PTR_ERR(iwm->dbg.fw_err_dentry);
+       if (IS_ERR(iwm->dbg.fw_err_dentry) && (result != -ENODEV)) {
+               IWM_ERR(iwm, "Couldn't create last FW err: %d\n", result);
+               goto error;
+       }
+
+
        return 0;
 
  error:
@@ -441,6 +537,7 @@ void iwm_debugfs_exit(struct iwm_priv *iwm)
        debugfs_remove(iwm->dbg.txq_dentry);
        debugfs_remove(iwm->dbg.tx_credit_dentry);
        debugfs_remove(iwm->dbg.rx_ticket_dentry);
+       debugfs_remove(iwm->dbg.fw_err_dentry);
        if (iwm->bus_ops->debugfs_exit)
                iwm->bus_ops->debugfs_exit(iwm);
 
index f5c2d6f3fd43d8efcf103582b02135ab7c005845..1b02a4e2a1aca8d14aa9cbcde2067ff4a207990d 100644 (file)
@@ -289,6 +289,8 @@ struct iwm_priv {
        u8 *resp_ie;
        int resp_ie_len;
 
+       struct iwm_fw_error_hdr *last_fw_err;
+
        char private[0] __attribute__((__aligned__(NETDEV_ALIGN)));
 };
 
index 6a5b76acb64547b0cca0854ee80421958160d73f..d668e47563245dff549e2f89d6a43ab168e83a3b 100644 (file)
@@ -260,6 +260,11 @@ int iwm_priv_init(struct iwm_priv *iwm)
        iwm->watchdog.data = (unsigned long)iwm;
        mutex_init(&iwm->mutex);
 
+       iwm->last_fw_err = kzalloc(sizeof(struct iwm_fw_error_hdr),
+                                  GFP_KERNEL);
+       if (iwm->last_fw_err == NULL)
+               return -ENOMEM;
+
        return 0;
 }
 
@@ -271,6 +276,7 @@ void iwm_priv_deinit(struct iwm_priv *iwm)
                destroy_workqueue(iwm->txq[i].wq);
 
        destroy_workqueue(iwm->rx_wq);
+       kfree(iwm->last_fw_err);
 }
 
 /*
index 14950b147f9138a4054cc4a6d638eeb2f704837d..40dbcbc16593b163e36cd7f3031cbb64187d904e 100644 (file)
@@ -102,6 +102,8 @@ static int iwm_ntf_error(struct iwm_priv *iwm, u8 *buf,
        error = (struct iwm_umac_notif_error *)buf;
        fw_err = &error->err;
 
+       memcpy(iwm->last_fw_err, fw_err, sizeof(struct iwm_fw_error_hdr));
+
        IWM_ERR(iwm, "%cMAC FW ERROR:\n",
         (le32_to_cpu(fw_err->category) == UMAC_SYS_ERR_CAT_LMAC) ? 'L' : 'U');
        IWM_ERR(iwm, "\tCategory:    %d\n", le32_to_cpu(fw_err->category));