summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Ortiz <sameo@linux.intel.com>2009-09-01 15:14:06 +0200
committerJohn W. Linville <linville@tuxdriver.com>2009-09-01 12:48:28 -0400
commit04e715cd46ba523806070fbf9ded009f10e107cd (patch)
treeed6f3130398a7d6e8b64d39ae87c241652348ed1
parentd210176eaaed0c7883caba52665bcfb5d420c660 (diff)
iwmc3200wifi: Add a last_fw_err debugfs entry
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>
-rw-r--r--drivers/net/wireless/iwmc3200wifi/debug.h2
-rw-r--r--drivers/net/wireless/iwmc3200wifi/debugfs.c105
-rw-r--r--drivers/net/wireless/iwmc3200wifi/iwm.h2
-rw-r--r--drivers/net/wireless/iwmc3200wifi/main.c6
-rw-r--r--drivers/net/wireless/iwmc3200wifi/rx.c2
5 files changed, 113 insertions, 4 deletions
diff --git a/drivers/net/wireless/iwmc3200wifi/debug.h b/drivers/net/wireless/iwmc3200wifi/debug.h
index 8fbb42d9c21f..e35c9b693d1f 100644
--- a/drivers/net/wireless/iwmc3200wifi/debug.h
+++ b/drivers/net/wireless/iwmc3200wifi/debug.h
@@ -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
diff --git a/drivers/net/wireless/iwmc3200wifi/debugfs.c b/drivers/net/wireless/iwmc3200wifi/debugfs.c
index 0fa7b9150d58..1465379f900a 100644
--- a/drivers/net/wireless/iwmc3200wifi/debugfs.c
+++ b/drivers/net/wireless/iwmc3200wifi/debugfs.c
@@ -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);
diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index f5c2d6f3fd43..1b02a4e2a1ac 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -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)));
};
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index 6a5b76acb645..d668e4756324 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -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);
}
/*
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
index 14950b147f91..40dbcbc16593 100644
--- a/drivers/net/wireless/iwmc3200wifi/rx.c
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -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));