aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net')
-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)