Skip to content

Commit

Permalink
wl12xx: Support routing FW logs to the host
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Ido Yariv authored and Luciano Coelho committed Jun 27, 2011
1 parent baacb9a commit 95dac04
Show file tree
Hide file tree
Showing 12 changed files with 459 additions and 4 deletions.
1 change: 1 addition & 0 deletions drivers/net/wireless/wl12xx/acx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,7 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl)
mem_conf->tx_free_req = mem->min_req_tx_blocks;
mem_conf->rx_free_req = mem->min_req_rx_blocks;
mem_conf->tx_min = mem->tx_min;
mem_conf->fwlog_blocks = wl->conf.fwlog.mem_blocks;

ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf,
sizeof(*mem_conf));
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/wireless/wl12xx/acx.h
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,8 @@ struct wl1271_acx_sta_config_memory {
u8 tx_free_req;
u8 rx_free_req;
u8 tx_min;
u8 fwlog_blocks;
u8 padding[3];
} __packed;

struct wl1271_acx_mem_map {
Expand Down
9 changes: 9 additions & 0 deletions drivers/net/wireless/wl12xx/boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,15 @@ static unsigned int wl12xx_get_fw_ver_quirks(struct wl1271 *wl)
(fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_AP_MIN))))
quirks |= WL12XX_QUIRK_USE_2_SPARE_BLOCKS;

/* Only new station firmwares support routing fw logs to the host */
if ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
(fw_ver[FW_VER_MINOR] < FW_VER_MINOR_FWLOG_STA_MIN))
quirks |= WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED;

/* This feature is not yet supported for AP mode */
if (fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP)
quirks |= WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED;

return quirks;
}

Expand Down
84 changes: 84 additions & 0 deletions drivers/net/wireless/wl12xx/cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1234,3 +1234,87 @@ int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid)
out:
return ret;
}

int wl12xx_cmd_config_fwlog(struct wl1271 *wl)
{
struct wl12xx_cmd_config_fwlog *cmd;
int ret = 0;

wl1271_debug(DEBUG_CMD, "cmd config firmware logger");

cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}

cmd->logger_mode = wl->conf.fwlog.mode;
cmd->log_severity = wl->conf.fwlog.severity;
cmd->timestamp = wl->conf.fwlog.timestamp;
cmd->output = wl->conf.fwlog.output;
cmd->threshold = wl->conf.fwlog.threshold;

ret = wl1271_cmd_send(wl, CMD_CONFIG_FWLOGGER, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("failed to send config firmware logger command");
goto out_free;
}

out_free:
kfree(cmd);

out:
return ret;
}

int wl12xx_cmd_start_fwlog(struct wl1271 *wl)
{
struct wl12xx_cmd_start_fwlog *cmd;
int ret = 0;

wl1271_debug(DEBUG_CMD, "cmd start firmware logger");

cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}

ret = wl1271_cmd_send(wl, CMD_START_FWLOGGER, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("failed to send start firmware logger command");
goto out_free;
}

out_free:
kfree(cmd);

out:
return ret;
}

int wl12xx_cmd_stop_fwlog(struct wl1271 *wl)
{
struct wl12xx_cmd_stop_fwlog *cmd;
int ret = 0;

wl1271_debug(DEBUG_CMD, "cmd stop firmware logger");

cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}

ret = wl1271_cmd_send(wl, CMD_STOP_FWLOGGER, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("failed to send stop firmware logger command");
goto out_free;
}

out_free:
kfree(cmd);

out:
return ret;
}
62 changes: 62 additions & 0 deletions drivers/net/wireless/wl12xx/cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ int wl1271_cmd_start_bss(struct wl1271 *wl);
int wl1271_cmd_stop_bss(struct wl1271 *wl);
int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid);
int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid);
int wl12xx_cmd_config_fwlog(struct wl1271 *wl);
int wl12xx_cmd_start_fwlog(struct wl1271 *wl);
int wl12xx_cmd_stop_fwlog(struct wl1271 *wl);

