aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 b5880eba06e..87caa94fd81 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 f1d55313617..d303265f163 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 2f0fb6a5bfd..101f7e0f632 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 f3d332d11f8..c9a1fa52327 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 5cac95d9480..1f7037292c1 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 aa79b437e60..b5a7b30afda 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 f5c2c9e6f84..cf40ac93cea 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 beed621a8ae..cfb3588a4dd 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 6926d0a3e5c..a3734bdf511 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 9357695340c..0450fb49dbb 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 75fabf83649..c88e3fa1d60 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 754a16ce5bc..d7db6e77047 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