Merge branch 'for-linville' of git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx

This commit is contained in:
John W. Linville 2011-07-06 15:03:16 -04:00
commit 333c0dbfcd
24 changed files with 1178 additions and 246 deletions

View File

@ -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

View File

@ -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 @@ out:
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;

View File

@ -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);

View File

@ -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]);

View File

@ -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 @@ out_free:
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;
}

View File

@ -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__ */

View File

@ -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;
};

View File

@ -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:

View File

@ -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

View File

@ -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 {

View File

@ -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;
}

View File

@ -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)

View File

@ -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));

View File

@ -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 @@ out:
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 @@ out:
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 @@ out:
}
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 @@ power_off:
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>");

View File

@ -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++;
}
}

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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 @@ out_ack:
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);
}
}
}

View File

@ -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