aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIdo Yariv <ido@wizery.com>2011-06-06 07:57:06 -0400
committerLuciano Coelho <coelho@ti.com>2011-06-27 08:10:56 -0400
commit95dac04f881322b510c45e5ae83f0dbee4f823a2 (patch)
tree6b79761eb78627be7ef14c339a708fdf3b26231a
parentbaacb9aed020b890ddf6a57837a169092a25fc9b (diff)
wl12xx: Support routing FW logs to the host
A recently added feature to the firmware enables the driver to retrieve firmware logs via the host bus (SDIO or SPI). There are two modes of operation: 1. On-demand: The FW collects its log in an internal ring buffer. This buffer can later be read, for example, upon recovery. 2. Continuous: The FW pushes the FW logs as special packets in the RX path. Reading the internal ring buffer does not involve the FW. Thus, as long as the HW is not in ELP, it should be possible to read the logs, even if the FW crashes. A sysfs binary file named "fwlog" was added to support this feature, letting a monitor process read the FW messages. The log is transferred from the FW only when available, so the reading process might block. Signed-off-by: Ido Yariv <ido@wizery.com> Signed-off-by: Luciano Coelho <coelho@ti.com>
-rw-r--r--drivers/net/wireless/wl12xx/acx.c1
-rw-r--r--drivers/net/wireless/wl12xx/acx.h2
-rw-r--r--drivers/net/wireless/wl12xx/boot.c9
-rw-r--r--drivers/net/wireless/wl12xx/cmd.c84
-rw-r--r--drivers/net/wireless/wl12xx/cmd.h62
-rw-r--r--drivers/net/wireless/wl12xx/conf.h25
-rw-r--r--drivers/net/wireless/wl12xx/init.c19
-rw-r--r--drivers/net/wireless/wl12xx/io.h14
-rw-r--r--drivers/net/wireless/wl12xx/main.c207
-rw-r--r--drivers/net/wireless/wl12xx/rx.c8
-rw-r--r--drivers/net/wireless/wl12xx/rx.h12
-rw-r--r--drivers/net/wireless/wl12xx/wl12xx.h20
12 files changed, 459 insertions, 4 deletions
diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c
index b5880eba06e5..87caa94fd815 100644
--- a/drivers/net/wireless/wl12xx/acx.c
+++ b/drivers/net/wireless/wl12xx/acx.c
@@ -1067,6 +1067,7 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl)
1067 mem_conf->tx_free_req = mem->min_req_tx_blocks; 1067 mem_conf->tx_free_req = mem->min_req_tx_blocks;
1068 mem_conf->rx_free_req = mem->min_req_rx_blocks; 1068 mem_conf->rx_free_req = mem->min_req_rx_blocks;
1069 mem_conf->tx_min = mem->tx_min; 1069 mem_conf->tx_min = mem->tx_min;
1070 mem_conf->fwlog_blocks = wl->conf.fwlog.mem_blocks;
1070 1071
1071 ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, 1072 ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf,
1072 sizeof(*mem_conf)); 1073 sizeof(*mem_conf));
diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h
index f1d553136173..d303265f163a 100644
--- a/drivers/net/wireless/wl12xx/acx.h
+++ b/drivers/net/wireless/wl12xx/acx.h
@@ -828,6 +828,8 @@ struct wl1271_acx_sta_config_memory {
828 u8 tx_free_req; 828 u8 tx_free_req;
829 u8 rx_free_req; 829 u8 rx_free_req;
830 u8 tx_min; 830 u8 tx_min;
831 u8 fwlog_blocks;
832 u8 padding[3];
831} __packed; 833} __packed;
832 834
833struct wl1271_acx_mem_map { 835struct wl1271_acx_mem_map {
diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c
index 2f0fb6a5bfdb..101f7e0f6329 100644
--- a/drivers/net/wireless/wl12xx/boot.c
+++ b/drivers/net/wireless/wl12xx/boot.c
@@ -117,6 +117,15 @@ static unsigned int wl12xx_get_fw_ver_quirks(struct wl1271 *wl)
117 (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_AP_MIN)))) 117 (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_AP_MIN))))
118 quirks |= WL12XX_QUIRK_USE_2_SPARE_BLOCKS; 118 quirks |= WL12XX_QUIRK_USE_2_SPARE_BLOCKS;
119 119
120 /* Only new station firmwares support routing fw logs to the host */
121 if ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
122 (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_FWLOG_STA_MIN))
123 quirks |= WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED;
124
125 /* This feature is not yet supported for AP mode */
126 if (fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP)
127 quirks |= WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED;
128
120 return quirks; 129 return quirks;
121} 130}
122 131
diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c
index f3d332d11f81..c9a1fa523274 100644
--- a/drivers/net/wireless/wl12xx/cmd.c
+++ b/drivers/net/wireless/wl12xx/cmd.c
@@ -1234,3 +1234,87 @@ out_free:
1234out: 1234out:
1235 return ret; 1235 return ret;
1236} 1236}
1237
1238int wl12xx_cmd_config_fwlog(struct wl1271 *wl)
1239{
1240 struct wl12xx_cmd_config_fwlog *cmd;
1241 int ret = 0;
1242
1243 wl1271_debug(DEBUG_CMD, "cmd config firmware logger");
1244
1245 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
1246 if (!cmd) {
1247 ret = -ENOMEM;
1248 goto out;
1249 }
1250
1251 cmd->logger_mode = wl->conf.fwlog.mode;
1252 cmd->log_severity = wl->conf.fwlog.severity;
1253 cmd->timestamp = wl->conf.fwlog.timestamp;
1254 cmd->output = wl->conf.fwlog.output;
1255 cmd->threshold = wl->conf.fwlog.threshold;
1256
1257 ret = wl1271_cmd_send(wl, CMD_CONFIG_FWLOGGER, cmd, sizeof(*cmd), 0);
1258 if (ret < 0) {
1259 wl1271_error("failed to send config firmware logger command");
1260 goto out_free;
1261 }
1262
1263out_free:
1264 kfree(cmd);
1265
1266out:
1267 return ret;
1268}
1269
1270int wl12xx_cmd_start_fwlog(struct wl1271 *wl)
1271{
1272 struct wl12xx_cmd_start_fwlog *cmd;
1273 int ret = 0;
1274
1275 wl1271_debug(DEBUG_CMD, "cmd start firmware logger");
1276
1277 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
1278 if (!cmd) {
1279 ret = -ENOMEM;
1280 goto out;
1281 }
1282
1283 ret = wl1271_cmd_send(wl, CMD_START_FWLOGGER, cmd, sizeof(*cmd), 0);
1284 if (ret < 0) {
1285 wl1271_error("failed to send start firmware logger command");
1286 goto out_free;
1287 }
1288
1289out_free:
1290 kfree(cmd);
1291
1292out:
1293 return ret;
1294}
1295
1296int wl12xx_cmd_stop_fwlog(struct wl1271 *wl)
1297{
1298 struct wl12xx_cmd_stop_fwlog *cmd;
1299 int ret = 0;
1300
1301 wl1271_debug(DEBUG_CMD, "cmd stop firmware logger");
1302
1303 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
1304 if (!cmd) {
1305 ret = -ENOMEM;
1306 goto out;
1307 }
1308
1309 ret = wl1271_cmd_send(wl, CMD_STOP_FWLOGGER, cmd, sizeof(*cmd), 0);
1310 if (ret < 0) {
1311 wl1271_error("failed to send stop firmware logger command");
1312 goto out_free;
1313 }
1314
1315out_free:
1316 kfree(cmd);
1317
1318out:
1319 return ret;
1320}
diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h
index 5cac95d9480c..1f7037292c15 100644
--- a/drivers/net/wireless/wl12xx/cmd.h
+++ b/drivers/net/wireless/wl12xx/cmd.h
@@ -70,6 +70,9 @@ int wl1271_cmd_start_bss(struct wl1271 *wl);
70int wl1271_cmd_stop_bss(struct wl1271 *wl); 70int wl1271_cmd_stop_bss(struct wl1271 *wl);
71int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid); 71int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid);
72int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid); 72int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid);
73int wl12xx_cmd_config_fwlog(struct wl1271 *wl);
74int wl12xx_cmd_start_fwlog(struct wl1271 *wl);
75int wl12xx_cmd_stop_fwlog(struct wl1271 *wl);
73 76
74enum wl1271_commands { 77enum wl1271_commands {
75 CMD_INTERROGATE = 1, /*use this to read information elements*/ 78 CMD_INTERROGATE = 1, /*use this to read information elements*/
@@ -107,6 +110,9 @@ enum wl1271_commands {
107 CMD_START_PERIODIC_SCAN = 50, 110 CMD_START_PERIODIC_SCAN = 50,
108 CMD_STOP_PERIODIC_SCAN = 51, 111 CMD_STOP_PERIODIC_SCAN = 51,
109 CMD_SET_STA_STATE = 52, 112 CMD_SET_STA_STATE = 52,
113 CMD_CONFIG_FWLOGGER = 53,
114 CMD_START_FWLOGGER = 54,
115 CMD_STOP_FWLOGGER = 55,
110 116
111 /* AP mode commands */ 117 /* AP mode commands */
112 CMD_BSS_START = 60, 118 CMD_BSS_START = 60,
@@ -575,4 +581,60 @@ struct wl1271_cmd_remove_sta {
575 u8 padding1; 581 u8 padding1;
576} __packed; 582} __packed;
577 583
584/*
585 * Continuous mode - packets are transferred to the host periodically
586 * via the data path.
587 * On demand - Log messages are stored in a cyclic buffer in the
588 * firmware, and only transferred to the host when explicitly requested
589 */
590enum wl12xx_fwlogger_log_mode {
591 WL12XX_FWLOG_CONTINUOUS,
592 WL12XX_FWLOG_ON_DEMAND
593};
594
595/* Include/exclude timestamps from the log messages */
596enum wl12xx_fwlogger_timestamp {
597 WL12XX_FWLOG_TIMESTAMP_DISABLED,
598 WL12XX_FWLOG_TIMESTAMP_ENABLED
599};
600
601/*
602 * Logs can be routed to the debug pinouts (where available), to the host bus
603 * (SDIO/SPI), or dropped
604 */
605enum wl12xx_fwlogger_output {
606 WL12XX_FWLOG_OUTPUT_NONE,
607 WL12XX_FWLOG_OUTPUT_DBG_PINS,
608 WL12XX_FWLOG_OUTPUT_HOST,
609};
610
611struct wl12xx_cmd_config_fwlog {
612 struct wl1271_cmd_header header;
613
614 /* See enum wl12xx_fwlogger_log_mode */
615 u8 logger_mode;
616
617 /* Minimum log level threshold */
618 u8 log_severity;
619
620 /* Include/exclude timestamps from the log messages */
621 u8 timestamp;
622
623 /* See enum wl1271_fwlogger_output */
624 u8 output;
625
626 /* Regulates the frequency of log messages */
627 u8 threshold;
628
629 u8 padding[3];
630} __packed;
631
632struct wl12xx_cmd_start_fwlog {
633 struct wl1271_cmd_header header;
634} __packed;
635
636struct wl12xx_cmd_stop_fwlog {
637 struct wl1271_cmd_header header;
638} __packed;
639
578#endif /* __WL1271_CMD_H__ */ 640#endif /* __WL1271_CMD_H__ */
diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h
index aa79b437e60e..b5a7b30afda3 100644
--- a/drivers/net/wireless/wl12xx/conf.h
+++ b/drivers/net/wireless/wl12xx/conf.h
@@ -1277,6 +1277,30 @@ struct conf_rx_streaming_settings {
1277 u8 always; 1277 u8 always;
1278}; 1278};
1279 1279
1280struct conf_fwlog {
1281 /* Continuous or on-demand */
1282 u8 mode;
1283
1284 /*
1285 * Number of memory blocks dedicated for the FW logger
1286 *
1287 * Range: 1-3, or 0 to disable the FW logger
1288 */
1289 u8 mem_blocks;
1290
1291 /* Minimum log level threshold */
1292 u8 severity;
1293
1294 /* Include/exclude timestamps from the log messages */
1295 u8 timestamp;
1296
1297 /* See enum wl1271_fwlogger_output */
1298 u8 output;
1299
1300 /* Regulates the frequency of log messages */
1301 u8 threshold;
1302};
1303
1280struct conf_drv_settings { 1304struct conf_drv_settings {
1281 struct conf_sg_settings sg; 1305 struct conf_sg_settings sg;
1282 struct conf_rx_settings rx; 1306 struct conf_rx_settings rx;
@@ -1293,6 +1317,7 @@ struct conf_drv_settings {
1293 struct conf_memory_settings mem_wl128x; 1317 struct conf_memory_settings mem_wl128x;
1294 struct conf_fm_coex fm_coex; 1318 struct conf_fm_coex fm_coex;
1295 struct conf_rx_streaming_settings rx_streaming; 1319 struct conf_rx_streaming_settings rx_streaming;
1320 struct conf_fwlog fwlog;
1296 u8 hci_io_ds; 1321 u8 hci_io_ds;
1297}; 1322};
1298 1323
diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c
index f5c2c9e6f84b..cf40ac93cead 100644
--- a/drivers/net/wireless/wl12xx/init.c
+++ b/drivers/net/wireless/wl12xx/init.c
@@ -321,6 +321,20 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl)
321 return 0; 321 return 0;
322} 322}
323 323
324static int wl12xx_init_fwlog(struct wl1271 *wl)
325{
326 int ret;
327
328 if (wl->quirks & WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED)
329 return 0;
330
331 ret = wl12xx_cmd_config_fwlog(wl);
332 if (ret < 0)
333 return ret;
334
335 return 0;
336}
337
324static int wl1271_sta_hw_init(struct wl1271 *wl) 338static int wl1271_sta_hw_init(struct wl1271 *wl)
325{ 339{
326 int ret; 340 int ret;
@@ -382,6 +396,11 @@ static int wl1271_sta_hw_init(struct wl1271 *wl)
382 if (ret < 0) 396 if (ret < 0)
383 return ret; 397 return ret;
384 398
399 /* Configure the FW logger */
400 ret = wl12xx_init_fwlog(wl);
401 if (ret < 0)
402 return ret;
403
385 return 0; 404 return 0;
386} 405}
387 406
diff --git a/drivers/net/wireless/wl12xx/io.h b/drivers/net/wireless/wl12xx/io.h
index beed621a8ae0..cfb3588a4ddf 100644
--- a/drivers/net/wireless/wl12xx/io.h
+++ b/drivers/net/wireless/wl12xx/io.h
@@ -128,6 +128,20 @@ static inline void wl1271_write(struct wl1271 *wl, int addr, void *buf,
128 wl1271_raw_write(wl, physical, buf, len, fixed); 128 wl1271_raw_write(wl, physical, buf, len, fixed);
129} 129}
130 130
131static inline void wl1271_read_hwaddr(struct wl1271 *wl, int hwaddr,
132 void *buf, size_t len, bool fixed)
133{
134 int physical;
135 int addr;
136
137 /* Addresses are stored internally as addresses to 32 bytes blocks */
138 addr = hwaddr << 5;
139
140 physical = wl1271_translate_addr(wl, addr);
141
142 wl1271_raw_read(wl, physical, buf, len, fixed);
143}
144
131static inline u32 wl1271_read32(struct wl1271 *wl, int addr) 145static inline u32 wl1271_read32(struct wl1271 *wl, int addr)
132{ 146{
133 return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr)); 147 return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr));
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 6926d0a3e5c6..a3734bdf5119 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -31,6 +31,7 @@
31#include <linux/platform_device.h> 31#include <linux/platform_device.h>
32#include <linux/slab.h> 32#include <linux/slab.h>
33#include <linux/wl12xx.h> 33#include <linux/wl12xx.h>
34#include <linux/sched.h>
34 35
35#include "wl12xx.h" 36#include "wl12xx.h"
36#include "wl12xx_80211.h" 37#include "wl12xx_80211.h"
@@ -368,9 +369,19 @@ static struct conf_drv_settings default_conf = {
368 .interval = 20, 369 .interval = 20,
369 .always = 0, 370 .always = 0,
370 }, 371 },
372 .fwlog = {
373 .mode = WL12XX_FWLOG_ON_DEMAND,
374 .mem_blocks = 2,
375 .severity = 0,
376 .timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED,
377 .output = WL12XX_FWLOG_OUTPUT_HOST,
378 .threshold = 0,
379 },
371 .hci_io_ds = HCI_IO_DS_6MA, 380 .hci_io_ds = HCI_IO_DS_6MA,
372}; 381};
373 382
383static char *fwlog_param;
384
374static void __wl1271_op_remove_interface(struct wl1271 *wl, 385static void __wl1271_op_remove_interface(struct wl1271 *wl,
375 bool reset_tx_queues); 386 bool reset_tx_queues);
376static void wl1271_free_ap_keys(struct wl1271 *wl); 387static void wl1271_free_ap_keys(struct wl1271 *wl);
@@ -617,8 +628,24 @@ static void wl1271_conf_init(struct wl1271 *wl)
617 628
618 /* apply driver default configuration */ 629 /* apply driver default configuration */
619 memcpy(&wl->conf, &default_conf, sizeof(default_conf)); 630 memcpy(&wl->conf, &default_conf, sizeof(default_conf));
620}
621 631
632 /* Adjust settings according to optional module parameters */
633 if (fwlog_param) {
634 if (!strcmp(fwlog_param, "continuous")) {
635 wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
636 } else if (!strcmp(fwlog_param, "ondemand")) {
637 wl->conf.fwlog.mode = WL12XX_FWLOG_ON_DEMAND;
638 } else if (!strcmp(fwlog_param, "dbgpins")) {
639 wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
640 wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_DBG_PINS;
641 } else if (!strcmp(fwlog_param, "disable")) {
642 wl->conf.fwlog.mem_blocks = 0;
643 wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_NONE;
644 } else {
645 wl1271_error("Unknown fwlog parameter %s", fwlog_param);
646 }
647 }
648}
622 649
623static int wl1271_plt_init(struct wl1271 *wl) 650static int wl1271_plt_init(struct wl1271 *wl)
624{ 651{
@@ -1105,6 +1132,83 @@ void wl12xx_queue_recovery_work(struct wl1271 *wl)
1105 ieee80211_queue_work(wl->hw, &wl->recovery_work); 1132 ieee80211_queue_work(wl->hw, &wl->recovery_work);
1106} 1133}
1107 1134
1135size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
1136{
1137 size_t len = 0;
1138
1139 /* The FW log is a length-value list, find where the log end */
1140 while (len < maxlen) {
1141 if (memblock[len] == 0)
1142 break;
1143 if (len + memblock[len] + 1 > maxlen)
1144 break;
1145 len += memblock[len] + 1;
1146 }
1147
1148 /* Make sure we have enough room */
1149 len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size));
1150
1151 /* Fill the FW log file, consumed by the sysfs fwlog entry */
1152 memcpy(wl->fwlog + wl->fwlog_size, memblock, len);
1153 wl->fwlog_size += len;
1154
1155 return len;
1156}
1157
1158static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
1159{
1160 u32 addr;
1161 u32 first_addr;
1162 u8 *block;
1163
1164 if ((wl->quirks & WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED) ||
1165 (wl->conf.fwlog.mode != WL12XX_FWLOG_ON_DEMAND) ||
1166 (wl->conf.fwlog.mem_blocks == 0))
1167 return;
1168
1169 wl1271_info("Reading FW panic log");
1170
1171 block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL);
1172 if (!block)
1173 return;
1174
1175 /*
1176 * Make sure the chip is awake and the logger isn't active.
1177 * This might fail if the firmware hanged.
1178 */
1179 if (!wl1271_ps_elp_wakeup(wl))
1180 wl12xx_cmd_stop_fwlog(wl);
1181
1182 /* Read the first memory block address */
1183 wl1271_fw_status(wl, wl->fw_status);
1184 first_addr = __le32_to_cpu(wl->fw_status->sta.log_start_addr);
1185 if (!first_addr)
1186 goto out;
1187
1188 /* Traverse the memory blocks linked list */
1189 addr = first_addr;
1190 do {
1191 memset(block, 0, WL12XX_HW_BLOCK_SIZE);
1192 wl1271_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
1193 false);
1194
1195 /*
1196 * Memory blocks are linked to one another. The first 4 bytes
1197 * of each memory block hold the hardware address of the next
1198 * one. The last memory block points to the first one.
1199 */
1200 addr = __le32_to_cpup((__le32 *)block);
1201 if (!wl12xx_copy_fwlog(wl, block + sizeof(addr),
1202 WL12XX_HW_BLOCK_SIZE - sizeof(addr)))
1203 break;
1204 } while (addr && (addr != first_addr));
1205
1206 wake_up_interruptible(&wl->fwlog_waitq);
1207
1208out:
1209 kfree(block);
1210}
1211
1108static void wl1271_recovery_work(struct work_struct *work) 1212static void wl1271_recovery_work(struct work_struct *work)
1109{ 1213{
1110 struct wl1271 *wl = 1214 struct wl1271 *wl =
@@ -1118,6 +1222,8 @@ static void wl1271_recovery_work(struct work_struct *work)
1118 /* Avoid a recursive recovery */ 1222 /* Avoid a recursive recovery */
1119 set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags); 1223 set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
1120 1224
1225 wl12xx_read_fwlog_panic(wl);
1226
1121 wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x", 1227 wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x",
1122 wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4)); 1228 wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4));
1123 1229
@@ -3942,6 +4048,69 @@ static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
3942static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR, 4048static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR,
3943 wl1271_sysfs_show_hw_pg_ver, NULL); 4049 wl1271_sysfs_show_hw_pg_ver, NULL);
3944 4050
4051static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
4052 struct bin_attribute *bin_attr,
4053 char *buffer, loff_t pos, size_t count)
4054{
4055 struct device *dev = container_of(kobj, struct device, kobj);
4056 struct wl1271 *wl = dev_get_drvdata(dev);
4057 ssize_t len;
4058 int ret;
4059
4060 ret = mutex_lock_interruptible(&wl->mutex);
4061 if (ret < 0)
4062 return -ERESTARTSYS;
4063
4064 /* Let only one thread read the log at a time, blocking others */
4065 while (wl->fwlog_size == 0) {
4066 DEFINE_WAIT(wait);
4067
4068 prepare_to_wait_exclusive(&wl->fwlog_waitq,
4069 &wait,
4070 TASK_INTERRUPTIBLE);
4071
4072 if (wl->fwlog_size != 0) {
4073 finish_wait(&wl->fwlog_waitq, &wait);
4074 break;
4075 }
4076
4077 mutex_unlock(&wl->mutex);
4078
4079 schedule();
4080 finish_wait(&wl->fwlog_waitq, &wait);
4081
4082 if (signal_pending(current))
4083 return -ERESTARTSYS;
4084
4085 ret = mutex_lock_interruptible(&wl->mutex);
4086 if (ret < 0)
4087 return -ERESTARTSYS;
4088 }
4089
4090 /* Check if the fwlog is still valid */
4091 if (wl->fwlog_size < 0) {
4092 mutex_unlock(&wl->mutex);
4093 return 0;
4094 }
4095
4096 /* Seeking is not supported - old logs are not kept. Disregard pos. */
4097 len = min(count, (size_t)wl->fwlog_size);
4098 wl->fwlog_size -= len;
4099 memcpy(buffer, wl->fwlog, len);
4100
4101 /* Make room for new messages */
4102 memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
4103
4104 mutex_unlock(&wl->mutex);
4105
4106 return len;
4107}
4108
4109static struct bin_attribute fwlog_attr = {
4110 .attr = {.name = "fwlog", .mode = S_IRUSR},
4111 .read = wl1271_sysfs_read_fwlog,
4112};
4113
3945int wl1271_register_hw(struct wl1271 *wl) 4114int wl1271_register_hw(struct wl1271 *wl)
3946{ 4115{
3947 int ret; 4116 int ret;
@@ -4160,6 +4329,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
4160 wl->sched_scanning = false; 4329 wl->sched_scanning = false;
4161 setup_timer(&wl->rx_streaming_timer, wl1271_rx_streaming_timer, 4330 setup_timer(&wl->rx_streaming_timer, wl1271_rx_streaming_timer,
4162 (unsigned long) wl); 4331 (unsigned long) wl);
4332 wl->fwlog_size = 0;
4333 init_waitqueue_head(&wl->fwlog_waitq);
4163 4334
4164 memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); 4335 memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
4165 for (i = 0; i < ACX_TX_DESCRIPTORS; i++) 4336 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
@@ -4186,11 +4357,18 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
4186 goto err_aggr; 4357 goto err_aggr;
4187 } 4358 }
4188 4359
4360 /* Allocate one page for the FW log */
4361 wl->fwlog = (u8 *)get_zeroed_page(GFP_KERNEL);
4362 if (!wl->fwlog) {
4363 ret = -ENOMEM;
4364 goto err_dummy_packet;
4365 }
4366
4189 /* Register platform device */ 4367 /* Register platform device */
4190 ret = platform_device_register(wl->plat_dev); 4368 ret = platform_device_register(wl->plat_dev);
4191 if (ret) { 4369 if (ret) {
4192 wl1271_error("couldn't register platform device"); 4370 wl1271_error("couldn't register platform device");
4193 goto err_dummy_packet; 4371 goto err_fwlog;
4194 } 4372 }
4195 dev_set_drvdata(&wl->plat_dev->dev, wl); 4373 dev_set_drvdata(&wl->plat_dev->dev, wl);
4196 4374
@@ -4208,14 +4386,27 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
4208 goto err_bt_coex_state; 4386 goto err_bt_coex_state;
4209 } 4387 }
4210 4388
4389 /* Create sysfs file for the FW log */
4390 ret = device_create_bin_file(&wl->plat_dev->dev, &fwlog_attr);
4391 if (ret < 0) {
4392 wl1271_error("failed to create sysfs file fwlog");
4393 goto err_hw_pg_ver;
4394 }
4395
4211 return hw; 4396 return hw;
4212 4397
4398err_hw_pg_ver:
4399 device_remove_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
4400
4213err_bt_coex_state: 4401err_bt_coex_state:
4214 device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state); 4402 device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
4215 4403
4216err_platform: 4404err_platform:
4217 platform_device_unregister(wl->plat_dev); 4405 platform_device_unregister(wl->plat_dev);
4218 4406
4407err_fwlog:
4408 free_page((unsigned long)wl->fwlog);
4409
4219err_dummy_packet: 4410err_dummy_packet:
4220 dev_kfree_skb(wl->dummy_packet); 4411 dev_kfree_skb(wl->dummy_packet);
4221 4412
@@ -4240,7 +4431,15 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
4240 4431
4241int wl1271_free_hw(struct wl1271 *wl) 4432int wl1271_free_hw(struct wl1271 *wl)
4242{ 4433{
4434 /* Unblock any fwlog readers */
4435 mutex_lock(&wl->mutex);
4436 wl->fwlog_size = -1;
4437 wake_up_interruptible_all(&wl->fwlog_waitq);
4438 mutex_unlock(&wl->mutex);
4439
4440 device_remove_bin_file(&wl->plat_dev->dev, &fwlog_attr);
4243 platform_device_unregister(wl->plat_dev); 4441 platform_device_unregister(wl->plat_dev);
4442 free_page((unsigned long)wl->fwlog);
4244 dev_kfree_skb(wl->dummy_packet); 4443 dev_kfree_skb(wl->dummy_packet);
4245 free_pages((unsigned long)wl->aggr_buf, 4444 free_pages((unsigned long)wl->aggr_buf,
4246 get_order(WL1271_AGGR_BUFFER_SIZE)); 4445 get_order(WL1271_AGGR_BUFFER_SIZE));
@@ -4268,6 +4467,10 @@ EXPORT_SYMBOL_GPL(wl12xx_debug_level);
4268module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR); 4467module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
4269MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); 4468MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
4270 4469
4470module_param_named(fwlog, fwlog_param, charp, 0);
4471MODULE_PARM_DESC(keymap,
4472 "FW logger options: continuous, ondemand, dbgpins or disable");
4473
4271MODULE_LICENSE("GPL"); 4474MODULE_LICENSE("GPL");
4272MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); 4475MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
4273MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); 4476MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c
index 9357695340cf..0450fb49dbb1 100644
--- a/drivers/net/wireless/wl12xx/rx.c
+++ b/drivers/net/wireless/wl12xx/rx.c
@@ -22,6 +22,7 @@
22 */ 22 */
23 23
24#include <linux/gfp.h> 24#include <linux/gfp.h>
25#include <linux/sched.h>
25 26
26#include "wl12xx.h" 27#include "wl12xx.h"
27#include "acx.h" 28#include "acx.h"
@@ -107,6 +108,13 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length)
107 /* the data read starts with the descriptor */ 108 /* the data read starts with the descriptor */
108 desc = (struct wl1271_rx_descriptor *) data; 109 desc = (struct wl1271_rx_descriptor *) data;
109 110
111 if (desc->packet_class == WL12XX_RX_CLASS_LOGGER) {
112 size_t len = length - sizeof(*desc);
113 wl12xx_copy_fwlog(wl, data + sizeof(*desc), len);
114 wake_up_interruptible(&wl->fwlog_waitq);
115 return 0;
116 }
117
110 switch (desc->status & WL1271_RX_DESC_STATUS_MASK) { 118 switch (desc->status & WL1271_RX_DESC_STATUS_MASK) {
111 /* discard corrupted packets */ 119 /* discard corrupted packets */
112 case WL1271_RX_DESC_DRIVER_RX_Q_FAIL: 120 case WL1271_RX_DESC_DRIVER_RX_Q_FAIL:
diff --git a/drivers/net/wireless/wl12xx/rx.h b/drivers/net/wireless/wl12xx/rx.h
index 75fabf836491..c88e3fa1d603 100644
--- a/drivers/net/wireless/wl12xx/rx.h
+++ b/drivers/net/wireless/wl12xx/rx.h
@@ -97,6 +97,18 @@
97#define RX_BUF_SIZE_MASK 0xFFF00 97#define RX_BUF_SIZE_MASK 0xFFF00
98#define RX_BUF_SIZE_SHIFT_DIV 6 98#define RX_BUF_SIZE_SHIFT_DIV 6
99 99
100enum {
101 WL12XX_RX_CLASS_UNKNOWN,
102 WL12XX_RX_CLASS_MANAGEMENT,
103 WL12XX_RX_CLASS_DATA,
104 WL12XX_RX_CLASS_QOS_DATA,
105 WL12XX_RX_CLASS_BCN_PRBRSP,
106 WL12XX_RX_CLASS_EAPOL,
107 WL12XX_RX_CLASS_BA_EVENT,
108 WL12XX_RX_CLASS_AMSDU,
109 WL12XX_RX_CLASS_LOGGER,
110};
111
100struct wl1271_rx_descriptor { 112struct wl1271_rx_descriptor {
101 __le16 length; 113 __le16 length;
102 u8 status; 114 u8 status;
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index 754a16ce5bc0..d7db6e77047a 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -226,6 +226,8 @@ enum {
226#define FW_VER_MINOR_1_SPARE_STA_MIN 58 226#define FW_VER_MINOR_1_SPARE_STA_MIN 58
227#define FW_VER_MINOR_1_SPARE_AP_MIN 47 227#define FW_VER_MINOR_1_SPARE_AP_MIN 47
228 228
229#define FW_VER_MINOR_FWLOG_STA_MIN 70
230
229struct wl1271_chip { 231struct wl1271_chip {
230 u32 id; 232 u32 id;
231 char fw_ver_str[ETHTOOL_BUSINFO_LEN]; 233 char fw_ver_str[ETHTOOL_BUSINFO_LEN];
@@ -284,8 +286,7 @@ struct wl1271_fw_sta_status {
284 u8 tx_total; 286 u8 tx_total;
285 u8 reserved1; 287 u8 reserved1;
286 __le16 reserved2; 288 __le16 reserved2;
287 /* Total structure size is 68 bytes */ 289 __le32 log_start_addr;
288 u32 padding;
289} __packed; 290} __packed;
290 291
291struct wl1271_fw_full_status { 292struct wl1271_fw_full_status {
@@ -472,6 +473,15 @@ struct wl1271 {
472 /* Network stack work */ 473 /* Network stack work */
473 struct work_struct netstack_work; 474 struct work_struct netstack_work;
474 475
476 /* FW log buffer */
477 u8 *fwlog;
478
479 /* Number of valid bytes in the FW log buffer */
480 ssize_t fwlog_size;
481
482 /* Sysfs FW log entry readers wait queue */
483 wait_queue_head_t fwlog_waitq;
484
475 /* Hardware recovery work */ 485 /* Hardware recovery work */
476 struct work_struct recovery_work; 486 struct work_struct recovery_work;
477 487
@@ -614,6 +624,7 @@ int wl1271_plt_start(struct wl1271 *wl);
614int wl1271_plt_stop(struct wl1271 *wl); 624int wl1271_plt_stop(struct wl1271 *wl);
615int wl1271_recalc_rx_streaming(struct wl1271 *wl); 625int wl1271_recalc_rx_streaming(struct wl1271 *wl);
616void wl12xx_queue_recovery_work(struct wl1271 *wl); 626void wl12xx_queue_recovery_work(struct wl1271 *wl);
627size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen);
617 628
618#define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */ 629#define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */
619 630
@@ -655,4 +666,9 @@ void wl12xx_queue_recovery_work(struct wl1271 *wl);
655 */ 666 */
656#define WL12XX_QUIRK_LPD_MODE BIT(3) 667#define WL12XX_QUIRK_LPD_MODE BIT(3)
657 668
669/* Older firmwares did not implement the FW logger over bus feature */
670#define WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED BIT(4)
671
672#define WL12XX_HW_BLOCK_SIZE 256
673
658#endif 674#endif