dect
/
linux-2.6
Archived
13
0
Fork 0

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

This commit is contained in:
John W. Linville 2012-06-22 14:41:57 -04:00
commit d217249d72
17 changed files with 333 additions and 110 deletions

View File

@ -174,7 +174,7 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
struct wl1271_nvs_file *nvs = (struct wl1271_nvs_file *)wl->nvs;
struct wl1271_radio_parms_cmd *radio_parms;
struct wl1271_ini_general_params *gp = &nvs->general_params;
int ret;
int ret, fem_idx;
if (!wl->nvs)
return -ENODEV;
@ -185,11 +185,13 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM;
fem_idx = WL12XX_FEM_TO_NVS_ENTRY(gp->tx_bip_fem_manufacturer);
/* 2.4GHz parameters */
memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2,
sizeof(struct wl1271_ini_band_params_2));
memcpy(&radio_parms->dyn_params_2,
&nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params,
&nvs->dyn_radio_params_2[fem_idx].params,
sizeof(struct wl1271_ini_fem_params_2));
/* 5GHz parameters */
@ -197,7 +199,7 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
&nvs->stat_radio_params_5,
sizeof(struct wl1271_ini_band_params_5));
memcpy(&radio_parms->dyn_params_5,
&nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params,
&nvs->dyn_radio_params_5[fem_idx].params,
sizeof(struct wl1271_ini_fem_params_5));
wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ",
@ -216,7 +218,7 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl)
struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs;
struct wl128x_radio_parms_cmd *radio_parms;
struct wl128x_ini_general_params *gp = &nvs->general_params;
int ret;
int ret, fem_idx;
if (!wl->nvs)
return -ENODEV;
@ -227,11 +229,13 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl)
radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM;
fem_idx = WL12XX_FEM_TO_NVS_ENTRY(gp->tx_bip_fem_manufacturer);
/* 2.4GHz parameters */
memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2,
sizeof(struct wl128x_ini_band_params_2));
memcpy(&radio_parms->dyn_params_2,
&nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params,
&nvs->dyn_radio_params_2[fem_idx].params,
sizeof(struct wl128x_ini_fem_params_2));
/* 5GHz parameters */
@ -239,7 +243,7 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl)
&nvs->stat_radio_params_5,
sizeof(struct wl128x_ini_band_params_5));
memcpy(&radio_parms->dyn_params_5,
&nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params,
&nvs->dyn_radio_params_5[fem_idx].params,
sizeof(struct wl128x_ini_fem_params_5));
radio_parms->fem_vendor_and_options = nvs->fem_vendor_and_options;

View File

