Commit 333c0dbf authored by John W. Linville's avatar John W. Linville
parents 115f9450 95dac04f
...@@ -11,7 +11,6 @@ config WL12XX ...@@ -11,7 +11,6 @@ config WL12XX
depends on WL12XX_MENU && GENERIC_HARDIRQS depends on WL12XX_MENU && GENERIC_HARDIRQS
depends on INET depends on INET
select FW_LOADER select FW_LOADER
select CRC7
---help--- ---help---
This module adds support for wireless adapters based on TI wl1271 and This module adds support for wireless adapters based on TI wl1271 and
TI wl1273 chipsets. This module does *not* include support for wl1251. TI wl1273 chipsets. This module does *not* include support for wl1251.
...@@ -33,6 +32,7 @@ config WL12XX_HT ...@@ -33,6 +32,7 @@ config WL12XX_HT
config WL12XX_SPI config WL12XX_SPI
tristate "TI wl12xx SPI support" tristate "TI wl12xx SPI support"
depends on WL12XX && SPI_MASTER depends on WL12XX && SPI_MASTER
select CRC7
---help--- ---help---
This module adds support for the SPI interface of adapters using This module adds support for the SPI interface of adapters using
TI wl12xx chipsets. Select this if your platform is using TI wl12xx chipsets. Select this if your platform is using
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/crc7.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -1068,6 +1067,7 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) ...@@ -1068,6 +1067,7 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl)
mem_conf->tx_free_req = mem->min_req_tx_blocks; mem_conf->tx_free_req = mem->min_req_tx_blocks;
mem_conf->rx_free_req = mem->min_req_rx_blocks; mem_conf->rx_free_req = mem->min_req_rx_blocks;
mem_conf->tx_min = mem->tx_min; 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, ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf,
sizeof(*mem_conf)); sizeof(*mem_conf));
...@@ -1577,6 +1577,53 @@ int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime) ...@@ -1577,6 +1577,53 @@ int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime)
return ret; return ret;
} }
int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable)
{
struct wl1271_acx_ps_rx_streaming *rx_streaming;
u32 conf_queues, enable_queues;
int i, ret = 0;
wl1271_debug(DEBUG_ACX, "acx ps rx streaming");
rx_streaming = kzalloc(sizeof(*rx_streaming), GFP_KERNEL);
if (!rx_streaming) {
ret = -ENOMEM;
goto out;
}
conf_queues = wl->conf.rx_streaming.queues;
if (enable)
enable_queues = conf_queues;
else
enable_queues = 0;
for (i = 0; i < 8; i++) {
/*
* Skip non-changed queues, to avoid redundant acxs.
* this check assumes conf.rx_streaming.queues can't
* be changed while rx_streaming is enabled.
*/
if (!(conf_queues & BIT(i)))
continue;
rx_streaming->tid = i;
rx_streaming->enable = enable_queues & BIT(i);
rx_streaming->period = wl->conf.rx_streaming.interval;
rx_streaming->timeout = wl->conf.rx_streaming.interval;
ret = wl1271_cmd_configure(wl, ACX_PS_RX_STREAMING,
rx_streaming,
sizeof(*rx_streaming));
if (ret < 0) {
wl1271_warning("acx ps rx streaming failed: %d", ret);
goto out;
}
}
out:
kfree(rx_streaming);
return ret;
}
int wl1271_acx_max_tx_retry(struct wl1271 *wl) int wl1271_acx_max_tx_retry(struct wl1271 *wl)
{ {
struct wl1271_acx_max_tx_retry *acx = NULL; struct wl1271_acx_max_tx_retry *acx = NULL;
......
...@@ -828,6 +828,8 @@ struct wl1271_acx_sta_config_memory { ...@@ -828,6 +828,8 @@ struct wl1271_acx_sta_config_memory {
u8 tx_free_req; u8 tx_free_req;
u8 rx_free_req; u8 rx_free_req;
u8 tx_min; u8 tx_min;
u8 fwlog_blocks;
u8 padding[3];
} __packed; } __packed;
struct wl1271_acx_mem_map { struct wl1271_acx_mem_map {
...@@ -1153,6 +1155,19 @@ struct wl1271_acx_fw_tsf_information { ...@@ -1153,6 +1155,19 @@ struct wl1271_acx_fw_tsf_information {
u8 padding[3]; u8 padding[3];
} __packed; } __packed;
struct wl1271_acx_ps_rx_streaming {
struct acx_header header;
u8 tid;
u8 enable;
/* interval between triggers (10-100 msec) */
u8 period;
/* timeout before first trigger (0-200 msec) */
u8 timeout;
} __packed;
struct wl1271_acx_max_tx_retry { struct wl1271_acx_max_tx_retry {
struct acx_header header; struct acx_header header;
...@@ -1384,6 +1399,7 @@ int wl1271_acx_set_ba_session(struct wl1271 *wl, ...@@ -1384,6 +1399,7 @@ int wl1271_acx_set_ba_session(struct wl1271 *wl,
int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn,
bool enable); bool enable);
int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable);
int wl1271_acx_max_tx_retry(struct wl1271 *wl); int wl1271_acx_max_tx_retry(struct wl1271 *wl);
int wl1271_acx_config_ps(struct wl1271 *wl); int wl1271_acx_config_ps(struct wl1271 *wl);
int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
......
...@@ -102,6 +102,33 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) ...@@ -102,6 +102,33 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
wl1271_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); wl1271_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl);
} }
static unsigned int wl12xx_get_fw_ver_quirks(struct wl1271 *wl)
{
unsigned int quirks = 0;
unsigned int *fw_ver = wl->chip.fw_ver;
/* Only for wl127x */
if ((fw_ver[FW_VER_CHIP] == FW_VER_CHIP_WL127X) &&
/* Check STA version */
(((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
(fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_STA_MIN)) ||
/* Check AP version */
((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) &&
(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;
}
static void wl1271_parse_fw_ver(struct wl1271 *wl) static void wl1271_parse_fw_ver(struct wl1271 *wl)
{ {
int ret; int ret;
...@@ -116,6 +143,9 @@ static void wl1271_parse_fw_ver(struct wl1271 *wl) ...@@ -116,6 +143,9 @@ static void wl1271_parse_fw_ver(struct wl1271 *wl)
memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver)); memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver));
return; return;
} }
/* Check if any quirks are needed with older fw versions */
wl->quirks |= wl12xx_get_fw_ver_quirks(wl);
} }
static void wl1271_boot_fw_version(struct wl1271 *wl) static void wl1271_boot_fw_version(struct wl1271 *wl)
...@@ -749,6 +779,9 @@ int wl1271_load_firmware(struct wl1271 *wl) ...@@ -749,6 +779,9 @@ int wl1271_load_firmware(struct wl1271 *wl)
clk |= (wl->ref_clock << 1) << 4; clk |= (wl->ref_clock << 1) << 4;
} }
if (wl->quirks & WL12XX_QUIRK_LPD_MODE)
clk |= SCRATCH_ENABLE_LPD;
wl1271_write32(wl, DRPW_SCRATCH_START, clk); wl1271_write32(wl, DRPW_SCRATCH_START, clk);
wl1271_set_partition(wl, &part_table[PART_WORK]); wl1271_set_partition(wl, &part_table[PART_WORK]);
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/crc7.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/ieee80211.h> #include <linux/ieee80211.h>
...@@ -106,7 +105,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, ...@@ -106,7 +105,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
fail: fail:
WARN_ON(1); WARN_ON(1);
ieee80211_queue_work(wl->hw, &wl->recovery_work); wl12xx_queue_recovery_work(wl);
return ret; return ret;
} }
...@@ -135,6 +134,11 @@ int wl1271_cmd_general_parms(struct wl1271 *wl) ...@@ -135,6 +134,11 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
/* Override the REF CLK from the NVS with the one from platform data */ /* Override the REF CLK from the NVS with the one from platform data */
gen_parms->general_params.ref_clock = wl->ref_clock; gen_parms->general_params.ref_clock = wl->ref_clock;
/* LPD mode enable (bits 6-7) in WL1271 AP mode only */
if (wl->quirks & WL12XX_QUIRK_LPD_MODE)
gen_parms->general_params.general_settings |=
GENERAL_SETTINGS_DRPW_LPD;
ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer); ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer);
if (ret < 0) { if (ret < 0) {
wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed");
...@@ -352,7 +356,7 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) ...@@ -352,7 +356,7 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask); ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask);
if (ret != 0) { if (ret != 0) {
ieee80211_queue_work(wl->hw, &wl->recovery_work); wl12xx_queue_recovery_work(wl);
return ret; return ret;
} }
...@@ -1223,3 +1227,87 @@ int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid) ...@@ -1223,3 +1227,87 @@ int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid)
out: out:
return ret; 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;
}
...@@ -70,6 +70,9 @@ int wl1271_cmd_start_bss(struct wl1271 *wl); ...@@ -70,6 +70,9 @@ int wl1271_cmd_start_bss(struct wl1271 *wl);
int wl1271_cmd_stop_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_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid);
int wl1271_cmd_remove_sta(struct wl1271 *wl, 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 { enum wl1271_commands {
CMD_INTERROGATE = 1, /*use this to read information elements*/ CMD_INTERROGATE = 1, /*use this to read information elements*/
...@@ -107,6 +110,9 @@ enum wl1271_commands { ...@@ -107,6 +110,9 @@ enum wl1271_commands {
CMD_START_PERIODIC_SCAN = 50, CMD_START_PERIODIC_SCAN = 50,
CMD_STOP_PERIODIC_SCAN = 51, CMD_STOP_PERIODIC_SCAN = 51,
CMD_SET_STA_STATE = 52, CMD_SET_STA_STATE = 52,
CMD_CONFIG_FWLOGGER = 53,
CMD_START_FWLOGGER = 54,
CMD_STOP_FWLOGGER = 55,
/* AP mode commands */ /* AP mode commands */
CMD_BSS_START = 60, CMD_BSS_START = 60,
...@@ -575,4 +581,60 @@ struct wl1271_cmd_remove_sta { ...@@ -575,4 +581,60 @@ struct wl1271_cmd_remove_sta {
u8 padding1; u8 padding1;
} __packed; } __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__ */ #endif /* __WL1271_CMD_H__ */
...@@ -1248,6 +1248,59 @@ struct conf_fm_coex { ...@@ -1248,6 +1248,59 @@ struct conf_fm_coex {
u8 swallow_clk_diff; u8 swallow_clk_diff;
}; };
struct conf_rx_streaming_settings {
/*
* RX Streaming duration (in msec) from last tx/rx
*
* Range: u32
*/
u32 duration;
/*
* Bitmap of tids to be polled during RX streaming.
* (Note: it doesn't look like it really matters)
*
* Range: 0x1-0xff
*/
u8 queues;
/*
* RX Streaming interval.
* (Note:this value is also used as the rx streaming timeout)
* Range: 0 (disabled), 10 - 100
*/
u8 interval;
/*
* enable rx streaming also when there is no coex activity
*/
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_drv_settings {
struct conf_sg_settings sg; struct conf_sg_settings sg;
struct conf_rx_settings rx; struct conf_rx_settings rx;
...@@ -1263,6 +1316,8 @@ struct conf_drv_settings { ...@@ -1263,6 +1316,8 @@ struct conf_drv_settings {
struct conf_memory_settings mem_wl127x; struct conf_memory_settings mem_wl127x;
struct conf_memory_settings mem_wl128x; struct conf_memory_settings mem_wl128x;
struct conf_fm_coex fm_coex; struct conf_fm_coex fm_coex;
struct conf_rx_streaming_settings rx_streaming;
struct conf_fwlog fwlog;
u8 hci_io_ds; u8 hci_io_ds;
}; };
......
...@@ -71,6 +71,14 @@ static const struct file_operations name## _ops = { \ ...@@ -71,6 +71,14 @@ static const struct file_operations name## _ops = { \
if (!entry || IS_ERR(entry)) \ if (!entry || IS_ERR(entry)) \
goto err; \ goto err; \
#define DEBUGFS_ADD_PREFIX(prefix, name, parent) \
do { \
entry = debugfs_create_file(#name, 0400, parent, \
wl, &prefix## _## name## _ops); \
if (!entry || IS_ERR(entry)) \
goto err; \
} while (0);
#define DEBUGFS_FWSTATS_FILE(sub, name, fmt) \ #define DEBUGFS_FWSTATS_FILE(sub, name, fmt) \
static ssize_t sub## _ ##name## _read(struct file *file, \ static ssize_t sub## _ ##name## _read(struct file *file, \
char __user *userbuf, \ char __user *userbuf, \
...@@ -298,7 +306,7 @@ static ssize_t start_recovery_write(struct file *file, ...@@ -298,7 +306,7 @@ static ssize_t start_recovery_write(struct file *file,
struct wl1271 *wl = file->private_data; struct wl1271 *wl = file->private_data;
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
ieee80211_queue_work(wl->hw, &wl->recovery_work); wl12xx_queue_recovery_work(wl);
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
return count; return count;
...@@ -527,11 +535,129 @@ static const struct file_operations beacon_interval_ops = { ...@@ -527,11 +535,129 @@ static const struct file_operations beacon_interval_ops = {
.llseek = default_llseek, .llseek = default_llseek,
}; };
static ssize_t rx_streaming_interval_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
char buf[10];
size_t len;
unsigned long value;
int ret;
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
buf[len] = '\0';
ret = kstrtoul(buf, 0, &value);
if (ret < 0) {
wl1271_warning("illegal value in rx_streaming_interval!");
return -EINVAL;
}
/* valid values: 0, 10-100 */
if (value && (value < 10 || value > 100)) {
wl1271_warning("value is not in range!");
return -ERANGE;
}
mutex_lock(&wl->mutex);
wl->conf.rx_streaming.interval = value;
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
wl1271_recalc_rx_streaming(wl);
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return count;
}
static ssize_t rx_streaming_interval_read(struct file *file,
char __user *userbuf,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
return wl1271_format_buffer(userbuf, count, ppos,
"%d\n", wl->conf.rx_streaming.interval);
}
static const struct file_operations rx_streaming_interval_ops = {
.read = rx_streaming_interval_read,
.write = rx_streaming_interval_write,
.open = wl1271_open_file_generic,
.llseek = default_llseek,
};
static ssize_t rx_streaming_always_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
char buf[10];
size_t len;
unsigned long value;
int ret;
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
buf[len] = '\0';
ret = kstrtoul(buf, 0, &value);
if (ret < 0) {
wl1271_warning("illegal value in rx_streaming_write!");
return -EINVAL;
}
/* valid values: 0, 10-100 */
if (!(value == 0 || value == 1)) {
wl1271_warning("value is not in valid!");
return -EINVAL;
}
mutex_lock(&wl->mutex);
wl->conf.rx_streaming.always = value;
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
wl1271_recalc_rx_streaming(wl);
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return count;
}
static ssize_t rx_streaming_always_read(struct file *file,
char __user *userbuf,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
return wl1271_format_buffer(userbuf, count, ppos,
"%d\n", wl->conf.rx_streaming.always);
}
static const struct file_operations rx_streaming_always_ops = {
.read = rx_streaming_always_read,
.write = rx_streaming_always_write,
.open = wl1271_open_file_generic,
.llseek = default_llseek,
};
static int wl1271_debugfs_add_files(struct wl1271 *wl, static int wl1271_debugfs_add_files(struct wl1271 *wl,
struct dentry *rootdir) struct dentry *rootdir)
{ {
int ret = 0; int ret = 0;
struct dentry *entry, *stats; struct dentry *entry, *stats, *streaming;
stats = debugfs_create_dir("fw-statistics", rootdir); stats = debugfs_create_dir("fw-statistics", rootdir);
if (!stats || IS_ERR(stats)) { if (!stats || IS_ERR(stats)) {
...@@ -640,6 +766,14 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, ...@@ -640,6 +766,14 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
DEBUGFS_ADD(dtim_interval, rootdir); DEBUGFS_ADD(dtim_interval, rootdir);
DEBUGFS_ADD(beacon_interval, rootdir); DEBUGFS_ADD(beacon_interval, rootdir);
streaming = debugfs_create_dir("rx_streaming", rootdir);
if (!streaming || IS_ERR(streaming))
goto err;
DEBUGFS_ADD_PREFIX(rx_streaming, interval, streaming);
DEBUGFS_ADD_PREFIX(rx_streaming, always, streaming);
return 0; return 0;
err: err:
......
...@@ -133,10 +133,13 @@ static int wl1271_event_ps_report(struct wl1271 *wl, ...@@ -133,10 +133,13 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
if (ret < 0) if (ret < 0)
break; break;
/* enable beacon early termination */ /*
ret = wl1271_acx_bet_enable(wl, true); * BET has only a minor effect in 5GHz and masks
if (ret < 0) * channel switch IEs, so we only enable BET on 2.4GHz
break; */
if (wl->band == IEEE80211_BAND_2GHZ)
/* enable beacon early termination */
ret = wl1271_acx_bet_enable(wl, true);
if (wl->ps_compl) { if (wl->ps_compl) {
complete(wl->ps_compl); complete(wl->ps_compl);
...@@ -183,6 +186,21 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, u8 ba_allowed) ...@@ -183,6 +186,21 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, u8 ba_allowed)
ieee80211_stop_rx_ba_session(wl->vif, wl->ba_rx_bitmap, wl->bssid); ieee80211_stop_rx_ba_session(wl->vif, wl->ba_rx_bitmap, wl->bssid);
} }
static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl,
u8 enable)
{
if (enable) {
/* disable dynamic PS when requested by the firmware */
ieee80211_disable_dyn_ps(wl->vif);
set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
} else {
ieee80211_enable_dyn_ps(wl->vif);
clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
wl1271_recalc_rx_streaming(wl);
}
}
static void wl1271_event_mbox_dump(struct event_mailbox *mbox) static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
{ {
wl1271_debug(DEBUG_EVENT, "MBOX DUMP:"); wl1271_debug(DEBUG_EVENT, "MBOX DUMP:");
...@@ -226,14 +244,10 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) ...@@ -226,14 +244,10 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
} }
} }
/* disable dynamic PS when requested by the firmware */
if (vector & SOFT_GEMINI_SENSE_EVENT_ID && if (vector & SOFT_GEMINI_SENSE_EVENT_ID &&
wl->bss_type == BSS_TYPE_STA_BSS) { wl->bss_type == BSS_TYPE_STA_BSS)
if (mbox->soft_gemini_sense_info) wl12xx_event_soft_gemini_sense(wl,
ieee80211_disable_dyn_ps(wl->vif); mbox->soft_gemini_sense_info);
else
ieee80211_enable_dyn_ps(wl->vif);
}
/* /*
* The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon
......
...@@ -24,6 +24,9 @@ ...@@ -24,6 +24,9 @@
#ifndef __INI_H__ #ifndef __INI_H__
#define __INI_H__ #define __INI_H__
#define GENERAL_SETTINGS_DRPW_LPD 0xc0
#define SCRATCH_ENABLE_LPD BIT(25)
#define WL1271_INI_MAX_SMART_REFLEX_PARAM 16 #define WL1271_INI_MAX_SMART_REFLEX_PARAM 16
struct wl1271_ini_general_params { struct wl1271_ini_general_params {
......
...@@ -321,6 +321,20 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl) ...@@ -321,6 +321,20 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl)
return 0; 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) static int wl1271_sta_hw_init(struct wl1271 *wl)
{ {
int ret; int ret;
...@@ -382,6 +396,11 @@ static int wl1271_sta_hw_init(struct wl1271 *wl) ...@@ -382,6 +396,11 @@ static int wl1271_sta_hw_init(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
return ret; return ret;
/* Configure the FW logger */
ret = wl12xx_init_fwlog(wl);
if (ret < 0)
return ret;
return 0; return 0;
} }
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/crc7.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include "wl12xx.h" #include "wl12xx.h"
...@@ -128,12 +127,14 @@ EXPORT_SYMBOL_GPL(wl1271_set_partition); ...@@ -128,12 +127,14 @@ EXPORT_SYMBOL_GPL(wl1271_set_partition);
void wl1271_io_reset(struct wl1271 *wl) void wl1271_io_reset(struct wl1271 *wl)
{ {
wl->if_ops->reset(wl); if (wl->if_ops->reset)
wl->if_ops->reset(wl);
} }
void wl1271_io_init(struct wl1271 *wl) void wl1271_io_init(struct wl1271 *wl)
{ {
wl->if_ops->init(wl); if (wl->if_ops->init)
wl->if_ops->init(wl);
} }
void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val) void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val)
......
...@@ -128,6 +128,20 @@ static inline void wl1271_write(struct wl1271 *wl, int addr, void *buf, ...@@ -128,6 +128,20 @@ static inline void wl1271_write(struct wl1271 *wl, int addr, void *buf,
wl1271_raw_write(wl, physical, buf, len, fixed); 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) static inline u32 wl1271_read32(struct wl1271 *wl, int addr)
{ {
return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr)); return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr));
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/wl12xx.h> #include <linux/wl12xx.h>
#include <linux/sched.h>
#include "wl12xx.h" #include "wl12xx.h"
#include "wl12xx_80211.h" #include "wl12xx_80211.h"
...@@ -362,9 +363,25 @@ static struct conf_drv_settings default_conf = { ...@@ -362,9 +363,25 @@ static struct conf_drv_settings default_conf = {
.fm_disturbed_band_margin = 0xff, /* default */ .fm_disturbed_band_margin = 0xff, /* default */
.swallow_clk_diff = 0xff, /* default */ .swallow_clk_diff = 0xff, /* default */
}, },
.rx_streaming = {
.duration = 150,
.queues = 0x1,
.interval = 20,
.always = 0,
},
.fwlog = {
.mode = WL12XX_FWLOG_ON_DEMAND,
.mem_blocks = 2,
.severity = 0,
.timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED,
.output = WL12XX_FWLOG_OUTPUT_HOST,
.threshold = 0,
},
.hci_io_ds = HCI_IO_DS_6MA, .hci_io_ds = HCI_IO_DS_6MA,
}; };
static char *fwlog_param;
static void __wl1271_op_remove_interface(struct wl1271 *wl, static void __wl1271_op_remove_interface(struct wl1271 *wl,
bool reset_tx_queues); bool reset_tx_queues);
static void wl1271_free_ap_keys(struct wl1271 *wl); static void wl1271_free_ap_keys(struct wl1271 *wl);
...@@ -388,6 +405,22 @@ static struct platform_device wl1271_device = { ...@@ -388,6 +405,22 @@ static struct platform_device wl1271_device = {
static DEFINE_MUTEX(wl_list_mutex); static DEFINE_MUTEX(wl_list_mutex);
static LIST_HEAD(wl_list); static LIST_HEAD(wl_list);
static int wl1271_check_operstate(struct wl1271 *wl, unsigned char operstate)
{
int ret;
if (operstate != IF_OPER_UP)
return 0;
if (test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags))
return 0;
ret = wl1271_cmd_set_sta_state(wl);
if (ret < 0)
return ret;
wl1271_info("Association completed.");
return 0;
}
static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
void *arg) void *arg)
{ {
...@@ -437,11 +470,7 @@ static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, ...@@ -437,11 +470,7 @@ static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
if (ret < 0) if (ret < 0)
goto out; goto out;
if ((dev->operstate == IF_OPER_UP) && wl1271_check_operstate(wl, dev->operstate);
!test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags)) {
wl1271_cmd_set_sta_state(wl);
wl1271_info("Association completed.");
}
wl1271_ps_elp_sleep(wl); wl1271_ps_elp_sleep(wl);
...@@ -473,6 +502,117 @@ static int wl1271_reg_notify(struct wiphy *wiphy, ...@@ -473,6 +502,117 @@ static int wl1271_reg_notify(struct wiphy *wiphy,
return 0; return 0;
} }
static int wl1271_set_rx_streaming(struct wl1271 *wl, bool enable)
{
int ret = 0;
/* we should hold wl->mutex */
ret = wl1271_acx_ps_rx_streaming(wl, enable);
if (ret < 0)
goto out;
if (enable)
set_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags);
else
clear_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags);
out:
return ret;
}
/*
* this function is being called when the rx_streaming interval
* has beed changed or rx_streaming should be disabled
*/
int wl1271_recalc_rx_streaming(struct wl1271 *wl)
{
int ret = 0;
int period = wl->conf.rx_streaming.interval;
/* don't reconfigure if rx_streaming is disabled */
if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
goto out;
/* reconfigure/disable according to new streaming_period */
if (period &&
test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) &&
(wl->conf.rx_streaming.always ||
test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
ret = wl1271_set_rx_streaming(wl, true);
else {
ret = wl1271_set_rx_streaming(wl, false);
/* don't cancel_work_sync since we might deadlock */
del_timer_sync(&wl->rx_streaming_timer);
}
out:
return ret;
}
static void wl1271_rx_streaming_enable_work(struct work_struct *work)
{
int ret;
struct wl1271 *wl =
container_of(work, struct wl1271, rx_streaming_enable_work);
mutex_lock(&wl->mutex);
if (test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags) ||
!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) ||
(!wl->conf.rx_streaming.always &&
!test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
goto out;
if (!wl->conf.rx_streaming.interval)
goto out;
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
ret = wl1271_set_rx_streaming(wl, true);
if (ret < 0)
goto out_sleep;
/* stop it after some time of inactivity */
mod_timer(&wl->rx_streaming_timer,
jiffies + msecs_to_jiffies(wl->conf.rx_streaming.duration));
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
}
static void wl1271_rx_streaming_disable_work(struct work_struct *work)
{
int ret;
struct wl1271 *wl =
container_of(work, struct wl1271, rx_streaming_disable_work);
mutex_lock(&wl->mutex);
if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
goto out;
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
ret = wl1271_set_rx_streaming(wl, false);
if (ret)
goto out_sleep;
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
}
static void wl1271_rx_streaming_timer(unsigned long data)
{
struct wl1271 *wl = (struct wl1271 *)data;
ieee80211_queue_work(wl->hw, &wl->rx_streaming_disable_work);
}
static void wl1271_conf_init(struct wl1271 *wl) static void wl1271_conf_init(struct wl1271 *wl)
{ {
...@@ -488,8 +628,24 @@ static void wl1271_conf_init(struct wl1271 *wl) ...@@ -488,8 +628,24 @@ static void wl1271_conf_init(struct wl1271 *wl)
/* apply driver default configuration */ /* apply driver default configuration */
memcpy(&wl->conf, &default_conf, sizeof(default_conf)); memcpy(&wl->conf, &default_conf, sizeof(default_conf));
}
/* Adjust settings according to optional module parameters */
if (fwlog_param) {
if (!strcmp(fwlog_param, "continuous")) {
wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
} else if (!strcmp(fwlog_param, "ondemand")) {
wl->conf.fwlog.mode = WL12XX_FWLOG_ON_DEMAND;
} else if (!strcmp(fwlog_param, "dbgpins")) {
wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_DBG_PINS;
} else if (!strcmp(fwlog_param, "disable")) {
wl->conf.fwlog.mem_blocks = 0;
wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_NONE;
} else {
wl1271_error("Unknown fwlog parameter %s", fwlog_param);
}
}
}
static int wl1271_plt_init(struct wl1271 *wl) static int wl1271_plt_init(struct wl1271 *wl)
{ {
...@@ -741,7 +897,7 @@ static void wl1271_flush_deferred_work(struct wl1271 *wl) ...@@ -741,7 +897,7 @@ static void wl1271_flush_deferred_work(struct wl1271 *wl)
/* Return sent skbs to the network stack */ /* Return sent skbs to the network stack */
while ((skb = skb_dequeue(&wl->deferred_tx_queue))) while ((skb = skb_dequeue(&wl->deferred_tx_queue)))
ieee80211_tx_status(wl->hw, skb); ieee80211_tx_status_ni(wl->hw, skb);
} }
static void wl1271_netstack_work(struct work_struct *work) static void wl1271_netstack_work(struct work_struct *work)
...@@ -808,7 +964,7 @@ irqreturn_t wl1271_irq(int irq, void *cookie) ...@@ -808,7 +964,7 @@ irqreturn_t wl1271_irq(int irq, void *cookie)
if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) { if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
wl1271_error("watchdog interrupt received! " wl1271_error("watchdog interrupt received! "
"starting recovery."); "starting recovery.");
ieee80211_queue_work(wl->hw, &wl->recovery_work); wl12xx_queue_recovery_work(wl);
/* restarting the chip. ignore any other interrupt. */ /* restarting the chip. ignore any other interrupt. */
goto out; goto out;
...@@ -970,6 +1126,89 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) ...@@ -970,6 +1126,89 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
return ret; return ret;
} }
void wl12xx_queue_recovery_work(struct wl1271 *wl)
{
if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
ieee80211_queue_work(wl->hw, &wl->recovery_work);
}
size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
{
size_t len = 0;
/* The FW log is a length-value list, find where the log end */
while (len < maxlen) {
if (memblock[len] == 0)
break;
if (len + memblock[len] + 1 > maxlen)
break;
len += memblock[len] + 1;
}
/* Make sure we have enough room */
len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size));
/* Fill the FW log file, consumed by the sysfs fwlog entry */
memcpy(wl->fwlog + wl->fwlog_size, memblock, len);
wl->fwlog_size += len;
return len;
}
static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
{
u32 addr;
u32 first_addr;
u8 *block;
if ((wl->quirks & WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED) ||
(wl->conf.fwlog.mode != WL12XX_FWLOG_ON_DEMAND) ||
(wl->conf.fwlog.mem_blocks == 0))
return;
wl1271_info("Reading FW panic log");
block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL);
if (!block)
return;
/*
* Make sure the chip is awake and the logger isn't active.
* This might fail if the firmware hanged.
*/
if (!wl1271_ps_elp_wakeup(wl))
wl12xx_cmd_stop_fwlog(wl);
/* Read the first memory block address */
wl1271_fw_status(wl, wl->fw_status);
first_addr = __le32_to_cpu(wl->fw_status->sta.log_start_addr);
if (!first_addr)
goto out;
/* Traverse the memory blocks linked list */
addr = first_addr;
do {
memset(block, 0, WL12XX_HW_BLOCK_SIZE);
wl1271_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
false);
/*
* Memory blocks are linked to one another. The first 4 bytes
* of each memory block hold the hardware address of the next
* one. The last memory block points to the first one.
*/
addr = __le32_to_cpup((__le32 *)block);
if (!wl12xx_copy_fwlog(wl, block + sizeof(addr),
WL12XX_HW_BLOCK_SIZE - sizeof(addr)))
break;
} while (addr && (addr != first_addr));
wake_up_interruptible(&wl->fwlog_waitq);
out:
kfree(block);
}
static void wl1271_recovery_work(struct work_struct *work) static void wl1271_recovery_work(struct work_struct *work)
{ {
struct wl1271 *wl = struct wl1271 *wl =
...@@ -980,6 +1219,11 @@ static void wl1271_recovery_work(struct work_struct *work) ...@@ -980,6 +1219,11 @@ static void wl1271_recovery_work(struct work_struct *work)
if (wl->state != WL1271_STATE_ON) if (wl->state != WL1271_STATE_ON)
goto out; goto out;
/* Avoid a recursive recovery */
set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
wl12xx_read_fwlog_panic(wl);
wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x", wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x",
wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4)); wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4));
...@@ -996,6 +1240,9 @@ static void wl1271_recovery_work(struct work_struct *work) ...@@ -996,6 +1240,9 @@ static void wl1271_recovery_work(struct work_struct *work)
/* reboot the chipset */ /* reboot the chipset */
__wl1271_op_remove_interface(wl, false); __wl1271_op_remove_interface(wl, false);
clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
ieee80211_restart_hw(wl->hw); ieee80211_restart_hw(wl->hw);
/* /*
...@@ -1074,9 +1321,13 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ...@@ -1074,9 +1321,13 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
wl->chip.id); wl->chip.id);
/* end-of-transaction flag should be set in wl127x AP mode */ /*
* 'end-of-transaction flag' and 'LPD mode flag'
* should be set in wl127x AP mode only
*/
if (wl->bss_type == BSS_TYPE_AP_BSS) if (wl->bss_type == BSS_TYPE_AP_BSS)
wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; wl->quirks |= (WL12XX_QUIRK_END_OF_TRANSACTION |
WL12XX_QUIRK_LPD_MODE);
ret = wl1271_setup(wl); ret = wl1271_setup(wl);
if (ret < 0) if (ret < 0)
...@@ -1089,6 +1340,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ...@@ -1089,6 +1340,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
ret = wl1271_setup(wl); ret = wl1271_setup(wl);
if (ret < 0) if (ret < 0)
goto out; goto out;
if (wl1271_set_block_size(wl)) if (wl1271_set_block_size(wl))
wl->quirks |= WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT; wl->quirks |= WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT;
break; break;
...@@ -1117,24 +1369,6 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ...@@ -1117,24 +1369,6 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
return ret; return ret;
} }
static unsigned int wl1271_get_fw_ver_quirks(struct wl1271 *wl)
{
unsigned int quirks = 0;
unsigned int *fw_ver = wl->chip.fw_ver;
/* Only for wl127x */
if ((fw_ver[FW_VER_CHIP] == FW_VER_CHIP_WL127X) &&
/* Check STA version */
(((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
(fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_STA_MIN)) ||
/* Check AP version */
((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) &&
(fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_AP_MIN))))
quirks |= WL12XX_QUIRK_USE_2_SPARE_BLOCKS;
return quirks;
}
int wl1271_plt_start(struct wl1271 *wl) int wl1271_plt_start(struct wl1271 *wl)
{ {
int retries = WL1271_BOOT_RETRIES; int retries = WL1271_BOOT_RETRIES;
...@@ -1171,8 +1405,6 @@ int wl1271_plt_start(struct wl1271 *wl) ...@@ -1171,8 +1405,6 @@ int wl1271_plt_start(struct wl1271 *wl)
wl1271_notice("firmware booted in PLT mode (%s)", wl1271_notice("firmware booted in PLT mode (%s)",
wl->chip.fw_ver_str); wl->chip.fw_ver_str);
/* Check if any quirks are needed with older fw versions */
wl->quirks |= wl1271_get_fw_ver_quirks(wl);
goto out; goto out;
irq_disable: irq_disable:
...@@ -1352,13 +1584,10 @@ static struct notifier_block wl1271_dev_notifier = { ...@@ -1352,13 +1584,10 @@ static struct notifier_block wl1271_dev_notifier = {
}; };
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int wl1271_configure_suspend(struct wl1271 *wl) static int wl1271_configure_suspend_sta(struct wl1271 *wl)
{ {
int ret; int ret;
if (wl->bss_type != BSS_TYPE_STA_BSS)
return 0;
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
ret = wl1271_ps_elp_wakeup(wl); ret = wl1271_ps_elp_wakeup(wl);
...@@ -1403,11 +1632,41 @@ static int wl1271_configure_suspend(struct wl1271 *wl) ...@@ -1403,11 +1632,41 @@ static int wl1271_configure_suspend(struct wl1271 *wl)
} }
static int wl1271_configure_suspend_ap(struct wl1271 *wl)
{
int ret;
mutex_lock(&wl->mutex);
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out_unlock;
ret = wl1271_acx_set_ap_beacon_filter(wl, true);
wl1271_ps_elp_sleep(wl);
out_unlock:
mutex_unlock(&wl->mutex);
return ret;
}
static int wl1271_configure_suspend(struct wl1271 *wl)
{
if (wl->bss_type == BSS_TYPE_STA_BSS)
return wl1271_configure_suspend_sta(wl);
if (wl->bss_type == BSS_TYPE_AP_BSS)
return wl1271_configure_suspend_ap(wl);
return 0;
}
static void wl1271_configure_resume(struct wl1271 *wl) static void wl1271_configure_resume(struct wl1271 *wl)
{ {
int ret; int ret;
bool is_sta = wl->bss_type == BSS_TYPE_STA_BSS;
bool is_ap = wl->bss_type == BSS_TYPE_AP_BSS;
if (wl->bss_type != BSS_TYPE_STA_BSS) if (!is_sta && !is_ap)
return; return;
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
...@@ -1415,10 +1674,14 @@ static void wl1271_configure_resume(struct wl1271 *wl) ...@@ -1415,10 +1674,14 @@ static void wl1271_configure_resume(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
goto out; goto out;
/* exit psm if it wasn't configured */ if (is_sta) {
if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) /* exit psm if it wasn't configured */
wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags))
wl->basic_rate, true); wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
wl->basic_rate, true);
} else if (is_ap) {
wl1271_acx_set_ap_beacon_filter(wl, false);
}
wl1271_ps_elp_sleep(wl); wl1271_ps_elp_sleep(wl);
out: out:
...@@ -1429,69 +1692,69 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, ...@@ -1429,69 +1692,69 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wow) struct cfg80211_wowlan *wow)
{ {
struct wl1271 *wl = hw->priv; struct wl1271 *wl = hw->priv;
int ret;
wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow); wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
wl->wow_enabled = !!wow; WARN_ON(!wow || !wow->any);
if (wl->wow_enabled) {
int ret;
ret = wl1271_configure_suspend(wl);
if (ret < 0) {
wl1271_warning("couldn't prepare device to suspend");
return ret;
}
/* flush any remaining work */
wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
flush_delayed_work(&wl->scan_complete_work);
/* wl->wow_enabled = true;
* disable and re-enable interrupts in order to flush ret = wl1271_configure_suspend(wl);
* the threaded_irq if (ret < 0) {
*/ wl1271_warning("couldn't prepare device to suspend");
wl1271_disable_interrupts(wl); return ret;
}
/* flush any remaining work */
wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
flush_delayed_work(&wl->scan_complete_work);
/* /*
* set suspended flag to avoid triggering a new threaded_irq * disable and re-enable interrupts in order to flush
* work. no need for spinlock as interrupts are disabled. * the threaded_irq
*/ */
set_bit(WL1271_FLAG_SUSPENDED, &wl->flags); wl1271_disable_interrupts(wl);
/*
* set suspended flag to avoid triggering a new threaded_irq
* work. no need for spinlock as interrupts are disabled.
*/
set_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
wl1271_enable_interrupts(wl);
flush_work(&wl->tx_work);
flush_delayed_work(&wl->pspoll_work);
flush_delayed_work(&wl->elp_work);
wl1271_enable_interrupts(wl);
flush_work(&wl->tx_work);
flush_delayed_work(&wl->pspoll_work);
flush_delayed_work(&wl->elp_work);
}
return 0; return 0;
} }
static int wl1271_op_resume(struct ieee80211_hw *hw) static int wl1271_op_resume(struct ieee80211_hw *hw)
{ {
struct wl1271 *wl = hw->priv; struct wl1271 *wl = hw->priv;
unsigned long flags;
bool run_irq_work = false;
wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d", wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d",
wl->wow_enabled); wl->wow_enabled);
WARN_ON(!wl->wow_enabled);
/* /*
* re-enable irq_work enqueuing, and call irq_work directly if * re-enable irq_work enqueuing, and call irq_work directly if
* there is a pending work. * there is a pending work.
*/ */
if (wl->wow_enabled) { spin_lock_irqsave(&wl->wl_lock, flags);
struct wl1271 *wl = hw->priv; clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
unsigned long flags; if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags))
bool run_irq_work = false; run_irq_work = true;
spin_unlock_irqrestore(&wl->wl_lock, flags);
spin_lock_irqsave(&wl->wl_lock, flags);
clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags))
run_irq_work = true;
spin_unlock_irqrestore(&wl->wl_lock, flags);
if (run_irq_work) {
wl1271_debug(DEBUG_MAC80211,
"run postponed irq_work directly");
wl1271_irq(0, wl);
wl1271_enable_interrupts(wl);
}
wl1271_configure_resume(wl); if (run_irq_work) {
wl1271_debug(DEBUG_MAC80211,
"run postponed irq_work directly");
wl1271_irq(0, wl);
wl1271_enable_interrupts(wl);
} }
wl1271_configure_resume(wl);
wl->wow_enabled = false;
return 0; return 0;
} }
...@@ -1629,9 +1892,6 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, ...@@ -1629,9 +1892,6 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
strncpy(wiphy->fw_version, wl->chip.fw_ver_str, strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
sizeof(wiphy->fw_version)); sizeof(wiphy->fw_version));
/* Check if any quirks are needed with older fw versions */
wl->quirks |= wl1271_get_fw_ver_quirks(wl);
/* /*
* Now we know if 11a is supported (info from the NVS), so disable * Now we know if 11a is supported (info from the NVS), so disable
* 11a channels if not supported * 11a channels if not supported
...@@ -1694,6 +1954,9 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, ...@@ -1694,6 +1954,9 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
cancel_delayed_work_sync(&wl->scan_complete_work); cancel_delayed_work_sync(&wl->scan_complete_work);
cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->netstack_work);
cancel_work_sync(&wl->tx_work); cancel_work_sync(&wl->tx_work);
del_timer_sync(&wl->rx_streaming_timer);
cancel_work_sync(&wl->rx_streaming_enable_work);
cancel_work_sync(&wl->rx_streaming_disable_work);
cancel_delayed_work_sync(&wl->pspoll_work); cancel_delayed_work_sync(&wl->pspoll_work);
cancel_delayed_work_sync(&wl->elp_work); cancel_delayed_work_sync(&wl->elp_work);
...@@ -2780,24 +3043,6 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, ...@@ -2780,24 +3043,6 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
} }
} }
if (changed & BSS_CHANGED_IBSS) {
wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d",
bss_conf->ibss_joined);
if (bss_conf->ibss_joined) {
u32 rates = bss_conf->basic_rates;
wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
rates);
wl->basic_rate = wl1271_tx_min_rate_get(wl);
/* by default, use 11b rates */
wl->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
ret = wl1271_acx_sta_rate_policies(wl);
if (ret < 0)
goto out;
}
}
ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed); ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -3023,6 +3268,24 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, ...@@ -3023,6 +3268,24 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
} }
} }
if (changed & BSS_CHANGED_IBSS) {
wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d",
bss_conf->ibss_joined);
if (bss_conf->ibss_joined) {
u32 rates = bss_conf->basic_rates;
wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
rates);
wl->basic_rate = wl1271_tx_min_rate_get(wl);
/* by default, use 11b rates */
wl->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
ret = wl1271_acx_sta_rate_policies(wl);
if (ret < 0)
goto out;
}
}
ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed); ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -3061,6 +3324,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, ...@@ -3061,6 +3324,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
wl1271_warning("cmd join failed %d", ret); wl1271_warning("cmd join failed %d", ret);
goto out; goto out;
} }
wl1271_check_operstate(wl, ieee80211_get_operstate(vif));
} }
out: out:
...@@ -3784,6 +4048,69 @@ static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev, ...@@ -3784,6 +4048,69 @@ static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR, static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR,
wl1271_sysfs_show_hw_pg_ver, NULL); wl1271_sysfs_show_hw_pg_ver, NULL);
static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct wl1271 *wl = dev_get_drvdata(dev);
ssize_t len;
int ret;
ret = mutex_lock_interruptible(&wl->mutex);
if (ret < 0)
return -ERESTARTSYS;
/* Let only one thread read the log at a time, blocking others */
while (wl->fwlog_size == 0) {
DEFINE_WAIT(wait);
prepare_to_wait_exclusive(&wl->fwlog_waitq,
&wait,
TASK_INTERRUPTIBLE);
if (wl->fwlog_size != 0) {
finish_wait(&wl->fwlog_waitq, &wait);
break;
}
mutex_unlock(&wl->mutex);
schedule();
finish_wait(&wl->fwlog_waitq, &wait);
if (signal_pending(current))
return -ERESTARTSYS;
ret = mutex_lock_interruptible(&wl->mutex);
if (ret < 0)
return -ERESTARTSYS;
}
/* Check if the fwlog is still valid */
if (wl->fwlog_size < 0) {
mutex_unlock(&wl->mutex);
return 0;
}
/* Seeking is not supported - old logs are not kept. Disregard pos. */
len = min(count, (size_t)wl->fwlog_size);
wl->fwlog_size -= len;
memcpy(buffer, wl->fwlog, len);
/* Make room for new messages */
memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
mutex_unlock(&wl->mutex);
return len;
}
static struct bin_attribute fwlog_attr = {
.attr = {.name = "fwlog", .mode = S_IRUSR},
.read = wl1271_sysfs_read_fwlog,
};
int wl1271_register_hw(struct wl1271 *wl) int wl1271_register_hw(struct wl1271 *wl)
{ {
int ret; int ret;
...@@ -3964,6 +4291,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void) ...@@ -3964,6 +4291,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
INIT_WORK(&wl->tx_work, wl1271_tx_work); INIT_WORK(&wl->tx_work, wl1271_tx_work);
INIT_WORK(&wl->recovery_work, wl1271_recovery_work); INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
INIT_WORK(&wl->rx_streaming_enable_work,
wl1271_rx_streaming_enable_work);
INIT_WORK(&wl->rx_streaming_disable_work,
wl1271_rx_streaming_disable_work);
wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
if (!wl->freezable_wq) {
ret = -ENOMEM;
goto err_hw;
}
wl->channel = WL1271_DEFAULT_CHANNEL; wl->channel = WL1271_DEFAULT_CHANNEL;
wl->beacon_int = WL1271_DEFAULT_BEACON_INT; wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
wl->default_key = 0; wl->default_key = 0;
...@@ -3989,6 +4327,10 @@ struct ieee80211_hw *wl1271_alloc_hw(void) ...@@ -3989,6 +4327,10 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->quirks = 0; wl->quirks = 0;
wl->platform_quirks = 0; wl->platform_quirks = 0;
wl->sched_scanning = false; wl->sched_scanning = false;
setup_timer(&wl->rx_streaming_timer, wl1271_rx_streaming_timer,
(unsigned long) wl);
wl->fwlog_size = 0;
init_waitqueue_head(&wl->fwlog_waitq);
memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
for (i = 0; i < ACX_TX_DESCRIPTORS; i++) for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
...@@ -4006,7 +4348,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) ...@@ -4006,7 +4348,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order); wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
if (!wl->aggr_buf) { if (!wl->aggr_buf) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_hw; goto err_wq;
} }
wl->dummy_packet = wl12xx_alloc_dummy_packet(wl); wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
...@@ -4015,11 +4357,18 @@ struct ieee80211_hw *wl1271_alloc_hw(void) ...@@ -4015,11 +4357,18 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
goto err_aggr; goto err_aggr;
} }
/* Allocate one page for the FW log */
wl->fwlog = (u8 *)get_zeroed_page(GFP_KERNEL);
if (!wl->fwlog) {
ret = -ENOMEM;
goto err_dummy_packet;
}
/* Register platform device */ /* Register platform device */
ret = platform_device_register(wl->plat_dev); ret = platform_device_register(wl->plat_dev);
if (ret) { if (ret) {
wl1271_error("couldn't register platform device"); wl1271_error("couldn't register platform device");
goto err_dummy_packet; goto err_fwlog;
} }
dev_set_drvdata(&wl->plat_dev->dev, wl); dev_set_drvdata(&wl->plat_dev->dev, wl);
...@@ -4037,20 +4386,36 @@ struct ieee80211_hw *wl1271_alloc_hw(void) ...@@ -4037,20 +4386,36 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
goto err_bt_coex_state; goto err_bt_coex_state;
} }
/* Create sysfs file for the FW log */
ret = device_create_bin_file(&wl->plat_dev->dev, &fwlog_attr);
if (ret < 0) {
wl1271_error("failed to create sysfs file fwlog");
goto err_hw_pg_ver;
}
return hw; return hw;
err_hw_pg_ver:
device_remove_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
err_bt_coex_state: err_bt_coex_state:
device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state); device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
err_platform: err_platform:
platform_device_unregister(wl->plat_dev); platform_device_unregister(wl->plat_dev);
err_fwlog:
free_page((unsigned long)wl->fwlog);
err_dummy_packet: err_dummy_packet:
dev_kfree_skb(wl->dummy_packet); dev_kfree_skb(wl->dummy_packet);
err_aggr: err_aggr:
free_pages((unsigned long)wl->aggr_buf, order); free_pages((unsigned long)wl->aggr_buf, order);
err_wq:
destroy_workqueue(wl->freezable_wq);
err_hw: err_hw:
wl1271_debugfs_exit(wl); wl1271_debugfs_exit(wl);
kfree(plat_dev); kfree(plat_dev);
...@@ -4066,7 +4431,15 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw); ...@@ -4066,7 +4431,15 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
int wl1271_free_hw(struct wl1271 *wl) int wl1271_free_hw(struct wl1271 *wl)
{ {
/* Unblock any fwlog readers */
mutex_lock(&wl->mutex);
wl->fwlog_size = -1;
wake_up_interruptible_all(&wl->fwlog_waitq);
mutex_unlock(&wl->mutex);
device_remove_bin_file(&wl->plat_dev->dev, &fwlog_attr);
platform_device_unregister(wl->plat_dev); platform_device_unregister(wl->plat_dev);
free_page((unsigned long)wl->fwlog);
dev_kfree_skb(wl->dummy_packet); dev_kfree_skb(wl->dummy_packet);
free_pages((unsigned long)wl->aggr_buf, free_pages((unsigned long)wl->aggr_buf,
get_order(WL1271_AGGR_BUFFER_SIZE)); get_order(WL1271_AGGR_BUFFER_SIZE));
...@@ -4081,6 +4454,7 @@ int wl1271_free_hw(struct wl1271 *wl) ...@@ -4081,6 +4454,7 @@ int wl1271_free_hw(struct wl1271 *wl)
kfree(wl->fw_status); kfree(wl->fw_status);
kfree(wl->tx_res_if); kfree(wl->tx_res_if);
destroy_workqueue(wl->freezable_wq);
ieee80211_free_hw(wl->hw); ieee80211_free_hw(wl->hw);
...@@ -4093,6 +4467,10 @@ EXPORT_SYMBOL_GPL(wl12xx_debug_level); ...@@ -4093,6 +4467,10 @@ EXPORT_SYMBOL_GPL(wl12xx_debug_level);
module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR); module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
module_param_named(fwlog, fwlog_param, charp, 0);
MODULE_PARM_DESC(keymap,
"FW logger options: continuous, ondemand, dbgpins or disable");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
...@@ -118,7 +118,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl) ...@@ -118,7 +118,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl)
&compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT)); &compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT));
if (ret == 0) { if (ret == 0) {
wl1271_error("ELP wakeup timeout!"); wl1271_error("ELP wakeup timeout!");
ieee80211_queue_work(wl->hw, &wl->recovery_work); wl12xx_queue_recovery_work(wl);
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
goto err; goto err;
} else if (ret < 0) { } else if (ret < 0) {
...@@ -169,9 +169,11 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, ...@@ -169,9 +169,11 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
wl1271_debug(DEBUG_PSM, "leaving psm"); wl1271_debug(DEBUG_PSM, "leaving psm");
/* disable beacon early termination */ /* disable beacon early termination */
ret = wl1271_acx_bet_enable(wl, false); if (wl->band == IEEE80211_BAND_2GHZ) {
if (ret < 0) ret = wl1271_acx_bet_enable(wl, false);
return ret; if (ret < 0)
return ret;
}
/* disable beacon filtering */ /* disable beacon filtering */
ret = wl1271_acx_beacon_filter_opt(wl, false); ret = wl1271_acx_beacon_filter_opt(wl, false);
...@@ -202,7 +204,7 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) ...@@ -202,7 +204,7 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
info = IEEE80211_SKB_CB(skb); info = IEEE80211_SKB_CB(skb);
info->flags |= IEEE80211_TX_STAT_TX_FILTERED; info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
info->status.rates[0].idx = -1; info->status.rates[0].idx = -1;
ieee80211_tx_status(wl->hw, skb); ieee80211_tx_status_ni(wl->hw, skb);
filtered++; filtered++;
} }
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
*/ */
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/sched.h>
#include "wl12xx.h" #include "wl12xx.h"
#include "acx.h" #include "acx.h"
...@@ -95,6 +96,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) ...@@ -95,6 +96,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length)
struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr;
u8 *buf; u8 *buf;
u8 beacon = 0; u8 beacon = 0;
u8 is_data = 0;
/* /*
* In PLT mode we seem to get frames and mac80211 warns about them, * In PLT mode we seem to get frames and mac80211 warns about them,
...@@ -106,6 +108,13 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) ...@@ -106,6 +108,13 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length)
/* the data read starts with the descriptor */ /* the data read starts with the descriptor */
desc = (struct wl1271_rx_descriptor *) data; desc = (struct wl1271_rx_descriptor *) data;
if (desc->packet_class == WL12XX_RX_CLASS_LOGGER) {
size_t len = length - sizeof(*desc);
wl12xx_copy_fwlog(wl, data + sizeof(*desc), len);
wake_up_interruptible(&wl->fwlog_waitq);
return 0;
}
switch (desc->status & WL1271_RX_DESC_STATUS_MASK) { switch (desc->status & WL1271_RX_DESC_STATUS_MASK) {
/* discard corrupted packets */ /* discard corrupted packets */
case WL1271_RX_DESC_DRIVER_RX_Q_FAIL: case WL1271_RX_DESC_DRIVER_RX_Q_FAIL:
...@@ -137,6 +146,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) ...@@ -137,6 +146,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length)
hdr = (struct ieee80211_hdr *)skb->data; hdr = (struct ieee80211_hdr *)skb->data;
if (ieee80211_is_beacon(hdr->frame_control)) if (ieee80211_is_beacon(hdr->frame_control))
beacon = 1; beacon = 1;
if (ieee80211_is_data_present(hdr->frame_control))
is_data = 1;
wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon); wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon);
...@@ -147,9 +158,9 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) ...@@ -147,9 +158,9 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length)
skb_trim(skb, skb->len - desc->pad_len); skb_trim(skb, skb->len - desc->pad_len);
skb_queue_tail(&wl->deferred_rx_queue, skb); skb_queue_tail(&wl->deferred_rx_queue, skb);
ieee80211_queue_work(wl->hw, &wl->netstack_work); queue_work(wl->freezable_wq, &wl->netstack_work);
return 0; return is_data;
} }
void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status)
...@@ -162,6 +173,8 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) ...@@ -162,6 +173,8 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status)
u32 mem_block; u32 mem_block;
u32 pkt_length; u32 pkt_length;
u32 pkt_offset; u32 pkt_offset;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
bool had_data = false;
while (drv_rx_counter != fw_rx_counter) { while (drv_rx_counter != fw_rx_counter) {
buf_size = 0; buf_size = 0;
...@@ -214,9 +227,11 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) ...@@ -214,9 +227,11 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status)
* conditions, in that case the received frame will just * conditions, in that case the received frame will just
* be dropped. * be dropped.
*/ */
wl1271_rx_handle_data(wl, if (wl1271_rx_handle_data(wl,
wl->aggr_buf + pkt_offset, wl->aggr_buf + pkt_offset,
pkt_length); pkt_length) == 1)
had_data = true;
wl->rx_counter++; wl->rx_counter++;
drv_rx_counter++; drv_rx_counter++;
drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK; drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
...@@ -230,6 +245,20 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) ...@@ -230,6 +245,20 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status)
*/ */
if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION) if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION)
wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter);
if (!is_ap && wl->conf.rx_streaming.interval && had_data &&
(wl->conf.rx_streaming.always ||
test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) {
u32 timeout = wl->conf.rx_streaming.duration;
/* restart rx streaming */
if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
ieee80211_queue_work(wl->hw,
&wl->rx_streaming_enable_work);
mod_timer(&wl->rx_streaming_timer,
jiffies + msecs_to_jiffies(timeout));
}
} }
void wl1271_set_default_filters(struct wl1271 *wl) void wl1271_set_default_filters(struct wl1271 *wl)
......
...@@ -97,6 +97,18 @@ ...@@ -97,6 +97,18 @@
#define RX_BUF_SIZE_MASK 0xFFF00 #define RX_BUF_SIZE_MASK 0xFFF00
#define RX_BUF_SIZE_SHIFT_DIV 6 #define RX_BUF_SIZE_SHIFT_DIV 6
enum {
WL12XX_RX_CLASS_UNKNOWN,
WL12XX_RX_CLASS_MANAGEMENT,
WL12XX_RX_CLASS_DATA,
WL12XX_RX_CLASS_QOS_DATA,
WL12XX_RX_CLASS_BCN_PRBRSP,
WL12XX_RX_CLASS_EAPOL,
WL12XX_RX_CLASS_BA_EVENT,
WL12XX_RX_CLASS_AMSDU,
WL12XX_RX_CLASS_LOGGER,
};
struct wl1271_rx_descriptor { struct wl1271_rx_descriptor {
__le16 length; __le16 length;
u8 status; u8 status;
......
...@@ -62,7 +62,7 @@ void wl1271_scan_complete_work(struct work_struct *work) ...@@ -62,7 +62,7 @@ void wl1271_scan_complete_work(struct work_struct *work)
if (wl->scan.failed) { if (wl->scan.failed) {
wl1271_info("Scan completed due to error."); wl1271_info("Scan completed due to error.");
ieee80211_queue_work(wl->hw, &wl->recovery_work); wl12xx_queue_recovery_work(wl);
} }
out: out:
...@@ -326,7 +326,7 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, ...@@ -326,7 +326,7 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
struct cfg80211_sched_scan_request *req, struct cfg80211_sched_scan_request *req,
struct conn_scan_ch_params *channels, struct conn_scan_ch_params *channels,
u32 band, bool radar, bool passive, u32 band, bool radar, bool passive,
int start) int start, int max_channels)
{ {
struct conf_sched_scan_settings *c = &wl->conf.sched_scan; struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
int i, j; int i, j;
...@@ -334,7 +334,7 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, ...@@ -334,7 +334,7 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
bool force_passive = !req->n_ssids; bool force_passive = !req->n_ssids;
for (i = 0, j = start; for (i = 0, j = start;
i < req->n_channels && j < MAX_CHANNELS_ALL_BANDS; i < req->n_channels && j < max_channels;
i++) { i++) {
flags = req->channels[i]->flags; flags = req->channels[i]->flags;
...@@ -380,46 +380,42 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, ...@@ -380,46 +380,42 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
return j - start; return j - start;
} }
static int static bool
wl1271_scan_sched_scan_channels(struct wl1271 *wl, wl1271_scan_sched_scan_channels(struct wl1271 *wl,
struct cfg80211_sched_scan_request *req, struct cfg80211_sched_scan_request *req,
struct wl1271_cmd_sched_scan_config *cfg) struct wl1271_cmd_sched_scan_config *cfg)
{ {
int idx = 0;
cfg->passive[0] = cfg->passive[0] =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
IEEE80211_BAND_2GHZ, IEEE80211_BAND_2GHZ,
false, true, idx); false, true, 0,
idx += cfg->passive[0]; MAX_CHANNELS_2GHZ);
cfg->active[0] = cfg->active[0] =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
IEEE80211_BAND_2GHZ, IEEE80211_BAND_2GHZ,
false, false, idx); false, false,
/* cfg->passive[0],
* 5GHz channels always start at position 14, not immediately MAX_CHANNELS_2GHZ);
* after the last 2.4GHz channel
*/
idx = 14;
cfg->passive[1] = cfg->passive[1] =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
IEEE80211_BAND_5GHZ, IEEE80211_BAND_5GHZ,
false, true, idx); false, true, 0,
idx += cfg->passive[1]; MAX_CHANNELS_5GHZ);
cfg->dfs = cfg->dfs =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
IEEE80211_BAND_5GHZ, IEEE80211_BAND_5GHZ,
true, true, idx); true, true,
idx += cfg->dfs; cfg->passive[1],
MAX_CHANNELS_5GHZ);
cfg->active[1] = cfg->active[1] =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
IEEE80211_BAND_5GHZ, IEEE80211_BAND_5GHZ,
false, false, idx); false, false,
idx += cfg->active[1]; cfg->passive[1] + cfg->dfs,
MAX_CHANNELS_5GHZ);
/* 802.11j channels are not supported yet */
cfg->passive[2] = 0;
cfg->active[2] = 0;
wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d", wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d",
cfg->active[0], cfg->passive[0]); cfg->active[0], cfg->passive[0]);
...@@ -427,7 +423,9 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl, ...@@ -427,7 +423,9 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl,
cfg->active[1], cfg->passive[1]); cfg->active[1], cfg->passive[1]);
wl1271_debug(DEBUG_SCAN, " DFS: %d", cfg->dfs); wl1271_debug(DEBUG_SCAN, " DFS: %d", cfg->dfs);
return idx; return cfg->passive[0] || cfg->active[0] ||
cfg->passive[1] || cfg->active[1] || cfg->dfs ||
cfg->passive[2] || cfg->active[2];
} }
int wl1271_scan_sched_scan_config(struct wl1271 *wl, int wl1271_scan_sched_scan_config(struct wl1271 *wl,
...@@ -436,7 +434,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, ...@@ -436,7 +434,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
{ {
struct wl1271_cmd_sched_scan_config *cfg = NULL; struct wl1271_cmd_sched_scan_config *cfg = NULL;
struct conf_sched_scan_settings *c = &wl->conf.sched_scan; struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
int i, total_channels, ret; int i, ret;
bool force_passive = !req->n_ssids; bool force_passive = !req->n_ssids;
wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
...@@ -471,8 +469,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, ...@@ -471,8 +469,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
cfg->ssid_len = 0; cfg->ssid_len = 0;
} }
total_channels = wl1271_scan_sched_scan_channels(wl, req, cfg); if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) {
if (total_channels == 0) {
wl1271_error("scan channel list is empty"); wl1271_error("scan channel list is empty");
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
......
...@@ -112,18 +112,13 @@ struct wl1271_cmd_trigger_scan_to { ...@@ -112,18 +112,13 @@ struct wl1271_cmd_trigger_scan_to {
__le32 timeout; __le32 timeout;
} __packed; } __packed;
#define MAX_CHANNELS_ALL_BANDS 41 #define MAX_CHANNELS_2GHZ 14
#define MAX_CHANNELS_5GHZ 23
#define MAX_CHANNELS_4GHZ 4
#define SCAN_MAX_CYCLE_INTERVALS 16 #define SCAN_MAX_CYCLE_INTERVALS 16
#define SCAN_MAX_BANDS 3 #define SCAN_MAX_BANDS 3
enum {
SCAN_CHANNEL_TYPE_2GHZ_PASSIVE,
SCAN_CHANNEL_TYPE_2GHZ_ACTIVE,
SCAN_CHANNEL_TYPE_5GHZ_PASSIVE,
SCAN_CHANNEL_TYPE_5GHZ_ACTIVE,
SCAN_CHANNEL_TYPE_5GHZ_DFS,
};
enum { enum {
SCAN_SSID_FILTER_ANY = 0, SCAN_SSID_FILTER_ANY = 0,
SCAN_SSID_FILTER_SPECIFIC = 1, SCAN_SSID_FILTER_SPECIFIC = 1,
...@@ -182,7 +177,9 @@ struct wl1271_cmd_sched_scan_config { ...@@ -182,7 +177,9 @@ struct wl1271_cmd_sched_scan_config {
u8 padding[3]; u8 padding[3];
struct conn_scan_ch_params channels[MAX_CHANNELS_ALL_BANDS]; struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
} __packed; } __packed;
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/crc7.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h> #include <linux/mmc/sdio_ids.h>
...@@ -45,7 +44,7 @@ ...@@ -45,7 +44,7 @@
#define SDIO_DEVICE_ID_TI_WL1271 0x4076 #define SDIO_DEVICE_ID_TI_WL1271 0x4076
#endif #endif
static const struct sdio_device_id wl1271_devices[] = { static const struct sdio_device_id wl1271_devices[] __devinitconst = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) }, { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) },
{} {}
}; };
...@@ -107,14 +106,6 @@ static void wl1271_sdio_enable_interrupts(struct wl1271 *wl) ...@@ -107,14 +106,6 @@ static void wl1271_sdio_enable_interrupts(struct wl1271 *wl)
enable_irq(wl->irq); enable_irq(wl->irq);
} }
static void wl1271_sdio_reset(struct wl1271 *wl)
{
}
static void wl1271_sdio_init(struct wl1271 *wl)
{
}
static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
size_t len, bool fixed) size_t len, bool fixed)
{ {
...@@ -170,10 +161,12 @@ static int wl1271_sdio_power_on(struct wl1271 *wl) ...@@ -170,10 +161,12 @@ static int wl1271_sdio_power_on(struct wl1271 *wl)
struct sdio_func *func = wl_to_func(wl); struct sdio_func *func = wl_to_func(wl);
int ret; int ret;
/* Make sure the card will not be powered off by runtime PM */ /* If enabled, tell runtime PM not to power off the card */
ret = pm_runtime_get_sync(&func->dev); if (pm_runtime_enabled(&func->dev)) {
if (ret < 0) ret = pm_runtime_get_sync(&func->dev);
goto out; if (ret)
goto out;
}
/* Runtime PM might be disabled, so power up the card manually */ /* Runtime PM might be disabled, so power up the card manually */
ret = mmc_power_restore_host(func->card->host); ret = mmc_power_restore_host(func->card->host);
...@@ -200,8 +193,11 @@ static int wl1271_sdio_power_off(struct wl1271 *wl) ...@@ -200,8 +193,11 @@ static int wl1271_sdio_power_off(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
return ret; return ret;
/* Let runtime PM know the card is powered off */ /* If enabled, let runtime PM know the card is powered off */
return pm_runtime_put_sync(&func->dev); if (pm_runtime_enabled(&func->dev))
ret = pm_runtime_put_sync(&func->dev);
return ret;
} }
static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable) static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
...@@ -215,8 +211,6 @@ static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable) ...@@ -215,8 +211,6 @@ static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
static struct wl1271_if_operations sdio_ops = { static struct wl1271_if_operations sdio_ops = {
.read = wl1271_sdio_raw_read, .read = wl1271_sdio_raw_read,
.write = wl1271_sdio_raw_write, .write = wl1271_sdio_raw_write,
.reset = wl1271_sdio_reset,
.init = wl1271_sdio_init,
.power = wl1271_sdio_set_power, .power = wl1271_sdio_set_power,
.dev = wl1271_sdio_wl_to_dev, .dev = wl1271_sdio_wl_to_dev,
.enable_irq = wl1271_sdio_enable_interrupts, .enable_irq = wl1271_sdio_enable_interrupts,
...@@ -278,17 +272,19 @@ static int __devinit wl1271_probe(struct sdio_func *func, ...@@ -278,17 +272,19 @@ static int __devinit wl1271_probe(struct sdio_func *func,
goto out_free; goto out_free;
} }
enable_irq_wake(wl->irq); ret = enable_irq_wake(wl->irq);
device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1); if (!ret) {
wl->irq_wake_enabled = true;
disable_irq(wl->irq); device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1);
/* if sdio can keep power while host is suspended, enable wow */ /* if sdio can keep power while host is suspended, enable wow */
mmcflags = sdio_get_host_pm_caps(func); mmcflags = sdio_get_host_pm_caps(func);
wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags); wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags);
if (mmcflags & MMC_PM_KEEP_POWER) if (mmcflags & MMC_PM_KEEP_POWER)
hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY; hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
}
disable_irq(wl->irq);
ret = wl1271_init_ieee80211(wl); ret = wl1271_init_ieee80211(wl);
if (ret) if (ret)
...@@ -303,8 +299,6 @@ static int __devinit wl1271_probe(struct sdio_func *func, ...@@ -303,8 +299,6 @@ static int __devinit wl1271_probe(struct sdio_func *func,
/* Tell PM core that we don't need the card to be powered now */ /* Tell PM core that we don't need the card to be powered now */
pm_runtime_put_noidle(&func->dev); pm_runtime_put_noidle(&func->dev);
wl1271_notice("initialized");
return 0; return 0;
out_irq: out_irq:
...@@ -324,8 +318,10 @@ static void __devexit wl1271_remove(struct sdio_func *func) ...@@ -324,8 +318,10 @@ static void __devexit wl1271_remove(struct sdio_func *func)
pm_runtime_get_noresume(&func->dev); pm_runtime_get_noresume(&func->dev);
wl1271_unregister_hw(wl); wl1271_unregister_hw(wl);
device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0); if (wl->irq_wake_enabled) {
disable_irq_wake(wl->irq); device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0);
disable_irq_wake(wl->irq);
}
free_irq(wl->irq, wl); free_irq(wl->irq, wl);
wl1271_free_hw(wl); wl1271_free_hw(wl);
} }
...@@ -402,23 +398,12 @@ static struct sdio_driver wl1271_sdio_driver = { ...@@ -402,23 +398,12 @@ static struct sdio_driver wl1271_sdio_driver = {
static int __init wl1271_init(void) static int __init wl1271_init(void)
{ {
int ret; return sdio_register_driver(&wl1271_sdio_driver);
ret = sdio_register_driver(&wl1271_sdio_driver);
if (ret < 0) {
wl1271_error("failed to register sdio driver: %d", ret);
goto out;
}
out:
return ret;
} }
static void __exit wl1271_exit(void) static void __exit wl1271_exit(void)
{ {
sdio_unregister_driver(&wl1271_sdio_driver); sdio_unregister_driver(&wl1271_sdio_driver);
wl1271_notice("unloaded");
} }
module_init(wl1271_init); module_init(wl1271_init);
......
...@@ -435,8 +435,6 @@ static int __devinit wl1271_probe(struct spi_device *spi) ...@@ -435,8 +435,6 @@ static int __devinit wl1271_probe(struct spi_device *spi)
if (ret) if (ret)
goto out_irq; goto out_irq;
wl1271_notice("initialized");
return 0; return 0;
out_irq: out_irq:
...@@ -473,23 +471,12 @@ static struct spi_driver wl1271_spi_driver = { ...@@ -473,23 +471,12 @@ static struct spi_driver wl1271_spi_driver = {
static int __init wl1271_init(void) static int __init wl1271_init(void)
{ {
int ret; return spi_register_driver(&wl1271_spi_driver);
ret = spi_register_driver(&wl1271_spi_driver);
if (ret < 0) {
wl1271_error("failed to register spi driver: %d", ret);
goto out;
}
out:
return ret;
} }
static void __exit wl1271_exit(void) static void __exit wl1271_exit(void)
{ {
spi_unregister_driver(&wl1271_spi_driver); spi_unregister_driver(&wl1271_spi_driver);
wl1271_notice("unloaded");
} }
module_init(wl1271_init); module_init(wl1271_init);
......
...@@ -260,7 +260,7 @@ static int wl1271_tm_cmd_recover(struct wl1271 *wl, struct nlattr *tb[]) ...@@ -260,7 +260,7 @@ static int wl1271_tm_cmd_recover(struct wl1271 *wl, struct nlattr *tb[])
{ {
wl1271_debug(DEBUG_TESTMODE, "testmode cmd recover"); wl1271_debug(DEBUG_TESTMODE, "testmode cmd recover");
ieee80211_queue_work(wl->hw, &wl->recovery_work); wl12xx_queue_recovery_work(wl);
return 0; return 0;
} }
......
...@@ -562,17 +562,29 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) ...@@ -562,17 +562,29 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb)
spin_unlock_irqrestore(&wl->wl_lock, flags); spin_unlock_irqrestore(&wl->wl_lock, flags);
} }
static bool wl1271_tx_is_data_present(struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data);
return ieee80211_is_data_present(hdr->frame_control);
}
void wl1271_tx_work_locked(struct wl1271 *wl) void wl1271_tx_work_locked(struct wl1271 *wl)
{ {
struct sk_buff *skb; struct sk_buff *skb;
u32 buf_offset = 0; u32 buf_offset = 0;
bool sent_packets = false; bool sent_packets = false;
bool had_data = false;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
int ret; int ret;
if (unlikely(wl->state == WL1271_STATE_OFF)) if (unlikely(wl->state == WL1271_STATE_OFF))
return; return;
while ((skb = wl1271_skb_dequeue(wl))) { while ((skb = wl1271_skb_dequeue(wl))) {
if (wl1271_tx_is_data_present(skb))
had_data = true;
ret = wl1271_prepare_tx_frame(wl, skb, buf_offset); ret = wl1271_prepare_tx_frame(wl, skb, buf_offset);
if (ret == -EAGAIN) { if (ret == -EAGAIN) {
/* /*
...@@ -619,6 +631,19 @@ void wl1271_tx_work_locked(struct wl1271 *wl) ...@@ -619,6 +631,19 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
wl1271_handle_tx_low_watermark(wl); wl1271_handle_tx_low_watermark(wl);
} }
if (!is_ap && wl->conf.rx_streaming.interval && had_data &&
(wl->conf.rx_streaming.always ||
test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) {
u32 timeout = wl->conf.rx_streaming.duration;
/* enable rx streaming */
if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
ieee80211_queue_work(wl->hw,
&wl->rx_streaming_enable_work);
mod_timer(&wl->rx_streaming_timer,
jiffies + msecs_to_jiffies(timeout));
}
} }
void wl1271_tx_work(struct work_struct *work) void wl1271_tx_work(struct work_struct *work)
...@@ -702,7 +727,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, ...@@ -702,7 +727,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
/* return the packet to the stack */ /* return the packet to the stack */
skb_queue_tail(&wl->deferred_tx_queue, skb); skb_queue_tail(&wl->deferred_tx_queue, skb);
ieee80211_queue_work(wl->hw, &wl->netstack_work); queue_work(wl->freezable_wq, &wl->netstack_work);
wl1271_free_tx_id(wl, result->id); wl1271_free_tx_id(wl, result->id);
} }
...@@ -757,7 +782,7 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) ...@@ -757,7 +782,7 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
info = IEEE80211_SKB_CB(skb); info = IEEE80211_SKB_CB(skb);
info->status.rates[0].idx = -1; info->status.rates[0].idx = -1;
info->status.rates[0].count = 0; info->status.rates[0].count = 0;
ieee80211_tx_status(wl->hw, skb); ieee80211_tx_status_ni(wl->hw, skb);
total++; total++;
} }
} }
...@@ -795,7 +820,7 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues) ...@@ -795,7 +820,7 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
info = IEEE80211_SKB_CB(skb); info = IEEE80211_SKB_CB(skb);
info->status.rates[0].idx = -1; info->status.rates[0].idx = -1;
info->status.rates[0].count = 0; info->status.rates[0].count = 0;
ieee80211_tx_status(wl->hw, skb); ieee80211_tx_status_ni(wl->hw, skb);
} }
} }
} }
...@@ -838,7 +863,7 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues) ...@@ -838,7 +863,7 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
info->status.rates[0].idx = -1; info->status.rates[0].idx = -1;
info->status.rates[0].count = 0; info->status.rates[0].count = 0;
ieee80211_tx_status(wl->hw, skb); ieee80211_tx_status_ni(wl->hw, skb);
} }
} }
} }
......
...@@ -226,6 +226,8 @@ enum { ...@@ -226,6 +226,8 @@ enum {
#define FW_VER_MINOR_1_SPARE_STA_MIN 58 #define FW_VER_MINOR_1_SPARE_STA_MIN 58
#define FW_VER_MINOR_1_SPARE_AP_MIN 47 #define FW_VER_MINOR_1_SPARE_AP_MIN 47
#define FW_VER_MINOR_FWLOG_STA_MIN 70
struct wl1271_chip { struct wl1271_chip {
u32 id; u32 id;
char fw_ver_str[ETHTOOL_BUSINFO_LEN]; char fw_ver_str[ETHTOOL_BUSINFO_LEN];
...@@ -284,8 +286,7 @@ struct wl1271_fw_sta_status { ...@@ -284,8 +286,7 @@ struct wl1271_fw_sta_status {
u8 tx_total; u8 tx_total;
u8 reserved1; u8 reserved1;
__le16 reserved2; __le16 reserved2;
/* Total structure size is 68 bytes */ __le32 log_start_addr;
u32 padding;
} __packed; } __packed;
struct wl1271_fw_full_status { struct wl1271_fw_full_status {
...@@ -359,6 +360,9 @@ enum wl12xx_flags { ...@@ -359,6 +360,9 @@ enum wl12xx_flags {
WL1271_FLAG_DUMMY_PACKET_PENDING, WL1271_FLAG_DUMMY_PACKET_PENDING,
WL1271_FLAG_SUSPENDED, WL1271_FLAG_SUSPENDED,
WL1271_FLAG_PENDING_WORK, WL1271_FLAG_PENDING_WORK,
WL1271_FLAG_SOFT_GEMINI,
WL1271_FLAG_RX_STREAMING_STARTED,
WL1271_FLAG_RECOVERY_IN_PROGRESS,
}; };
struct wl1271_link { struct wl1271_link {
...@@ -443,6 +447,7 @@ struct wl1271 { ...@@ -443,6 +447,7 @@ struct wl1271 {
struct sk_buff_head deferred_tx_queue; struct sk_buff_head deferred_tx_queue;
struct work_struct tx_work; struct work_struct tx_work;
struct workqueue_struct *freezable_wq;
/* Pending TX frames */ /* Pending TX frames */
unsigned long tx_frames_map[BITS_TO_LONGS(ACX_TX_DESCRIPTORS)]; unsigned long tx_frames_map[BITS_TO_LONGS(ACX_TX_DESCRIPTORS)];
...@@ -468,6 +473,15 @@ struct wl1271 { ...@@ -468,6 +473,15 @@ struct wl1271 {
/* Network stack work */ /* Network stack work */
struct work_struct netstack_work; struct work_struct netstack_work;
/* FW log buffer */
u8 *fwlog;
/* Number of valid bytes in the FW log buffer */
ssize_t fwlog_size;
/* Sysfs FW log entry readers wait queue */
wait_queue_head_t fwlog_waitq;
/* Hardware recovery work */ /* Hardware recovery work */
struct work_struct recovery_work; struct work_struct recovery_work;
...@@ -508,6 +522,11 @@ struct wl1271 { ...@@ -508,6 +522,11 @@ struct wl1271 {
/* Default key (for WEP) */ /* Default key (for WEP) */
u32 default_key; u32 default_key;
/* Rx Streaming */
struct work_struct rx_streaming_enable_work;
struct work_struct rx_streaming_disable_work;
struct timer_list rx_streaming_timer;
unsigned int filters; unsigned int filters;
unsigned int rx_config; unsigned int rx_config;
unsigned int rx_filter; unsigned int rx_filter;
...@@ -573,6 +592,7 @@ struct wl1271 { ...@@ -573,6 +592,7 @@ struct wl1271 {
* (currently, only "ANY" trigger is supported) * (currently, only "ANY" trigger is supported)
*/ */
bool wow_enabled; bool wow_enabled;
bool irq_wake_enabled;
/* /*
* AP-mode - links indexed by HLID. The global and broadcast links * AP-mode - links indexed by HLID. The global and broadcast links
...@@ -602,6 +622,9 @@ struct wl1271_station { ...@@ -602,6 +622,9 @@ struct wl1271_station {
int wl1271_plt_start(struct wl1271 *wl); int wl1271_plt_start(struct wl1271 *wl);
int wl1271_plt_stop(struct wl1271 *wl); int wl1271_plt_stop(struct wl1271 *wl);
int wl1271_recalc_rx_streaming(struct wl1271 *wl);
void wl12xx_queue_recovery_work(struct wl1271 *wl);
size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen);
#define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */ #define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */
...@@ -637,4 +660,15 @@ int wl1271_plt_stop(struct wl1271 *wl); ...@@ -637,4 +660,15 @@ int wl1271_plt_stop(struct wl1271 *wl);
/* WL128X requires aggregated packets to be aligned to the SDIO block size */ /* WL128X requires aggregated packets to be aligned to the SDIO block size */
#define WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT BIT(2) #define WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT BIT(2)
/*
* WL127X AP mode requires Low Power DRPw (LPD) enable to reduce power
* consumption
*/
#define WL12XX_QUIRK_LPD_MODE BIT(3)
/* Older firmwares did not implement the FW logger over bus feature */
#define WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED BIT(4)
#define WL12XX_HW_BLOCK_SIZE 256
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment