forked from luck/tmp_suning_uos_patched
Merge branch 'for-linville' of git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx
This commit is contained in:
commit
333c0dbfcd
@ -11,7 +11,6 @@ config WL12XX
|
||||
depends on WL12XX_MENU && GENERIC_HARDIRQS
|
||||
depends on INET
|
||||
select FW_LOADER
|
||||
select CRC7
|
||||
---help---
|
||||
This module adds support for wireless adapters based on TI wl1271 and
|
||||
TI wl1273 chipsets. This module does *not* include support for wl1251.
|
||||
@ -33,6 +32,7 @@ config WL12XX_HT
|
||||
config WL12XX_SPI
|
||||
tristate "TI wl12xx SPI support"
|
||||
depends on WL12XX && SPI_MASTER
|
||||
select CRC7
|
||||
---help---
|
||||
This module adds support for the SPI interface of adapters using
|
||||
TI wl12xx chipsets. Select this if your platform is using
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/crc7.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
@ -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->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));
|
||||
@ -1577,6 +1577,53 @@ int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime)
|
||||
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)
|
||||
{
|
||||
struct wl1271_acx_max_tx_retry *acx = NULL;
|
||||
|
@ -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 {
|
||||
@ -1153,6 +1155,19 @@ struct wl1271_acx_fw_tsf_information {
|
||||
u8 padding[3];
|
||||
} __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 acx_header header;
|
||||
|
||||
@ -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,
|
||||
bool enable);
|
||||
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_config_ps(struct wl1271 *wl);
|
||||
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)
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
@ -116,6 +143,9 @@ static void wl1271_parse_fw_ver(struct wl1271 *wl)
|
||||
memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver));
|
||||
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)
|
||||
@ -749,6 +779,9 @@ int wl1271_load_firmware(struct wl1271 *wl)
|
||||
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_set_partition(wl, &part_table[PART_WORK]);
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/crc7.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ieee80211.h>
|
||||
@ -106,7 +105,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
|
||||
|
||||
fail:
|
||||
WARN_ON(1);
|
||||
ieee80211_queue_work(wl->hw, &wl->recovery_work);
|
||||
wl12xx_queue_recovery_work(wl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -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 */
|
||||
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);
|
||||
if (ret < 0) {
|
||||
wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed");
|
||||
@ -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);
|
||||
if (ret != 0) {
|
||||
ieee80211_queue_work(wl->hw, &wl->recovery_work);
|
||||
wl12xx_queue_recovery_work(wl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1223,3 +1227,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;
|
||||
}
|
||||
|
@ -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*/
|
||||
@ -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,
|
||||
@ -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__ */
|
||||
|
@ -1248,6 +1248,59 @@ struct conf_fm_coex {
|
||||
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_sg_settings sg;
|
||||
struct conf_rx_settings rx;
|
||||
@ -1263,6 +1316,8 @@ struct conf_drv_settings {
|
||||
struct conf_memory_settings mem_wl127x;
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -71,6 +71,14 @@ static const struct file_operations name## _ops = { \
|
||||
if (!entry || IS_ERR(entry)) \
|
||||
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) \
|
||||
static ssize_t sub## _ ##name## _read(struct file *file, \
|
||||
char __user *userbuf, \
|
||||
@ -298,7 +306,7 @@ static ssize_t start_recovery_write(struct file *file,
|
||||
struct wl1271 *wl = file->private_data;
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
ieee80211_queue_work(wl->hw, &wl->recovery_work);
|
||||
wl12xx_queue_recovery_work(wl);
|
||||
mutex_unlock(&wl->mutex);
|
||||
|
||||
return count;
|
||||
@ -527,11 +535,129 @@ static const struct file_operations beacon_interval_ops = {
|
||||
.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,
|
||||
struct dentry *rootdir)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dentry *entry, *stats;
|
||||
struct dentry *entry, *stats, *streaming;
|
||||
|
||||
stats = debugfs_create_dir("fw-statistics", rootdir);
|
||||
if (!stats || IS_ERR(stats)) {
|
||||
@ -640,6 +766,14 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
|
||||
DEBUGFS_ADD(dtim_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;
|
||||
|
||||
err:
|
||||
|
@ -133,10 +133,13 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
/* enable beacon early termination */
|
||||
ret = wl1271_acx_bet_enable(wl, true);
|
||||
if (ret < 0)
|
||||
break;
|
||||
/*
|
||||
* BET has only a minor effect in 5GHz and masks
|
||||
* channel switch IEs, so we only enable BET on 2.4GHz
|
||||
*/
|
||||
if (wl->band == IEEE80211_BAND_2GHZ)
|
||||
/* enable beacon early termination */
|
||||
ret = wl1271_acx_bet_enable(wl, true);
|
||||
|
||||
if (wl->ps_compl) {
|
||||
complete(wl->ps_compl);
|
||||
@ -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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
wl1271_debug(DEBUG_EVENT, "MBOX DUMP:");
|
||||
@ -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 &&
|
||||
wl->bss_type == BSS_TYPE_STA_BSS) {
|
||||
if (mbox->soft_gemini_sense_info)
|
||||
ieee80211_disable_dyn_ps(wl->vif);
|
||||
else
|
||||
ieee80211_enable_dyn_ps(wl->vif);
|
||||
}
|
||||
wl->bss_type == BSS_TYPE_STA_BSS)
|
||||
wl12xx_event_soft_gemini_sense(wl,
|
||||
mbox->soft_gemini_sense_info);
|
||||
|
||||
/*
|
||||
* The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon
|
||||
|
@ -24,6 +24,9 @@
|
||||
#ifndef __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
|
||||
|
||||
struct wl1271_ini_general_params {
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/crc7.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "wl12xx.h"
|
||||
@ -128,12 +127,14 @@ EXPORT_SYMBOL_GPL(wl1271_set_partition);
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
@ -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));
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wl12xx.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "wl12xx.h"
|
||||
#include "wl12xx_80211.h"
|
||||
@ -362,9 +363,25 @@ static struct conf_drv_settings default_conf = {
|
||||
.fm_disturbed_band_margin = 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,
|
||||
};
|
||||
|
||||
static char *fwlog_param;
|
||||
|
||||
static void __wl1271_op_remove_interface(struct wl1271 *wl,
|
||||
bool reset_tx_queues);
|
||||
static void wl1271_free_ap_keys(struct wl1271 *wl);
|
||||
@ -388,6 +405,22 @@ static struct platform_device wl1271_device = {
|
||||
static DEFINE_MUTEX(wl_list_mutex);
|
||||
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,
|
||||
void *arg)
|
||||
{
|
||||
@ -437,11 +470,7 @@ static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if ((dev->operstate == IF_OPER_UP) &&
|
||||
!test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags)) {
|
||||
wl1271_cmd_set_sta_state(wl);
|
||||
wl1271_info("Association completed.");
|
||||
}
|
||||
wl1271_check_operstate(wl, dev->operstate);
|
||||
|
||||
wl1271_ps_elp_sleep(wl);
|
||||
|
||||
@ -473,6 +502,117 @@ static int wl1271_reg_notify(struct wiphy *wiphy,
|
||||
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)
|
||||
{
|
||||
|
||||
@ -488,8 +628,24 @@ static void wl1271_conf_init(struct wl1271 *wl)
|
||||
|
||||
/* apply driver default configuration */
|
||||
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)
|
||||
{
|
||||
@ -741,7 +897,7 @@ static void wl1271_flush_deferred_work(struct wl1271 *wl)
|
||||
|
||||
/* Return sent skbs to the network stack */
|
||||
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)
|
||||
@ -808,7 +964,7 @@ irqreturn_t wl1271_irq(int irq, void *cookie)
|
||||
if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
|
||||
wl1271_error("watchdog interrupt received! "
|
||||
"starting recovery.");
|
||||
ieee80211_queue_work(wl->hw, &wl->recovery_work);
|
||||
wl12xx_queue_recovery_work(wl);
|
||||
|
||||
/* restarting the chip. ignore any other interrupt. */
|
||||
goto out;
|
||||
@ -970,6 +1126,89 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
|
||||
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)
|
||||
{
|
||||
struct wl1271 *wl =
|
||||
@ -980,6 +1219,11 @@ static void wl1271_recovery_work(struct work_struct *work)
|
||||
if (wl->state != WL1271_STATE_ON)
|
||||
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",
|
||||
wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4));
|
||||
|
||||
@ -996,6 +1240,9 @@ static void wl1271_recovery_work(struct work_struct *work)
|
||||
|
||||
/* reboot the chipset */
|
||||
__wl1271_op_remove_interface(wl, false);
|
||||
|
||||
clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
|
||||
|
||||
ieee80211_restart_hw(wl->hw);
|
||||
|
||||
/*
|
||||
@ -1074,9 +1321,13 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
|
||||
wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
|
||||
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)
|
||||
wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION;
|
||||
wl->quirks |= (WL12XX_QUIRK_END_OF_TRANSACTION |
|
||||
WL12XX_QUIRK_LPD_MODE);
|
||||
|
||||
ret = wl1271_setup(wl);
|
||||
if (ret < 0)
|
||||
@ -1089,6 +1340,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
|
||||
ret = wl1271_setup(wl);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (wl1271_set_block_size(wl))
|
||||
wl->quirks |= WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT;
|
||||
break;
|
||||
@ -1117,24 +1369,6 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
|
||||
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 retries = WL1271_BOOT_RETRIES;
|
||||
@ -1171,8 +1405,6 @@ int wl1271_plt_start(struct wl1271 *wl)
|
||||
wl1271_notice("firmware booted in PLT mode (%s)",
|
||||
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;
|
||||
|
||||
irq_disable:
|
||||
@ -1352,13 +1584,10 @@ static struct notifier_block wl1271_dev_notifier = {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int wl1271_configure_suspend(struct wl1271 *wl)
|
||||
static int wl1271_configure_suspend_sta(struct wl1271 *wl)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (wl->bss_type != BSS_TYPE_STA_BSS)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
|
||||
ret = wl1271_ps_elp_wakeup(wl);
|
||||
@ -1403,11 +1632,41 @@ static int wl1271_configure_suspend(struct wl1271 *wl)
|
||||
|
||||
}
|
||||
|
||||
static void wl1271_configure_resume(struct wl1271 *wl)
|
||||
static int wl1271_configure_suspend_ap(struct wl1271 *wl)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (wl->bss_type != BSS_TYPE_STA_BSS)
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
bool is_sta = wl->bss_type == BSS_TYPE_STA_BSS;
|
||||
bool is_ap = wl->bss_type == BSS_TYPE_AP_BSS;
|
||||
|
||||
if (!is_sta && !is_ap)
|
||||
return;
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
@ -1415,10 +1674,14 @@ static void wl1271_configure_resume(struct wl1271 *wl)
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* exit psm if it wasn't configured */
|
||||
if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags))
|
||||
wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
|
||||
wl->basic_rate, true);
|
||||
if (is_sta) {
|
||||
/* exit psm if it wasn't configured */
|
||||
if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags))
|
||||
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);
|
||||
out:
|
||||
@ -1429,69 +1692,69 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
|
||||
struct cfg80211_wowlan *wow)
|
||||
{
|
||||
struct wl1271 *wl = hw->priv;
|
||||
int ret;
|
||||
|
||||
wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
|
||||
wl->wow_enabled = !!wow;
|
||||
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);
|
||||
WARN_ON(!wow || !wow->any);
|
||||
|
||||
/*
|
||||
* disable and re-enable interrupts in order to flush
|
||||
* the threaded_irq
|
||||
*/
|
||||
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);
|
||||
wl->wow_enabled = true;
|
||||
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);
|
||||
|
||||
/*
|
||||
* disable and re-enable interrupts in order to flush
|
||||
* the threaded_irq
|
||||
*/
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wl1271_op_resume(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct wl1271 *wl = hw->priv;
|
||||
unsigned long flags;
|
||||
bool run_irq_work = false;
|
||||
|
||||
wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d",
|
||||
wl->wow_enabled);
|
||||
WARN_ON(!wl->wow_enabled);
|
||||
|
||||
/*
|
||||
* re-enable irq_work enqueuing, and call irq_work directly if
|
||||
* there is a pending work.
|
||||
*/
|
||||
if (wl->wow_enabled) {
|
||||
struct wl1271 *wl = hw->priv;
|
||||
unsigned long flags;
|
||||
bool run_irq_work = false;
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
@ -1629,9 +1892,6 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
|
||||
strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
|
||||
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
|
||||
* 11a channels if not supported
|
||||
@ -1694,6 +1954,9 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
|
||||
cancel_delayed_work_sync(&wl->scan_complete_work);
|
||||
cancel_work_sync(&wl->netstack_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->elp_work);
|
||||
|
||||
@ -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);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@ -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);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@ -3061,6 +3324,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
|
||||
wl1271_warning("cmd join failed %d", ret);
|
||||
goto out;
|
||||
}
|
||||
wl1271_check_operstate(wl, ieee80211_get_operstate(vif));
|
||||
}
|
||||
|
||||
out:
|
||||
@ -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,
|
||||
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 ret;
|
||||
@ -3964,6 +4291,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
|
||||
INIT_WORK(&wl->tx_work, wl1271_tx_work);
|
||||
INIT_WORK(&wl->recovery_work, wl1271_recovery_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->beacon_int = WL1271_DEFAULT_BEACON_INT;
|
||||
wl->default_key = 0;
|
||||
@ -3989,6 +4327,10 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
|
||||
wl->quirks = 0;
|
||||
wl->platform_quirks = 0;
|
||||
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));
|
||||
for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
|
||||
@ -4006,7 +4348,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
|
||||
wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
|
||||
if (!wl->aggr_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto err_hw;
|
||||
goto err_wq;
|
||||
}
|
||||
|
||||
wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
|
||||
@ -4015,11 +4357,18 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
|
||||
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 */
|
||||
ret = platform_device_register(wl->plat_dev);
|
||||
if (ret) {
|
||||
wl1271_error("couldn't register platform device");
|
||||
goto err_dummy_packet;
|
||||
goto err_fwlog;
|
||||
}
|
||||
dev_set_drvdata(&wl->plat_dev->dev, wl);
|
||||
|
||||
@ -4037,20 +4386,36 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
|
||||
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;
|
||||
|
||||
err_hw_pg_ver:
|
||||
device_remove_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
|
||||
|
||||
err_bt_coex_state:
|
||||
device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
|
||||
|
||||
err_platform:
|
||||
platform_device_unregister(wl->plat_dev);
|
||||
|
||||
err_fwlog:
|
||||
free_page((unsigned long)wl->fwlog);
|
||||
|
||||
err_dummy_packet:
|
||||
dev_kfree_skb(wl->dummy_packet);
|
||||
|
||||
err_aggr:
|
||||
free_pages((unsigned long)wl->aggr_buf, order);
|
||||
|
||||
err_wq:
|
||||
destroy_workqueue(wl->freezable_wq);
|
||||
|
||||
err_hw:
|
||||
wl1271_debugfs_exit(wl);
|
||||
kfree(plat_dev);
|
||||
@ -4066,7 +4431,15 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
|
||||
|
||||
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);
|
||||
free_page((unsigned long)wl->fwlog);
|
||||
dev_kfree_skb(wl->dummy_packet);
|
||||
free_pages((unsigned long)wl->aggr_buf,
|
||||
get_order(WL1271_AGGR_BUFFER_SIZE));
|
||||
@ -4081,6 +4454,7 @@ int wl1271_free_hw(struct wl1271 *wl)
|
||||
|
||||
kfree(wl->fw_status);
|
||||
kfree(wl->tx_res_if);
|
||||
destroy_workqueue(wl->freezable_wq);
|
||||
|
||||
ieee80211_free_hw(wl->hw);
|
||||
|
||||
@ -4093,6 +4467,10 @@ EXPORT_SYMBOL_GPL(wl12xx_debug_level);
|
||||
module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
|
||||
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_AUTHOR("Luciano Coelho <coelho@ti.com>");
|
||||
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
|
||||
|
@ -118,7 +118,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl)
|
||||
&compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT));
|
||||
if (ret == 0) {
|
||||
wl1271_error("ELP wakeup timeout!");
|
||||
ieee80211_queue_work(wl->hw, &wl->recovery_work);
|
||||
wl12xx_queue_recovery_work(wl);
|
||||
ret = -ETIMEDOUT;
|
||||
goto err;
|
||||
} else if (ret < 0) {
|
||||
@ -169,9 +169,11 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
|
||||
wl1271_debug(DEBUG_PSM, "leaving psm");
|
||||
|
||||
/* disable beacon early termination */
|
||||
ret = wl1271_acx_bet_enable(wl, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (wl->band == IEEE80211_BAND_2GHZ) {
|
||||
ret = wl1271_acx_bet_enable(wl, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* disable beacon filtering */
|
||||
ret = wl1271_acx_beacon_filter_opt(wl, false);
|
||||
@ -202,7 +204,7 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
|
||||
info->status.rates[0].idx = -1;
|
||||
ieee80211_tx_status(wl->hw, skb);
|
||||
ieee80211_tx_status_ni(wl->hw, skb);
|
||||
filtered++;
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "wl12xx.h"
|
||||
#include "acx.h"
|
||||
@ -95,6 +96,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length)
|
||||
struct ieee80211_hdr *hdr;
|
||||
u8 *buf;
|
||||
u8 beacon = 0;
|
||||
u8 is_data = 0;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
/* the data read starts with the descriptor */
|
||||
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) {
|
||||
/* discard corrupted packets */
|
||||
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)
|
||||
hdr = (struct ieee80211_hdr *)skb->data;
|
||||
if (ieee80211_is_beacon(hdr->frame_control))
|
||||
beacon = 1;
|
||||
if (ieee80211_is_data_present(hdr->frame_control))
|
||||
is_data = 1;
|
||||
|
||||
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)
|
||||
skb_trim(skb, skb->len - desc->pad_len);
|
||||
|
||||
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)
|
||||
@ -162,6 +173,8 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status)
|
||||
u32 mem_block;
|
||||
u32 pkt_length;
|
||||
u32 pkt_offset;
|
||||
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
|
||||
bool had_data = false;
|
||||
|
||||
while (drv_rx_counter != fw_rx_counter) {
|
||||
buf_size = 0;
|
||||
@ -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
|
||||
* be dropped.
|
||||
*/
|
||||
wl1271_rx_handle_data(wl,
|
||||
wl->aggr_buf + pkt_offset,
|
||||
pkt_length);
|
||||
if (wl1271_rx_handle_data(wl,
|
||||
wl->aggr_buf + pkt_offset,
|
||||
pkt_length) == 1)
|
||||
had_data = true;
|
||||
|
||||
wl->rx_counter++;
|
||||
drv_rx_counter++;
|
||||
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)
|
||||
*/
|
||||
if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION)
|
||||
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)
|
||||
|
@ -97,6 +97,18 @@
|
||||
#define RX_BUF_SIZE_MASK 0xFFF00
|
||||
#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 {
|
||||
__le16 length;
|
||||
u8 status;
|
||||
|
@ -62,7 +62,7 @@ void wl1271_scan_complete_work(struct work_struct *work)
|
||||
|
||||
if (wl->scan.failed) {
|
||||
wl1271_info("Scan completed due to error.");
|
||||
ieee80211_queue_work(wl->hw, &wl->recovery_work);
|
||||
wl12xx_queue_recovery_work(wl);
|
||||
}
|
||||
|
||||
out:
|
||||
@ -326,7 +326,7 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct conn_scan_ch_params *channels,
|
||||
u32 band, bool radar, bool passive,
|
||||
int start)
|
||||
int start, int max_channels)
|
||||
{
|
||||
struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
|
||||
int i, j;
|
||||
@ -334,7 +334,7 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
|
||||
bool force_passive = !req->n_ssids;
|
||||
|
||||
for (i = 0, j = start;
|
||||
i < req->n_channels && j < MAX_CHANNELS_ALL_BANDS;
|
||||
i < req->n_channels && j < max_channels;
|
||||
i++) {
|
||||
flags = req->channels[i]->flags;
|
||||
|
||||
@ -380,46 +380,42 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
|
||||
return j - start;
|
||||
}
|
||||
|
||||
static int
|
||||
static bool
|
||||
wl1271_scan_sched_scan_channels(struct wl1271 *wl,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct wl1271_cmd_sched_scan_config *cfg)
|
||||
{
|
||||
int idx = 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,
|
||||
false, true, idx);
|
||||
idx += cfg->passive[0];
|
||||
|
||||
false, true, 0,
|
||||
MAX_CHANNELS_2GHZ);
|
||||
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,
|
||||
false, false, idx);
|
||||
/*
|
||||
* 5GHz channels always start at position 14, not immediately
|
||||
* after the last 2.4GHz channel
|
||||
*/
|
||||
idx = 14;
|
||||
|
||||
false, false,
|
||||
cfg->passive[0],
|
||||
MAX_CHANNELS_2GHZ);
|
||||
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,
|
||||
false, true, idx);
|
||||
idx += cfg->passive[1];
|
||||
|
||||
false, true, 0,
|
||||
MAX_CHANNELS_5GHZ);
|
||||
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,
|
||||
true, true, idx);
|
||||
idx += cfg->dfs;
|
||||
|
||||
true, true,
|
||||
cfg->passive[1],
|
||||
MAX_CHANNELS_5GHZ);
|
||||
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,
|
||||
false, false, idx);
|
||||
idx += cfg->active[1];
|
||||
false, false,
|
||||
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",
|
||||
cfg->active[0], cfg->passive[0]);
|
||||
@ -427,7 +423,9 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl,
|
||||
cfg->active[1], cfg->passive[1]);
|
||||
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,
|
||||
@ -436,7 +434,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
|
||||
{
|
||||
struct wl1271_cmd_sched_scan_config *cfg = NULL;
|
||||
struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
|
||||
int i, total_channels, ret;
|
||||
int i, ret;
|
||||
bool force_passive = !req->n_ssids;
|
||||
|
||||
wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
|
||||
@ -471,8 +469,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
|
||||
cfg->ssid_len = 0;
|
||||
}
|
||||
|
||||
total_channels = wl1271_scan_sched_scan_channels(wl, req, cfg);
|
||||
if (total_channels == 0) {
|
||||
if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) {
|
||||
wl1271_error("scan channel list is empty");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
|
@ -112,18 +112,13 @@ struct wl1271_cmd_trigger_scan_to {
|
||||
__le32 timeout;
|
||||
} __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_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 {
|
||||
SCAN_SSID_FILTER_ANY = 0,
|
||||
SCAN_SSID_FILTER_SPECIFIC = 1,
|
||||
@ -182,7 +177,9 @@ struct wl1271_cmd_sched_scan_config {
|
||||
|
||||
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;
|
||||
|
||||
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/crc7.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
#include <linux/mmc/sdio_ids.h>
|
||||
@ -45,7 +44,7 @@
|
||||
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
|
||||
#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) },
|
||||
{}
|
||||
};
|
||||
@ -107,14 +106,6 @@ static void wl1271_sdio_enable_interrupts(struct wl1271 *wl)
|
||||
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,
|
||||
size_t len, bool fixed)
|
||||
{
|
||||
@ -170,10 +161,12 @@ static int wl1271_sdio_power_on(struct wl1271 *wl)
|
||||
struct sdio_func *func = wl_to_func(wl);
|
||||
int ret;
|
||||
|
||||
/* Make sure the card will not be powered off by runtime PM */
|
||||
ret = pm_runtime_get_sync(&func->dev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
/* If enabled, tell runtime PM not to power off the card */
|
||||
if (pm_runtime_enabled(&func->dev)) {
|
||||
ret = pm_runtime_get_sync(&func->dev);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Runtime PM might be disabled, so power up the card manually */
|
||||
ret = mmc_power_restore_host(func->card->host);
|
||||
@ -200,8 +193,11 @@ static int wl1271_sdio_power_off(struct wl1271 *wl)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Let runtime PM know the card is powered off */
|
||||
return pm_runtime_put_sync(&func->dev);
|
||||
/* If enabled, let runtime PM know the card is powered off */
|
||||
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)
|
||||
@ -215,8 +211,6 @@ static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
|
||||
static struct wl1271_if_operations sdio_ops = {
|
||||
.read = wl1271_sdio_raw_read,
|
||||
.write = wl1271_sdio_raw_write,
|
||||
.reset = wl1271_sdio_reset,
|
||||
.init = wl1271_sdio_init,
|
||||
.power = wl1271_sdio_set_power,
|
||||
.dev = wl1271_sdio_wl_to_dev,
|
||||
.enable_irq = wl1271_sdio_enable_interrupts,
|
||||
@ -278,18 +272,20 @@ static int __devinit wl1271_probe(struct sdio_func *func,
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
enable_irq_wake(wl->irq);
|
||||
device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1);
|
||||
ret = enable_irq_wake(wl->irq);
|
||||
if (!ret) {
|
||||
wl->irq_wake_enabled = true;
|
||||
device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1);
|
||||
|
||||
/* if sdio can keep power while host is suspended, enable wow */
|
||||
mmcflags = sdio_get_host_pm_caps(func);
|
||||
wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags);
|
||||
|
||||
if (mmcflags & MMC_PM_KEEP_POWER)
|
||||
hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
|
||||
}
|
||||
disable_irq(wl->irq);
|
||||
|
||||
/* if sdio can keep power while host is suspended, enable wow */
|
||||
mmcflags = sdio_get_host_pm_caps(func);
|
||||
wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags);
|
||||
|
||||
if (mmcflags & MMC_PM_KEEP_POWER)
|
||||
hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
|
||||
|
||||
ret = wl1271_init_ieee80211(wl);
|
||||
if (ret)
|
||||
goto out_irq;
|
||||
@ -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 */
|
||||
pm_runtime_put_noidle(&func->dev);
|
||||
|
||||
wl1271_notice("initialized");
|
||||
|
||||
return 0;
|
||||
|
||||
out_irq:
|
||||
@ -324,8 +318,10 @@ static void __devexit wl1271_remove(struct sdio_func *func)
|
||||
pm_runtime_get_noresume(&func->dev);
|
||||
|
||||
wl1271_unregister_hw(wl);
|
||||
device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0);
|
||||
disable_irq_wake(wl->irq);
|
||||
if (wl->irq_wake_enabled) {
|
||||
device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0);
|
||||
disable_irq_wake(wl->irq);
|
||||
}
|
||||
free_irq(wl->irq, wl);
|
||||
wl1271_free_hw(wl);
|
||||
}
|
||||
@ -402,23 +398,12 @@ static struct sdio_driver wl1271_sdio_driver = {
|
||||
|
||||
static int __init wl1271_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sdio_register_driver(&wl1271_sdio_driver);
|
||||
if (ret < 0) {
|
||||
wl1271_error("failed to register sdio driver: %d", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return sdio_register_driver(&wl1271_sdio_driver);
|
||||
}
|
||||
|
||||
static void __exit wl1271_exit(void)
|
||||
{
|
||||
sdio_unregister_driver(&wl1271_sdio_driver);
|
||||
|
||||
wl1271_notice("unloaded");
|
||||
}
|
||||
|
||||
module_init(wl1271_init);
|
||||
|
@ -435,8 +435,6 @@ static int __devinit wl1271_probe(struct spi_device *spi)
|
||||
if (ret)
|
||||
goto out_irq;
|
||||
|
||||
wl1271_notice("initialized");
|
||||
|
||||
return 0;
|
||||
|
||||
out_irq:
|
||||
@ -473,23 +471,12 @@ static struct spi_driver wl1271_spi_driver = {
|
||||
|
||||
static int __init wl1271_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = spi_register_driver(&wl1271_spi_driver);
|
||||
if (ret < 0) {
|
||||
wl1271_error("failed to register spi driver: %d", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return spi_register_driver(&wl1271_spi_driver);
|
||||
}
|
||||
|
||||
static void __exit wl1271_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&wl1271_spi_driver);
|
||||
|
||||
wl1271_notice("unloaded");
|
||||
}
|
||||
|
||||
module_init(wl1271_init);
|
||||
|
@ -260,7 +260,7 @@ static int wl1271_tm_cmd_recover(struct wl1271 *wl, struct nlattr *tb[])
|
||||
{
|
||||
wl1271_debug(DEBUG_TESTMODE, "testmode cmd recover");
|
||||
|
||||
ieee80211_queue_work(wl->hw, &wl->recovery_work);
|
||||
wl12xx_queue_recovery_work(wl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -562,17 +562,29 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb)
|
||||
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)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u32 buf_offset = 0;
|
||||
bool sent_packets = false;
|
||||
bool had_data = false;
|
||||
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
|
||||
int ret;
|
||||
|
||||
if (unlikely(wl->state == WL1271_STATE_OFF))
|
||||
return;
|
||||
|
||||
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);
|
||||
if (ret == -EAGAIN) {
|
||||
/*
|
||||
@ -619,6 +631,19 @@ void wl1271_tx_work_locked(struct wl1271 *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)
|
||||
@ -702,7 +727,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
|
||||
|
||||
/* return the packet to the stack */
|
||||
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);
|
||||
}
|
||||
|
||||
@ -757,7 +782,7 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
info->status.rates[0].idx = -1;
|
||||
info->status.rates[0].count = 0;
|
||||
ieee80211_tx_status(wl->hw, skb);
|
||||
ieee80211_tx_status_ni(wl->hw, skb);
|
||||
total++;
|
||||
}
|
||||
}
|
||||
@ -795,7 +820,7 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
info->status.rates[0].idx = -1;
|
||||
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)
|
||||
info->status.rates[0].idx = -1;
|
||||
info->status.rates[0].count = 0;
|
||||
|
||||
ieee80211_tx_status(wl->hw, skb);
|
||||
ieee80211_tx_status_ni(wl->hw, skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -226,6 +226,8 @@ enum {
|
||||
#define FW_VER_MINOR_1_SPARE_STA_MIN 58
|
||||
#define FW_VER_MINOR_1_SPARE_AP_MIN 47
|
||||
|
||||
#define FW_VER_MINOR_FWLOG_STA_MIN 70
|
||||
|
||||
struct wl1271_chip {
|
||||
u32 id;
|
||||
char fw_ver_str[ETHTOOL_BUSINFO_LEN];
|
||||
@ -284,8 +286,7 @@ struct wl1271_fw_sta_status {
|
||||
u8 tx_total;
|
||||
u8 reserved1;
|
||||
__le16 reserved2;
|
||||
/* Total structure size is 68 bytes */
|
||||
u32 padding;
|
||||
__le32 log_start_addr;
|
||||
} __packed;
|
||||
|
||||
struct wl1271_fw_full_status {
|
||||
@ -359,6 +360,9 @@ enum wl12xx_flags {
|
||||
WL1271_FLAG_DUMMY_PACKET_PENDING,
|
||||
WL1271_FLAG_SUSPENDED,
|
||||
WL1271_FLAG_PENDING_WORK,
|
||||
WL1271_FLAG_SOFT_GEMINI,
|
||||
WL1271_FLAG_RX_STREAMING_STARTED,
|
||||
WL1271_FLAG_RECOVERY_IN_PROGRESS,
|
||||
};
|
||||
|
||||
struct wl1271_link {
|
||||
@ -443,6 +447,7 @@ struct wl1271 {
|
||||
struct sk_buff_head deferred_tx_queue;
|
||||
|
||||
struct work_struct tx_work;
|
||||
struct workqueue_struct *freezable_wq;
|
||||
|
||||
/* Pending TX frames */
|
||||
unsigned long tx_frames_map[BITS_TO_LONGS(ACX_TX_DESCRIPTORS)];
|
||||
@ -468,6 +473,15 @@ struct wl1271 {
|
||||
/* Network stack 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 */
|
||||
struct work_struct recovery_work;
|
||||
|
||||
@ -508,6 +522,11 @@ struct wl1271 {
|
||||
/* Default key (for WEP) */
|
||||
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 rx_config;
|
||||
unsigned int rx_filter;
|
||||
@ -573,6 +592,7 @@ struct wl1271 {
|
||||
* (currently, only "ANY" trigger is supported)
|
||||
*/
|
||||
bool wow_enabled;
|
||||
bool irq_wake_enabled;
|
||||
|
||||
/*
|
||||
* AP-mode - links indexed by HLID. The global and broadcast links
|
||||
@ -602,6 +622,9 @@ struct wl1271_station {
|
||||
|
||||
int wl1271_plt_start(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 */
|
||||
|
||||
@ -637,4 +660,15 @@ int wl1271_plt_stop(struct wl1271 *wl);
|
||||
/* WL128X requires aggregated packets to be aligned to the SDIO block size */
|
||||
#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
|
||||
|
Loading…
Reference in New Issue
Block a user