@ -246,6 +246,7 @@ static struct wlcore_conf wl12xx_conf = {
.forced_ps = false,
.keep_alive_interval = 55000,
.max_listen_interval = 20,
.sta_sleep_auth = WL1271_PSM_ILLEGAL,
},
.itrim = {
.enable = false,
@ -1448,10 +1449,8 @@ static int __devinit wl12xx_probe(struct platform_device *pdev)
wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0;
wl->fw_status_priv_len = 0;
wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics);
memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], &wl12xx_ht_cap,
sizeof(wl12xx_ht_cap));
memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ], &wl12xx_ht_cap,
sizeof(wl12xx_ht_cap));
wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl12xx_ht_cap);
wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl12xx_ht_cap);
wl12xx_conf_init(wl);
if (!fref_param) {

View File

@ -43,10 +43,11 @@
#define WL18XX_RX_CHECKSUM_MASK 0x40
static char *ht_mode_param = "wide";
static char *ht_mode_param = "default";
static char *board_type_param = "hdk";
static bool checksum_param = false;
static bool enable_11a_param = true;
static int num_rx_desc_param = -1;
/* phy paramters */
static int dc2dc_param = -1;
@ -372,6 +373,7 @@ static struct wlcore_conf wl18xx_conf = {
.forced_ps = false,
.keep_alive_interval = 55000,
.max_listen_interval = 20,
.sta_sleep_auth = WL1271_PSM_ILLEGAL,
},
.itrim = {
.enable = false,
@ -606,8 +608,8 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
wl->plt_fw_name = WL18XX_FW_NAME;
wl->quirks |= WLCORE_QUIRK_NO_ELP |
WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
WLCORE_QUIRK_TX_PAD_LAST_FRAME;
break;
case CHIP_ID_185x_PG10:
wl1271_debug(DEBUG_BOOT, "chip id 0x%x (185x PG10)",
@ -1021,8 +1023,7 @@ static int wl18xx_conf_init(struct wl1271 *wl, struct device *dev)
}
if (fw->size != WL18XX_CONF_SIZE) {
wl1271_error("configuration binary file size is wrong, "
"expected %ld got %zd",
wl1271_error("configuration binary file size is wrong, expected %zu got %zu",
WL18XX_CONF_SIZE, fw->size);
ret = -EINVAL;
goto out;
@ -1214,8 +1215,8 @@ static struct wlcore_ops wl18xx_ops = {
.pre_pkt_send = wl18xx_pre_pkt_send,
};
/* HT cap appropriate for wide channels */
static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap = {
/* HT cap appropriate for wide channels in 2Ghz */
static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40,
.ht_supported = true,
@ -1228,6 +1229,20 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap = {
},
};
/* HT cap appropriate for wide channels in 5Ghz */
static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_SUP_WIDTH_20_40,
.ht_supported = true,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
.mcs = {
.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
.rx_highest = cpu_to_le16(150),
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
},
};
/* HT cap appropriate for SISO 20 */
static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
.cap = IEEE80211_HT_CAP_SGI_20,
@ -1254,18 +1269,6 @@ static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = {
},
};
static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_5ghz = {
.cap = IEEE80211_HT_CAP_SGI_20,
.ht_supported = true,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
.mcs = {
.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
.rx_highest = cpu_to_le16(72),
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
},
};
static int __devinit wl18xx_probe(struct platform_device *pdev)
{
struct wl1271 *wl;
@ -1286,7 +1289,7 @@ static int __devinit wl18xx_probe(struct platform_device *pdev)
wl->ptable = wl18xx_ptable;
wl->rtable = wl18xx_rtable;
wl->num_tx_desc = 32;
wl->num_rx_desc = 16;
wl->num_rx_desc = 32;
wl->band_rate_to_idx = wl18xx_band_rate_to_idx;
wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX;
wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0;
@ -1294,32 +1297,8 @@ static int __devinit wl18xx_probe(struct platform_device *pdev)
wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics);
wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv);
if (!strcmp(ht_mode_param, "wide")) {
memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ],
&wl18xx_siso40_ht_cap,
sizeof(wl18xx_siso40_ht_cap));
memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ],
&wl18xx_siso40_ht_cap,
sizeof(wl18xx_siso40_ht_cap));
} else if (!strcmp(ht_mode_param, "mimo")) {
memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ],
&wl18xx_mimo_ht_cap_2ghz,
sizeof(wl18xx_mimo_ht_cap_2ghz));
memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ],
&wl18xx_mimo_ht_cap_5ghz,
sizeof(wl18xx_mimo_ht_cap_5ghz));
} else if (!strcmp(ht_mode_param, "siso20")) {
memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ],
&wl18xx_siso20_ht_cap,
sizeof(wl18xx_siso20_ht_cap));
memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ],
&wl18xx_siso20_ht_cap,
sizeof(wl18xx_siso20_ht_cap));
} else {
wl1271_error("invalid ht_mode '%s'", ht_mode_param);
ret = -EINVAL;
goto out_free;
}
if (num_rx_desc_param != -1)
wl->num_rx_desc = num_rx_desc_param;
ret = wl18xx_conf_init(wl, &pdev->dev);
if (ret < 0)
@ -1366,6 +1345,37 @@ static int __devinit wl18xx_probe(struct platform_device *pdev)
if (dc2dc_param != -1)
priv->conf.phy.external_pa_dc2dc = dc2dc_param;
if (!strcmp(ht_mode_param, "default")) {
/*
* Only support mimo with multiple antennas. Fall back to
* siso20.
*/
if (priv->conf.phy.number_of_assembled_ant2_4 >= 2)
wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ,
&wl18xx_mimo_ht_cap_2ghz);
else
wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ,
&wl18xx_siso20_ht_cap);
/* 5Ghz is always wide */
wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ,
&wl18xx_siso40_ht_cap_5ghz);
} else if (!strcmp(ht_mode_param, "wide")) {
wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ,
&wl18xx_siso40_ht_cap_2ghz);
wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ,
&wl18xx_siso40_ht_cap_5ghz);
} else if (!strcmp(ht_mode_param, "siso20")) {
wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ,
&wl18xx_siso20_ht_cap);
wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ,
&wl18xx_siso20_ht_cap);
} else {
wl1271_error("invalid ht_mode '%s'", ht_mode_param);
ret = -EINVAL;
goto out_free;
}
if (!checksum_param) {
wl18xx_ops.set_rx_csum = NULL;
wl18xx_ops.init_vif = NULL;
@ -1410,7 +1420,7 @@ static void __exit wl18xx_exit(void)
module_exit(wl18xx_exit);
module_param_named(ht_mode, ht_mode_param, charp, S_IRUSR);
MODULE_PARM_DESC(ht_mode, "Force HT mode: wide (default), mimo or siso20");
MODULE_PARM_DESC(ht_mode, "Force HT mode: wide or siso20");
module_param_named(board_type, board_type_param, charp, S_IRUSR);
MODULE_PARM_DESC(board_type, "Board type: fpga, hdk (default), evb, com8 or "
@ -1458,6 +1468,11 @@ module_param_named(pwr_limit_reference_11_abg,
MODULE_PARM_DESC(pwr_limit_reference_11_abg, "Power limit reference: u8 "
"(default is 0xc8)");
module_param_named(num_rx_desc,
num_rx_desc_param, int, S_IRUSR);
MODULE_PARM_DESC(num_rx_desc_param,
"Number of Rx descriptors: u8 (default is 32)");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_FIRMWARE(WL18XX_FW_NAME);

View File

@ -70,7 +70,7 @@ int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth)
struct acx_sleep_auth *auth;
int ret;
wl1271_debug(DEBUG_ACX, "acx sleep auth");
wl1271_debug(DEBUG_ACX, "acx sleep auth %d", sleep_auth);
auth = kzalloc(sizeof(*auth), GFP_KERNEL);
if (!auth) {
@ -81,7 +81,13 @@ int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth)
auth->sleep_auth = sleep_auth;
ret = wl1271_cmd_configure(wl, ACX_SLEEP_AUTH, auth, sizeof(*auth));
if (ret < 0) {
wl1271_error("could not configure sleep_auth to %d: %d",
sleep_auth, ret);
goto out;
}
wl->sleep_auth = sleep_auth;
out:
kfree(auth);
return ret;

View File

@ -118,6 +118,11 @@ enum wl1271_psm_mode {
/* Extreme low power */
WL1271_PSM_ELP = 2,
WL1271_PSM_MAX = WL1271_PSM_ELP,
/* illegal out of band value of PSM mode */
WL1271_PSM_ILLEGAL = 0xff
};
struct acx_sleep_auth {

View File

@ -652,4 +652,25 @@ struct wl12xx_cmd_stop_channel_switch {
struct wl1271_cmd_header header;
} __packed;
/* Used to check radio status after calibration */
#define MAX_TLV_LENGTH 500
#define TEST_CMD_P2G_CAL 2 /* TX BiP */
struct wl1271_cmd_cal_p2g {
struct wl1271_cmd_header header;
struct wl1271_cmd_test_header test;
__le32 ver;
__le16 len;
u8 buf[MAX_TLV_LENGTH];
u8 type;
u8 padding;
__le16 radio_status;
u8 sub_band_mask;
u8 padding2;
} __packed;
#endif /* __WL1271_CMD_H__ */

View File

@ -951,6 +951,12 @@ struct conf_conn_settings {
* Range: u16
*/
u8 max_listen_interval;
/*
* Default sleep authorization for a new STA interface. This determines
* whether we can go to ELP.
*/
u8 sta_sleep_auth;
} __packed;
enum {
@ -1276,7 +1282,7 @@ struct conf_hangover_settings {
* version, the two LSB are the lower driver's private conf
* version.
*/
#define WLCORE_CONF_VERSION (0x0001 << 16)
#define WLCORE_CONF_VERSION (0x0002 << 16)
#define WLCORE_CONF_MASK 0xffff0000
#define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \
sizeof(struct wlcore_conf))

View File

@ -963,6 +963,68 @@ static const struct file_operations fw_stats_raw_ops = {
.llseek = default_llseek,
};
static ssize_t sleep_auth_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
return wl1271_format_buffer(user_buf, count,
ppos, "%d\n",
wl->sleep_auth);
}
static ssize_t sleep_auth_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
unsigned long value;
int ret;
ret = kstrtoul_from_user(user_buf, count, 0, &value);
if (ret < 0) {
wl1271_warning("illegal value in sleep_auth");
return -EINVAL;
}
if (value < 0 || value > WL1271_PSM_MAX) {
wl1271_warning("sleep_auth must be between 0 and %d",
WL1271_PSM_MAX);
return -ERANGE;
}
mutex_lock(&wl->mutex);
wl->conf.conn.sta_sleep_auth = value;
if (wl->state == WL1271_STATE_OFF) {
/* this will show up on "read" in case we are off */
wl->sleep_auth = value;
goto out;
}
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
ret = wl1271_acx_sleep_auth(wl, value);
if (ret < 0)
goto out_sleep;
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return count;
}
static const struct file_operations sleep_auth_ops = {
.read = sleep_auth_read,
.write = sleep_auth_write,
.open = simple_open,
.llseek = default_llseek,
};
static int wl1271_debugfs_add_files(struct wl1271 *wl,
struct dentry *rootdir)
{
@ -988,6 +1050,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
DEBUGFS_ADD(irq_blk_threshold, rootdir);
DEBUGFS_ADD(irq_timeout, rootdir);
DEBUGFS_ADD(fw_stats_raw, rootdir);
DEBUGFS_ADD(sleep_auth, rootdir);
streaming = debugfs_create_dir("rx_streaming", rootdir);
if (!streaming || IS_ERR(streaming))

View File

@ -172,7 +172,19 @@ struct wl128x_ini_fem_params_5 {
/* NVS data structure */
#define WL1271_INI_NVS_SECTION_SIZE 468
#define WL1271_INI_FEM_MODULE_COUNT 2
/* We have four FEM module types: 0-RFMD, 1-TQS, 2-SKW, 3-TQS_HP */
#define WL1271_INI_FEM_MODULE_COUNT 4
/*
* In NVS we only store two FEM module entries -
* FEM modules 0,2,3 are stored in entry 0
* FEM module 1 is stored in entry 1
*/
#define WL12XX_NVS_FEM_MODULE_COUNT 2
#define WL12XX_FEM_TO_NVS_ENTRY(ini_fem_module) \
((ini_fem_module) == 1 ? 1 : 0)
#define WL1271_INI_LEGACY_NVS_FILE_SIZE 800
@ -188,13 +200,13 @@ struct wl1271_nvs_file {
struct {
struct wl1271_ini_fem_params_2 params;
u8 padding;
} dyn_radio_params_2[WL1271_INI_FEM_MODULE_COUNT];
} dyn_radio_params_2[WL12XX_NVS_FEM_MODULE_COUNT];
struct wl1271_ini_band_params_5 stat_radio_params_5;
u8 padding3;
struct {
struct wl1271_ini_fem_params_5 params;
u8 padding;
} dyn_radio_params_5[WL1271_INI_FEM_MODULE_COUNT];
} dyn_radio_params_5[WL12XX_NVS_FEM_MODULE_COUNT];
} __packed;
struct wl128x_nvs_file {
@ -209,12 +221,12 @@ struct wl128x_nvs_file {
struct {
struct wl128x_ini_fem_params_2 params;
u8 padding;
} dyn_radio_params_2[WL1271_INI_FEM_MODULE_COUNT];
} dyn_radio_params_2[WL12XX_NVS_FEM_MODULE_COUNT];
struct wl128x_ini_band_params_5 stat_radio_params_5;
u8 padding3;
struct {
struct wl128x_ini_fem_params_5 params;
u8 padding;
} dyn_radio_params_5[WL1271_INI_FEM_MODULE_COUNT];
} dyn_radio_params_5[WL12XX_NVS_FEM_MODULE_COUNT];
} __packed;
#endif

View File

@ -554,29 +554,28 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
int ret, i;
/*
* consider all existing roles before configuring psm.
* TODO: reconfigure on interface removal.
*/
if (!wl->ap_count) {
if (is_ap) {
/* Configure for power always on */
/* consider all existing roles before configuring psm. */
if (wl->ap_count == 0 && is_ap) { /* first AP */
/* Configure for power always on */
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
if (ret < 0)
return ret;
/* first STA, no APs */
} else if (wl->sta_count == 0 && wl->ap_count == 0 && !is_ap) {
u8 sta_auth = wl->conf.conn.sta_sleep_auth;
/* Configure for power according to debugfs */
if (sta_auth != WL1271_PSM_ILLEGAL)
ret = wl1271_acx_sleep_auth(wl, sta_auth);
/* Configure for power always on */
else if (wl->quirks & WLCORE_QUIRK_NO_ELP)
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
if (ret < 0)
return ret;
} else if (!wl->sta_count) {
if (wl->quirks & WLCORE_QUIRK_NO_ELP) {
/* Configure for power always on */
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
if (ret < 0)
return ret;
} else {
/* Configure for ELP power saving */
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
if (ret < 0)
return ret;
}
}
/* Configure for ELP power saving */
else
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
if (ret < 0)
return ret;
}
/* Mode specific init */

View File

@ -770,14 +770,16 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
return len;
}
#define WLCORE_FW_LOG_END 0x2000000
static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
{
u32 addr;
u32 first_addr;
u32 offset;
u32 end_of_log;
u8 *block;
if ((wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) ||
(wl->conf.fwlog.mode != WL12XX_FWLOG_ON_DEMAND) ||
(wl->conf.fwlog.mem_blocks == 0))
return;
@ -791,19 +793,26 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
* Make sure the chip is awake and the logger isn't active.
* Do not send a stop fwlog command if the fw is hanged.
*/
if (!wl1271_ps_elp_wakeup(wl) && !wl->watchdog_recovery)
wl12xx_cmd_stop_fwlog(wl);
else
if (wl1271_ps_elp_wakeup(wl))
goto out;
if (!wl->watchdog_recovery)
wl12xx_cmd_stop_fwlog(wl);
/* Read the first memory block address */
wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
first_addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
if (!first_addr)
addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
if (!addr)
goto out;
if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) {
offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor);
end_of_log = WLCORE_FW_LOG_END;
} else {
offset = sizeof(addr);
end_of_log = addr;
}
/* 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,
@ -812,13 +821,14 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
/*
* 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.
* one. The last memory block points to the first one in
* on demand mode and is equal to 0x2000000 in continuous mode.
*/
addr = le32_to_cpup((__le32 *)block);
if (!wl12xx_copy_fwlog(wl, block + sizeof(addr),
WL12XX_HW_BLOCK_SIZE - sizeof(addr)))
if (!wl12xx_copy_fwlog(wl, block + offset,
WL12XX_HW_BLOCK_SIZE - offset))
break;
} while (addr && (addr != first_addr));
} while (addr && (addr != end_of_log));
wake_up_interruptible(&wl->fwlog_waitq);
@ -1082,6 +1092,7 @@ int wl1271_plt_stop(struct wl1271 *wl)
mutex_lock(&wl->mutex);
wl1271_power_off(wl);
wl->flags = 0;
wl->sleep_auth = WL1271_PSM_ILLEGAL;
wl->state = WL1271_STATE_OFF;
wl->plt = false;
wl->rx_counter = 0;
@ -1740,6 +1751,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
wl->ap_fw_ps_map = 0;
wl->ap_ps_map = 0;
wl->sched_scanning = false;
wl->sleep_auth = WL1271_PSM_ILLEGAL;
memset(wl->roles_map, 0, sizeof(wl->roles_map));
memset(wl->links_map, 0, sizeof(wl->links_map));
memset(wl->roc_map, 0, sizeof(wl->roc_map));
@ -2146,6 +2158,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int i, ret;
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
@ -2226,11 +2239,25 @@ deinit:
wlvif->role_id = WL12XX_INVALID_ROLE_ID;
wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID;
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
if (is_ap)
wl->ap_count--;
else
wl->sta_count--;
/* Last AP, have more stations. Configure according to STA. */
if (wl->ap_count == 0 && is_ap && wl->sta_count) {
u8 sta_auth = wl->conf.conn.sta_sleep_auth;
/* Configure for power according to debugfs */
if (sta_auth != WL1271_PSM_ILLEGAL)
wl1271_acx_sleep_auth(wl, sta_auth);
/* Configure for power always on */
else if (wl->quirks & WLCORE_QUIRK_NO_ELP)
wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
/* Configure for ELP power saving */
else
wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
}
mutex_unlock(&wl->mutex);
del_timer_sync(&wlvif->rx_streaming_timer);
@ -2454,6 +2481,7 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
wlvif->channel_type = conf->channel_type;
if (is_ap) {
wl1271_set_band_rate(wl, wlvif);
ret = wl1271_init_ap_rates(wl, wlvif);
if (ret < 0)
wl1271_error("AP rate policy change failed %d",
@ -4090,16 +4118,13 @@ out:
static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
struct survey_info *survey)
{
struct wl1271 *wl = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
if (idx != 0)
return -ENOENT;
survey->channel = conf->channel;
survey->filled = SURVEY_INFO_NOISE_DBM;
survey->noise = wl->noise;
survey->filled = 0;
return 0;
}
@ -4365,9 +4390,14 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
case IEEE80211_AMPDU_RX_STOP:
if (!(*ba_bitmap & BIT(tid))) {
ret = -EINVAL;
wl1271_error("no active RX BA session on tid: %d",
/*
* this happens on reconfig - so only output a debug
* message for now, and don't fail the function.
*/
wl1271_debug(DEBUG_MAC80211,
"no active RX BA session on tid: %d",
tid);
ret = 0;
break;
}
@ -4976,6 +5006,29 @@ static void wl1271_unregister_hw(struct wl1271 *wl)
}
static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
{
.max = 2,
.types = BIT(NL80211_IFTYPE_STATION),
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_CLIENT),
},
};
static const struct ieee80211_iface_combination
wlcore_iface_combinations[] = {
{
.num_different_channels = 1,
.max_interfaces = 2,
.limits = wlcore_iface_limits,
.n_limits = ARRAY_SIZE(wlcore_iface_limits),
},
};
static int wl1271_init_ieee80211(struct wl1271 *wl)
{
static const u32 cipher_suites[] = {
@ -5069,6 +5122,11 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
/* allowed interface combinations */
wl->hw->wiphy->iface_combinations = wlcore_iface_combinations;
wl->hw->wiphy->n_iface_combinations =
ARRAY_SIZE(wlcore_iface_combinations);
SET_IEEE80211_DEV(wl->hw, wl->dev);
wl->hw->sta_data_size = sizeof(struct wl1271_station);
@ -5140,6 +5198,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size)
wl->channel_type = NL80211_CHAN_NO_HT;
wl->flags = 0;
wl->sg_enabled = true;
wl->sleep_auth = WL1271_PSM_ILLEGAL;
wl->hw_pg_ver = -1;
wl->ap_ps_map = 0;
wl->ap_fw_ps_map = 0;

View File

@ -76,7 +76,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl)
struct wl12xx_vif *wlvif;
u32 timeout;
if (wl->quirks & WLCORE_QUIRK_NO_ELP)
if (wl->sleep_auth != WL1271_PSM_ELP)
return;
/* we shouldn't get consecutive sleep requests */

