提交 56e1bd77 编写于 作者: J John W. Linville

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

Conflicts:
	drivers/net/wireless/ti/wlcore/main.c
menuconfig WL1251 menuconfig WL1251
tristate "TI wl1251 driver support" tristate "TI wl1251 driver support"
depends on MAC80211 && EXPERIMENTAL && GENERIC_HARDIRQS depends on MAC80211 && GENERIC_HARDIRQS
select FW_LOADER select FW_LOADER
select CRC7 select CRC7
---help--- ---help---
......
wl12xx-objs = main.o cmd.o acx.o debugfs.o wl12xx-objs = main.o cmd.o acx.o debugfs.o scan.o event.o
obj-$(CONFIG_WL12XX) += wl12xx.o obj-$(CONFIG_WL12XX) += wl12xx.o
...@@ -284,3 +284,40 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl) ...@@ -284,3 +284,40 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl)
kfree(radio_parms); kfree(radio_parms);
return ret; return ret;
} }
int wl12xx_cmd_channel_switch(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct ieee80211_channel_switch *ch_switch)
{
struct wl12xx_cmd_channel_switch *cmd;
int ret;
wl1271_debug(DEBUG_ACX, "cmd channel switch");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
cmd->role_id = wlvif->role_id;
cmd->channel = ch_switch->channel->hw_value;
cmd->switch_time = ch_switch->count;
cmd->stop_tx = ch_switch->block_tx;
/* FIXME: control from mac80211 in the future */
/* Enable TX on the target channel */
cmd->post_switch_tx_disable = 0;
ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("failed to send channel switch command");
goto out_free;
}
out_free:
kfree(cmd);
out:
return ret;
}
...@@ -103,10 +103,30 @@ struct wl1271_ext_radio_parms_cmd { ...@@ -103,10 +103,30 @@ struct wl1271_ext_radio_parms_cmd {
u8 padding[3]; u8 padding[3];
} __packed; } __packed;
struct wl12xx_cmd_channel_switch {
struct wl1271_cmd_header header;
u8 role_id;
/* The new serving channel */
u8 channel;
/* Relative time of the serving channel switch in TBTT units */
u8 switch_time;
/* Stop the role TX, should expect it after radar detection */
u8 stop_tx;
/* The target channel tx status 1-stopped 0-open*/
u8 post_switch_tx_disable;
u8 padding[3];
} __packed;
int wl1271_cmd_general_parms(struct wl1271 *wl); int wl1271_cmd_general_parms(struct wl1271 *wl);
int wl128x_cmd_general_parms(struct wl1271 *wl); int wl128x_cmd_general_parms(struct wl1271 *wl);
int wl1271_cmd_radio_parms(struct wl1271 *wl); int wl1271_cmd_radio_parms(struct wl1271 *wl);
int wl128x_cmd_radio_parms(struct wl1271 *wl); int wl128x_cmd_radio_parms(struct wl1271 *wl);
int wl1271_cmd_ext_radio_parms(struct wl1271 *wl); int wl1271_cmd_ext_radio_parms(struct wl1271 *wl);
int wl12xx_cmd_channel_switch(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct ieee80211_channel_switch *ch_switch);
#endif /* __WL12XX_CMD_H__ */ #endif /* __WL12XX_CMD_H__ */
/*
* This file is part of wl12xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "event.h"
#include "scan.h"
#include "../wlcore/cmd.h"
#include "../wlcore/debug.h"
int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
bool *timeout)
{
u32 local_event;
switch (event) {
case WLCORE_EVENT_ROLE_STOP_COMPLETE:
local_event = ROLE_STOP_COMPLETE_EVENT_ID;
break;
case WLCORE_EVENT_PEER_REMOVE_COMPLETE:
local_event = PEER_REMOVE_COMPLETE_EVENT_ID;
break;
default:
/* event not implemented */
return 0;
}
return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
}
int wl12xx_process_mailbox_events(struct wl1271 *wl)
{
struct wl12xx_event_mailbox *mbox = wl->mbox;
u32 vector;
vector = le32_to_cpu(mbox->events_vector);
vector &= ~(le32_to_cpu(mbox->events_mask));
wl1271_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector);
if (vector & SCAN_COMPLETE_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "status: 0x%x",
mbox->scheduled_scan_status);
if (wl->scan_wlvif)
wl12xx_scan_completed(wl, wl->scan_wlvif);
}
if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
wl1271_debug(DEBUG_EVENT,
"PERIODIC_SCAN_REPORT_EVENT (status 0x%0x)",
mbox->scheduled_scan_status);
wlcore_scan_sched_scan_results(wl);
}
if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID)
wlcore_event_sched_scan_completed(wl,
mbox->scheduled_scan_status);
if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
wlcore_event_soft_gemini_sense(wl,
mbox->soft_gemini_sense_info);
if (vector & BSS_LOSE_EVENT_ID)
wlcore_event_beacon_loss(wl, 0xff);
if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID)
wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric);
if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)
wlcore_event_ba_rx_constraint(wl,
BIT(mbox->role_id),
mbox->rx_ba_allowed);
if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID)
wlcore_event_channel_switch(wl, 0xff,
mbox->channel_switch_status);
if (vector & DUMMY_PACKET_EVENT_ID)
wlcore_event_dummy_packet(wl);
/*
* "TX retries exceeded" has a different meaning according to mode.
* In AP mode the offending station is disconnected.
*/
if (vector & MAX_TX_RETRY_EVENT_ID)
wlcore_event_max_tx_failure(wl,
le16_to_cpu(mbox->sta_tx_retry_exceeded));
if (vector & INACTIVE_STA_EVENT_ID)
wlcore_event_inactive_sta(wl,
le16_to_cpu(mbox->sta_aging_status));
if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
wlcore_event_roc_complete(wl);
return 0;
}
/*
* This file is part of wl12xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL12XX_EVENT_H__
#define __WL12XX_EVENT_H__
#include "../wlcore/wlcore.h"
enum {
MEASUREMENT_START_EVENT_ID = BIT(8),
MEASUREMENT_COMPLETE_EVENT_ID = BIT(9),
SCAN_COMPLETE_EVENT_ID = BIT(10),
WFD_DISCOVERY_COMPLETE_EVENT_ID = BIT(11),
AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12),
RESERVED1 = BIT(13),
PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14),
ROLE_STOP_COMPLETE_EVENT_ID = BIT(15),
RADAR_DETECTED_EVENT_ID = BIT(16),
CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17),
BSS_LOSE_EVENT_ID = BIT(18),
REGAINED_BSS_EVENT_ID = BIT(19),
MAX_TX_RETRY_EVENT_ID = BIT(20),
DUMMY_PACKET_EVENT_ID = BIT(21),
SOFT_GEMINI_SENSE_EVENT_ID = BIT(22),
CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID = BIT(23),
SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24),
PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25),
INACTIVE_STA_EVENT_ID = BIT(26),
PEER_REMOVE_COMPLETE_EVENT_ID = BIT(27),
PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(28),
PERIODIC_SCAN_REPORT_EVENT_ID = BIT(29),
BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(30),
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(31),
};
struct wl12xx_event_mailbox {
__le32 events_vector;
__le32 events_mask;
__le32 reserved_1;
__le32 reserved_2;
u8 number_of_scan_results;
u8 scan_tag;
u8 completed_scan_status;
u8 reserved_3;
u8 soft_gemini_sense_info;
u8 soft_gemini_protective_info;
s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
u8 change_auto_mode_timeout;
u8 scheduled_scan_status;
u8 reserved4;
/* tuned channel (roc) */
u8 roc_channel;
__le16 hlid_removed_bitmap;
/* bitmap of aged stations (by HLID) */
__le16 sta_aging_status;
/* bitmap of stations (by HLID) which exceeded max tx retries */
__le16 sta_tx_retry_exceeded;
/* discovery completed results */
u8 discovery_tag;
u8 number_of_preq_results;
u8 number_of_prsp_results;
u8 reserved_5;
/* rx ba constraint */
u8 role_id; /* 0xFF means any role. */
u8 rx_ba_allowed;
u8 reserved_6[2];
/* Channel switch results */
u8 channel_switch_role_id;
u8 channel_switch_status;
u8 reserved_7[2];
u8 ps_poll_delivery_failure_role_ids;
u8 stopped_role_ids;
u8 started_role_ids;
u8 reserved_8[9];
} __packed;
int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
bool *timeout);
int wl12xx_process_mailbox_events(struct wl1271 *wl);
#endif
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
#include "reg.h" #include "reg.h"
#include "cmd.h" #include "cmd.h"
#include "acx.h" #include "acx.h"
#include "scan.h"
#include "event.h"
#include "debugfs.h" #include "debugfs.h"
static char *fref_param; static char *fref_param;
...@@ -208,6 +210,8 @@ static struct wlcore_conf wl12xx_conf = { ...@@ -208,6 +210,8 @@ static struct wlcore_conf wl12xx_conf = {
.tmpl_short_retry_limit = 10, .tmpl_short_retry_limit = 10,
.tmpl_long_retry_limit = 10, .tmpl_long_retry_limit = 10,
.tx_watchdog_timeout = 5000, .tx_watchdog_timeout = 5000,
.slow_link_thold = 3,
.fast_link_thold = 10,
}, },
.conn = { .conn = {
.wake_up_event = CONF_WAKE_UP_EVENT_DTIM, .wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
...@@ -265,8 +269,10 @@ static struct wlcore_conf wl12xx_conf = { ...@@ -265,8 +269,10 @@ static struct wlcore_conf wl12xx_conf = {
.scan = { .scan = {
.min_dwell_time_active = 7500, .min_dwell_time_active = 7500,
.max_dwell_time_active = 30000, .max_dwell_time_active = 30000,
.min_dwell_time_passive = 100000, .min_dwell_time_active_long = 25000,
.max_dwell_time_passive = 100000, .max_dwell_time_active_long = 50000,
.dwell_time_passive = 100000,
.dwell_time_dfs = 150000,
.num_probe_reqs = 2, .num_probe_reqs = 2,
.split_scan_timeout = 50000, .split_scan_timeout = 50000,
}, },
...@@ -368,6 +374,10 @@ static struct wlcore_conf wl12xx_conf = { ...@@ -368,6 +374,10 @@ static struct wlcore_conf wl12xx_conf = {
.increase_time = 1, .increase_time = 1,
.window_size = 16, .window_size = 16,
}, },
.recovery = {
.bug_on_recovery = 0,
.no_recovery = 0,
},
}; };
static struct wl12xx_priv_conf wl12xx_default_priv_conf = { static struct wl12xx_priv_conf wl12xx_default_priv_conf = {
...@@ -601,9 +611,9 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) ...@@ -601,9 +611,9 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
{ {
int ret; int ret;
if (wl->chip.id != CHIP_ID_1283_PG20) { if (wl->chip.id != CHIP_ID_128X_PG20) {
struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map; struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
struct wl127x_rx_mem_pool_addr rx_mem_addr; struct wl12xx_priv *priv = wl->priv;
/* /*
* Choose the block we want to read * Choose the block we want to read
...@@ -612,13 +622,13 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) ...@@ -612,13 +622,13 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
*/ */
u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK; u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK;
rx_mem_addr.addr = (mem_block << 8) + priv->rx_mem_addr->addr = (mem_block << 8) +
le32_to_cpu(wl_mem_map->packet_memory_pool_start); le32_to_cpu(wl_mem_map->packet_memory_pool_start);
rx_mem_addr.addr_extra = rx_mem_addr.addr + 4; priv->rx_mem_addr->addr_extra = priv->rx_mem_addr->addr + 4;
ret = wlcore_write(wl, WL1271_SLV_REG_DATA, &rx_mem_addr, ret = wlcore_write(wl, WL1271_SLV_REG_DATA, priv->rx_mem_addr,
sizeof(rx_mem_addr), false); sizeof(*priv->rx_mem_addr), false);
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
...@@ -631,13 +641,15 @@ static int wl12xx_identify_chip(struct wl1271 *wl) ...@@ -631,13 +641,15 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
int ret = 0; int ret = 0;
switch (wl->chip.id) { switch (wl->chip.id) {
case CHIP_ID_1271_PG10: case CHIP_ID_127X_PG10:
wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete", wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
wl->chip.id); wl->chip.id);
wl->quirks |= WLCORE_QUIRK_LEGACY_NVS | wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
WLCORE_QUIRK_DUAL_PROBE_TMPL | WLCORE_QUIRK_DUAL_PROBE_TMPL |
WLCORE_QUIRK_TKIP_HEADER_SPACE; WLCORE_QUIRK_TKIP_HEADER_SPACE |
WLCORE_QUIRK_START_STA_FAILS |
WLCORE_QUIRK_AP_ZERO_SESSION_ID;
wl->sr_fw_name = WL127X_FW_NAME_SINGLE; wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
wl->mr_fw_name = WL127X_FW_NAME_MULTI; wl->mr_fw_name = WL127X_FW_NAME_MULTI;
memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x, memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
...@@ -646,18 +658,22 @@ static int wl12xx_identify_chip(struct wl1271 *wl) ...@@ -646,18 +658,22 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
/* read data preparation is only needed by wl127x */ /* read data preparation is only needed by wl127x */
wl->ops->prepare_read = wl127x_prepare_read; wl->ops->prepare_read = wl127x_prepare_read;
wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER, wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
WL127X_MAJOR_VER, WL127X_SUBTYPE_VER, WL127X_IFTYPE_SR_VER, WL127X_MAJOR_SR_VER,
WL127X_MINOR_VER); WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
WL127X_IFTYPE_MR_VER, WL127X_MAJOR_MR_VER,
WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
break; break;
case CHIP_ID_1271_PG20: case CHIP_ID_127X_PG20:
wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
wl->chip.id); wl->chip.id);
wl->quirks |= WLCORE_QUIRK_LEGACY_NVS | wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
WLCORE_QUIRK_DUAL_PROBE_TMPL | WLCORE_QUIRK_DUAL_PROBE_TMPL |
WLCORE_QUIRK_TKIP_HEADER_SPACE; WLCORE_QUIRK_TKIP_HEADER_SPACE |
WLCORE_QUIRK_START_STA_FAILS |
WLCORE_QUIRK_AP_ZERO_SESSION_ID;
wl->plt_fw_name = WL127X_PLT_FW_NAME; wl->plt_fw_name = WL127X_PLT_FW_NAME;
wl->sr_fw_name = WL127X_FW_NAME_SINGLE; wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
wl->mr_fw_name = WL127X_FW_NAME_MULTI; wl->mr_fw_name = WL127X_FW_NAME_MULTI;
...@@ -667,12 +683,14 @@ static int wl12xx_identify_chip(struct wl1271 *wl) ...@@ -667,12 +683,14 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
/* read data preparation is only needed by wl127x */ /* read data preparation is only needed by wl127x */
wl->ops->prepare_read = wl127x_prepare_read; wl->ops->prepare_read = wl127x_prepare_read;
wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER, wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
WL127X_MAJOR_VER, WL127X_SUBTYPE_VER, WL127X_IFTYPE_SR_VER, WL127X_MAJOR_SR_VER,
WL127X_MINOR_VER); WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
WL127X_IFTYPE_MR_VER, WL127X_MAJOR_MR_VER,
WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
break; break;
case CHIP_ID_1283_PG20: case CHIP_ID_128X_PG20:
wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)", wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
wl->chip.id); wl->chip.id);
wl->plt_fw_name = WL128X_PLT_FW_NAME; wl->plt_fw_name = WL128X_PLT_FW_NAME;
...@@ -682,19 +700,29 @@ static int wl12xx_identify_chip(struct wl1271 *wl) ...@@ -682,19 +700,29 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
/* wl128x requires TX blocksize alignment */ /* wl128x requires TX blocksize alignment */
wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN | wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
WLCORE_QUIRK_DUAL_PROBE_TMPL | WLCORE_QUIRK_DUAL_PROBE_TMPL |
WLCORE_QUIRK_TKIP_HEADER_SPACE; WLCORE_QUIRK_TKIP_HEADER_SPACE |
WLCORE_QUIRK_START_STA_FAILS |
wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, WL128X_IFTYPE_VER, WLCORE_QUIRK_AP_ZERO_SESSION_ID;
WL128X_MAJOR_VER, WL128X_SUBTYPE_VER,
WL128X_MINOR_VER); wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER,
WL128X_IFTYPE_SR_VER, WL128X_MAJOR_SR_VER,
WL128X_SUBTYPE_SR_VER, WL128X_MINOR_SR_VER,
WL128X_IFTYPE_MR_VER, WL128X_MAJOR_MR_VER,
WL128X_SUBTYPE_MR_VER, WL128X_MINOR_MR_VER);
break; break;
case CHIP_ID_1283_PG10: case CHIP_ID_128X_PG10:
default: default:
wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
ret = -ENODEV; ret = -ENODEV;
goto out; goto out;
} }
/* common settings */
wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY;
wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY;
wl->sched_scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
wl->sched_scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
wl->max_channels_5 = WL12XX_MAX_CHANNELS_5GHZ;
out: out:
return ret; return ret;
} }
...@@ -1067,7 +1095,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl) ...@@ -1067,7 +1095,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
u32 clk; u32 clk;
int selected_clock = -1; int selected_clock = -1;
if (wl->chip.id == CHIP_ID_1283_PG20) { if (wl->chip.id == CHIP_ID_128X_PG20) {
ret = wl128x_boot_clk(wl, &selected_clock); ret = wl128x_boot_clk(wl, &selected_clock);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -1098,7 +1126,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl) ...@@ -1098,7 +1126,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
if (wl->chip.id == CHIP_ID_1283_PG20) if (wl->chip.id == CHIP_ID_128X_PG20)
clk |= ((selected_clock & 0x3) << 1) << 4; clk |= ((selected_clock & 0x3) << 1) << 4;
else else
clk |= (priv->ref_clock << 1) << 4; clk |= (priv->ref_clock << 1) << 4;
...@@ -1152,7 +1180,7 @@ static int wl12xx_pre_upload(struct wl1271 *wl) ...@@ -1152,7 +1180,7 @@ static int wl12xx_pre_upload(struct wl1271 *wl)
/* WL1271: The reference driver skips steps 7 to 10 (jumps directly /* WL1271: The reference driver skips steps 7 to 10 (jumps directly
* to upload_fw) */ * to upload_fw) */
if (wl->chip.id == CHIP_ID_1283_PG20) { if (wl->chip.id == CHIP_ID_128X_PG20) {
ret = wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA); ret = wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -1219,6 +1247,23 @@ static int wl12xx_boot(struct wl1271 *wl) ...@@ -1219,6 +1247,23 @@ static int wl12xx_boot(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
goto out; goto out;
wl->event_mask = BSS_LOSE_EVENT_ID |
REGAINED_BSS_EVENT_ID |
SCAN_COMPLETE_EVENT_ID |
ROLE_STOP_COMPLETE_EVENT_ID |
RSSI_SNR_TRIGGER_0_EVENT_ID |
PSPOLL_DELIVERY_FAILURE_EVENT_ID |
SOFT_GEMINI_SENSE_EVENT_ID |
PERIODIC_SCAN_REPORT_EVENT_ID |
PERIODIC_SCAN_COMPLETE_EVENT_ID |
DUMMY_PACKET_EVENT_ID |
PEER_REMOVE_COMPLETE_EVENT_ID |
BA_SESSION_RX_CONSTRAINT_EVENT_ID |
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
INACTIVE_STA_EVENT_ID |
MAX_TX_RETRY_EVENT_ID |
CHANNEL_SWITCH_COMPLETE_EVENT_ID;
ret = wlcore_boot_run_firmware(wl); ret = wlcore_boot_run_firmware(wl);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -1261,7 +1306,7 @@ static void ...@@ -1261,7 +1306,7 @@ static void
wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
u32 blks, u32 spare_blks) u32 blks, u32 spare_blks)
{ {
if (wl->chip.id == CHIP_ID_1283_PG20) { if (wl->chip.id == CHIP_ID_128X_PG20) {
desc->wl128x_mem.total_mem_blocks = blks; desc->wl128x_mem.total_mem_blocks = blks;
} else { } else {
desc->wl127x_mem.extra_blocks = spare_blks; desc->wl127x_mem.extra_blocks = spare_blks;
...@@ -1275,7 +1320,7 @@ wl12xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, ...@@ -1275,7 +1320,7 @@ wl12xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
{ {
u32 aligned_len = wlcore_calc_packet_alignment(wl, skb->len); u32 aligned_len = wlcore_calc_packet_alignment(wl, skb->len);
if (wl->chip.id == CHIP_ID_1283_PG20) { if (wl->chip.id == CHIP_ID_128X_PG20) {
desc->wl128x_mem.extra_bytes = aligned_len - skb->len; desc->wl128x_mem.extra_bytes = aligned_len - skb->len;
desc->length = cpu_to_le16(aligned_len >> 2); desc->length = cpu_to_le16(aligned_len >> 2);
...@@ -1339,7 +1384,7 @@ static int wl12xx_hw_init(struct wl1271 *wl) ...@@ -1339,7 +1384,7 @@ static int wl12xx_hw_init(struct wl1271 *wl)
{ {
int ret; int ret;
if (wl->chip.id == CHIP_ID_1283_PG20) { if (wl->chip.id == CHIP_ID_128X_PG20) {
u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE; u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;
ret = wl128x_cmd_general_parms(wl); ret = wl128x_cmd_general_parms(wl);
...@@ -1394,22 +1439,6 @@ static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl, ...@@ -1394,22 +1439,6 @@ static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl,
return wlvif->rate_set; return wlvif->rate_set;
} }
static int wl12xx_identify_fw(struct wl1271 *wl)
{
unsigned int *fw_ver = wl->chip.fw_ver;
/* 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))
wl->quirks |= WLCORE_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)
wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED;
return 0;
}
static void wl12xx_conf_init(struct wl1271 *wl) static void wl12xx_conf_init(struct wl1271 *wl)
{ {
struct wl12xx_priv *priv = wl->priv; struct wl12xx_priv *priv = wl->priv;
...@@ -1426,7 +1455,7 @@ static bool wl12xx_mac_in_fuse(struct wl1271 *wl) ...@@ -1426,7 +1455,7 @@ static bool wl12xx_mac_in_fuse(struct wl1271 *wl)
bool supported = false; bool supported = false;
u8 major, minor; u8 major, minor;
if (wl->chip.id == CHIP_ID_1283_PG20) { if (wl->chip.id == CHIP_ID_128X_PG20) {
major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver); major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver);
minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver); minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver);
...@@ -1482,7 +1511,7 @@ static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver) ...@@ -1482,7 +1511,7 @@ static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
u16 die_info; u16 die_info;
int ret; int ret;
if (wl->chip.id == CHIP_ID_1283_PG20) if (wl->chip.id == CHIP_ID_128X_PG20)
ret = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1, ret = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1,
&die_info); &die_info);
else else
...@@ -1589,16 +1618,46 @@ static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd, ...@@ -1589,16 +1618,46 @@ static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
return wlcore_set_key(wl, cmd, vif, sta, key_conf); return wlcore_set_key(wl, cmd, vif, sta, key_conf);
} }
static int wl12xx_set_peer_cap(struct wl1271 *wl,
struct ieee80211_sta_ht_cap *ht_cap,
bool allow_ht_operation,
u32 rate_set, u8 hlid)
{
return wl1271_acx_set_ht_capabilities(wl, ht_cap, allow_ht_operation,
hlid);
}
static bool wl12xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
struct wl1271_link *lnk)
{
u8 thold;
if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map))
thold = wl->conf.tx.fast_link_thold;
else
thold = wl->conf.tx.slow_link_thold;
return lnk->allocated_pkts < thold;
}
static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
struct wl1271_link *lnk)
{
/* any link is good for low priority */
return true;
}
static int wl12xx_setup(struct wl1271 *wl); static int wl12xx_setup(struct wl1271 *wl);
static struct wlcore_ops wl12xx_ops = { static struct wlcore_ops wl12xx_ops = {
.setup = wl12xx_setup, .setup = wl12xx_setup,
.identify_chip = wl12xx_identify_chip, .identify_chip = wl12xx_identify_chip,
.identify_fw = wl12xx_identify_fw,
.boot = wl12xx_boot, .boot = wl12xx_boot,
.plt_init = wl12xx_plt_init, .plt_init = wl12xx_plt_init,
.trigger_cmd = wl12xx_trigger_cmd, .trigger_cmd = wl12xx_trigger_cmd,
.ack_event = wl12xx_ack_event, .ack_event = wl12xx_ack_event,
.wait_for_event = wl12xx_wait_for_event,
.process_mailbox_events = wl12xx_process_mailbox_events,
.calc_tx_blocks = wl12xx_calc_tx_blocks, .calc_tx_blocks = wl12xx_calc_tx_blocks,
.set_tx_desc_blocks = wl12xx_set_tx_desc_blocks, .set_tx_desc_blocks = wl12xx_set_tx_desc_blocks,
.set_tx_desc_data_len = wl12xx_set_tx_desc_data_len, .set_tx_desc_data_len = wl12xx_set_tx_desc_data_len,
...@@ -1615,9 +1674,17 @@ static struct wlcore_ops wl12xx_ops = { ...@@ -1615,9 +1674,17 @@ static struct wlcore_ops wl12xx_ops = {
.set_rx_csum = NULL, .set_rx_csum = NULL,
.ap_get_mimo_wide_rate_mask = NULL, .ap_get_mimo_wide_rate_mask = NULL,
.debugfs_init = wl12xx_debugfs_add_files, .debugfs_init = wl12xx_debugfs_add_files,
.scan_start = wl12xx_scan_start,
.scan_stop = wl12xx_scan_stop,
.sched_scan_start = wl12xx_sched_scan_start,
.sched_scan_stop = wl12xx_scan_sched_scan_stop,
.get_spare_blocks = wl12xx_get_spare_blocks, .get_spare_blocks = wl12xx_get_spare_blocks,
.set_key = wl12xx_set_key, .set_key = wl12xx_set_key,
.channel_switch = wl12xx_cmd_channel_switch,
.pre_pkt_send = NULL, .pre_pkt_send = NULL,
.set_peer_cap = wl12xx_set_peer_cap,
.lnk_high_prio = wl12xx_lnk_high_prio,
.lnk_low_prio = wl12xx_lnk_low_prio,
}; };
static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
...@@ -1641,6 +1708,7 @@ static int wl12xx_setup(struct wl1271 *wl) ...@@ -1641,6 +1708,7 @@ static int wl12xx_setup(struct wl1271 *wl)
wl->rtable = wl12xx_rtable; wl->rtable = wl12xx_rtable;
wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS; wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS; wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS;
wl->num_channels = 1;
wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES; wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES;
wl->band_rate_to_idx = wl12xx_band_rate_to_idx; wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX; wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
...@@ -1693,6 +1761,10 @@ static int wl12xx_setup(struct wl1271 *wl) ...@@ -1693,6 +1761,10 @@ static int wl12xx_setup(struct wl1271 *wl)
wl1271_error("Invalid tcxo parameter %s", tcxo_param); wl1271_error("Invalid tcxo parameter %s", tcxo_param);
} }
priv->rx_mem_addr = kmalloc(sizeof(*priv->rx_mem_addr), GFP_KERNEL);
if (!priv->rx_mem_addr)
return -ENOMEM;
return 0; return 0;
} }
...@@ -1703,7 +1775,8 @@ static int wl12xx_probe(struct platform_device *pdev) ...@@ -1703,7 +1775,8 @@ static int wl12xx_probe(struct platform_device *pdev)
int ret; int ret;
hw = wlcore_alloc_hw(sizeof(struct wl12xx_priv), hw = wlcore_alloc_hw(sizeof(struct wl12xx_priv),
WL12XX_AGGR_BUFFER_SIZE); WL12XX_AGGR_BUFFER_SIZE,
sizeof(struct wl12xx_event_mailbox));
if (IS_ERR(hw)) { if (IS_ERR(hw)) {
wl1271_error("can't allocate hw"); wl1271_error("can't allocate hw");
ret = PTR_ERR(hw); ret = PTR_ERR(hw);
...@@ -1725,6 +1798,21 @@ static int wl12xx_probe(struct platform_device *pdev) ...@@ -1725,6 +1798,21 @@ static int wl12xx_probe(struct platform_device *pdev)
return ret; return ret;
} }
static int wl12xx_remove(struct platform_device *pdev)
{
struct wl1271 *wl = platform_get_drvdata(pdev);
struct wl12xx_priv *priv;
if (!wl)
goto out;
priv = wl->priv;
kfree(priv->rx_mem_addr);
out:
return wlcore_remove(pdev);
}
static const struct platform_device_id wl12xx_id_table[] = { static const struct platform_device_id wl12xx_id_table[] = {
{ "wl12xx", 0 }, { "wl12xx", 0 },
{ } /* Terminating Entry */ { } /* Terminating Entry */
...@@ -1733,7 +1821,7 @@ MODULE_DEVICE_TABLE(platform, wl12xx_id_table); ...@@ -1733,7 +1821,7 @@ MODULE_DEVICE_TABLE(platform, wl12xx_id_table);
static struct platform_driver wl12xx_driver = { static struct platform_driver wl12xx_driver = {
.probe = wl12xx_probe, .probe = wl12xx_probe,
.remove = wlcore_remove, .remove = wl12xx_remove,
.id_table = wl12xx_id_table, .id_table = wl12xx_id_table,
.driver = { .driver = {
.name = "wl12xx_driver", .name = "wl12xx_driver",
......
/*
* This file is part of wl12xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/ieee80211.h>
#include "scan.h"
#include "../wlcore/debug.h"
#include "../wlcore/tx.h"
static int wl1271_get_scan_channels(struct wl1271 *wl,
struct cfg80211_scan_request *req,
struct basic_scan_channel_params *channels,
enum ieee80211_band band, bool passive)
{
struct conf_scan_settings *c = &wl->conf.scan;
int i, j;
u32 flags;
for (i = 0, j = 0;
i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS;
i++) {
flags = req->channels[i]->flags;
if (!test_bit(i, wl->scan.scanned_ch) &&
!(flags & IEEE80211_CHAN_DISABLED) &&
(req->channels[i]->band == band) &&
/*
* In passive scans, we scan all remaining
* channels, even if not marked as such.
* In active scans, we only scan channels not
* marked as passive.
*/
(passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) {
wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
req->channels[i]->band,
req->channels[i]->center_freq);
wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
req->channels[i]->hw_value,
req->channels[i]->flags);
wl1271_debug(DEBUG_SCAN,
"max_antenna_gain %d, max_power %d",
req->channels[i]->max_antenna_gain,
req->channels[i]->max_power);
wl1271_debug(DEBUG_SCAN, "beacon_found %d",
req->channels[i]->beacon_found);
if (!passive) {
channels[j].min_duration =
cpu_to_le32(c->min_dwell_time_active);
channels[j].max_duration =
cpu_to_le32(c->max_dwell_time_active);
} else {
channels[j].min_duration =
cpu_to_le32(c->dwell_time_passive);
channels[j].max_duration =
cpu_to_le32(c->dwell_time_passive);
}
channels[j].early_termination = 0;
channels[j].tx_power_att = req->channels[i]->max_power;
channels[j].channel = req->channels[i]->hw_value;
memset(&channels[j].bssid_lsb, 0xff, 4);
memset(&channels[j].bssid_msb, 0xff, 2);
/* Mark the channels we already used */
set_bit(i, wl->scan.scanned_ch);
j++;
}
}
return j;
}
#define WL1271_NOTHING_TO_SCAN 1
static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum ieee80211_band band,
bool passive, u32 basic_rate)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct wl1271_cmd_scan *cmd;
struct wl1271_cmd_trigger_scan_to *trigger;
int ret;
u16 scan_options = 0;
/* skip active scans if we don't have SSIDs */
if (!passive && wl->scan.req->n_ssids == 0)
return WL1271_NOTHING_TO_SCAN;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
if (!cmd || !trigger) {
ret = -ENOMEM;
goto out;
}
if (wl->conf.scan.split_scan_timeout)
scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN;
if (passive)
scan_options |= WL1271_SCAN_OPT_PASSIVE;
cmd->params.role_id = wlvif->role_id;
if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) {
ret = -EINVAL;
goto out;
}
cmd->params.scan_options = cpu_to_le16(scan_options);
cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
cmd->channels,
band, passive);
if (cmd->params.n_ch == 0) {
ret = WL1271_NOTHING_TO_SCAN;
goto out;
}
cmd->params.tx_rate = cpu_to_le32(basic_rate);
cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
cmd->params.tid_trigger = CONF_TX_AC_ANY_TID;
cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
if (band == IEEE80211_BAND_2GHZ)
cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ;
else
cmd->params.band = WL1271_SCAN_BAND_5_GHZ;
if (wl->scan.ssid_len && wl->scan.ssid) {
cmd->params.ssid_len = wl->scan.ssid_len;
memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
}
memcpy(cmd->addr, vif->addr, ETH_ALEN);
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
cmd->params.role_id, band,
wl->scan.ssid, wl->scan.ssid_len,
wl->scan.req->ie,
wl->scan.req->ie_len, false);
if (ret < 0) {
wl1271_error("PROBE request template failed");
goto out;
}
trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout);
ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
sizeof(*trigger), 0);
if (ret < 0) {
wl1271_error("trigger scan to failed for hw scan");
goto out;
}
wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("SCAN failed");
goto out;
}
out:
kfree(cmd);
kfree(trigger);
return ret;
}
int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct wl1271_cmd_header *cmd = NULL;
int ret = 0;
if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE))
return -EINVAL;
wl1271_debug(DEBUG_CMD, "cmd scan stop");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd,
sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("cmd stop_scan failed");
goto out;
}
out:
kfree(cmd);
return ret;
}
void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret = 0;
enum ieee80211_band band;
u32 rate, mask;
switch (wl->scan.state) {
case WL1271_SCAN_STATE_IDLE:
break;
case WL1271_SCAN_STATE_2GHZ_ACTIVE:
band = IEEE80211_BAND_2GHZ;
mask = wlvif->bitrate_masks[band];
if (wl->scan.req->no_cck) {
mask &= ~CONF_TX_CCK_RATES;
if (!mask)
mask = CONF_TX_RATE_MASK_BASIC_P2P;
}
rate = wl1271_tx_min_rate_get(wl, mask);
ret = wl1271_scan_send(wl, wlvif, band, false, rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
wl1271_scan_stm(wl, wlvif);
}
break;
case WL1271_SCAN_STATE_2GHZ_PASSIVE:
band = IEEE80211_BAND_2GHZ;
mask = wlvif->bitrate_masks[band];
if (wl->scan.req->no_cck) {
mask &= ~CONF_TX_CCK_RATES;
if (!mask)
mask = CONF_TX_RATE_MASK_BASIC_P2P;
}
rate = wl1271_tx_min_rate_get(wl, mask);
ret = wl1271_scan_send(wl, wlvif, band, true, rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
if (wl->enable_11a)
wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
else
wl->scan.state = WL1271_SCAN_STATE_DONE;
wl1271_scan_stm(wl, wlvif);
}
break;
case WL1271_SCAN_STATE_5GHZ_ACTIVE:
band = IEEE80211_BAND_5GHZ;
rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
ret = wl1271_scan_send(wl, wlvif, band, false, rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
wl1271_scan_stm(wl, wlvif);
}
break;
case WL1271_SCAN_STATE_5GHZ_PASSIVE:
band = IEEE80211_BAND_5GHZ;
rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
ret = wl1271_scan_send(wl, wlvif, band, true, rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
wl->scan.state = WL1271_SCAN_STATE_DONE;
wl1271_scan_stm(wl, wlvif);
}
break;
case WL1271_SCAN_STATE_DONE:
wl->scan.failed = false;
cancel_delayed_work(&wl->scan_complete_work);
ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
msecs_to_jiffies(0));
break;
default:
wl1271_error("invalid scan state");
break;
}
if (ret < 0) {
cancel_delayed_work(&wl->scan_complete_work);
ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
msecs_to_jiffies(0));
}
}
static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd,
struct wlcore_scan_channels *cmd_channels)
{
memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive));
memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active));
cmd->dfs = cmd_channels->dfs;
cmd->n_pactive_ch = cmd_channels->passive_active;
memcpy(cmd->channels_2, cmd_channels->channels_2,
sizeof(cmd->channels_2));
memcpy(cmd->channels_5, cmd_channels->channels_5,
sizeof(cmd->channels_2));
/* channels_4 are not supported, so no need to copy them */
}
int wl1271_scan_sched_scan_config(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies)
{
struct wl1271_cmd_sched_scan_config *cfg = NULL;
struct wlcore_scan_channels *cfg_channels = NULL;
struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
int i, ret;
bool force_passive = !req->n_ssids;
wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
cfg->role_id = wlvif->role_id;
cfg->rssi_threshold = c->rssi_threshold;
cfg->snr_threshold = c->snr_threshold;
cfg->n_probe_reqs = c->num_probe_reqs;
/* cycles set to 0 it means infinite (until manually stopped) */
cfg->cycles = 0;
/* report APs when at least 1 is found */
cfg->report_after = 1;
/* don't stop scanning automatically when something is found */
cfg->terminate = 0;
cfg->tag = WL1271_SCAN_DEFAULT_TAG;
/* don't filter on BSS type */
cfg->bss_type = SCAN_BSS_TYPE_ANY;
/* currently NL80211 supports only a single interval */
for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++)
cfg->intervals[i] = cpu_to_le32(req->interval);
cfg->ssid_len = 0;
ret = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req);
if (ret < 0)
goto out;
cfg->filter_type = ret;
wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type);
cfg_channels = kzalloc(sizeof(*cfg_channels), GFP_KERNEL);
if (!cfg_channels) {
ret = -ENOMEM;
goto out;
}
if (!wlcore_set_scan_chan_params(wl, cfg_channels, req->channels,
req->n_channels, req->n_ssids,
SCAN_TYPE_PERIODIC)) {
wl1271_error("scan channel list is empty");
ret = -EINVAL;
goto out;
}
wl12xx_adjust_channels(cfg, cfg_channels);
if (!force_passive && cfg->active[0]) {
u8 band = IEEE80211_BAND_2GHZ;
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
wlvif->role_id, band,
req->ssids[0].ssid,
req->ssids[0].ssid_len,
ies->ie[band],
ies->len[band], true);
if (ret < 0) {
wl1271_error("2.4GHz PROBE request template failed");
goto out;
}
}
if (!force_passive && cfg->active[1]) {
u8 band = IEEE80211_BAND_5GHZ;
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
wlvif->role_id, band,
req->ssids[0].ssid,
req->ssids[0].ssid_len,
ies->ie[band],
ies->len[band], true);
if (ret < 0) {
wl1271_error("5GHz PROBE request template failed");
goto out;
}
}
wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg));
ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg,
sizeof(*cfg), 0);
if (ret < 0) {
wl1271_error("SCAN configuration failed");
goto out;
}
out:
kfree(cfg_channels);
kfree(cfg);
return ret;
}
int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct wl1271_cmd_sched_scan_start *start;
int ret = 0;
wl1271_debug(DEBUG_CMD, "cmd periodic scan start");
if (wlvif->bss_type != BSS_TYPE_STA_BSS)
return -EOPNOTSUPP;
if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) &&
test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
return -EBUSY;
start = kzalloc(sizeof(*start), GFP_KERNEL);
if (!start)
return -ENOMEM;
start->role_id = wlvif->role_id;
start->tag = WL1271_SCAN_DEFAULT_TAG;
ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start,
sizeof(*start), 0);
if (ret < 0) {
wl1271_error("failed to send scan start command");
goto out_free;
}
out_free:
kfree(start);
return ret;
}
int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies)
{
int ret;
ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
if (ret < 0)
return ret;
return wl1271_scan_sched_scan_start(wl, wlvif);
}
void wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct wl1271_cmd_sched_scan_stop *stop;
int ret = 0;
wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
/* FIXME: what to do if alloc'ing to stop fails? */
stop = kzalloc(sizeof(*stop), GFP_KERNEL);
if (!stop) {
wl1271_error("failed to alloc memory to send sched scan stop");
return;
}
stop->role_id = wlvif->role_id;
stop->tag = WL1271_SCAN_DEFAULT_TAG;
ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop,
sizeof(*stop), 0);
if (ret < 0) {
wl1271_error("failed to send sched scan stop command");
goto out_free;
}
out_free:
kfree(stop);
}
int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_scan_request *req)
{
wl1271_scan_stm(wl, wlvif);
return 0;
}
void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
wl1271_scan_stm(wl, wlvif);
}
/*
* This file is part of wl12xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL12XX_SCAN_H__
#define __WL12XX_SCAN_H__
#include "../wlcore/wlcore.h"
#include "../wlcore/cmd.h"
#include "../wlcore/scan.h"
#define WL12XX_MAX_CHANNELS_5GHZ 23
struct basic_scan_params {
/* Scan option flags (WL1271_SCAN_OPT_*) */
__le16 scan_options;
u8 role_id;
/* Number of scan channels in the list (maximum 30) */
u8 n_ch;
/* This field indicates the number of probe requests to send
per channel for an active scan */
u8 n_probe_reqs;
u8 tid_trigger;
u8 ssid_len;
u8 use_ssid_list;
/* Rate bit field for sending the probes */
__le32 tx_rate;
u8 ssid[IEEE80211_MAX_SSID_LEN];
/* Band to scan */
u8 band;
u8 scan_tag;
u8 padding2[2];
} __packed;
struct basic_scan_channel_params {
/* Duration in TU to wait for frames on a channel for active scan */
__le32 min_duration;
__le32 max_duration;
__le32 bssid_lsb;
__le16 bssid_msb;
u8 early_termination;
u8 tx_power_att;
u8 channel;
/* FW internal use only! */
u8 dfs_candidate;
u8 activity_detected;
u8 pad;
} __packed;
struct wl1271_cmd_scan {
struct wl1271_cmd_header header;
struct basic_scan_params params;
struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
/* src mac address */
u8 addr[ETH_ALEN];
u8 padding[2];
} __packed;
struct wl1271_cmd_sched_scan_config {
struct wl1271_cmd_header header;
__le32 intervals[SCAN_MAX_CYCLE_INTERVALS];
s8 rssi_threshold; /* for filtering (in dBm) */
s8 snr_threshold; /* for filtering (in dB) */
u8 cycles; /* maximum number of scan cycles */
u8 report_after; /* report when this number of results are received */
u8 terminate; /* stop scanning after reporting */
u8 tag;
u8 bss_type; /* for filtering */
u8 filter_type;
u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 n_probe_reqs; /* Number of probes requests per channel */
u8 passive[SCAN_MAX_BANDS];
u8 active[SCAN_MAX_BANDS];
u8 dfs;
u8 n_pactive_ch; /* number of pactive (passive until fw detects energy)
channels in BG band */
u8 role_id;
u8 padding[1];
struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
struct conn_scan_ch_params channels_5[WL12XX_MAX_CHANNELS_5GHZ];
struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
} __packed;
struct wl1271_cmd_sched_scan_start {
struct wl1271_cmd_header header;
u8 tag;
u8 role_id;
u8 padding[2];
} __packed;
struct wl1271_cmd_sched_scan_stop {
struct wl1271_cmd_header header;
u8 tag;
u8 role_id;
u8 padding[2];
} __packed;
int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_scan_request *req);
int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies);
void wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
#endif
...@@ -24,19 +24,37 @@ ...@@ -24,19 +24,37 @@
#include "conf.h" #include "conf.h"
/* minimum FW required for driver for wl127x */ /* WiLink 6/7 chip IDs */
#define CHIP_ID_127X_PG10 (0x04030101)
#define CHIP_ID_127X_PG20 (0x04030111)
#define CHIP_ID_128X_PG10 (0x05030101)
#define CHIP_ID_128X_PG20 (0x05030111)
/* FW chip version for wl127x */
#define WL127X_CHIP_VER 6 #define WL127X_CHIP_VER 6
#define WL127X_IFTYPE_VER 3 /* minimum single-role FW version for wl127x */
#define WL127X_MAJOR_VER 10 #define WL127X_IFTYPE_SR_VER 3
#define WL127X_SUBTYPE_VER 2 #define WL127X_MAJOR_SR_VER 10
#define WL127X_MINOR_VER 115 #define WL127X_SUBTYPE_SR_VER WLCORE_FW_VER_IGNORE
#define WL127X_MINOR_SR_VER 115
/* minimum multi-role FW version for wl127x */
#define WL127X_IFTYPE_MR_VER 5
#define WL127X_MAJOR_MR_VER 7
#define WL127X_SUBTYPE_MR_VER WLCORE_FW_VER_IGNORE
#define WL127X_MINOR_MR_VER 115
/* minimum FW required for driver for wl128x */ /* FW chip version for wl128x */
#define WL128X_CHIP_VER 7 #define WL128X_CHIP_VER 7
#define WL128X_IFTYPE_VER 3 /* minimum single-role FW version for wl128x */
#define WL128X_MAJOR_VER 10 #define WL128X_IFTYPE_SR_VER 3
#define WL128X_SUBTYPE_VER 2 #define WL128X_MAJOR_SR_VER 10
#define WL128X_MINOR_VER 115 #define WL128X_SUBTYPE_SR_VER WLCORE_FW_VER_IGNORE
#define WL128X_MINOR_SR_VER 115
/* minimum multi-role FW version for wl128x */
#define WL128X_IFTYPE_MR_VER 5
#define WL128X_MAJOR_MR_VER 7
#define WL128X_SUBTYPE_MR_VER WLCORE_FW_VER_IGNORE
#define WL128X_MINOR_MR_VER 42
#define WL12XX_AGGR_BUFFER_SIZE (4 * PAGE_SIZE) #define WL12XX_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
...@@ -55,6 +73,8 @@ struct wl12xx_priv { ...@@ -55,6 +73,8 @@ struct wl12xx_priv {
int ref_clock; int ref_clock;
int tcxo_clock; int tcxo_clock;
struct wl127x_rx_mem_pool_addr *rx_mem_addr;
}; };
#endif /* __WL12XX_PRIV_H__ */ #endif /* __WL12XX_PRIV_H__ */
wl18xx-objs = main.o acx.o tx.o io.o debugfs.o wl18xx-objs = main.o acx.o tx.o io.o debugfs.o scan.o cmd.o event.o
obj-$(CONFIG_WL18XX) += wl18xx.o obj-$(CONFIG_WL18XX) += wl18xx.o
...@@ -75,7 +75,7 @@ int wl18xx_acx_set_checksum_state(struct wl1271 *wl) ...@@ -75,7 +75,7 @@ int wl18xx_acx_set_checksum_state(struct wl1271 *wl)
acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED; acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED;
ret = wl1271_cmd_configure(wl, ACX_CHECKSUM_CONFIG, acx, sizeof(*acx)); ret = wl1271_cmd_configure(wl, ACX_CSUM_CONFIG, acx, sizeof(*acx));
if (ret < 0) { if (ret < 0) {
wl1271_warning("failed to set Tx checksum state: %d", ret); wl1271_warning("failed to set Tx checksum state: %d", ret);
goto out; goto out;
...@@ -109,3 +109,88 @@ int wl18xx_acx_clear_statistics(struct wl1271 *wl) ...@@ -109,3 +109,88 @@ int wl18xx_acx_clear_statistics(struct wl1271 *wl)
kfree(acx); kfree(acx);
return ret; return ret;
} }
int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide)
{
struct wlcore_peer_ht_operation_mode *acx;
int ret;
wl1271_debug(DEBUG_ACX, "acx peer ht operation mode hlid %d bw %d",
hlid, wide);
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
ret = -ENOMEM;
goto out;
}
acx->hlid = hlid;
acx->bandwidth = wide ? WLCORE_BANDWIDTH_40MHZ : WLCORE_BANDWIDTH_20MHZ;
ret = wl1271_cmd_configure(wl, ACX_PEER_HT_OPERATION_MODE_CFG, acx,
sizeof(*acx));
if (ret < 0) {
wl1271_warning("acx peer ht operation mode failed: %d", ret);
goto out;
}
out:
kfree(acx);
return ret;
}
/*
* this command is basically the same as wl1271_acx_ht_capabilities,
* with the addition of supported rates. they should be unified in
* the next fw api change
*/
int wl18xx_acx_set_peer_cap(struct wl1271 *wl,
struct ieee80211_sta_ht_cap *ht_cap,
bool allow_ht_operation,
u32 rate_set, u8 hlid)
{
struct wlcore_acx_peer_cap *acx;
int ret = 0;
u32 ht_capabilites = 0;
wl1271_debug(DEBUG_ACX,
"acx set cap ht_supp: %d ht_cap: %d rates: 0x%x",
ht_cap->ht_supported, ht_cap->cap, rate_set);
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
ret = -ENOMEM;
goto out;
}
if (allow_ht_operation && ht_cap->ht_supported) {
/* no need to translate capabilities - use the spec values */
ht_capabilites = ht_cap->cap;
/*
* this bit is not employed by the spec but only by FW to
* indicate peer HT support
*/
ht_capabilites |= WL12XX_HT_CAP_HT_OPERATION;
/* get data from A-MPDU parameters field */
acx->ampdu_max_length = ht_cap->ampdu_factor;
acx->ampdu_min_spacing = ht_cap->ampdu_density;
}
acx->hlid = hlid;
acx->ht_capabilites = cpu_to_le32(ht_capabilites);
acx->supported_rates = cpu_to_le32(rate_set);
ret = wl1271_cmd_configure(wl, ACX_PEER_CAP, acx, sizeof(*acx));
if (ret < 0) {
wl1271_warning("acx ht capabilities setting failed: %d", ret);
goto out;
}
out:
kfree(acx);
return ret;
}
...@@ -26,7 +26,13 @@ ...@@ -26,7 +26,13 @@
#include "../wlcore/acx.h" #include "../wlcore/acx.h"
enum { enum {
ACX_CLEAR_STATISTICS = 0x0047, ACX_NS_IPV6_FILTER = 0x0050,
ACX_PEER_HT_OPERATION_MODE_CFG = 0x0051,
ACX_CSUM_CONFIG = 0x0052,
ACX_SIM_CONFIG = 0x0053,
ACX_CLEAR_STATISTICS = 0x0054,
ACX_AUTO_RX_STREAMING = 0x0055,
ACX_PEER_CAP = 0x0056
}; };
/* numbers of bits the length field takes (add 1 for the actual number) */ /* numbers of bits the length field takes (add 1 for the actual number) */
...@@ -278,10 +284,57 @@ struct wl18xx_acx_clear_statistics { ...@@ -278,10 +284,57 @@ struct wl18xx_acx_clear_statistics {
struct acx_header header; struct acx_header header;
}; };
enum wlcore_bandwidth {
WLCORE_BANDWIDTH_20MHZ,
WLCORE_BANDWIDTH_40MHZ,
};
struct wlcore_peer_ht_operation_mode {
struct acx_header header;
u8 hlid;
u8 bandwidth; /* enum wlcore_bandwidth */
u8 padding[2];
};
/*
* ACX_PEER_CAP
* this struct is very similar to wl1271_acx_ht_capabilities, with the
* addition of supported rates
*/
struct wlcore_acx_peer_cap {
struct acx_header header;
/* bitmask of capability bits supported by the peer */
__le32 ht_capabilites;
/* rates supported by the remote peer */
__le32 supported_rates;
/* Indicates to which link these capabilities apply. */
u8 hlid;
/*
* This the maximum A-MPDU length supported by the AP. The FW may not
* exceed this length when sending A-MPDUs
*/
u8 ampdu_max_length;
/* This is the minimal spacing required when sending A-MPDUs to the AP*/
u8 ampdu_min_spacing;
u8 padding;
} __packed;
int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
u32 sdio_blk_size, u32 extra_mem_blks, u32 sdio_blk_size, u32 extra_mem_blks,
u32 len_field_size); u32 len_field_size);
int wl18xx_acx_set_checksum_state(struct wl1271 *wl); int wl18xx_acx_set_checksum_state(struct wl1271 *wl);
int wl18xx_acx_clear_statistics(struct wl1271 *wl); int wl18xx_acx_clear_statistics(struct wl1271 *wl);
int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide);
int wl18xx_acx_set_peer_cap(struct wl1271 *wl,
struct ieee80211_sta_ht_cap *ht_cap,
bool allow_ht_operation,
u32 rate_set, u8 hlid);
#endif /* __WL18XX_ACX_H__ */ #endif /* __WL18XX_ACX_H__ */
/*
* This file is part of wl18xx
*
* Copyright (C) 2011 Texas Instruments Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "../wlcore/cmd.h"
#include "../wlcore/debug.h"
#include "../wlcore/hw_ops.h"
#include "cmd.h"
int wl18xx_cmd_channel_switch(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct ieee80211_channel_switch *ch_switch)
{
struct wl18xx_cmd_channel_switch *cmd;
u32 supported_rates;
int ret;
wl1271_debug(DEBUG_ACX, "cmd channel switch");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
cmd->role_id = wlvif->role_id;
cmd->channel = ch_switch->channel->hw_value;
cmd->switch_time = ch_switch->count;
cmd->stop_tx = ch_switch->block_tx;
switch (ch_switch->channel->band) {
case IEEE80211_BAND_2GHZ:
cmd->band = WLCORE_BAND_2_4GHZ;
break;
case IEEE80211_BAND_5GHZ:
cmd->band = WLCORE_BAND_5GHZ;
break;
default:
wl1271_error("invalid channel switch band: %d",
ch_switch->channel->band);
ret = -EINVAL;
goto out_free;
}
supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
wlcore_hw_sta_get_ap_rate_mask(wl, wlvif);
if (wlvif->p2p)
supported_rates &= ~CONF_TX_CCK_RATES;
cmd->local_supported_rates = cpu_to_le32(supported_rates);
cmd->channel_type = wlvif->channel_type;
ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("failed to send channel switch command");
goto out_free;
}
out_free:
kfree(cmd);
out:
return ret;
}
/*
* This file is part of wl18xx
*
* Copyright (C) 2011 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL18XX_CMD_H__
#define __WL18XX_CMD_H__
#include "../wlcore/wlcore.h"
#include "../wlcore/acx.h"
struct wl18xx_cmd_channel_switch {
struct wl1271_cmd_header header;
u8 role_id;
/* The new serving channel */
u8 channel;
/* Relative time of the serving channel switch in TBTT units */
u8 switch_time;
/* Stop the role TX, should expect it after radar detection */
u8 stop_tx;
__le32 local_supported_rates;
u8 channel_type;
u8 band;
u8 padding[2];
} __packed;
int wl18xx_cmd_channel_switch(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct ieee80211_channel_switch *ch_switch);
#endif
...@@ -23,20 +23,21 @@ ...@@ -23,20 +23,21 @@
#define __WL18XX_CONF_H__ #define __WL18XX_CONF_H__
#define WL18XX_CONF_MAGIC 0x10e100ca #define WL18XX_CONF_MAGIC 0x10e100ca
#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0003) #define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0005)
#define WL18XX_CONF_MASK 0x0000ffff #define WL18XX_CONF_MASK 0x0000ffff
#define WL18XX_CONF_SIZE (WLCORE_CONF_SIZE + \ #define WL18XX_CONF_SIZE (WLCORE_CONF_SIZE + \
sizeof(struct wl18xx_priv_conf)) sizeof(struct wl18xx_priv_conf))
#define NUM_OF_CHANNELS_11_ABG 150 #define NUM_OF_CHANNELS_11_ABG 150
#define NUM_OF_CHANNELS_11_P 7 #define NUM_OF_CHANNELS_11_P 7
#define WL18XX_NUM_OF_SUB_BANDS 9
#define SRF_TABLE_LEN 16 #define SRF_TABLE_LEN 16
#define PIN_MUXING_SIZE 2 #define PIN_MUXING_SIZE 2
#define WL18XX_TRACE_LOSS_GAPS_TX 10
#define WL18XX_TRACE_LOSS_GAPS_RX 18
struct wl18xx_mac_and_phy_params { struct wl18xx_mac_and_phy_params {
u8 phy_standalone; u8 phy_standalone;
u8 rdl; u8 spare0;
u8 enable_clpc; u8 enable_clpc;
u8 enable_tx_low_pwr_on_siso_rdl; u8 enable_tx_low_pwr_on_siso_rdl;
u8 auto_detect; u8 auto_detect;
...@@ -69,18 +70,26 @@ struct wl18xx_mac_and_phy_params { ...@@ -69,18 +70,26 @@ struct wl18xx_mac_and_phy_params {
u8 pwr_limit_reference_11_abg; u8 pwr_limit_reference_11_abg;
u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P]; u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P];
u8 pwr_limit_reference_11p; u8 pwr_limit_reference_11p;
u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS]; u8 spare1[9];
u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS]; u8 spare2[9];
u8 primary_clock_setting_time; u8 primary_clock_setting_time;
u8 clock_valid_on_wake_up; u8 clock_valid_on_wake_up;
u8 secondary_clock_setting_time; u8 secondary_clock_setting_time;
u8 board_type; u8 board_type;
/* enable point saturation */ /* enable point saturation */
u8 psat; u8 psat;
/* low/medium/high Tx power in dBm */ /* low/medium/high Tx power in dBm for STA-HP BG */
s8 low_power_val; s8 low_power_val;
s8 med_power_val; s8 med_power_val;
s8 high_power_val; s8 high_power_val;
s8 per_sub_band_tx_trace_loss[WL18XX_TRACE_LOSS_GAPS_TX];
s8 per_sub_band_rx_trace_loss[WL18XX_TRACE_LOSS_GAPS_RX];
u8 tx_rf_margin;
/* low/medium/high Tx power in dBm for other role */
s8 low_power_val_2nd;
s8 med_power_val_2nd;
s8 high_power_val_2nd;
u8 padding[1]; u8 padding[1];
} __packed; } __packed;
......
/*
* This file is part of wl12xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "event.h"
#include "scan.h"
#include "../wlcore/cmd.h"
#include "../wlcore/debug.h"
int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
bool *timeout)
{
u32 local_event;
switch (event) {
case WLCORE_EVENT_PEER_REMOVE_COMPLETE:
local_event = PEER_REMOVE_COMPLETE_EVENT_ID;
break;
case WLCORE_EVENT_DFS_CONFIG_COMPLETE:
local_event = DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
break;
default:
/* event not implemented */
return 0;
}
return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
}
int wl18xx_process_mailbox_events(struct wl1271 *wl)
{
struct wl18xx_event_mailbox *mbox = wl->mbox;
u32 vector;
vector = le32_to_cpu(mbox->events_vector);
wl1271_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector);
if (vector & SCAN_COMPLETE_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "scan results: %d",
mbox->number_of_scan_results);
if (wl->scan_wlvif)
wl18xx_scan_completed(wl, wl->scan_wlvif);
}
if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
wl1271_debug(DEBUG_EVENT,
"PERIODIC_SCAN_REPORT_EVENT (results %d)",
mbox->number_of_sched_scan_results);
wlcore_scan_sched_scan_results(wl);
}
if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID)
wlcore_event_sched_scan_completed(wl, 1);
if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID)
wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric);
if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)
wlcore_event_ba_rx_constraint(wl,
le16_to_cpu(mbox->rx_ba_role_id_bitmap),
le16_to_cpu(mbox->rx_ba_allowed_bitmap));
if (vector & BSS_LOSS_EVENT_ID)
wlcore_event_beacon_loss(wl,
le16_to_cpu(mbox->bss_loss_bitmap));
if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID)
wlcore_event_channel_switch(wl,
le16_to_cpu(mbox->channel_switch_role_id_bitmap),
true);
if (vector & DUMMY_PACKET_EVENT_ID)
wlcore_event_dummy_packet(wl);
/*
* "TX retries exceeded" has a different meaning according to mode.
* In AP mode the offending station is disconnected.
*/
if (vector & MAX_TX_FAILURE_EVENT_ID)
wlcore_event_max_tx_failure(wl,
le32_to_cpu(mbox->tx_retry_exceeded_bitmap));
if (vector & INACTIVE_STA_EVENT_ID)
wlcore_event_inactive_sta(wl,
le32_to_cpu(mbox->inactive_sta_bitmap));
if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
wlcore_event_roc_complete(wl);
return 0;
}
/*
* This file is part of wl18xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL18XX_EVENT_H__
#define __WL18XX_EVENT_H__
#include "../wlcore/wlcore.h"
enum {
SCAN_COMPLETE_EVENT_ID = BIT(8),
RADAR_DETECTED_EVENT_ID = BIT(9),
CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(10),
BSS_LOSS_EVENT_ID = BIT(11),
MAX_TX_FAILURE_EVENT_ID = BIT(12),
DUMMY_PACKET_EVENT_ID = BIT(13),
INACTIVE_STA_EVENT_ID = BIT(14),
PEER_REMOVE_COMPLETE_EVENT_ID = BIT(15),
PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(16),
BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(17),
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(18),
DFS_CHANNELS_CONFIG_COMPLETE_EVENT = BIT(19),
PERIODIC_SCAN_REPORT_EVENT_ID = BIT(20),
};
struct wl18xx_event_mailbox {
__le32 events_vector;
u8 number_of_scan_results;
u8 number_of_sched_scan_results;
__le16 channel_switch_role_id_bitmap;
s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
/* bitmap of removed links */
__le32 hlid_removed_bitmap;
/* rx ba constraint */
__le16 rx_ba_role_id_bitmap; /* 0xfff means any role. */
__le16 rx_ba_allowed_bitmap;
/* bitmap of roc completed (by role id) */
__le16 roc_completed_bitmap;
/* bitmap of stations (by role id) with bss loss */
__le16 bss_loss_bitmap;
/* bitmap of stations (by HLID) which exceeded max tx retries */
__le32 tx_retry_exceeded_bitmap;
/* bitmap of inactive stations (by HLID) */
__le32 inactive_sta_bitmap;
} __packed;
int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
bool *timeout);
int wl18xx_process_mailbox_events(struct wl1271 *wl);
#endif
...@@ -34,10 +34,13 @@ ...@@ -34,10 +34,13 @@
#include "reg.h" #include "reg.h"
#include "conf.h" #include "conf.h"
#include "cmd.h"
#include "acx.h" #include "acx.h"
#include "tx.h" #include "tx.h"
#include "wl18xx.h" #include "wl18xx.h"
#include "io.h" #include "io.h"
#include "scan.h"
#include "event.h"
#include "debugfs.h" #include "debugfs.h"
#define WL18XX_RX_CHECKSUM_MASK 0x40 #define WL18XX_RX_CHECKSUM_MASK 0x40
...@@ -334,6 +337,8 @@ static struct wlcore_conf wl18xx_conf = { ...@@ -334,6 +337,8 @@ static struct wlcore_conf wl18xx_conf = {
.tmpl_short_retry_limit = 10, .tmpl_short_retry_limit = 10,
.tmpl_long_retry_limit = 10, .tmpl_long_retry_limit = 10,
.tx_watchdog_timeout = 5000, .tx_watchdog_timeout = 5000,
.slow_link_thold = 3,
.fast_link_thold = 30,
}, },
.conn = { .conn = {
.wake_up_event = CONF_WAKE_UP_EVENT_DTIM, .wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
...@@ -391,8 +396,10 @@ static struct wlcore_conf wl18xx_conf = { ...@@ -391,8 +396,10 @@ static struct wlcore_conf wl18xx_conf = {
.scan = { .scan = {
.min_dwell_time_active = 7500, .min_dwell_time_active = 7500,
.max_dwell_time_active = 30000, .max_dwell_time_active = 30000,
.min_dwell_time_passive = 100000, .min_dwell_time_active_long = 25000,
.max_dwell_time_passive = 100000, .max_dwell_time_active_long = 50000,
.dwell_time_passive = 100000,
.dwell_time_dfs = 150000,
.num_probe_reqs = 2, .num_probe_reqs = 2,
.split_scan_timeout = 50000, .split_scan_timeout = 50000,
}, },
...@@ -489,6 +496,10 @@ static struct wlcore_conf wl18xx_conf = { ...@@ -489,6 +496,10 @@ static struct wlcore_conf wl18xx_conf = {
.increase_time = 1, .increase_time = 1,
.window_size = 16, .window_size = 16,
}, },
.recovery = {
.bug_on_recovery = 0,
.no_recovery = 0,
},
}; };
static struct wl18xx_priv_conf wl18xx_default_priv_conf = { static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
...@@ -501,7 +512,6 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { ...@@ -501,7 +512,6 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
.clock_valid_on_wake_up = 0x00, .clock_valid_on_wake_up = 0x00,
.secondary_clock_setting_time = 0x05, .secondary_clock_setting_time = 0x05,
.board_type = BOARD_TYPE_HDK_18XX, .board_type = BOARD_TYPE_HDK_18XX,
.rdl = 0x01,
.auto_detect = 0x00, .auto_detect = 0x00,
.dedicated_fem = FEM_NONE, .dedicated_fem = FEM_NONE,
.low_band_component = COMPONENT_3_WAY_SWITCH, .low_band_component = COMPONENT_3_WAY_SWITCH,
...@@ -517,14 +527,39 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { ...@@ -517,14 +527,39 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
.enable_clpc = 0x00, .enable_clpc = 0x00,
.enable_tx_low_pwr_on_siso_rdl = 0x00, .enable_tx_low_pwr_on_siso_rdl = 0x00,
.rx_profile = 0x00, .rx_profile = 0x00,
.pwr_limit_reference_11_abg = 0xc8, .pwr_limit_reference_11_abg = 0x64,
.per_chan_pwr_limit_arr_11abg = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
.pwr_limit_reference_11p = 0x64,
.per_chan_pwr_limit_arr_11p = { 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff },
.psat = 0, .psat = 0,
.low_power_val = 0x00, .low_power_val = 0x08,
.med_power_val = 0x0a, .med_power_val = 0x12,
.high_power_val = 0x1e, .high_power_val = 0x18,
.low_power_val_2nd = 0x05,
.med_power_val_2nd = 0x0a,
.high_power_val_2nd = 0x14,
.external_pa_dc2dc = 0, .external_pa_dc2dc = 0,
.number_of_assembled_ant2_4 = 1, .number_of_assembled_ant2_4 = 2,
.number_of_assembled_ant5 = 1, .number_of_assembled_ant5 = 1,
.tx_rf_margin = 1,
}, },
}; };
...@@ -595,7 +630,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = { ...@@ -595,7 +630,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = {
}; };
/* TODO: maybe move to a new header file? */ /* TODO: maybe move to a new header file? */
#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw.bin" #define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-2.bin"
static int wl18xx_identify_chip(struct wl1271 *wl) static int wl18xx_identify_chip(struct wl1271 *wl)
{ {
...@@ -608,15 +643,18 @@ static int wl18xx_identify_chip(struct wl1271 *wl) ...@@ -608,15 +643,18 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
wl->sr_fw_name = WL18XX_FW_NAME; wl->sr_fw_name = WL18XX_FW_NAME;
/* wl18xx uses the same firmware for PLT */ /* wl18xx uses the same firmware for PLT */
wl->plt_fw_name = WL18XX_FW_NAME; wl->plt_fw_name = WL18XX_FW_NAME;
wl->quirks |= WLCORE_QUIRK_NO_ELP | wl->quirks |= WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN | WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN | WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN |
WLCORE_QUIRK_TX_PAD_LAST_FRAME; WLCORE_QUIRK_TX_PAD_LAST_FRAME |
WLCORE_QUIRK_REGDOMAIN_CONF |
wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER, WL18XX_IFTYPE_VER, WLCORE_QUIRK_DUAL_PROBE_TMPL;
WL18XX_MAJOR_VER, WL18XX_SUBTYPE_VER,
WL18XX_MINOR_VER); wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER,
WL18XX_IFTYPE_VER, WL18XX_MAJOR_VER,
WL18XX_SUBTYPE_VER, WL18XX_MINOR_VER,
/* there's no separate multi-role FW */
0, 0, 0, 0);
break; break;
case CHIP_ID_185x_PG10: case CHIP_ID_185x_PG10:
wl1271_warning("chip id 0x%x (185x PG10) is deprecated", wl1271_warning("chip id 0x%x (185x PG10) is deprecated",
...@@ -630,6 +668,11 @@ static int wl18xx_identify_chip(struct wl1271 *wl) ...@@ -630,6 +668,11 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
goto out; goto out;
} }
wl->scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
wl->scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC;
wl->sched_scan_templ_id_5 = CMD_TEMPL_PROBE_REQ_5_PERIODIC;
wl->max_channels_5 = WL18XX_MAX_CHANNELS_5GHZ;
out: out:
return ret; return ret;
} }
...@@ -843,6 +886,20 @@ static int wl18xx_boot(struct wl1271 *wl) ...@@ -843,6 +886,20 @@ static int wl18xx_boot(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
goto out; goto out;
wl->event_mask = BSS_LOSS_EVENT_ID |
SCAN_COMPLETE_EVENT_ID |
RSSI_SNR_TRIGGER_0_EVENT_ID |
PERIODIC_SCAN_COMPLETE_EVENT_ID |
PERIODIC_SCAN_REPORT_EVENT_ID |
DUMMY_PACKET_EVENT_ID |
PEER_REMOVE_COMPLETE_EVENT_ID |
BA_SESSION_RX_CONSTRAINT_EVENT_ID |
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
INACTIVE_STA_EVENT_ID |
MAX_TX_FAILURE_EVENT_ID |
CHANNEL_SWITCH_COMPLETE_EVENT_ID |
DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
ret = wlcore_boot_run_firmware(wl); ret = wlcore_boot_run_firmware(wl);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -964,7 +1021,7 @@ static int wl18xx_hw_init(struct wl1271 *wl) ...@@ -964,7 +1021,7 @@ static int wl18xx_hw_init(struct wl1271 *wl)
/* (re)init private structures. Relevant on recovery as well. */ /* (re)init private structures. Relevant on recovery as well. */
priv->last_fw_rls_idx = 0; priv->last_fw_rls_idx = 0;
priv->extra_spare_vif_count = 0; priv->extra_spare_key_count = 0;
/* set the default amount of spare blocks in the bitmap */ /* set the default amount of spare blocks in the bitmap */
ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE); ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE);
...@@ -1022,7 +1079,12 @@ static bool wl18xx_is_mimo_supported(struct wl1271 *wl) ...@@ -1022,7 +1079,12 @@ static bool wl18xx_is_mimo_supported(struct wl1271 *wl)
{ {
struct wl18xx_priv *priv = wl->priv; struct wl18xx_priv *priv = wl->priv;
return priv->conf.phy.number_of_assembled_ant2_4 >= 2; /* only support MIMO with multiple antennas, and when SISO
* is not forced through config
*/
return (priv->conf.phy.number_of_assembled_ant2_4 >= 2) &&
(priv->conf.ht.mode != HT_MODE_WIDE) &&
(priv->conf.ht.mode != HT_MODE_SISO20);
} }
/* /*
...@@ -1223,8 +1285,8 @@ static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem) ...@@ -1223,8 +1285,8 @@ static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
{ {
struct wl18xx_priv *priv = wl->priv; struct wl18xx_priv *priv = wl->priv;
/* If we have VIFs requiring extra spare, indulge them */ /* If we have keys requiring extra spare, indulge them */
if (priv->extra_spare_vif_count) if (priv->extra_spare_key_count)
return WL18XX_TX_HW_EXTRA_BLOCK_SPARE; return WL18XX_TX_HW_EXTRA_BLOCK_SPARE;
return WL18XX_TX_HW_BLOCK_SPARE; return WL18XX_TX_HW_BLOCK_SPARE;
...@@ -1236,42 +1298,48 @@ static int wl18xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd, ...@@ -1236,42 +1298,48 @@ static int wl18xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
struct ieee80211_key_conf *key_conf) struct ieee80211_key_conf *key_conf)
{ {
struct wl18xx_priv *priv = wl->priv; struct wl18xx_priv *priv = wl->priv;
bool change_spare = false; bool change_spare = false, special_enc;
int ret; int ret;
wl1271_debug(DEBUG_CRYPT, "extra spare keys before: %d",
priv->extra_spare_key_count);
special_enc = key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
key_conf->cipher == WLAN_CIPHER_SUITE_TKIP;
ret = wlcore_set_key(wl, cmd, vif, sta, key_conf);
if (ret < 0)
goto out;
/* /*
* when adding the first or removing the last GEM/TKIP interface, * when adding the first or removing the last GEM/TKIP key,
* we have to adjust the number of spare blocks. * we have to adjust the number of spare blocks.
*/ */
change_spare = (key_conf->cipher == WL1271_CIPHER_SUITE_GEM || if (special_enc) {
key_conf->cipher == WLAN_CIPHER_SUITE_TKIP) && if (cmd == SET_KEY) {
((priv->extra_spare_vif_count == 0 && cmd == SET_KEY) || /* first key */
(priv->extra_spare_vif_count == 1 && cmd == DISABLE_KEY)); change_spare = (priv->extra_spare_key_count == 0);
priv->extra_spare_key_count++;
} else if (cmd == DISABLE_KEY) {
/* last key */
change_spare = (priv->extra_spare_key_count == 1);
priv->extra_spare_key_count--;
}
}
/* no need to change spare - just regular set_key */ wl1271_debug(DEBUG_CRYPT, "extra spare keys after: %d",
if (!change_spare) priv->extra_spare_key_count);
return wlcore_set_key(wl, cmd, vif, sta, key_conf);
ret = wlcore_set_key(wl, cmd, vif, sta, key_conf); if (!change_spare)
if (ret < 0)
goto out; goto out;
/* key is now set, change the spare blocks */ /* key is now set, change the spare blocks */
if (cmd == SET_KEY) { if (priv->extra_spare_key_count)
ret = wl18xx_set_host_cfg_bitmap(wl, ret = wl18xx_set_host_cfg_bitmap(wl,
WL18XX_TX_HW_EXTRA_BLOCK_SPARE); WL18XX_TX_HW_EXTRA_BLOCK_SPARE);
if (ret < 0) else
goto out;
priv->extra_spare_vif_count++;
} else {
ret = wl18xx_set_host_cfg_bitmap(wl, ret = wl18xx_set_host_cfg_bitmap(wl,
WL18XX_TX_HW_BLOCK_SPARE); WL18XX_TX_HW_BLOCK_SPARE);
if (ret < 0)
goto out;
priv->extra_spare_vif_count--;
}
out: out:
return ret; return ret;
...@@ -1296,6 +1364,92 @@ static u32 wl18xx_pre_pkt_send(struct wl1271 *wl, ...@@ -1296,6 +1364,92 @@ static u32 wl18xx_pre_pkt_send(struct wl1271 *wl,
return buf_offset; return buf_offset;
} }
static void wl18xx_sta_rc_update(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct ieee80211_sta *sta,
u32 changed)
{
bool wide = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide);
if (!(changed & IEEE80211_RC_BW_CHANGED))
return;
mutex_lock(&wl->mutex);
/* sanity */
if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
goto out;
/* ignore the change before association */
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
goto out;
/*
* If we started out as wide, we can change the operation mode. If we
* thought this was a 20mhz AP, we have to reconnect
*/
if (wlvif->sta.role_chan_type == NL80211_CHAN_HT40MINUS ||
wlvif->sta.role_chan_type == NL80211_CHAN_HT40PLUS)
wl18xx_acx_peer_ht_operation_mode(wl, wlvif->sta.hlid, wide);
else
ieee80211_connection_loss(wl12xx_wlvif_to_vif(wlvif));
out:
mutex_unlock(&wl->mutex);
}
static int wl18xx_set_peer_cap(struct wl1271 *wl,
struct ieee80211_sta_ht_cap *ht_cap,
bool allow_ht_operation,
u32 rate_set, u8 hlid)
{
return wl18xx_acx_set_peer_cap(wl, ht_cap, allow_ht_operation,
rate_set, hlid);
}
static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
struct wl1271_link *lnk)
{
u8 thold;
struct wl18xx_fw_status_priv *status_priv =
(struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
/* suspended links are never high priority */
if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
return false;
/* the priority thresholds are taken from FW */
if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) &&
!test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map))
thold = status_priv->tx_fast_link_prio_threshold;
else
thold = status_priv->tx_slow_link_prio_threshold;
return lnk->allocated_pkts < thold;
}
static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
struct wl1271_link *lnk)
{
u8 thold;
struct wl18xx_fw_status_priv *status_priv =
(struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
thold = status_priv->tx_suspend_threshold;
else if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) &&
!test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map))
thold = status_priv->tx_fast_stop_threshold;
else
thold = status_priv->tx_slow_stop_threshold;
return lnk->allocated_pkts < thold;
}
static int wl18xx_setup(struct wl1271 *wl); static int wl18xx_setup(struct wl1271 *wl);
static struct wlcore_ops wl18xx_ops = { static struct wlcore_ops wl18xx_ops = {
...@@ -1305,6 +1459,8 @@ static struct wlcore_ops wl18xx_ops = { ...@@ -1305,6 +1459,8 @@ static struct wlcore_ops wl18xx_ops = {
.plt_init = wl18xx_plt_init, .plt_init = wl18xx_plt_init,
.trigger_cmd = wl18xx_trigger_cmd, .trigger_cmd = wl18xx_trigger_cmd,
.ack_event = wl18xx_ack_event, .ack_event = wl18xx_ack_event,
.wait_for_event = wl18xx_wait_for_event,
.process_mailbox_events = wl18xx_process_mailbox_events,
.calc_tx_blocks = wl18xx_calc_tx_blocks, .calc_tx_blocks = wl18xx_calc_tx_blocks,
.set_tx_desc_blocks = wl18xx_set_tx_desc_blocks, .set_tx_desc_blocks = wl18xx_set_tx_desc_blocks,
.set_tx_desc_data_len = wl18xx_set_tx_desc_data_len, .set_tx_desc_data_len = wl18xx_set_tx_desc_data_len,
...@@ -1320,16 +1476,26 @@ static struct wlcore_ops wl18xx_ops = { ...@@ -1320,16 +1476,26 @@ static struct wlcore_ops wl18xx_ops = {
.ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask, .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask,
.get_mac = wl18xx_get_mac, .get_mac = wl18xx_get_mac,
.debugfs_init = wl18xx_debugfs_add_files, .debugfs_init = wl18xx_debugfs_add_files,
.scan_start = wl18xx_scan_start,
.scan_stop = wl18xx_scan_stop,
.sched_scan_start = wl18xx_sched_scan_start,
.sched_scan_stop = wl18xx_scan_sched_scan_stop,
.handle_static_data = wl18xx_handle_static_data, .handle_static_data = wl18xx_handle_static_data,
.get_spare_blocks = wl18xx_get_spare_blocks, .get_spare_blocks = wl18xx_get_spare_blocks,
.set_key = wl18xx_set_key, .set_key = wl18xx_set_key,
.channel_switch = wl18xx_cmd_channel_switch,
.pre_pkt_send = wl18xx_pre_pkt_send, .pre_pkt_send = wl18xx_pre_pkt_send,
.sta_rc_update = wl18xx_sta_rc_update,
.set_peer_cap = wl18xx_set_peer_cap,
.lnk_high_prio = wl18xx_lnk_high_prio,
.lnk_low_prio = wl18xx_lnk_low_prio,
}; };
/* HT cap appropriate for wide channels in 2Ghz */ /* HT cap appropriate for wide channels in 2Ghz */
static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = { static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40, IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40 |
IEEE80211_HT_CAP_GRN_FLD,
.ht_supported = true, .ht_supported = true,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
...@@ -1343,7 +1509,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = { ...@@ -1343,7 +1509,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
/* HT cap appropriate for wide channels in 5Ghz */ /* HT cap appropriate for wide channels in 5Ghz */
static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = { static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_SUP_WIDTH_20_40, IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_GRN_FLD,
.ht_supported = true, .ht_supported = true,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
...@@ -1356,7 +1523,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = { ...@@ -1356,7 +1523,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
/* HT cap appropriate for SISO 20 */ /* HT cap appropriate for SISO 20 */
static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = { static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
.cap = IEEE80211_HT_CAP_SGI_20, .cap = IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_GRN_FLD,
.ht_supported = true, .ht_supported = true,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
...@@ -1369,7 +1537,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = { ...@@ -1369,7 +1537,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
/* HT cap appropriate for MIMO rates in 20mhz channel */ /* HT cap appropriate for MIMO rates in 20mhz channel */
static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = { static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = {
.cap = IEEE80211_HT_CAP_SGI_20, .cap = IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_GRN_FLD,
.ht_supported = true, .ht_supported = true,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
...@@ -1387,7 +1556,8 @@ static int wl18xx_setup(struct wl1271 *wl) ...@@ -1387,7 +1556,8 @@ static int wl18xx_setup(struct wl1271 *wl)
wl->rtable = wl18xx_rtable; wl->rtable = wl18xx_rtable;
wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS; wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS;
wl->num_rx_desc = WL18XX_NUM_TX_DESCRIPTORS; wl->num_rx_desc = WL18XX_NUM_RX_DESCRIPTORS;
wl->num_channels = 2;
wl->num_mac_addr = WL18XX_NUM_MAC_ADDRESSES; wl->num_mac_addr = WL18XX_NUM_MAC_ADDRESSES;
wl->band_rate_to_idx = wl18xx_band_rate_to_idx; wl->band_rate_to_idx = wl18xx_band_rate_to_idx;
wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX; wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX;
...@@ -1506,7 +1676,8 @@ static int wl18xx_probe(struct platform_device *pdev) ...@@ -1506,7 +1676,8 @@ static int wl18xx_probe(struct platform_device *pdev)
int ret; int ret;
hw = wlcore_alloc_hw(sizeof(struct wl18xx_priv), hw = wlcore_alloc_hw(sizeof(struct wl18xx_priv),
WL18XX_AGGR_BUFFER_SIZE); WL18XX_AGGR_BUFFER_SIZE,
sizeof(struct wl18xx_event_mailbox));
if (IS_ERR(hw)) { if (IS_ERR(hw)) {
wl1271_error("can't allocate hw"); wl1271_error("can't allocate hw");
ret = PTR_ERR(hw); ret = PTR_ERR(hw);
......
/*
* This file is part of wl18xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/ieee80211.h>
#include "scan.h"
#include "../wlcore/debug.h"
static void wl18xx_adjust_channels(struct wl18xx_cmd_scan_params *cmd,
struct wlcore_scan_channels *cmd_channels)
{
memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive));
memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active));
cmd->dfs = cmd_channels->dfs;
cmd->passive_active = cmd_channels->passive_active;
memcpy(cmd->channels_2, cmd_channels->channels_2,
sizeof(cmd->channels_2));
memcpy(cmd->channels_5, cmd_channels->channels_5,
sizeof(cmd->channels_2));
/* channels_4 are not supported, so no need to copy them */
}
static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_scan_request *req)
{
struct wl18xx_cmd_scan_params *cmd;
struct wlcore_scan_channels *cmd_channels = NULL;
int ret;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
cmd->role_id = wlvif->role_id;
if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) {
ret = -EINVAL;
goto out;
}
cmd->scan_type = SCAN_TYPE_SEARCH;
cmd->rssi_threshold = -127;
cmd->snr_threshold = 0;
cmd->bss_type = SCAN_BSS_TYPE_ANY;
cmd->ssid_from_list = 0;
cmd->filter = 0;
cmd->add_broadcast = 0;
cmd->urgency = 0;
cmd->protect = 0;
cmd->n_probe_reqs = wl->conf.scan.num_probe_reqs;
cmd->terminate_after = 0;
/* configure channels */
WARN_ON(req->n_ssids > 1);
cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL);
if (!cmd_channels) {
ret = -ENOMEM;
goto out;
}
wlcore_set_scan_chan_params(wl, cmd_channels, req->channels,
req->n_channels, req->n_ssids,
SCAN_TYPE_SEARCH);
wl18xx_adjust_channels(cmd, cmd_channels);
/*
* all the cycles params (except total cycles) should
* remain 0 for normal scan
*/
cmd->total_cycles = 1;
if (req->no_cck)
cmd->rate = WL18XX_SCAN_RATE_6;
cmd->tag = WL1271_SCAN_DEFAULT_TAG;
if (req->n_ssids) {
cmd->ssid_len = req->ssids[0].ssid_len;
memcpy(cmd->ssid, req->ssids[0].ssid, cmd->ssid_len);
}
/* TODO: per-band ies? */
if (cmd->active[0]) {
u8 band = IEEE80211_BAND_2GHZ;
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
cmd->role_id, band,
req->ssids ? req->ssids[0].ssid : NULL,
req->ssids ? req->ssids[0].ssid_len : 0,
req->ie,
req->ie_len,
false);
if (ret < 0) {
wl1271_error("2.4GHz PROBE request template failed");
goto out;
}
}
if (cmd->active[1] || cmd->dfs) {
u8 band = IEEE80211_BAND_5GHZ;
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
cmd->role_id, band,
req->ssids ? req->ssids[0].ssid : NULL,
req->ssids ? req->ssids[0].ssid_len : 0,
req->ie,
req->ie_len,
false);
if (ret < 0) {
wl1271_error("5GHz PROBE request template failed");
goto out;
}
}
wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("SCAN failed");
goto out;
}
out:
kfree(cmd_channels);
kfree(cmd);
return ret;
}
void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
wl->scan.failed = false;
cancel_delayed_work(&wl->scan_complete_work);
ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
msecs_to_jiffies(0));
}
static
int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies)
{
struct wl18xx_cmd_scan_params *cmd;
struct wlcore_scan_channels *cmd_channels = NULL;
struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
int ret;
int filter_type;
wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
filter_type = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req);
if (filter_type < 0)
return filter_type;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
cmd->role_id = wlvif->role_id;
if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) {
ret = -EINVAL;
goto out;
}
cmd->scan_type = SCAN_TYPE_PERIODIC;
cmd->rssi_threshold = c->rssi_threshold;
cmd->snr_threshold = c->snr_threshold;
/* don't filter on BSS type */
cmd->bss_type = SCAN_BSS_TYPE_ANY;
cmd->ssid_from_list = 1;
if (filter_type == SCAN_SSID_FILTER_LIST)
cmd->filter = 1;
cmd->add_broadcast = 0;
cmd->urgency = 0;
cmd->protect = 0;
cmd->n_probe_reqs = c->num_probe_reqs;
/* don't stop scanning automatically when something is found */
cmd->terminate_after = 0;
cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL);
if (!cmd_channels) {
ret = -ENOMEM;
goto out;
}
/* configure channels */
wlcore_set_scan_chan_params(wl, cmd_channels, req->channels,
req->n_channels, req->n_ssids,
SCAN_TYPE_PERIODIC);
wl18xx_adjust_channels(cmd, cmd_channels);
cmd->short_cycles_sec = 0;
cmd->long_cycles_sec = cpu_to_le16(req->interval);
cmd->short_cycles_count = 0;
cmd->total_cycles = 0;
cmd->tag = WL1271_SCAN_DEFAULT_TAG;
/* create a PERIODIC_SCAN_REPORT_EVENT whenever we've got a match */
cmd->report_threshold = 1;
cmd->terminate_on_report = 0;
if (cmd->active[0]) {
u8 band = IEEE80211_BAND_2GHZ;
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
cmd->role_id, band,
req->ssids ? req->ssids[0].ssid : NULL,
req->ssids ? req->ssids[0].ssid_len : 0,
ies->ie[band],
ies->len[band],
true);
if (ret < 0) {
wl1271_error("2.4GHz PROBE request template failed");
goto out;
}
}
if (cmd->active[1] || cmd->dfs) {
u8 band = IEEE80211_BAND_5GHZ;
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
cmd->role_id, band,
req->ssids ? req->ssids[0].ssid : NULL,
req->ssids ? req->ssids[0].ssid_len : 0,
ies->ie[band],
ies->len[band],
true);
if (ret < 0) {
wl1271_error("5GHz PROBE request template failed");
goto out;
}
}
wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("SCAN failed");
goto out;
}
out:
kfree(cmd_channels);
kfree(cmd);
return ret;
}
int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies)
{
return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies);
}
static int __wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 scan_type)
{
struct wl18xx_cmd_scan_stop *stop;
int ret;
wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
stop = kzalloc(sizeof(*stop), GFP_KERNEL);
if (!stop) {
wl1271_error("failed to alloc memory to send sched scan stop");
return -ENOMEM;
}
stop->role_id = wlvif->role_id;
stop->scan_type = scan_type;
ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, stop, sizeof(*stop), 0);
if (ret < 0) {
wl1271_error("failed to send sched scan stop command");
goto out_free;
}
out_free:
kfree(stop);
return ret;
}
void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
__wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_PERIODIC);
}
int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_scan_request *req)
{
return wl18xx_scan_send(wl, wlvif, req);
}
int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
return __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_SEARCH);
}
/*
* This file is part of wl18xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL18XX_SCAN_H__
#define __WL18XX_SCAN_H__
#include "../wlcore/wlcore.h"
#include "../wlcore/cmd.h"
#include "../wlcore/scan.h"
struct tracking_ch_params {
struct conn_scan_ch_params channel;
__le32 bssid_lsb;
__le16 bssid_msb;
u8 padding[2];
} __packed;
/* probe request rate */
enum
{
WL18XX_SCAN_RATE_1 = 0,
WL18XX_SCAN_RATE_5_5 = 1,
WL18XX_SCAN_RATE_6 = 2,
};
#define WL18XX_MAX_CHANNELS_5GHZ 32
struct wl18xx_cmd_scan_params {
struct wl1271_cmd_header header;
u8 role_id;
u8 scan_type;
s8 rssi_threshold; /* for filtering (in dBm) */
s8 snr_threshold; /* for filtering (in dB) */
u8 bss_type; /* for filtering */
u8 ssid_from_list; /* use ssid from configured ssid list */
u8 filter; /* forward only results with matching ssids */
/*
* add broadcast ssid in addition to the configured ssids.
* the driver should add dummy entry for it (?).
*/
u8 add_broadcast;
u8 urgency;
u8 protect; /* ??? */
u8 n_probe_reqs; /* Number of probes requests per channel */
u8 terminate_after; /* early terminate scan operation */
u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */
u8 active[SCAN_MAX_BANDS]; /* number of active scan channels */
u8 dfs; /* number of dfs channels in 5ghz */
u8 passive_active; /* number of passive before active channels 2.4ghz */
__le16 short_cycles_sec;
__le16 long_cycles_sec;
u8 short_cycles_count;
u8 total_cycles; /* 0 - infinite */
u8 padding[2];
union {
struct {
struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
struct conn_scan_ch_params channels_5[WL18XX_MAX_CHANNELS_5GHZ];
struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
};
struct tracking_ch_params channels_tracking[WL1271_SCAN_MAX_CHANNELS];
} ;
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */
u8 tag;
u8 rate;
/* send SCAN_REPORT_EVENT in periodic scans after each cycle
* if number of results >= report_threshold. Must be 0 for
* non periodic scans
*/
u8 report_threshold;
/* Should periodic scan stop after a report event was created.
* Must be 0 for non periodic scans.
*/
u8 terminate_on_report;
u8 padding1[3];
} __packed;
struct wl18xx_cmd_scan_stop {
struct wl1271_cmd_header header;
u8 role_id;
u8 scan_type;
u8 padding[2];
} __packed;
int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_scan_request *req);
int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies);
void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
#endif
...@@ -28,6 +28,49 @@ ...@@ -28,6 +28,49 @@
#include "wl18xx.h" #include "wl18xx.h"
#include "tx.h" #include "tx.h"
static
void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
struct ieee80211_tx_rate *rate)
{
u8 fw_rate = wl->fw_status_2->counters.tx_last_rate;
if (fw_rate > CONF_HW_RATE_INDEX_MAX) {
wl1271_error("last Tx rate invalid: %d", fw_rate);
rate->idx = 0;
rate->flags = 0;
return;
}
if (fw_rate <= CONF_HW_RATE_INDEX_54MBPS) {
rate->idx = fw_rate;
rate->flags = 0;
} else {
rate->flags = IEEE80211_TX_RC_MCS;
rate->idx = fw_rate - CONF_HW_RATE_INDEX_MCS0;
/* SGI modifier is counted as a separate rate */
if (fw_rate >= CONF_HW_RATE_INDEX_MCS7_SGI)
(rate->idx)--;
if (fw_rate == CONF_HW_RATE_INDEX_MCS15_SGI)
(rate->idx)--;
/* this also covers the 40Mhz SGI case (= MCS15) */
if (fw_rate == CONF_HW_RATE_INDEX_MCS7_SGI ||
fw_rate == CONF_HW_RATE_INDEX_MCS15_SGI)
rate->flags |= IEEE80211_TX_RC_SHORT_GI;
if (fw_rate > CONF_HW_RATE_INDEX_MCS7_SGI && vif) {
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
if (wlvif->channel_type == NL80211_CHAN_HT40MINUS ||
wlvif->channel_type == NL80211_CHAN_HT40PLUS) {
/* adjustment needed for range 0-7 */
rate->idx -= 8;
rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
}
}
}
}
static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte) static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
{ {
struct ieee80211_tx_info *info; struct ieee80211_tx_info *info;
...@@ -44,7 +87,6 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte) ...@@ -44,7 +87,6 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
/* a zero bit indicates Tx success */ /* a zero bit indicates Tx success */
tx_success = !(tx_stat_byte & BIT(WL18XX_TX_STATUS_STAT_BIT_IDX)); tx_success = !(tx_stat_byte & BIT(WL18XX_TX_STATUS_STAT_BIT_IDX));
skb = wl->tx_frames[id]; skb = wl->tx_frames[id];
info = IEEE80211_SKB_CB(skb); info = IEEE80211_SKB_CB(skb);
...@@ -56,11 +98,13 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte) ...@@ -56,11 +98,13 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
/* update the TX status info */ /* update the TX status info */
if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK))
info->flags |= IEEE80211_TX_STAT_ACK; info->flags |= IEEE80211_TX_STAT_ACK;
/*
* first pass info->control.vif while it's valid, and then fill out
* the info->status structures
*/
wl18xx_get_last_tx_rate(wl, info->control.vif, &info->status.rates[0]);
/* no real data about Tx completion */ info->status.rates[0].count = 1; /* no data about retries */
info->status.rates[0].idx = -1;
info->status.rates[0].count = 0;
info->status.rates[0].flags = 0;
info->status.ack_signal = -1; info->status.ack_signal = -1;
if (!tx_success) if (!tx_success)
......
...@@ -26,10 +26,10 @@ ...@@ -26,10 +26,10 @@
/* minimum FW required for driver */ /* minimum FW required for driver */
#define WL18XX_CHIP_VER 8 #define WL18XX_CHIP_VER 8
#define WL18XX_IFTYPE_VER 2 #define WL18XX_IFTYPE_VER 5
#define WL18XX_MAJOR_VER 0 #define WL18XX_MAJOR_VER WLCORE_FW_VER_IGNORE
#define WL18XX_SUBTYPE_VER 0 #define WL18XX_SUBTYPE_VER WLCORE_FW_VER_IGNORE
#define WL18XX_MINOR_VER 100 #define WL18XX_MINOR_VER 28
#define WL18XX_CMD_MAX_SIZE 740 #define WL18XX_CMD_MAX_SIZE 740
...@@ -49,8 +49,8 @@ struct wl18xx_priv { ...@@ -49,8 +49,8 @@ struct wl18xx_priv {
/* Index of last released Tx desc in FW */ /* Index of last released Tx desc in FW */
u8 last_fw_rls_idx; u8 last_fw_rls_idx;
/* number of VIFs requiring extra spare mem-blocks */ /* number of keys requiring extra spare mem-blocks */
int extra_spare_vif_count; int extra_spare_key_count;
}; };
#define WL18XX_FW_MAX_TX_STATUS_DESC 33 #define WL18XX_FW_MAX_TX_STATUS_DESC 33
...@@ -68,7 +68,43 @@ struct wl18xx_fw_status_priv { ...@@ -68,7 +68,43 @@ struct wl18xx_fw_status_priv {
*/ */
u8 released_tx_desc[WL18XX_FW_MAX_TX_STATUS_DESC]; u8 released_tx_desc[WL18XX_FW_MAX_TX_STATUS_DESC];
u8 padding[2]; /* A bitmap representing the currently suspended links. The suspend
* is short lived, for multi-channel Tx requirements.
*/
__le32 link_suspend_bitmap;
/* packet threshold for an "almost empty" AC,
* for Tx schedulng purposes
*/
u8 tx_ac_threshold;
/* number of packets to queue up for a link in PS */
u8 tx_ps_threshold;
/* number of packet to queue up for a suspended link */
u8 tx_suspend_threshold;
/* Should have less than this number of packets in queue of a slow
* link to qualify as high priority link
*/
u8 tx_slow_link_prio_threshold;
/* Should have less than this number of packets in queue of a fast
* link to qualify as high priority link
*/
u8 tx_fast_link_prio_threshold;
/* Should have less than this number of packets in queue of a slow
* link before we stop queuing up packets for it.
*/
u8 tx_slow_stop_threshold;
/* Should have less than this number of packets in queue of a fast
* link before we stop queuing up packets for it.
*/
u8 tx_fast_stop_threshold;
u8 padding[3];
}; };
#define WL18XX_PHY_VERSION_MAX_LEN 20 #define WL18XX_PHY_VERSION_MAX_LEN 20
......
...@@ -1340,6 +1340,8 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, ...@@ -1340,6 +1340,8 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
kfree(acx); kfree(acx);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(wl1271_acx_set_ht_capabilities);
int wl1271_acx_set_ht_information(struct wl1271 *wl, int wl1271_acx_set_ht_information(struct wl1271 *wl,
struct wl12xx_vif *wlvif, struct wl12xx_vif *wlvif,
...@@ -1433,13 +1435,22 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, ...@@ -1433,13 +1435,22 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
acx->win_size = wl->conf.ht.rx_ba_win_size; acx->win_size = wl->conf.ht.rx_ba_win_size;
acx->ssn = ssn; acx->ssn = ssn;
ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_RX_SETUP, acx, ret = wlcore_cmd_configure_failsafe(wl, ACX_BA_SESSION_RX_SETUP, acx,
sizeof(*acx)); sizeof(*acx),
BIT(CMD_STATUS_NO_RX_BA_SESSION));
if (ret < 0) { if (ret < 0) {
wl1271_warning("acx ba receiver session failed: %d", ret); wl1271_warning("acx ba receiver session failed: %d", ret);
goto out; goto out;
} }
/* sometimes we can't start the session */
if (ret == CMD_STATUS_NO_RX_BA_SESSION) {
wl1271_warning("no fw rx ba on tid %d", tid_index);
ret = -EBUSY;
goto out;
}
ret = 0;
out: out:
kfree(acx); kfree(acx);
return ret; return ret;
......
...@@ -1025,7 +1025,6 @@ enum { ...@@ -1025,7 +1025,6 @@ enum {
ACX_CONFIG_HANGOVER = 0x0042, ACX_CONFIG_HANGOVER = 0x0042,
ACX_FEATURE_CFG = 0x0043, ACX_FEATURE_CFG = 0x0043,
ACX_PROTECTION_CFG = 0x0044, ACX_PROTECTION_CFG = 0x0044,
ACX_CHECKSUM_CONFIG = 0x0045,
}; };
......
...@@ -84,47 +84,57 @@ static int wlcore_boot_parse_fw_ver(struct wl1271 *wl, ...@@ -84,47 +84,57 @@ static int wlcore_boot_parse_fw_ver(struct wl1271 *wl,
static int wlcore_validate_fw_ver(struct wl1271 *wl) static int wlcore_validate_fw_ver(struct wl1271 *wl)
{ {
unsigned int *fw_ver = wl->chip.fw_ver; unsigned int *fw_ver = wl->chip.fw_ver;
unsigned int *min_ver = wl->min_fw_ver; unsigned int *min_ver = (wl->fw_type == WL12XX_FW_TYPE_NORMAL) ?
wl->min_sr_fw_ver : wl->min_mr_fw_ver;
char min_fw_str[32] = "";
int i;
/* the chip must be exactly equal */ /* the chip must be exactly equal */
if (min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP]) if ((min_ver[FW_VER_CHIP] != WLCORE_FW_VER_IGNORE) &&
(min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP]))
goto fail; goto fail;
/* always check the next digit if all previous ones are equal */ /* the firmware type must be equal */
if ((min_ver[FW_VER_IF_TYPE] != WLCORE_FW_VER_IGNORE) &&
if (min_ver[FW_VER_IF_TYPE] < fw_ver[FW_VER_IF_TYPE]) (min_ver[FW_VER_IF_TYPE] != fw_ver[FW_VER_IF_TYPE]))
goto out;
else if (min_ver[FW_VER_IF_TYPE] > fw_ver[FW_VER_IF_TYPE])
goto fail; goto fail;
if (min_ver[FW_VER_MAJOR] < fw_ver[FW_VER_MAJOR]) /* the project number must be equal */
goto out; if ((min_ver[FW_VER_SUBTYPE] != WLCORE_FW_VER_IGNORE) &&
else if (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR]) (min_ver[FW_VER_SUBTYPE] != fw_ver[FW_VER_SUBTYPE]))
goto fail; goto fail;
if (min_ver[FW_VER_SUBTYPE] < fw_ver[FW_VER_SUBTYPE]) /* the API version must be greater or equal */
goto out; if ((min_ver[FW_VER_MAJOR] != WLCORE_FW_VER_IGNORE) &&
else if (min_ver[FW_VER_SUBTYPE] > fw_ver[FW_VER_SUBTYPE]) (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR]))
goto fail; goto fail;
if (min_ver[FW_VER_MINOR] < fw_ver[FW_VER_MINOR]) /* if the API version is equal... */
goto out; if (((min_ver[FW_VER_MAJOR] == WLCORE_FW_VER_IGNORE) ||
else if (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR]) (min_ver[FW_VER_MAJOR] == fw_ver[FW_VER_MAJOR])) &&
/* ...the minor must be greater or equal */
((min_ver[FW_VER_MINOR] != WLCORE_FW_VER_IGNORE) &&
(min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR])))
goto fail; goto fail;
out:
return 0; return 0;
fail: fail:
wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is outdated.\n" for (i = 0; i < NUM_FW_VER; i++)
"Please use at least FW %u.%u.%u.%u.%u.\n" if (min_ver[i] == WLCORE_FW_VER_IGNORE)
"You can get more information at:\n" snprintf(min_fw_str, sizeof(min_fw_str),
"http://wireless.kernel.org/en/users/Drivers/wl12xx", "%s*.", min_fw_str);
else
snprintf(min_fw_str, sizeof(min_fw_str),
"%s%u.", min_fw_str, min_ver[i]);
wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is invalid.\n"
"Please use at least FW %s\n"
"You can get the latest firmwares at:\n"
"git://github.com/TI-OpenLink/firmwares.git",
fw_ver[FW_VER_CHIP], fw_ver[FW_VER_IF_TYPE], fw_ver[FW_VER_CHIP], fw_ver[FW_VER_IF_TYPE],
fw_ver[FW_VER_MAJOR], fw_ver[FW_VER_SUBTYPE], fw_ver[FW_VER_MAJOR], fw_ver[FW_VER_SUBTYPE],
fw_ver[FW_VER_MINOR], min_ver[FW_VER_CHIP], fw_ver[FW_VER_MINOR], min_fw_str);
min_ver[FW_VER_IF_TYPE], min_ver[FW_VER_MAJOR],
min_ver[FW_VER_SUBTYPE], min_ver[FW_VER_MINOR]);
return -EINVAL; return -EINVAL;
} }
...@@ -491,7 +501,7 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) ...@@ -491,7 +501,7 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
return ret; return ret;
wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); wl->mbox_ptr[1] = wl->mbox_ptr[0] + wl->mbox_size;
wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x", wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x",
wl->mbox_ptr[0], wl->mbox_ptr[1]); wl->mbox_ptr[0], wl->mbox_ptr[1]);
...@@ -508,23 +518,6 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) ...@@ -508,23 +518,6 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
*/ */
/* unmask required mbox events */ /* unmask required mbox events */
wl->event_mask = BSS_LOSE_EVENT_ID |
REGAINED_BSS_EVENT_ID |
SCAN_COMPLETE_EVENT_ID |
ROLE_STOP_COMPLETE_EVENT_ID |
RSSI_SNR_TRIGGER_0_EVENT_ID |
PSPOLL_DELIVERY_FAILURE_EVENT_ID |
SOFT_GEMINI_SENSE_EVENT_ID |
PERIODIC_SCAN_REPORT_EVENT_ID |
PERIODIC_SCAN_COMPLETE_EVENT_ID |
DUMMY_PACKET_EVENT_ID |
PEER_REMOVE_COMPLETE_EVENT_ID |
BA_SESSION_RX_CONSTRAINT_EVENT_ID |
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
INACTIVE_STA_EVENT_ID |
MAX_TX_RETRY_EVENT_ID |
CHANNEL_SWITCH_COMPLETE_EVENT_ID;
ret = wl1271_event_unmask(wl); ret = wl1271_event_unmask(wl);
if (ret < 0) { if (ret < 0) {
wl1271_error("EVENT mask setting failed"); wl1271_error("EVENT mask setting failed");
......
...@@ -31,6 +31,8 @@ struct acx_header; ...@@ -31,6 +31,8 @@ struct acx_header;
int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
size_t res_len); size_t res_len);
int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len,
size_t res_len, unsigned long valid_rets);
int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type, int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
u8 *role_id); u8 *role_id);
int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id); int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id);
...@@ -39,11 +41,14 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif); ...@@ -39,11 +41,14 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum ieee80211_band band, int channel);
int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len);
int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len);
int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
size_t len, unsigned long valid_rets);
int wl1271_cmd_data_path(struct wl1271 *wl, bool enable); int wl1271_cmd_data_path(struct wl1271 *wl, bool enable);
int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 ps_mode, u16 auto_ps_timeout); u8 ps_mode, u16 auto_ps_timeout);
...@@ -75,22 +80,30 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, ...@@ -75,22 +80,30 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u16 action, u8 id, u8 key_type, u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32, u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
u16 tx_seq_16); u16 tx_seq_16);
int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid); int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif,
int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id); u8 hlid);
int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
enum ieee80211_band band, u8 channel);
int wl12xx_croc(struct wl1271 *wl, u8 role_id); int wl12xx_croc(struct wl1271 *wl, u8 role_id);
int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct ieee80211_sta *sta, u8 hlid); struct ieee80211_sta *sta, u8 hlid);
int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid); int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid);
void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
enum ieee80211_band band);
int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl);
int wl12xx_cmd_config_fwlog(struct wl1271 *wl); int wl12xx_cmd_config_fwlog(struct wl1271 *wl);
int wl12xx_cmd_start_fwlog(struct wl1271 *wl); int wl12xx_cmd_start_fwlog(struct wl1271 *wl);
int wl12xx_cmd_stop_fwlog(struct wl1271 *wl); int wl12xx_cmd_stop_fwlog(struct wl1271 *wl);
int wl12xx_cmd_channel_switch(struct wl1271 *wl, int wl12xx_cmd_channel_switch(struct wl1271 *wl,
struct wl12xx_vif *wlvif, struct wl12xx_vif *wlvif,
struct ieee80211_channel_switch *ch_switch); struct ieee80211_channel_switch *ch_switch);
int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl); int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 *hlid); u8 *hlid);
void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid);
int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
u32 mask, bool *timeout);
enum wl1271_commands { enum wl1271_commands {
CMD_INTERROGATE = 1, /* use this to read information elements */ CMD_INTERROGATE = 1, /* use this to read information elements */
...@@ -149,8 +162,11 @@ enum wl1271_commands { ...@@ -149,8 +162,11 @@ enum wl1271_commands {
CMD_WFD_START_DISCOVERY = 45, CMD_WFD_START_DISCOVERY = 45,
CMD_WFD_STOP_DISCOVERY = 46, CMD_WFD_STOP_DISCOVERY = 46,
CMD_WFD_ATTRIBUTE_CONFIG = 47, CMD_WFD_ATTRIBUTE_CONFIG = 47,
CMD_NOP = 48, CMD_GENERIC_CFG = 48,
CMD_LAST_COMMAND, CMD_NOP = 49,
/* start of 18xx specific commands */
CMD_DFS_CHANNEL_CONFIG = 60,
MAX_COMMAND_ID = 0xFFFF, MAX_COMMAND_ID = 0xFFFF,
}; };
...@@ -167,8 +183,8 @@ enum cmd_templ { ...@@ -167,8 +183,8 @@ enum cmd_templ {
CMD_TEMPL_PS_POLL, CMD_TEMPL_PS_POLL,
CMD_TEMPL_KLV, CMD_TEMPL_KLV,
CMD_TEMPL_DISCONNECT, CMD_TEMPL_DISCONNECT,
CMD_TEMPL_APP_PROBE_REQ_2_4, CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY,
CMD_TEMPL_APP_PROBE_REQ_5, CMD_TEMPL_APP_PROBE_REQ_5_LEGACY,
CMD_TEMPL_BAR, /* for firmware internal use only */ CMD_TEMPL_BAR, /* for firmware internal use only */
CMD_TEMPL_CTS, /* CMD_TEMPL_CTS, /*
* For CTS-to-self (FastCTS) mechanism * For CTS-to-self (FastCTS) mechanism
...@@ -179,6 +195,8 @@ enum cmd_templ { ...@@ -179,6 +195,8 @@ enum cmd_templ {
CMD_TEMPL_DEAUTH_AP, CMD_TEMPL_DEAUTH_AP,
CMD_TEMPL_TEMPORARY, CMD_TEMPL_TEMPORARY,
CMD_TEMPL_LINK_MEASUREMENT_REPORT, CMD_TEMPL_LINK_MEASUREMENT_REPORT,
CMD_TEMPL_PROBE_REQ_2_4_PERIODIC,
CMD_TEMPL_PROBE_REQ_5_PERIODIC,
CMD_TEMPL_MAX = 0xff CMD_TEMPL_MAX = 0xff
}; };
...@@ -220,7 +238,8 @@ enum { ...@@ -220,7 +238,8 @@ enum {
CMD_STATUS_FW_RESET = 22, /* Driver internal use.*/ CMD_STATUS_FW_RESET = 22, /* Driver internal use.*/
CMD_STATUS_TEMPLATE_OOM = 23, CMD_STATUS_TEMPLATE_OOM = 23,
CMD_STATUS_NO_RX_BA_SESSION = 24, CMD_STATUS_NO_RX_BA_SESSION = 24,
MAX_COMMAND_STATUS = 0xff
MAX_COMMAND_STATUS
}; };
#define CMDMBOX_HEADER_LEN 4 #define CMDMBOX_HEADER_LEN 4
...@@ -345,7 +364,15 @@ struct wl12xx_cmd_role_start { ...@@ -345,7 +364,15 @@ struct wl12xx_cmd_role_start {
u8 reset_tsf; u8 reset_tsf;
u8 padding_1[4]; /*
* ap supports wmm (note that there is additional
* per-sta wmm configuration)
*/
u8 wmm;
u8 bcast_session_id;
u8 global_session_id;
u8 padding_1[1];
} __packed ap; } __packed ap;
}; };
} __packed; } __packed;
...@@ -515,7 +542,14 @@ struct wl12xx_cmd_set_peer_state { ...@@ -515,7 +542,14 @@ struct wl12xx_cmd_set_peer_state {
u8 hlid; u8 hlid;
u8 state; u8 state;
u8 padding[2];
/*
* wmm is relevant for sta role only.
* ap role configures the per-sta wmm params in
* the add_peer command.
*/
u8 wmm;
u8 padding[1];
} __packed; } __packed;
struct wl12xx_cmd_roc { struct wl12xx_cmd_roc {
...@@ -558,7 +592,7 @@ struct wl12xx_cmd_add_peer { ...@@ -558,7 +592,7 @@ struct wl12xx_cmd_add_peer {
u8 bss_index; u8 bss_index;
u8 sp_len; u8 sp_len;
u8 wmm; u8 wmm;
u8 padding1; u8 session_id;
} __packed; } __packed;
struct wl12xx_cmd_remove_peer { struct wl12xx_cmd_remove_peer {
...@@ -597,6 +631,13 @@ enum wl12xx_fwlogger_output { ...@@ -597,6 +631,13 @@ enum wl12xx_fwlogger_output {
WL12XX_FWLOG_OUTPUT_HOST, WL12XX_FWLOG_OUTPUT_HOST,
}; };
struct wl12xx_cmd_regdomain_dfs_config {
struct wl1271_cmd_header header;
__le32 ch_bit_map1;
__le32 ch_bit_map2;
} __packed;
struct wl12xx_cmd_config_fwlog { struct wl12xx_cmd_config_fwlog {
struct wl1271_cmd_header header; struct wl1271_cmd_header header;
...@@ -626,27 +667,13 @@ struct wl12xx_cmd_stop_fwlog { ...@@ -626,27 +667,13 @@ struct wl12xx_cmd_stop_fwlog {
struct wl1271_cmd_header header; struct wl1271_cmd_header header;
} __packed; } __packed;
struct wl12xx_cmd_channel_switch { struct wl12xx_cmd_stop_channel_switch {
struct wl1271_cmd_header header; struct wl1271_cmd_header header;
u8 role_id; u8 role_id;
/* The new serving channel */
u8 channel;
/* Relative time of the serving channel switch in TBTT units */
u8 switch_time;
/* Stop the role TX, should expect it after radar detection */
u8 stop_tx;
/* The target channel tx status 1-stopped 0-open*/
u8 post_switch_tx_disable;
u8 padding[3]; u8 padding[3];
} __packed; } __packed;
struct wl12xx_cmd_stop_channel_switch {
struct wl1271_cmd_header header;
} __packed;
/* Used to check radio status after calibration */ /* Used to check radio status after calibration */
#define MAX_TLV_LENGTH 500 #define MAX_TLV_LENGTH 500
#define TEST_CMD_P2G_CAL 2 /* TX BiP */ #define TEST_CMD_P2G_CAL 2 /* TX BiP */
......
...@@ -57,20 +57,49 @@ enum { ...@@ -57,20 +57,49 @@ enum {
}; };
enum { enum {
CONF_HW_RATE_INDEX_1MBPS = 0, CONF_HW_RATE_INDEX_1MBPS = 0,
CONF_HW_RATE_INDEX_2MBPS = 1, CONF_HW_RATE_INDEX_2MBPS = 1,
CONF_HW_RATE_INDEX_5_5MBPS = 2, CONF_HW_RATE_INDEX_5_5MBPS = 2,
CONF_HW_RATE_INDEX_6MBPS = 3, CONF_HW_RATE_INDEX_11MBPS = 3,
CONF_HW_RATE_INDEX_9MBPS = 4, CONF_HW_RATE_INDEX_6MBPS = 4,
CONF_HW_RATE_INDEX_11MBPS = 5, CONF_HW_RATE_INDEX_9MBPS = 5,
CONF_HW_RATE_INDEX_12MBPS = 6, CONF_HW_RATE_INDEX_12MBPS = 6,
CONF_HW_RATE_INDEX_18MBPS = 7, CONF_HW_RATE_INDEX_18MBPS = 7,
CONF_HW_RATE_INDEX_22MBPS = 8, CONF_HW_RATE_INDEX_24MBPS = 8,
CONF_HW_RATE_INDEX_24MBPS = 9, CONF_HW_RATE_INDEX_36MBPS = 9,
CONF_HW_RATE_INDEX_36MBPS = 10, CONF_HW_RATE_INDEX_48MBPS = 10,
CONF_HW_RATE_INDEX_48MBPS = 11, CONF_HW_RATE_INDEX_54MBPS = 11,
CONF_HW_RATE_INDEX_54MBPS = 12, CONF_HW_RATE_INDEX_MCS0 = 12,
CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_54MBPS, CONF_HW_RATE_INDEX_MCS1 = 13,
CONF_HW_RATE_INDEX_MCS2 = 14,
CONF_HW_RATE_INDEX_MCS3 = 15,
CONF_HW_RATE_INDEX_MCS4 = 16,
CONF_HW_RATE_INDEX_MCS5 = 17,
CONF_HW_RATE_INDEX_MCS6 = 18,
CONF_HW_RATE_INDEX_MCS7 = 19,
CONF_HW_RATE_INDEX_MCS7_SGI = 20,
CONF_HW_RATE_INDEX_MCS0_40MHZ = 21,
CONF_HW_RATE_INDEX_MCS1_40MHZ = 22,
CONF_HW_RATE_INDEX_MCS2_40MHZ = 23,
CONF_HW_RATE_INDEX_MCS3_40MHZ = 24,
CONF_HW_RATE_INDEX_MCS4_40MHZ = 25,
CONF_HW_RATE_INDEX_MCS5_40MHZ = 26,
CONF_HW_RATE_INDEX_MCS6_40MHZ = 27,
CONF_HW_RATE_INDEX_MCS7_40MHZ = 28,
CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI = 29,
/* MCS8+ rates overlap with 40Mhz rates */
CONF_HW_RATE_INDEX_MCS8 = 21,
CONF_HW_RATE_INDEX_MCS9 = 22,
CONF_HW_RATE_INDEX_MCS10 = 23,
CONF_HW_RATE_INDEX_MCS11 = 24,
CONF_HW_RATE_INDEX_MCS12 = 25,
CONF_HW_RATE_INDEX_MCS13 = 26,
CONF_HW_RATE_INDEX_MCS14 = 27,
CONF_HW_RATE_INDEX_MCS15 = 28,
CONF_HW_RATE_INDEX_MCS15_SGI = 29,
CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI,
}; };
#define CONF_HW_RXTX_RATE_UNSUPPORTED 0xff #define CONF_HW_RXTX_RATE_UNSUPPORTED 0xff
...@@ -415,11 +444,11 @@ struct conf_rx_settings { ...@@ -415,11 +444,11 @@ struct conf_rx_settings {
#define CONF_TX_RATE_MASK_BASIC_P2P CONF_HW_BIT_RATE_6MBPS #define CONF_TX_RATE_MASK_BASIC_P2P CONF_HW_BIT_RATE_6MBPS
/* /*
* Rates supported for data packets when operating as AP. Note the absence * Rates supported for data packets when operating as STA/AP. Note the absence
* of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop * of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop
* one. The rate dropped is not mandatory under any operating mode. * one. The rate dropped is not mandatory under any operating mode.
*/ */
#define CONF_TX_AP_ENABLED_RATES (CONF_HW_BIT_RATE_1MBPS | \ #define CONF_TX_ENABLED_RATES (CONF_HW_BIT_RATE_1MBPS | \
CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \
CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS | \ CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS | \
CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS | \ CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS | \
...@@ -677,6 +706,18 @@ struct conf_tx_settings { ...@@ -677,6 +706,18 @@ struct conf_tx_settings {
/* Time in ms for Tx watchdog timer to expire */ /* Time in ms for Tx watchdog timer to expire */
u32 tx_watchdog_timeout; u32 tx_watchdog_timeout;
/*
* when a slow link has this much packets pending, it becomes a low
* priority link, scheduling-wise
*/
u8 slow_link_thold;
/*
* when a fast link has this much packets pending, it becomes a low
* priority link, scheduling-wise
*/
u8 fast_link_thold;
} __packed; } __packed;
enum { enum {
...@@ -1047,6 +1088,7 @@ struct conf_roam_trigger_settings { ...@@ -1047,6 +1088,7 @@ struct conf_roam_trigger_settings {
struct conf_scan_settings { struct conf_scan_settings {
/* /*
* The minimum time to wait on each channel for active scans * The minimum time to wait on each channel for active scans
* This value will be used whenever there's a connected interface.
* *
* Range: u32 tu/1000 * Range: u32 tu/1000
*/ */
...@@ -1054,24 +1096,37 @@ struct conf_scan_settings { ...@@ -1054,24 +1096,37 @@ struct conf_scan_settings {
/* /*
* The maximum time to wait on each channel for active scans * The maximum time to wait on each channel for active scans
* This value will be currently used whenever there's a
* connected interface. It shouldn't exceed 30000 (~30ms) to avoid
* possible interference of voip traffic going on while scanning.
* *
* Range: u32 tu/1000 * Range: u32 tu/1000
*/ */
u32 max_dwell_time_active; u32 max_dwell_time_active;
/* /* The minimum time to wait on each channel for active scans
* The minimum time to wait on each channel for passive scans * when it's possible to have longer scan dwell times.
* Currently this is used whenever we're idle on all interfaces.
* Longer dwell times improve detection of networks within a
* single scan.
* *
* Range: u32 tu/1000 * Range: u32 tu/1000
*/ */
u32 min_dwell_time_passive; u32 min_dwell_time_active_long;
/* /* The maximum time to wait on each channel for active scans
* The maximum time to wait on each channel for passive scans * when it's possible to have longer scan dwell times.
* See min_dwell_time_active_long
* *
* Range: u32 tu/1000 * Range: u32 tu/1000
*/ */
u32 max_dwell_time_passive; u32 max_dwell_time_active_long;
/* time to wait on the channel for passive scans (in TU/1000) */
u32 dwell_time_passive;
/* time to wait on the channel for DFS scans (in TU/1000) */
u32 dwell_time_dfs;
/* /*
* Number of probe requests to transmit on each active scan channel * Number of probe requests to transmit on each active scan channel
...@@ -1276,12 +1331,20 @@ struct conf_hangover_settings { ...@@ -1276,12 +1331,20 @@ struct conf_hangover_settings {
u8 window_size; u8 window_size;
} __packed; } __packed;
struct conf_recovery_settings {
/* BUG() on fw recovery */
u8 bug_on_recovery;
/* Prevent HW recovery. FW will remain stuck. */
u8 no_recovery;
} __packed;
/* /*
* The conf version consists of 4 bytes. The two MSB are the wlcore * The conf version consists of 4 bytes. The two MSB are the wlcore
* version, the two LSB are the lower driver's private conf * version, the two LSB are the lower driver's private conf
* version. * version.
*/ */
#define WLCORE_CONF_VERSION (0x0002 << 16) #define WLCORE_CONF_VERSION (0x0005 << 16)
#define WLCORE_CONF_MASK 0xffff0000 #define WLCORE_CONF_MASK 0xffff0000
#define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \ #define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \
sizeof(struct wlcore_conf)) sizeof(struct wlcore_conf))
...@@ -1309,6 +1372,7 @@ struct wlcore_conf { ...@@ -1309,6 +1372,7 @@ struct wlcore_conf {
struct conf_fwlog fwlog; struct conf_fwlog fwlog;
struct conf_rate_policy_settings rate; struct conf_rate_policy_settings rate;
struct conf_hangover_settings hangover; struct conf_hangover_settings hangover;
struct conf_recovery_settings recovery;
} __packed; } __packed;
struct wlcore_conf_file { struct wlcore_conf_file {
......
...@@ -490,7 +490,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, ...@@ -490,7 +490,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
DRIVER_STATE_PRINT_HEX(chip.id); DRIVER_STATE_PRINT_HEX(chip.id);
DRIVER_STATE_PRINT_STR(chip.fw_ver_str); DRIVER_STATE_PRINT_STR(chip.fw_ver_str);
DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str); DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str);
DRIVER_STATE_PRINT_INT(sched_scanning); DRIVER_STATE_PRINT_INT(recovery_count);
#undef DRIVER_STATE_PRINT_INT #undef DRIVER_STATE_PRINT_INT
#undef DRIVER_STATE_PRINT_LONG #undef DRIVER_STATE_PRINT_LONG
...@@ -560,7 +560,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, ...@@ -560,7 +560,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
if (wlvif->bss_type == BSS_TYPE_STA_BSS || if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
wlvif->bss_type == BSS_TYPE_IBSS) { wlvif->bss_type == BSS_TYPE_IBSS) {
VIF_STATE_PRINT_INT(sta.hlid); VIF_STATE_PRINT_INT(sta.hlid);
VIF_STATE_PRINT_INT(sta.ba_rx_bitmap);
VIF_STATE_PRINT_INT(sta.basic_rate_idx); VIF_STATE_PRINT_INT(sta.basic_rate_idx);
VIF_STATE_PRINT_INT(sta.ap_rate_idx); VIF_STATE_PRINT_INT(sta.ap_rate_idx);
VIF_STATE_PRINT_INT(sta.p2p_rate_idx); VIF_STATE_PRINT_INT(sta.p2p_rate_idx);
...@@ -577,6 +576,10 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, ...@@ -577,6 +576,10 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]); VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]);
} }
VIF_STATE_PRINT_INT(last_tx_hlid); VIF_STATE_PRINT_INT(last_tx_hlid);
VIF_STATE_PRINT_INT(tx_queue_count[0]);
VIF_STATE_PRINT_INT(tx_queue_count[1]);
VIF_STATE_PRINT_INT(tx_queue_count[2]);
VIF_STATE_PRINT_INT(tx_queue_count[3]);
VIF_STATE_PRINT_LHEX(links_map[0]); VIF_STATE_PRINT_LHEX(links_map[0]);
VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len); VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len);
VIF_STATE_PRINT_INT(band); VIF_STATE_PRINT_INT(band);
...@@ -589,7 +592,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, ...@@ -589,7 +592,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
VIF_STATE_PRINT_INT(beacon_int); VIF_STATE_PRINT_INT(beacon_int);
VIF_STATE_PRINT_INT(default_key); VIF_STATE_PRINT_INT(default_key);
VIF_STATE_PRINT_INT(aid); VIF_STATE_PRINT_INT(aid);
VIF_STATE_PRINT_INT(session_counter);
VIF_STATE_PRINT_INT(psm_entry_retry); VIF_STATE_PRINT_INT(psm_entry_retry);
VIF_STATE_PRINT_INT(power_level); VIF_STATE_PRINT_INT(power_level);
VIF_STATE_PRINT_INT(rssi_thold); VIF_STATE_PRINT_INT(rssi_thold);
...@@ -993,7 +995,7 @@ static ssize_t sleep_auth_write(struct file *file, ...@@ -993,7 +995,7 @@ static ssize_t sleep_auth_write(struct file *file,
return -EINVAL; return -EINVAL;
} }
if (value < 0 || value > WL1271_PSM_MAX) { if (value > WL1271_PSM_MAX) {
wl1271_warning("sleep_auth must be between 0 and %d", wl1271_warning("sleep_auth must be between 0 and %d",
WL1271_PSM_MAX); WL1271_PSM_MAX);
return -ERANGE; return -ERANGE;
......
...@@ -29,34 +29,39 @@ ...@@ -29,34 +29,39 @@
#include "scan.h" #include "scan.h"
#include "wl12xx_80211.h" #include "wl12xx_80211.h"
static void wl1271_event_rssi_trigger(struct wl1271 *wl, void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr)
struct wl12xx_vif *wlvif,
struct event_mailbox *mbox)
{ {
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct wl12xx_vif *wlvif;
struct ieee80211_vif *vif;
enum nl80211_cqm_rssi_threshold_event event; enum nl80211_cqm_rssi_threshold_event event;
s8 metric = mbox->rssi_snr_trigger_metric[0]; s8 metric = metric_arr[0];
wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric); wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric);
if (metric <= wlvif->rssi_thold) /* TODO: check actual multi-role support */
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; wl12xx_for_each_wlvif_sta(wl, wlvif) {
else if (metric <= wlvif->rssi_thold)
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
else
if (event != wlvif->last_rssi_event) event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL);
wlvif->last_rssi_event = event; vif = wl12xx_wlvif_to_vif(wlvif);
if (event != wlvif->last_rssi_event)
ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL);
wlvif->last_rssi_event = event;
}
} }
EXPORT_SYMBOL_GPL(wlcore_event_rssi_trigger);
static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif) static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{ {
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
if (wlvif->bss_type != BSS_TYPE_AP_BSS) { if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
if (!wlvif->sta.ba_rx_bitmap) u8 hlid = wlvif->sta.hlid;
if (!wl->links[hlid].ba_bitmap)
return; return;
ieee80211_stop_rx_ba_session(vif, wlvif->sta.ba_rx_bitmap, ieee80211_stop_rx_ba_session(vif, wl->links[hlid].ba_bitmap,
vif->bss_conf.bssid); vif->bss_conf.bssid);
} else { } else {
u8 hlid; u8 hlid;
...@@ -74,8 +79,7 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif) ...@@ -74,8 +79,7 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
} }
} }
static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl, void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable)
u8 enable)
{ {
struct wl12xx_vif *wlvif; struct wl12xx_vif *wlvif;
...@@ -87,201 +91,169 @@ static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl, ...@@ -87,201 +91,169 @@ static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl,
wl1271_recalc_rx_streaming(wl, wlvif); wl1271_recalc_rx_streaming(wl, wlvif);
} }
} }
} }
EXPORT_SYMBOL_GPL(wlcore_event_soft_gemini_sense);
static void wl1271_event_mbox_dump(struct event_mailbox *mbox) void wlcore_event_sched_scan_completed(struct wl1271 *wl,
u8 status)
{ {
wl1271_debug(DEBUG_EVENT, "MBOX DUMP:"); wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)",
wl1271_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector); status);
wl1271_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask);
if (wl->sched_vif) {
ieee80211_sched_scan_stopped(wl->hw);
wl->sched_vif = NULL;
}
} }
EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_completed);
static int wl1271_event_process(struct wl1271 *wl) void wlcore_event_ba_rx_constraint(struct wl1271 *wl,
unsigned long roles_bitmap,
unsigned long allowed_bitmap)
{ {
struct event_mailbox *mbox = wl->mbox;
struct ieee80211_vif *vif;
struct wl12xx_vif *wlvif; struct wl12xx_vif *wlvif;
u32 vector;
bool disconnect_sta = false;
unsigned long sta_bitmap = 0;
int ret;
wl1271_event_mbox_dump(mbox);
vector = le32_to_cpu(mbox->events_vector);
vector &= ~(le32_to_cpu(mbox->events_mask));
wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector);
if (vector & SCAN_COMPLETE_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx allowed=0x%lx",
wl1271_debug(DEBUG_EVENT, "status: 0x%x", __func__, roles_bitmap, allowed_bitmap);
mbox->scheduled_scan_status);
wl1271_scan_stm(wl, wl->scan_vif);
}
if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { wl12xx_for_each_wlvif(wl, wlvif) {
wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT " if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
"(status 0x%0x)", mbox->scheduled_scan_status); !test_bit(wlvif->role_id , &roles_bitmap))
continue;
wl1271_scan_sched_scan_results(wl); wlvif->ba_allowed = !!test_bit(wlvif->role_id,
&allowed_bitmap);
if (!wlvif->ba_allowed)
wl1271_stop_ba_event(wl, wlvif);
} }
}
EXPORT_SYMBOL_GPL(wlcore_event_ba_rx_constraint);
if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) { void wlcore_event_channel_switch(struct wl1271 *wl,
wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT " unsigned long roles_bitmap,
"(status 0x%0x)", mbox->scheduled_scan_status); bool success)
if (wl->sched_scanning) { {
ieee80211_sched_scan_stopped(wl->hw); struct wl12xx_vif *wlvif;
wl->sched_scanning = false; struct ieee80211_vif *vif;
}
}
if (vector & SOFT_GEMINI_SENSE_EVENT_ID) wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d",
wl12xx_event_soft_gemini_sense(wl, __func__, roles_bitmap, success);
mbox->soft_gemini_sense_info);
/* wl12xx_for_each_wlvif_sta(wl, wlvif) {
* We are HW_MONITOR device. On beacon loss - queue if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
* connection loss work. Cancel it on REGAINED event. !test_bit(wlvif->role_id , &roles_bitmap))
*/ continue;
if (vector & BSS_LOSE_EVENT_ID) {
/* TODO: check for multi-role */
int delay = wl->conf.conn.synch_fail_thold *
wl->conf.conn.bss_lose_timeout;
wl1271_info("Beacon loss detected.");
/* if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
* if the work is already queued, it should take place. We &wlvif->flags))
* don't want to delay the connection loss indication continue;
* any more.
*/
ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work,
msecs_to_jiffies(delay));
wl12xx_for_each_wlvif_sta(wl, wlvif) { vif = wl12xx_wlvif_to_vif(wlvif);
vif = wl12xx_wlvif_to_vif(wlvif);
ieee80211_cqm_rssi_notify( ieee80211_chswitch_done(vif, success);
vif, cancel_delayed_work(&wlvif->channel_switch_work);
NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
GFP_KERNEL);
}
} }
}
EXPORT_SYMBOL_GPL(wlcore_event_channel_switch);
if (vector & REGAINED_BSS_EVENT_ID) { void wlcore_event_dummy_packet(struct wl1271 *wl)
/* TODO: check for multi-role */ {
wl1271_info("Beacon regained."); wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
cancel_delayed_work(&wl->connection_loss_work); wl1271_tx_dummy_packet(wl);
}
/* sanity check - we can't lose and gain the beacon together */ EXPORT_SYMBOL_GPL(wlcore_event_dummy_packet);
WARN(vector & BSS_LOSE_EVENT_ID,
"Concurrent beacon loss and gain from FW");
}
if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap)
/* TODO: check actual multi-role support */ {
wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT"); u32 num_packets = wl->conf.tx.max_tx_retries;
wl12xx_for_each_wlvif_sta(wl, wlvif) { struct wl12xx_vif *wlvif;
wl1271_event_rssi_trigger(wl, wlvif, mbox); struct ieee80211_vif *vif;
struct ieee80211_sta *sta;
const u8 *addr;
int h;
for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) {
bool found = false;
/* find the ap vif connected to this sta */
wl12xx_for_each_wlvif_ap(wl, wlvif) {
if (!test_bit(h, wlvif->ap.sta_hlid_map))
continue;
found = true;
break;
} }
} if (!found)
continue;
if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) { vif = wl12xx_wlvif_to_vif(wlvif);
u8 role_id = mbox->role_id; addr = wl->links[h].addr;
wl1271_debug(DEBUG_EVENT, "BA_SESSION_RX_CONSTRAINT_EVENT_ID. "
"ba_allowed = 0x%x, role_id=%d",
mbox->rx_ba_allowed, role_id);
wl12xx_for_each_wlvif(wl, wlvif) { rcu_read_lock();
if (role_id != 0xff && role_id != wlvif->role_id) sta = ieee80211_find_sta(vif, addr);
continue; if (sta) {
wl1271_debug(DEBUG_EVENT, "remove sta %d", h);
wlvif->ba_allowed = !!mbox->rx_ba_allowed; ieee80211_report_low_ack(sta, num_packets);
if (!wlvif->ba_allowed)
wl1271_stop_ba_event(wl, wlvif);
} }
rcu_read_unlock();
} }
}
if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) { void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap)
wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. " {
"status = 0x%x", wl1271_debug(DEBUG_EVENT, "MAX_TX_FAILURE_EVENT_ID");
mbox->channel_switch_status); wlcore_disconnect_sta(wl, sta_bitmap);
/* }
* That event uses for two cases: EXPORT_SYMBOL_GPL(wlcore_event_max_tx_failure);
* 1) channel switch complete with status=0
* 2) channel switch failed status=1
*/
/* TODO: configure only the relevant vif */
wl12xx_for_each_wlvif_sta(wl, wlvif) {
bool success;
if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
&wlvif->flags))
continue;
success = mbox->channel_switch_status ? false : true;
vif = wl12xx_wlvif_to_vif(wlvif);
ieee80211_chswitch_done(vif, success); void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap)
} {
} wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
wlcore_disconnect_sta(wl, sta_bitmap);
}
EXPORT_SYMBOL_GPL(wlcore_event_inactive_sta);
if ((vector & DUMMY_PACKET_EVENT_ID)) { void wlcore_event_roc_complete(struct wl1271 *wl)
wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); {
ret = wl1271_tx_dummy_packet(wl); wl1271_debug(DEBUG_EVENT, "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID");
if (ret < 0) if (wl->roc_vif)
return ret; ieee80211_ready_on_channel(wl->hw);
} }
EXPORT_SYMBOL_GPL(wlcore_event_roc_complete);
void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap)
{
/* /*
* "TX retries exceeded" has a different meaning according to mode. * We are HW_MONITOR device. On beacon loss - queue
* In AP mode the offending station is disconnected. * connection loss work. Cancel it on REGAINED event.
*/ */
if (vector & MAX_TX_RETRY_EVENT_ID) { struct wl12xx_vif *wlvif;
wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID"); struct ieee80211_vif *vif;
sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded); int delay = wl->conf.conn.synch_fail_thold *
disconnect_sta = true; wl->conf.conn.bss_lose_timeout;
}
if (vector & INACTIVE_STA_EVENT_ID) { wl1271_info("Beacon loss detected. roles:0x%lx", roles_bitmap);
wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
sta_bitmap |= le16_to_cpu(mbox->sta_aging_status);
disconnect_sta = true;
}
if (disconnect_sta) { wl12xx_for_each_wlvif_sta(wl, wlvif) {
u32 num_packets = wl->conf.tx.max_tx_retries; if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
struct ieee80211_sta *sta; !test_bit(wlvif->role_id , &roles_bitmap))
const u8 *addr; continue;
int h;
for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) {
bool found = false;
/* find the ap vif connected to this sta */
wl12xx_for_each_wlvif_ap(wl, wlvif) {
if (!test_bit(h, wlvif->ap.sta_hlid_map))
continue;
found = true;
break;
}
if (!found)
continue;
vif = wl12xx_wlvif_to_vif(wlvif); /*
addr = wl->links[h].addr; * if the work is already queued, it should take place.
* We don't want to delay the connection loss
* indication any more.
*/
ieee80211_queue_delayed_work(wl->hw,
&wlvif->connection_loss_work,
msecs_to_jiffies(delay));
rcu_read_lock(); vif = wl12xx_wlvif_to_vif(wlvif);
sta = ieee80211_find_sta(vif, addr); ieee80211_cqm_rssi_notify(
if (sta) { vif,
wl1271_debug(DEBUG_EVENT, "remove sta %d", h); NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
ieee80211_report_low_ack(sta, num_packets); GFP_KERNEL);
}
rcu_read_unlock();
}
} }
return 0;
} }
EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss);
int wl1271_event_unmask(struct wl1271 *wl) int wl1271_event_unmask(struct wl1271 *wl)
{ {
...@@ -305,12 +277,12 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) ...@@ -305,12 +277,12 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
/* first we read the mbox descriptor */ /* first we read the mbox descriptor */
ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox, ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox,
sizeof(*wl->mbox), false); wl->mbox_size, false);
if (ret < 0) if (ret < 0)
return ret; return ret;
/* process the descriptor */ /* process the descriptor */
ret = wl1271_event_process(wl); ret = wl->ops->process_mailbox_events(wl);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
...@@ -46,33 +46,17 @@ enum { ...@@ -46,33 +46,17 @@ enum {
RSSI_SNR_TRIGGER_5_EVENT_ID = BIT(5), RSSI_SNR_TRIGGER_5_EVENT_ID = BIT(5),
RSSI_SNR_TRIGGER_6_EVENT_ID = BIT(6), RSSI_SNR_TRIGGER_6_EVENT_ID = BIT(6),
RSSI_SNR_TRIGGER_7_EVENT_ID = BIT(7), RSSI_SNR_TRIGGER_7_EVENT_ID = BIT(7),
MEASUREMENT_START_EVENT_ID = BIT(8),
MEASUREMENT_COMPLETE_EVENT_ID = BIT(9),
SCAN_COMPLETE_EVENT_ID = BIT(10),
WFD_DISCOVERY_COMPLETE_EVENT_ID = BIT(11),
AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12),
RESERVED1 = BIT(13),
PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14),
ROLE_STOP_COMPLETE_EVENT_ID = BIT(15),
RADAR_DETECTED_EVENT_ID = BIT(16),
CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17),
BSS_LOSE_EVENT_ID = BIT(18),
REGAINED_BSS_EVENT_ID = BIT(19),
MAX_TX_RETRY_EVENT_ID = BIT(20),
DUMMY_PACKET_EVENT_ID = BIT(21),
SOFT_GEMINI_SENSE_EVENT_ID = BIT(22),
CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID = BIT(23),
SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24),
PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25),
INACTIVE_STA_EVENT_ID = BIT(26),
PEER_REMOVE_COMPLETE_EVENT_ID = BIT(27),
PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(28),
PERIODIC_SCAN_REPORT_EVENT_ID = BIT(29),
BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(30),
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(31),
EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff, EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff,
}; };
/* events the driver might want to wait for */
enum wlcore_wait_event {
WLCORE_EVENT_ROLE_STOP_COMPLETE,
WLCORE_EVENT_PEER_REMOVE_COMPLETE,
WLCORE_EVENT_DFS_CONFIG_COMPLETE
};
enum { enum {
EVENT_ENTER_POWER_SAVE_FAIL = 0, EVENT_ENTER_POWER_SAVE_FAIL = 0,
EVENT_ENTER_POWER_SAVE_SUCCESS, EVENT_ENTER_POWER_SAVE_SUCCESS,
...@@ -80,61 +64,24 @@ enum { ...@@ -80,61 +64,24 @@ enum {
#define NUM_OF_RSSI_SNR_TRIGGERS 8 #define NUM_OF_RSSI_SNR_TRIGGERS 8
struct event_mailbox {
__le32 events_vector;
__le32 events_mask;
__le32 reserved_1;
__le32 reserved_2;
u8 number_of_scan_results;
u8 scan_tag;
u8 completed_scan_status;
u8 reserved_3;
u8 soft_gemini_sense_info;
u8 soft_gemini_protective_info;
s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
u8 change_auto_mode_timeout;
u8 scheduled_scan_status;
u8 reserved4;
/* tuned channel (roc) */
u8 roc_channel;
__le16 hlid_removed_bitmap;
/* bitmap of aged stations (by HLID) */
__le16 sta_aging_status;
/* bitmap of stations (by HLID) which exceeded max tx retries */
__le16 sta_tx_retry_exceeded;
/* discovery completed results */
u8 discovery_tag;
u8 number_of_preq_results;
u8 number_of_prsp_results;
u8 reserved_5;
/* rx ba constraint */
u8 role_id; /* 0xFF means any role. */
u8 rx_ba_allowed;
u8 reserved_6[2];
/* Channel switch results */
u8 channel_switch_role_id;
u8 channel_switch_status;
u8 reserved_7[2];
u8 ps_poll_delivery_failure_role_ids;
u8 stopped_role_ids;
u8 started_role_ids;
u8 reserved_8[9];
} __packed;
struct wl1271; struct wl1271;
int wl1271_event_unmask(struct wl1271 *wl); int wl1271_event_unmask(struct wl1271 *wl);
int wl1271_event_handle(struct wl1271 *wl, u8 mbox); int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable);
void wlcore_event_sched_scan_completed(struct wl1271 *wl,
u8 status);
void wlcore_event_ba_rx_constraint(struct wl1271 *wl,
unsigned long roles_bitmap,
unsigned long allowed_bitmap);
void wlcore_event_channel_switch(struct wl1271 *wl,
unsigned long roles_bitmap,
bool success);
void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap);
void wlcore_event_dummy_packet(struct wl1271 *wl);
void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap);
void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap);
void wlcore_event_roc_complete(struct wl1271 *wl);
void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr);
#endif #endif
...@@ -201,4 +201,45 @@ wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len) ...@@ -201,4 +201,45 @@ wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len)
return buf_offset; return buf_offset;
} }
static inline void
wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct ieee80211_sta *sta, u32 changed)
{
if (wl->ops->sta_rc_update)
wl->ops->sta_rc_update(wl, wlvif, sta, changed);
}
static inline int
wlcore_hw_set_peer_cap(struct wl1271 *wl,
struct ieee80211_sta_ht_cap *ht_cap,
bool allow_ht_operation,
u32 rate_set, u8 hlid)
{
if (wl->ops->set_peer_cap)
return wl->ops->set_peer_cap(wl, ht_cap, allow_ht_operation,
rate_set, hlid);
return 0;
}
static inline bool
wlcore_hw_lnk_high_prio(struct wl1271 *wl, u8 hlid,
struct wl1271_link *lnk)
{
if (!wl->ops->lnk_high_prio)
BUG_ON(1);
return wl->ops->lnk_high_prio(wl, hlid, lnk);
}
static inline bool
wlcore_hw_lnk_low_prio(struct wl1271 *wl, u8 hlid,
struct wl1271_link *lnk)
{
if (!wl->ops->lnk_low_prio)
BUG_ON(1);
return wl->ops->lnk_low_prio(wl, hlid, lnk);
}
#endif #endif
...@@ -41,14 +41,14 @@ int wl1271_init_templates_config(struct wl1271 *wl) ...@@ -41,14 +41,14 @@ int wl1271_init_templates_config(struct wl1271 *wl)
/* send empty templates for fw memory reservation */ /* send empty templates for fw memory reservation */
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, wl->scan_templ_id_2_4, NULL,
WL1271_CMD_TEMPL_MAX_SIZE, WL1271_CMD_TEMPL_MAX_SIZE,
0, WL1271_RATE_AUTOMATIC); 0, WL1271_RATE_AUTOMATIC);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_CFG_PROBE_REQ_5, wl->scan_templ_id_5,
NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0,
WL1271_RATE_AUTOMATIC); WL1271_RATE_AUTOMATIC);
if (ret < 0) if (ret < 0)
...@@ -56,14 +56,16 @@ int wl1271_init_templates_config(struct wl1271 *wl) ...@@ -56,14 +56,16 @@ int wl1271_init_templates_config(struct wl1271 *wl)
if (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL) { if (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL) {
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_APP_PROBE_REQ_2_4, NULL, wl->sched_scan_templ_id_2_4,
NULL,
WL1271_CMD_TEMPL_MAX_SIZE, WL1271_CMD_TEMPL_MAX_SIZE,
0, WL1271_RATE_AUTOMATIC); 0, WL1271_RATE_AUTOMATIC);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_APP_PROBE_REQ_5, NULL, wl->sched_scan_templ_id_5,
NULL,
WL1271_CMD_TEMPL_MAX_SIZE, WL1271_CMD_TEMPL_MAX_SIZE,
0, WL1271_RATE_AUTOMATIC); 0, WL1271_RATE_AUTOMATIC);
if (ret < 0) if (ret < 0)
...@@ -463,7 +465,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif) ...@@ -463,7 +465,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES)) if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
supported_rates = CONF_TX_OFDM_RATES; supported_rates = CONF_TX_OFDM_RATES;
else else
supported_rates = CONF_TX_AP_ENABLED_RATES; supported_rates = CONF_TX_ENABLED_RATES;
/* unconditionally enable HT rates */ /* unconditionally enable HT rates */
supported_rates |= CONF_TX_MCS_RATES; supported_rates |= CONF_TX_MCS_RATES;
...@@ -575,9 +577,6 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) ...@@ -575,9 +577,6 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
/* Configure for power according to debugfs */ /* Configure for power according to debugfs */
if (sta_auth != WL1271_PSM_ILLEGAL) if (sta_auth != WL1271_PSM_ILLEGAL)
ret = wl1271_acx_sleep_auth(wl, sta_auth); 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);
/* Configure for ELP power saving */ /* Configure for ELP power saving */
else else
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
...@@ -679,6 +678,10 @@ int wl1271_hw_init(struct wl1271 *wl) ...@@ -679,6 +678,10 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = wlcore_cmd_regdomain_config_locked(wl);
if (ret < 0)
return ret;
/* Bluetooth WLAN coexistence */ /* Bluetooth WLAN coexistence */
ret = wl1271_init_pta(wl); ret = wl1271_init_pta(wl);
if (ret < 0) if (ret < 0)
......
...@@ -105,13 +105,13 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr, ...@@ -105,13 +105,13 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr,
{ {
int ret; int ret;
ret = wlcore_raw_read(wl, addr, &wl->buffer_32, ret = wlcore_raw_read(wl, addr, wl->buffer_32,
sizeof(wl->buffer_32), false); sizeof(*wl->buffer_32), false);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (val) if (val)
*val = le32_to_cpu(wl->buffer_32); *val = le32_to_cpu(*wl->buffer_32);
return 0; return 0;
} }
...@@ -119,9 +119,9 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr, ...@@ -119,9 +119,9 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr,
static inline int __must_check wlcore_raw_write32(struct wl1271 *wl, int addr, static inline int __must_check wlcore_raw_write32(struct wl1271 *wl, int addr,
u32 val) u32 val)
{ {
wl->buffer_32 = cpu_to_le32(val); *wl->buffer_32 = cpu_to_le32(val);
return wlcore_raw_write(wl, addr, &wl->buffer_32, return wlcore_raw_write(wl, addr, wl->buffer_32,
sizeof(wl->buffer_32), false); sizeof(*wl->buffer_32), false);
} }
static inline int __must_check wlcore_read(struct wl1271 *wl, int addr, static inline int __must_check wlcore_read(struct wl1271 *wl, int addr,
......
...@@ -151,9 +151,6 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl) ...@@ -151,9 +151,6 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl)
wl12xx_queue_recovery_work(wl); wl12xx_queue_recovery_work(wl);
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
goto err; goto err;
} else if (ret < 0) {
wl1271_error("ELP wakeup completion error.");
goto err;
} }
} }
...@@ -242,11 +239,12 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) ...@@ -242,11 +239,12 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
struct ieee80211_tx_info *info; struct ieee80211_tx_info *info;
unsigned long flags; unsigned long flags;
int filtered[NUM_TX_QUEUES]; int filtered[NUM_TX_QUEUES];
struct wl1271_link *lnk = &wl->links[hlid];
/* filter all frames currently in the low level queues for this hlid */ /* filter all frames currently in the low level queues for this hlid */
for (i = 0; i < NUM_TX_QUEUES; i++) { for (i = 0; i < NUM_TX_QUEUES; i++) {
filtered[i] = 0; filtered[i] = 0;
while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
filtered[i]++; filtered[i]++;
if (WARN_ON(wl12xx_is_dummy_packet(wl, skb))) if (WARN_ON(wl12xx_is_dummy_packet(wl, skb)))
...@@ -260,8 +258,11 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) ...@@ -260,8 +258,11 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
} }
spin_lock_irqsave(&wl->wl_lock, flags); spin_lock_irqsave(&wl->wl_lock, flags);
for (i = 0; i < NUM_TX_QUEUES; i++) for (i = 0; i < NUM_TX_QUEUES; i++) {
wl->tx_queue_count[i] -= filtered[i]; wl->tx_queue_count[i] -= filtered[i];
if (lnk->wlvif)
lnk->wlvif->tx_queue_count[i] -= filtered[i];
}
spin_unlock_irqrestore(&wl->wl_lock, flags); spin_unlock_irqrestore(&wl->wl_lock, flags);
wl1271_handle_tx_low_watermark(wl); wl1271_handle_tx_low_watermark(wl);
......
...@@ -92,11 +92,16 @@ static void wl1271_rx_status(struct wl1271 *wl, ...@@ -92,11 +92,16 @@ static void wl1271_rx_status(struct wl1271 *wl,
status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED | status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED |
RX_FLAG_DECRYPTED; RX_FLAG_DECRYPTED;
if (unlikely(desc_err_code == WL1271_RX_DESC_MIC_FAIL)) { if (unlikely(desc_err_code & WL1271_RX_DESC_MIC_FAIL)) {
status->flag |= RX_FLAG_MMIC_ERROR; status->flag |= RX_FLAG_MMIC_ERROR;
wl1271_warning("Michael MIC error"); wl1271_warning("Michael MIC error. Desc: 0x%x",
desc_err_code);
} }
} }
if (beacon)
wlcore_set_pending_regdomain_ch(wl, (u16)desc->channel,
status->band);
} }
static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
...@@ -108,7 +113,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, ...@@ -108,7 +113,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
u8 *buf; u8 *buf;
u8 beacon = 0; u8 beacon = 0;
u8 is_data = 0; u8 is_data = 0;
u8 reserved = 0; u8 reserved = 0, offset_to_data = 0;
u16 seq_num; u16 seq_num;
u32 pkt_data_len; u32 pkt_data_len;
...@@ -128,6 +133,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, ...@@ -128,6 +133,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
if (rx_align == WLCORE_RX_BUF_UNALIGNED) if (rx_align == WLCORE_RX_BUF_UNALIGNED)
reserved = RX_BUF_ALIGN; reserved = RX_BUF_ALIGN;
else if (rx_align == WLCORE_RX_BUF_PADDED)
offset_to_data = RX_BUF_ALIGN;
/* the data read starts with the descriptor */ /* the data read starts with the descriptor */
desc = (struct wl1271_rx_descriptor *) data; desc = (struct wl1271_rx_descriptor *) data;
...@@ -139,19 +146,15 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, ...@@ -139,19 +146,15 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
return 0; return 0;
} }
switch (desc->status & WL1271_RX_DESC_STATUS_MASK) {
/* discard corrupted packets */ /* discard corrupted packets */
case WL1271_RX_DESC_DRIVER_RX_Q_FAIL: if (desc->status & WL1271_RX_DESC_DECRYPT_FAIL) {
case WL1271_RX_DESC_DECRYPT_FAIL: hdr = (void *)(data + sizeof(*desc) + offset_to_data);
wl1271_warning("corrupted packet in RX with status: 0x%x", wl1271_warning("corrupted packet in RX: status: 0x%x len: %d",
desc->status & WL1271_RX_DESC_STATUS_MASK); desc->status & WL1271_RX_DESC_STATUS_MASK,
return -EINVAL; pkt_data_len);
case WL1271_RX_DESC_SUCCESS: wl1271_dump((DEBUG_RX|DEBUG_CMD), "PKT: ", data + sizeof(*desc),
case WL1271_RX_DESC_MIC_FAIL: min(pkt_data_len,
break; ieee80211_hdrlen(hdr->frame_control)));
default:
wl1271_error("invalid RX descriptor status: 0x%x",
desc->status & WL1271_RX_DESC_STATUS_MASK);
return -EINVAL; return -EINVAL;
} }
......
...@@ -84,12 +84,11 @@ ...@@ -84,12 +84,11 @@
* Bits 3-5 - process_id tag (AP mode FW) * Bits 3-5 - process_id tag (AP mode FW)
* Bits 6-7 - reserved * Bits 6-7 - reserved
*/ */
#define WL1271_RX_DESC_STATUS_MASK 0x03 #define WL1271_RX_DESC_STATUS_MASK 0x07
#define WL1271_RX_DESC_SUCCESS 0x00 #define WL1271_RX_DESC_SUCCESS 0x00
#define WL1271_RX_DESC_DECRYPT_FAIL 0x01 #define WL1271_RX_DESC_DECRYPT_FAIL 0x01
#define WL1271_RX_DESC_MIC_FAIL 0x02 #define WL1271_RX_DESC_MIC_FAIL 0x02
#define WL1271_RX_DESC_DRIVER_RX_Q_FAIL 0x03
#define RX_MEM_BLOCK_MASK 0xFF #define RX_MEM_BLOCK_MASK 0xFF
#define RX_BUF_SIZE_MASK 0xFFF00 #define RX_BUF_SIZE_MASK 0xFFF00
......
...@@ -326,8 +326,7 @@ static void wl1271_remove(struct sdio_func *func) ...@@ -326,8 +326,7 @@ static void wl1271_remove(struct sdio_func *func)
/* Undo decrement done above in wl1271_probe */ /* Undo decrement done above in wl1271_probe */
pm_runtime_get_noresume(&func->dev); pm_runtime_get_noresume(&func->dev);
platform_device_del(glue->core); platform_device_unregister(glue->core);
platform_device_put(glue->core);
kfree(glue); kfree(glue);
} }
......
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册