aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Ketrenos <jketreno@linux.intel.com>2005-08-12 10:36:32 -0400
committerJames Ketrenos <jketreno@linux.intel.com>2005-11-07 18:50:55 -0500
commitb39860c60b135ef16c1c16a3e2a757ff8070c5ad (patch)
treedde06d69fb792a0993e3a756d35a0cc9509388cf
parente666619e232308c8ee2aba6b87f28ad26b38d905 (diff)
Switched firmware error dumping so that it will capture a log available
via sysfs even if debugging disabled. When a firmware error is captured, it will be dumped to the kernel log (if debug enabled) and captured in memory to be retrieved via sysfs. If an error has already been captured, subsequent errors will be dropped. The existing error can be cleared by writing to the error log entry. Signed-off-by: James Ketrenos <jketreno@linux.intel.com>
-rw-r--r--drivers/net/wireless/ipw2200.c251
-rw-r--r--drivers/net/wireless/ipw2200.h30
2 files changed, 204 insertions, 77 deletions
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c
index 6e862c2905de..a3283caaa872 100644
--- a/drivers/net/wireless/ipw2200.c
+++ b/drivers/net/wireless/ipw2200.c
@@ -428,6 +428,7 @@ static inline void ipw_disable_interrupts(struct ipw_priv *priv)
428 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL); 428 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
429} 429}
430 430
431#ifdef CONFIG_IPW_DEBUG
431static char *ipw_error_desc(u32 val) 432static char *ipw_error_desc(u32 val)
432{ 433{
433 switch (val) { 434 switch (val) {
@@ -466,56 +467,35 @@ static char *ipw_error_desc(u32 val)
466 } 467 }
467} 468}
468 469
469static void ipw_dump_nic_error_log(struct ipw_priv *priv) 470static void ipw_dump_error_log(struct ipw_priv *priv,
471 struct ipw_fw_error *error)
470{ 472{
471 u32 desc, time, blink1, blink2, ilink1, ilink2, idata, i, count, base; 473 u32 i;
472
473 base = ipw_read32(priv, IPWSTATUS_ERROR_LOG);
474 count = ipw_read_reg32(priv, base);
475 474
476 if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { 475 if (!error) {
477 IPW_ERROR("Start IPW Error Log Dump:\n"); 476 IPW_ERROR("Error allocating and capturing error log. "
478 IPW_ERROR("Status: 0x%08X, Config: %08X\n", 477 "Nothing to dump.\n");
479 priv->status, priv->config); 478 return;
480 } 479 }
481 480
482 for (i = ERROR_START_OFFSET; 481 IPW_ERROR("Start IPW Error Log Dump:\n");
483 i <= count * ERROR_ELEM_SIZE; i += ERROR_ELEM_SIZE) { 482 IPW_ERROR("Status: 0x%08X, Config: %08X\n",
484 desc = ipw_read_reg32(priv, base + i); 483 error->status, error->config);
485 time = ipw_read_reg32(priv, base + i + 1 * sizeof(u32));
486 blink1 = ipw_read_reg32(priv, base + i + 2 * sizeof(u32));
487 blink2 = ipw_read_reg32(priv, base + i + 3 * sizeof(u32));
488 ilink1 = ipw_read_reg32(priv, base + i + 4 * sizeof(u32));
489 ilink2 = ipw_read_reg32(priv, base + i + 5 * sizeof(u32));
490 idata = ipw_read_reg32(priv, base + i + 6 * sizeof(u32));
491 484
485 for (i = 0; i < error->elem_len; i++)
492 IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", 486 IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
493 ipw_error_desc(desc), time, blink1, blink2, 487 ipw_error_desc(error->elem[i].desc),
494 ilink1, ilink2, idata); 488 error->elem[i].time,
495 } 489 error->elem[i].blink1,
490 error->elem[i].blink2,
491 error->elem[i].link1,
492 error->elem[i].link2, error->elem[i].data);
493 for (i = 0; i < error->log_len; i++)
494 IPW_ERROR("%i\t0x%08x\t%i\n",
495 error->log[i].time,
496 error->log[i].event, error->log[i].data);
496} 497}
497
498static void ipw_dump_nic_event_log(struct ipw_priv *priv)
499{
500 u32 ev, time, data, i, count, base;
501
502 base = ipw_read32(priv, IPW_EVENT_LOG);
503 count = ipw_read_reg32(priv, base);
504
505 if (EVENT_START_OFFSET <= count * EVENT_ELEM_SIZE)
506 IPW_ERROR("Start IPW Event Log Dump:\n");
507
508 for (i = EVENT_START_OFFSET;
509 i <= count * EVENT_ELEM_SIZE; i += EVENT_ELEM_SIZE) {
510 ev = ipw_read_reg32(priv, base + i);
511 time = ipw_read_reg32(priv, base + i + 1 * sizeof(u32));
512 data = ipw_read_reg32(priv, base + i + 2 * sizeof(u32));
513
514#ifdef CONFIG_IPW_DEBUG
515 IPW_ERROR("%i\t0x%08x\t%i\n", time, data, ev);
516#endif 498#endif
517 }
518}
519 499
520static inline int ipw_is_init(struct ipw_priv *priv) 500static inline int ipw_is_init(struct ipw_priv *priv)
521{ 501{
@@ -1058,6 +1038,130 @@ static ssize_t store_debug_level(struct device_driver *d, const char *buf,
1058static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, 1038static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
1059 show_debug_level, store_debug_level); 1039 show_debug_level, store_debug_level);
1060 1040
1041static inline u32 ipw_get_event_log_len(struct ipw_priv *priv)
1042{
1043 return ipw_read_reg32(priv, ipw_read32(priv, IPW_EVENT_LOG));
1044}
1045
1046static void ipw_capture_event_log(struct ipw_priv *priv,
1047 u32 log_len, struct ipw_event *log)
1048{
1049 u32 base;
1050
1051 if (log_len) {
1052 base = ipw_read32(priv, IPW_EVENT_LOG);
1053 ipw_read_indirect(priv, base + sizeof(base) + sizeof(u32),
1054 (u8 *) log, sizeof(*log) * log_len);
1055 }
1056}
1057
1058static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv)
1059{
1060 struct ipw_fw_error *error;
1061 u32 log_len = ipw_get_event_log_len(priv);
1062 u32 base = ipw_read32(priv, IPW_ERROR_LOG);
1063 u32 elem_len = ipw_read_reg32(priv, base);
1064
1065 error = kmalloc(sizeof(*error) +
1066 sizeof(*error->elem) * elem_len +
1067 sizeof(*error->log) * log_len, GFP_ATOMIC);
1068 if (!error) {
1069 IPW_ERROR("Memory allocation for firmware error log "
1070 "failed.\n");
1071 return NULL;
1072 }
1073 error->status = priv->status;
1074 error->config = priv->config;
1075 error->elem_len = elem_len;
1076 error->log_len = log_len;
1077 error->elem = (struct ipw_error_elem *)error->payload;
1078 error->log = (struct ipw_event *)(error->elem +
1079 (sizeof(*error->elem) * elem_len));
1080
1081 ipw_capture_event_log(priv, log_len, error->log);
1082
1083 if (elem_len)
1084 ipw_read_indirect(priv, base + sizeof(base), (u8 *) error->elem,
1085 sizeof(*error->elem) * elem_len);
1086
1087 return error;
1088}
1089
1090static void ipw_free_error_log(struct ipw_fw_error *error)
1091{
1092 if (error)
1093 kfree(error);
1094}
1095
1096static ssize_t show_event_log(struct device *d,
1097 struct device_attribute *attr, char *buf)
1098{
1099 struct ipw_priv *priv = dev_get_drvdata(d);
1100 u32 log_len = ipw_get_event_log_len(priv);
1101 struct ipw_event log[log_len];
1102 u32 len = 0, i;
1103
1104 ipw_capture_event_log(priv, log_len, log);
1105
1106 len += snprintf(buf + len, PAGE_SIZE - len, "%08X", log_len);
1107 for (i = 0; i < log_len; i++)
1108 len += snprintf(buf + len, PAGE_SIZE - len,
1109 "\n%08X%08X%08X",
1110 log[i].time, log[i].event, log[i].data);
1111 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1112 return len;
1113}
1114
1115static DEVICE_ATTR(event_log, S_IRUGO, show_event_log, NULL);
1116
1117static ssize_t show_error(struct device *d,
1118 struct device_attribute *attr, char *buf)
1119{
1120 struct ipw_priv *priv = dev_get_drvdata(d);
1121 u32 len = 0, i;
1122 if (!priv->error)
1123 return 0;
1124 len += snprintf(buf + len, PAGE_SIZE - len,
1125 "%08X%08X%08X",
1126 priv->error->status,
1127 priv->error->config, priv->error->elem_len);
1128 for (i = 0; i < priv->error->elem_len; i++)
1129 len += snprintf(buf + len, PAGE_SIZE - len,
1130 "\n%08X%08X%08X%08X%08X%08X%08X",
1131 priv->error->elem[i].time,
1132 priv->error->elem[i].desc,
1133 priv->error->elem[i].blink1,
1134 priv->error->elem[i].blink2,
1135 priv->error->elem[i].link1,
1136 priv->error->elem[i].link2,
1137 priv->error->elem[i].data);
1138
1139 len += snprintf(buf + len, PAGE_SIZE - len,
1140 "\n%08X", priv->error->log_len);
1141 for (i = 0; i < priv->error->log_len; i++)
1142 len += snprintf(buf + len, PAGE_SIZE - len,
1143 "\n%08X%08X%08X",
1144 priv->error->log[i].time,
1145 priv->error->log[i].event,
1146 priv->error->log[i].data);
1147 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1148 return len;
1149}
1150
1151static ssize_t clear_error(struct device *d,
1152 struct device_attribute *attr,
1153 const char *buf, size_t count)
1154{
1155 struct ipw_priv *priv = dev_get_drvdata(d);
1156 if (priv->error) {
1157 ipw_free_error_log(priv->error);
1158 priv->error = NULL;
1159 }
1160 return count;
1161}
1162
1163static DEVICE_ATTR(error, S_IRUGO | S_IWUSR, show_error, clear_error);
1164
1061static ssize_t show_scan_age(struct device *d, struct device_attribute *attr, 1165static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
1062 char *buf) 1166 char *buf)
1063{ 1167{
@@ -1163,34 +1267,6 @@ static ssize_t show_nic_type(struct device *d,
1163 1267
1164static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL); 1268static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL);
1165 1269
1166static ssize_t dump_error_log(struct device *d,
1167 struct device_attribute *attr, const char *buf,
1168 size_t count)
1169{
1170 char *p = (char *)buf;
1171
1172 if (p[0] == '1')
1173 ipw_dump_nic_error_log((struct ipw_priv *)d->driver_data);
1174
1175 return strnlen(buf, count);
1176}
1177
1178static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, dump_error_log);
1179
1180static ssize_t dump_event_log(struct device *d,
1181 struct device_attribute *attr, const char *buf,
1182 size_t count)
1183{
1184 char *p = (char *)buf;
1185
1186 if (p[0] == '1')
1187 ipw_dump_nic_event_log((struct ipw_priv *)d->driver_data);
1188
1189 return strnlen(buf, count);
1190}
1191
1192static DEVICE_ATTR(dump_events, S_IWUSR, NULL, dump_event_log);
1193
1194static ssize_t show_ucode_version(struct device *d, 1270static ssize_t show_ucode_version(struct device *d,
1195 struct device_attribute *attr, char *buf) 1271 struct device_attribute *attr, char *buf)
1196{ 1272{
@@ -1614,12 +1690,30 @@ static void ipw_irq_tasklet(struct ipw_priv *priv)
1614 1690
1615 if (inta & IPW_INTA_BIT_FATAL_ERROR) { 1691 if (inta & IPW_INTA_BIT_FATAL_ERROR) {
1616 IPW_ERROR("Firmware error detected. Restarting.\n"); 1692 IPW_ERROR("Firmware error detected. Restarting.\n");
1693 if (priv->error) {
1694 IPW_ERROR("Sysfs 'error' log already exists.\n");
1617#ifdef CONFIG_IPW_DEBUG 1695#ifdef CONFIG_IPW_DEBUG
1618 if (ipw_debug_level & IPW_DL_FW_ERRORS) { 1696 if (ipw_debug_level & IPW_DL_FW_ERRORS) {
1619 ipw_dump_nic_error_log(priv); 1697 struct ipw_fw_error *error =
1620 ipw_dump_nic_event_log(priv); 1698 ipw_alloc_error_log(priv);
1621 } 1699 ipw_dump_error_log(priv, error);
1700 if (error)
1701 ipw_free_error_log(error);
1702 }
1703#endif
1704 } else {
1705 priv->error = ipw_alloc_error_log(priv);
1706 if (priv->error)
1707 IPW_ERROR("Sysfs 'error' log captured.\n");
1708 else
1709 IPW_ERROR("Error allocating sysfs 'error' "
1710 "log.\n");
1711#ifdef CONFIG_IPW_DEBUG
1712 if (ipw_debug_level & IPW_DL_FW_ERRORS)
1713 ipw_dump_error_log(priv, priv->error);
1622#endif 1714#endif
1715 }
1716
1623 /* XXX: If hardware encryption is for WPA/WPA2, 1717 /* XXX: If hardware encryption is for WPA/WPA2,
1624 * we have to notify the supplicant. */ 1718 * we have to notify the supplicant. */
1625 if (priv->ieee->sec.encrypt) { 1719 if (priv->ieee->sec.encrypt) {
@@ -10958,8 +11052,8 @@ static struct attribute *ipw_sysfs_entries[] = {
10958 &dev_attr_nic_type.attr, 11052 &dev_attr_nic_type.attr,
10959 &dev_attr_status.attr, 11053 &dev_attr_status.attr,
10960 &dev_attr_cfg.attr, 11054 &dev_attr_cfg.attr,
10961 &dev_attr_dump_errors.attr, 11055 &dev_attr_error.attr,
10962 &dev_attr_dump_events.attr, 11056 &dev_attr_event_log.attr,
10963 &dev_attr_eeprom_delay.attr, 11057 &dev_attr_eeprom_delay.attr,
10964 &dev_attr_ucode_version.attr, 11058 &dev_attr_ucode_version.attr,
10965 &dev_attr_rtc.attr, 11059 &dev_attr_rtc.attr,
@@ -11172,6 +11266,11 @@ static void ipw_pci_remove(struct pci_dev *pdev)
11172 } 11266 }
11173 } 11267 }
11174 11268
11269 if (priv->error) {
11270 ipw_free_error_log(priv->error);
11271 priv->error = NULL;
11272 }
11273
11175 free_irq(pdev->irq, priv); 11274 free_irq(pdev->irq, priv);
11176 iounmap(priv->hw_base); 11275 iounmap(priv->hw_base);
11177 pci_release_regions(pdev); 11276 pci_release_regions(pdev);
diff --git a/drivers/net/wireless/ipw2200.h b/drivers/net/wireless/ipw2200.h
index 9bf8aa427d8c..9bf03ac55390 100644
--- a/drivers/net/wireless/ipw2200.h
+++ b/drivers/net/wireless/ipw2200.h
@@ -1085,6 +1085,32 @@ struct ipw_ibss_seq {
1085 struct list_head list; 1085 struct list_head list;
1086}; 1086};
1087 1087
1088struct ipw_error_elem {
1089 u32 desc;
1090 u32 time;
1091 u32 blink1;
1092 u32 blink2;
1093 u32 link1;
1094 u32 link2;
1095 u32 data;
1096};
1097
1098struct ipw_event {
1099 u32 event;
1100 u32 time;
1101 u32 data;
1102} __attribute__ ((packed));
1103
1104struct ipw_fw_error {
1105 u32 status;
1106 u32 config;
1107 u32 elem_len;
1108 u32 log_len;
1109 struct ipw_error_elem *elem;
1110 struct ipw_event *log;
1111 u8 payload[0];
1112} __attribute__ ((packed));
1113
1088struct ipw_priv { 1114struct ipw_priv {
1089 /* ieee device used by generic ieee processing code */ 1115 /* ieee device used by generic ieee processing code */
1090 struct ieee80211_device *ieee; 1116 struct ieee80211_device *ieee;
@@ -1245,6 +1271,8 @@ struct ipw_priv {
1245 u32 pm_state[16]; 1271 u32 pm_state[16];
1246#endif 1272#endif
1247 1273
1274 struct ipw_fw_error *error;
1275
1248 /* network state */ 1276 /* network state */
1249 1277
1250 /* Used to pass the current INTA value from ISR to Tasklet */ 1278 /* Used to pass the current INTA value from ISR to Tasklet */
@@ -1803,7 +1831,7 @@ enum {
1803 IPW_ORD_TABLE_7_LAST 1831 IPW_ORD_TABLE_7_LAST
1804}; 1832};
1805 1833
1806#define IPWSTATUS_ERROR_LOG (IPW_SHARED_LOWER_BOUND + 0x410) 1834#define IPW_ERROR_LOG (IPW_SHARED_LOWER_BOUND + 0x410)
1807#define IPW_EVENT_LOG (IPW_SHARED_LOWER_BOUND + 0x414) 1835#define IPW_EVENT_LOG (IPW_SHARED_LOWER_BOUND + 0x414)
1808#define IPW_ORDINALS_TABLE_LOWER (IPW_SHARED_LOWER_BOUND + 0x500) 1836#define IPW_ORDINALS_TABLE_LOWER (IPW_SHARED_LOWER_BOUND + 0x500)
1809#define IPW_ORDINALS_TABLE_0 (IPW_SHARED_LOWER_BOUND + 0x180) 1837#define IPW_ORDINALS_TABLE_0 (IPW_SHARED_LOWER_BOUND + 0x180)