enum wl1271_commands {
CMD_INTERROGATE = 1, /*use this to read information elements*/
Expand Down Expand Up @@ -107,6 +110,9 @@ enum wl1271_commands {
CMD_START_PERIODIC_SCAN = 50,
CMD_STOP_PERIODIC_SCAN = 51,
CMD_SET_STA_STATE = 52,
CMD_CONFIG_FWLOGGER = 53,
CMD_START_FWLOGGER = 54,
CMD_STOP_FWLOGGER = 55,

/* AP mode commands */
CMD_BSS_START = 60,
Expand Down Expand Up @@ -575,4 +581,60 @@ struct wl1271_cmd_remove_sta {
u8 padding1;
} __packed;

/*
* Continuous mode - packets are transferred to the host periodically
* via the data path.
* On demand - Log messages are stored in a cyclic buffer in the
* firmware, and only transferred to the host when explicitly requested
*/
enum wl12xx_fwlogger_log_mode {
WL12XX_FWLOG_CONTINUOUS,
WL12XX_FWLOG_ON_DEMAND
};

/* Include/exclude timestamps from the log messages */
enum wl12xx_fwlogger_timestamp {
WL12XX_FWLOG_TIMESTAMP_DISABLED,
WL12XX_FWLOG_TIMESTAMP_ENABLED
};

/*
* Logs can be routed to the debug pinouts (where available), to the host bus
* (SDIO/SPI), or dropped
*/
enum wl12xx_fwlogger_output {
WL12XX_FWLOG_OUTPUT_NONE,
WL12XX_FWLOG_OUTPUT_DBG_PINS,
WL12XX_FWLOG_OUTPUT_HOST,
};

struct wl12xx_cmd_config_fwlog {
struct wl1271_cmd_header header;

/* See enum wl12xx_fwlogger_log_mode */
u8 logger_mode;

/* Minimum log level threshold */
u8 log_severity;

/* Include/exclude timestamps from the log messages */
u8 timestamp;

/* See enum wl1271_fwlogger_output */
u8 output;

/* Regulates the frequency of log messages */
u8 threshold;

u8 padding[3];
} __packed;

struct wl12xx_cmd_start_fwlog {
struct wl1271_cmd_header header;
} __packed;

struct wl12xx_cmd_stop_fwlog {
struct wl1271_cmd_header header;
} __packed;

#endif /* __WL1271_CMD_H__ */
25 changes: 25 additions & 0 deletions drivers/net/wireless/wl12xx/conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1277,6 +1277,30 @@ struct conf_rx_streaming_settings {
u8 always;
};

struct conf_fwlog {
/* Continuous or on-demand */
u8 mode;

/*
* Number of memory blocks dedicated for the FW logger
*
* Range: 1-3, or 0 to disable the FW logger
*/
u8 mem_blocks;

/* Minimum log level threshold */
u8 severity;

/* Include/exclude timestamps from the log messages */
u8 timestamp;

/* See enum wl1271_fwlogger_output */
u8 output;

/* Regulates the frequency of log messages */
u8 threshold;
};

struct conf_drv_settings {
struct conf_sg_settings sg;
struct conf_rx_settings rx;
Expand All @@ -1293,6 +1317,7 @@ struct conf_drv_settings {
struct conf_memory_settings mem_wl128x;
struct conf_fm_coex fm_coex;
struct conf_rx_streaming_settings rx_streaming;
struct conf_fwlog fwlog;
u8 hci_io_ds;
};

Expand Down
19 changes: 19 additions & 0 deletions drivers/net/wireless/wl12xx/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,20 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl)
return 0;
}

static int wl12xx_init_fwlog(struct wl1271 *wl)
{
int ret;

if (wl->quirks & WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED)
return 0;

ret = wl12xx_cmd_config_fwlog(wl);
if (ret < 0)
return ret;

return 0;
}

static int wl1271_sta_hw_init(struct wl1271 *wl)
{
int ret;
Expand Down Expand Up @@ -382,6 +396,11 @@ static int wl1271_sta_hw_init(struct wl1271 *wl)
if (ret < 0)
return ret;

/* Configure the FW logger */
ret = wl12xx_init_fwlog(wl);
if (ret < 0)
return ret;

return 0;
}

Expand Down
14 changes: 14 additions & 0 deletions drivers/net/wireless/wl12xx/io.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ static inline void wl1271_write(struct wl1271 *wl, int addr, void *buf,
wl1271_raw_write(wl, physical, buf, len, fixed);
}

static inline void wl1271_read_hwaddr(struct wl1271 *wl, int hwaddr,
void *buf, size_t len, bool fixed)
{
int physical;
int addr;

/* Addresses are stored internally as addresses to 32 bytes blocks */
addr = hwaddr << 5;

physical = wl1271_translate_addr(wl, addr);

wl1271_raw_read(wl, physical, buf, len, fixed);
}

static inline u32 wl1271_read32(struct wl1271 *wl, int addr)
{
return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr));
Expand Down
Loading

0 comments on commit 95dac04

Please sign in to comment.