From 04e715cd46ba523806070fbf9ded009f10e107cd Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 1 Sep 2009 15:14:06 +0200 Subject: 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 Signed-off-by: John W. Linville --- drivers/net/wireless/iwmc3200wifi/debug.h | 2 + drivers/net/wireless/iwmc3200wifi/debugfs.c | 105 ++++++++++++++++++++++++++-- drivers/net/wireless/iwmc3200wifi/iwm.h | 2 + drivers/net/wireless/iwmc3200wifi/main.c | 6 ++ drivers/net/wireless/iwmc3200wifi/rx.c | 2 + 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)); -- cgit v1.2.3