View File

@ -127,7 +127,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
}
if (rx_align == WLCORE_RX_BUF_UNALIGNED)
reserved = NET_IP_ALIGN;
reserved = RX_BUF_ALIGN;
/* the data read starts with the descriptor */
desc = (struct wl1271_rx_descriptor *) data;
@ -175,7 +175,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
*/
memcpy(buf, data + sizeof(*desc), pkt_data_len);
if (rx_align == WLCORE_RX_BUF_PADDED)
skb_pull(skb, NET_IP_ALIGN);
skb_pull(skb, RX_BUF_ALIGN);
*hlid = desc->hlid;

View File

@ -103,6 +103,12 @@
/* If set, the buffer was padded by the FW to be 4 bytes aligned */
#define RX_BUF_PADDED_PAYLOAD BIT(30)
/*
* Account for the padding inserted by the FW in case of RX_ALIGNMENT
* or for fixing alignment in case the packet wasn't aligned.
*/
#define RX_BUF_ALIGN 2
/* Describes the alignment state of a Rx buffer */
enum wl_rx_buf_align {
WLCORE_RX_BUF_ALIGNED,

View File

@ -108,6 +108,20 @@ static int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[])
}
if (answer) {
/* If we got bip calibration answer print radio status */
struct wl1271_cmd_cal_p2g *params =
(struct wl1271_cmd_cal_p2g *) buf;
s16 radio_status = (s16) le16_to_cpu(params->radio_status);
if (params->test.id == TEST_CMD_P2G_CAL &&
radio_status < 0)
wl1271_warning("testmode cmd: radio status=%d",
radio_status);
else
wl1271_info("testmode cmd: radio status=%d",
radio_status);
len = nla_total_size(buf_len);
skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len);
if (!skb) {

View File

@ -305,11 +305,15 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
if (is_dummy || !wlvif)
rate_idx = 0;
else if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
/* if the packets are destined for AP (have a STA entry)
send them with AP rate policies, otherwise use default
basic rates */
/*
* if the packets are destined for AP (have a STA entry)
* send them with AP rate policies (EAPOLs are an exception),
* otherwise use default basic rates
*/
if (control->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
rate_idx = wlvif->sta.p2p_rate_idx;
else if (skb->protocol == cpu_to_be16(ETH_P_PAE))
rate_idx = wlvif->sta.basic_rate_idx;
else if (control->control.sta)
rate_idx = wlvif->sta.ap_rate_idx;
else

View File

@ -387,6 +387,9 @@ struct wl1271 {
/* mutex for protecting the tx_flush function */
struct mutex flush_mutex;
/* sleep auth value currently configured to FW */
int sleep_auth;
};
int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
@ -398,6 +401,13 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf);
static inline void
wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
struct ieee80211_sta_ht_cap *ht_cap)
{
memcpy(&wl->ht_cap[band], ht_cap, sizeof(*ht_cap));
}
/* Firmware image load chunk size */
#define CHUNK_SIZE 16384