mirror of https://gitee.com/openkylin/linux.git
Merge branch 'for-linville' of git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx
Conflicts: drivers/net/wireless/ti/wlcore/main.c
This commit is contained in:
commit
56e1bd7706
|
@ -1,6 +1,6 @@
|
|||
menuconfig WL1251
|
||||
tristate "TI wl1251 driver support"
|
||||
depends on MAC80211 && EXPERIMENTAL && GENERIC_HARDIRQS
|
||||
depends on MAC80211 && GENERIC_HARDIRQS
|
||||
select FW_LOADER
|
||||
select CRC7
|
||||
---help---
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
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
|
||||
|
|
|
@ -284,3 +284,40 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl)
|
|||
kfree(radio_parms);
|
||||
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 {
|
|||
u8 padding[3];
|
||||
} __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 wl128x_cmd_general_parms(struct wl1271 *wl);
|
||||
int wl1271_cmd_radio_parms(struct wl1271 *wl);
|
||||
int wl128x_cmd_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__ */
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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 @@
|
|||
#include "reg.h"
|
||||
#include "cmd.h"
|
||||
#include "acx.h"
|
||||
#include "scan.h"
|
||||
#include "event.h"
|
||||
#include "debugfs.h"
|
||||
|
||||
static char *fref_param;
|
||||
|
@ -208,6 +210,8 @@ static struct wlcore_conf wl12xx_conf = {
|
|||
.tmpl_short_retry_limit = 10,
|
||||
.tmpl_long_retry_limit = 10,
|
||||
.tx_watchdog_timeout = 5000,
|
||||
.slow_link_thold = 3,
|
||||
.fast_link_thold = 10,
|
||||
},
|
||||
.conn = {
|
||||
.wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
|
||||
|
@ -265,8 +269,10 @@ static struct wlcore_conf wl12xx_conf = {
|
|||
.scan = {
|
||||
.min_dwell_time_active = 7500,
|
||||
.max_dwell_time_active = 30000,
|
||||
.min_dwell_time_passive = 100000,
|
||||
.max_dwell_time_passive = 100000,
|
||||
.min_dwell_time_active_long = 25000,
|
||||
.max_dwell_time_active_long = 50000,
|
||||
.dwell_time_passive = 100000,
|
||||
.dwell_time_dfs = 150000,
|
||||
.num_probe_reqs = 2,
|
||||
.split_scan_timeout = 50000,
|
||||
},
|
||||
|
@ -368,6 +374,10 @@ static struct wlcore_conf wl12xx_conf = {
|
|||
.increase_time = 1,
|
||||
.window_size = 16,
|
||||
},
|
||||
.recovery = {
|
||||
.bug_on_recovery = 0,
|
||||
.no_recovery = 0,
|
||||
},
|
||||
};
|
||||
|
||||
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)
|
|||
{
|
||||
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 wl127x_rx_mem_pool_addr rx_mem_addr;
|
||||
struct wl12xx_priv *priv = wl->priv;
|
||||
|
||||
/*
|
||||
* Choose the block we want to read
|
||||
|
@ -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;
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
sizeof(rx_mem_addr), false);
|
||||
ret = wlcore_write(wl, WL1271_SLV_REG_DATA, priv->rx_mem_addr,
|
||||
sizeof(*priv->rx_mem_addr), false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
@ -631,13 +641,15 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
|
|||
int ret = 0;
|
||||
|
||||
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",
|
||||
wl->chip.id);
|
||||
|
||||
wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
|
||||
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->mr_fw_name = WL127X_FW_NAME_MULTI;
|
||||
memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
|
||||
|
@ -646,18 +658,22 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
|
|||
/* read data preparation is only needed by wl127x */
|
||||
wl->ops->prepare_read = wl127x_prepare_read;
|
||||
|
||||
wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER,
|
||||
WL127X_MAJOR_VER, WL127X_SUBTYPE_VER,
|
||||
WL127X_MINOR_VER);
|
||||
wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
|
||||
WL127X_IFTYPE_SR_VER, WL127X_MAJOR_SR_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;
|
||||
|
||||
case CHIP_ID_1271_PG20:
|
||||
case CHIP_ID_127X_PG20:
|
||||
wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
|
||||
wl->chip.id);
|
||||
|
||||
wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
|
||||
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->sr_fw_name = WL127X_FW_NAME_SINGLE;
|
||||
wl->mr_fw_name = WL127X_FW_NAME_MULTI;
|
||||
|
@ -667,12 +683,14 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
|
|||
/* read data preparation is only needed by wl127x */
|
||||
wl->ops->prepare_read = wl127x_prepare_read;
|
||||
|
||||
wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER,
|
||||
WL127X_MAJOR_VER, WL127X_SUBTYPE_VER,
|
||||
WL127X_MINOR_VER);
|
||||
wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
|
||||
WL127X_IFTYPE_SR_VER, WL127X_MAJOR_SR_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;
|
||||
|
||||
case CHIP_ID_1283_PG20:
|
||||
case CHIP_ID_128X_PG20:
|
||||
wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
|
||||
wl->chip.id);
|
||||
wl->plt_fw_name = WL128X_PLT_FW_NAME;
|
||||
|
@ -682,19 +700,29 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
|
|||
/* wl128x requires TX blocksize alignment */
|
||||
wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
|
||||
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;
|
||||
|
||||
wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, WL128X_IFTYPE_VER,
|
||||
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;
|
||||
case CHIP_ID_1283_PG10:
|
||||
case CHIP_ID_128X_PG10:
|
||||
default:
|
||||
wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
|
||||
ret = -ENODEV;
|
||||
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:
|
||||
return ret;
|
||||
}
|
||||
|
@ -1067,7 +1095,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
|
|||
u32 clk;
|
||||
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);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
@ -1098,7 +1126,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
|
|||
|
||||
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;
|
||||
else
|
||||
clk |= (priv->ref_clock << 1) << 4;
|
||||
|
@ -1152,7 +1180,7 @@ static int wl12xx_pre_upload(struct wl1271 *wl)
|
|||
/* WL1271: The reference driver skips steps 7 to 10 (jumps directly
|
||||
* 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);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
@ -1219,6 +1247,23 @@ static int wl12xx_boot(struct wl1271 *wl)
|
|||
if (ret < 0)
|
||||
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);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
@ -1261,7 +1306,7 @@ static void
|
|||
wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
|
||||
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;
|
||||
} else {
|
||||
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,
|
|||
{
|
||||
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->length = cpu_to_le16(aligned_len >> 2);
|
||||
|
||||
|
@ -1339,7 +1384,7 @@ static int wl12xx_hw_init(struct wl1271 *wl)
|
|||
{
|
||||
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;
|
||||
|
||||
ret = wl128x_cmd_general_parms(wl);
|
||||
|
@ -1394,22 +1439,6 @@ static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl,
|
|||
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)
|
||||
{
|
||||
struct wl12xx_priv *priv = wl->priv;
|
||||
|
@ -1426,7 +1455,7 @@ static bool wl12xx_mac_in_fuse(struct wl1271 *wl)
|
|||
bool supported = false;
|
||||
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);
|
||||
minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver);
|
||||
|
||||
|
@ -1482,7 +1511,7 @@ static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
|
|||
u16 die_info;
|
||||
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,
|
||||
&die_info);
|
||||
else
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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 struct wlcore_ops wl12xx_ops = {
|
||||
.setup = wl12xx_setup,
|
||||
.identify_chip = wl12xx_identify_chip,
|
||||
.identify_fw = wl12xx_identify_fw,
|
||||
.boot = wl12xx_boot,
|
||||
.plt_init = wl12xx_plt_init,
|
||||
.trigger_cmd = wl12xx_trigger_cmd,
|
||||
.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,
|
||||
.set_tx_desc_blocks = wl12xx_set_tx_desc_blocks,
|
||||
.set_tx_desc_data_len = wl12xx_set_tx_desc_data_len,
|
||||
|
@ -1615,9 +1674,17 @@ static struct wlcore_ops wl12xx_ops = {
|
|||
.set_rx_csum = NULL,
|
||||
.ap_get_mimo_wide_rate_mask = NULL,
|
||||
.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,
|
||||
.set_key = wl12xx_set_key,
|
||||
.channel_switch = wl12xx_cmd_channel_switch,
|
||||
.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 = {
|
||||
|
@ -1641,6 +1708,7 @@ static int wl12xx_setup(struct wl1271 *wl)
|
|||
wl->rtable = wl12xx_rtable;
|
||||
wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
|
||||
wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS;
|
||||
wl->num_channels = 1;
|
||||
wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES;
|
||||
wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
|
||||
wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
|
||||
|
@ -1693,6 +1761,10 @@ static int wl12xx_setup(struct wl1271 *wl)
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -1703,7 +1775,8 @@ static int wl12xx_probe(struct platform_device *pdev)
|
|||
int ret;
|
||||
|
||||
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)) {
|
||||
wl1271_error("can't allocate hw");
|
||||
ret = PTR_ERR(hw);
|
||||
|
@ -1725,6 +1798,21 @@ static int wl12xx_probe(struct platform_device *pdev)
|
|||
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[] = {
|
||||
{ "wl12xx", 0 },
|
||||
{ } /* Terminating Entry */
|
||||
|
@ -1733,7 +1821,7 @@ MODULE_DEVICE_TABLE(platform, wl12xx_id_table);
|
|||
|
||||
static struct platform_driver wl12xx_driver = {
|
||||
.probe = wl12xx_probe,
|
||||
.remove = wlcore_remove,
|
||||
.remove = wl12xx_remove,
|
||||
.id_table = wl12xx_id_table,
|
||||
.driver = {
|
||||
.name = "wl12xx_driver",
|
||||
|
|
|
@ -0,0 +1,501 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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 @@
|
|||
|
||||
#include "conf.h"
|
||||
|
||||
/* minimum FW required for driver for wl127x */
|
||||
#define WL127X_CHIP_VER 6
|
||||
#define WL127X_IFTYPE_VER 3
|
||||
#define WL127X_MAJOR_VER 10
|
||||
#define WL127X_SUBTYPE_VER 2
|
||||
#define WL127X_MINOR_VER 115
|
||||
/* 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)
|
||||
|
||||
/* minimum FW required for driver for wl128x */
|
||||
/* FW chip version for wl127x */
|
||||
#define WL127X_CHIP_VER 6
|
||||
/* minimum single-role FW version for wl127x */
|
||||
#define WL127X_IFTYPE_SR_VER 3
|
||||
#define WL127X_MAJOR_SR_VER 10
|
||||
#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
|
||||
|
||||
/* FW chip version for wl128x */
|
||||
#define WL128X_CHIP_VER 7
|
||||
#define WL128X_IFTYPE_VER 3
|
||||
#define WL128X_MAJOR_VER 10
|
||||
#define WL128X_SUBTYPE_VER 2
|
||||
#define WL128X_MINOR_VER 115
|
||||
/* minimum single-role FW version for wl128x */
|
||||
#define WL128X_IFTYPE_SR_VER 3
|
||||
#define WL128X_MAJOR_SR_VER 10
|
||||
#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)
|
||||
|
||||
|
@ -55,6 +73,8 @@ struct wl12xx_priv {
|
|||
|
||||
int ref_clock;
|
||||
int tcxo_clock;
|
||||
|
||||
struct wl127x_rx_mem_pool_addr *rx_mem_addr;
|
||||
};
|
||||
|
||||
#endif /* __WL12XX_PRIV_H__ */
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
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
|
||||
|
|
|
@ -75,7 +75,7 @@ int wl18xx_acx_set_checksum_state(struct wl1271 *wl)
|
|||
|
||||
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) {
|
||||
wl1271_warning("failed to set Tx checksum state: %d", ret);
|
||||
goto out;
|
||||
|
@ -109,3 +109,88 @@ int wl18xx_acx_clear_statistics(struct wl1271 *wl)
|
|||
kfree(acx);
|
||||
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 @@
|
|||
#include "../wlcore/acx.h"
|
||||
|
||||
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) */
|
||||
|
@ -278,10 +284,57 @@ struct wl18xx_acx_clear_statistics {
|
|||
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,
|
||||
u32 sdio_blk_size, u32 extra_mem_blks,
|
||||
u32 len_field_size);
|
||||
int wl18xx_acx_set_checksum_state(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__ */
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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 @@
|
|||
#define __WL18XX_CONF_H__
|
||||
|
||||
#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_SIZE (WLCORE_CONF_SIZE + \
|
||||
sizeof(struct wl18xx_priv_conf))
|
||||
|
||||
#define NUM_OF_CHANNELS_11_ABG 150
|
||||
#define NUM_OF_CHANNELS_11_P 7
|
||||
#define WL18XX_NUM_OF_SUB_BANDS 9
|
||||
#define SRF_TABLE_LEN 16
|
||||
#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 {
|
||||
u8 phy_standalone;
|
||||
u8 rdl;
|
||||
u8 spare0;
|
||||
u8 enable_clpc;
|
||||
u8 enable_tx_low_pwr_on_siso_rdl;
|
||||
u8 auto_detect;
|
||||
|
@ -69,18 +70,26 @@ struct wl18xx_mac_and_phy_params {
|
|||
u8 pwr_limit_reference_11_abg;
|
||||
u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P];
|
||||
u8 pwr_limit_reference_11p;
|
||||
u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
|
||||
u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
|
||||
u8 spare1[9];
|
||||
u8 spare2[9];
|
||||
u8 primary_clock_setting_time;
|
||||
u8 clock_valid_on_wake_up;
|
||||
u8 secondary_clock_setting_time;
|
||||
u8 board_type;
|
||||
/* enable point saturation */
|
||||
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 med_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];
|
||||
} __packed;
|
||||
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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 @@
|
|||
|
||||
#include "reg.h"
|
||||
#include "conf.h"
|
||||
#include "cmd.h"
|
||||
#include "acx.h"
|
||||
#include "tx.h"
|
||||
#include "wl18xx.h"
|
||||
#include "io.h"
|
||||
#include "scan.h"
|
||||
#include "event.h"
|
||||
#include "debugfs.h"
|
||||
|
||||
#define WL18XX_RX_CHECKSUM_MASK 0x40
|
||||
|
@ -334,6 +337,8 @@ static struct wlcore_conf wl18xx_conf = {
|
|||
.tmpl_short_retry_limit = 10,
|
||||
.tmpl_long_retry_limit = 10,
|
||||
.tx_watchdog_timeout = 5000,
|
||||
.slow_link_thold = 3,
|
||||
.fast_link_thold = 30,
|
||||
},
|
||||
.conn = {
|
||||
.wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
|
||||
|
@ -391,8 +396,10 @@ static struct wlcore_conf wl18xx_conf = {
|
|||
.scan = {
|
||||
.min_dwell_time_active = 7500,
|
||||
.max_dwell_time_active = 30000,
|
||||
.min_dwell_time_passive = 100000,
|
||||
.max_dwell_time_passive = 100000,
|
||||
.min_dwell_time_active_long = 25000,
|
||||
.max_dwell_time_active_long = 50000,
|
||||
.dwell_time_passive = 100000,
|
||||
.dwell_time_dfs = 150000,
|
||||
.num_probe_reqs = 2,
|
||||
.split_scan_timeout = 50000,
|
||||
},
|
||||
|
@ -489,6 +496,10 @@ static struct wlcore_conf wl18xx_conf = {
|
|||
.increase_time = 1,
|
||||
.window_size = 16,
|
||||
},
|
||||
.recovery = {
|
||||
.bug_on_recovery = 0,
|
||||
.no_recovery = 0,
|
||||
},
|
||||
};
|
||||
|
||||
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,
|
||||
.secondary_clock_setting_time = 0x05,
|
||||
.board_type = BOARD_TYPE_HDK_18XX,
|
||||
.rdl = 0x01,
|
||||
.auto_detect = 0x00,
|
||||
.dedicated_fem = FEM_NONE,
|
||||
.low_band_component = COMPONENT_3_WAY_SWITCH,
|
||||
|
@ -517,14 +527,39 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
|
|||
.enable_clpc = 0x00,
|
||||
.enable_tx_low_pwr_on_siso_rdl = 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,
|
||||
.low_power_val = 0x00,
|
||||
.med_power_val = 0x0a,
|
||||
.high_power_val = 0x1e,
|
||||
.low_power_val = 0x08,
|
||||
.med_power_val = 0x12,
|
||||
.high_power_val = 0x18,
|
||||
.low_power_val_2nd = 0x05,
|
||||
.med_power_val_2nd = 0x0a,
|
||||
.high_power_val_2nd = 0x14,
|
||||
.external_pa_dc2dc = 0,
|
||||
.number_of_assembled_ant2_4 = 1,
|
||||
.number_of_assembled_ant2_4 = 2,
|
||||
.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] = {
|
|||
};
|
||||
|
||||
/* 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)
|
||||
{
|
||||
|
@ -608,15 +643,18 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
|
|||
wl->sr_fw_name = WL18XX_FW_NAME;
|
||||
/* wl18xx uses the same firmware for PLT */
|
||||
wl->plt_fw_name = WL18XX_FW_NAME;
|
||||
wl->quirks |= WLCORE_QUIRK_NO_ELP |
|
||||
WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
|
||||
wl->quirks |= WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
|
||||
WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
|
||||
WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN |
|
||||
WLCORE_QUIRK_TX_PAD_LAST_FRAME;
|
||||
WLCORE_QUIRK_TX_PAD_LAST_FRAME |
|
||||
WLCORE_QUIRK_REGDOMAIN_CONF |
|
||||
WLCORE_QUIRK_DUAL_PROBE_TMPL;
|
||||
|
||||
wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER, WL18XX_IFTYPE_VER,
|
||||
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;
|
||||
case CHIP_ID_185x_PG10:
|
||||
wl1271_warning("chip id 0x%x (185x PG10) is deprecated",
|
||||
|
@ -630,6 +668,11 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
|
|||
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:
|
||||
return ret;
|
||||
}
|
||||
|
@ -843,6 +886,20 @@ static int wl18xx_boot(struct wl1271 *wl)
|
|||
if (ret < 0)
|
||||
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);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
@ -964,7 +1021,7 @@ static int wl18xx_hw_init(struct wl1271 *wl)
|
|||
|
||||
/* (re)init private structures. Relevant on recovery as well. */
|
||||
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 */
|
||||
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)
|
|||
{
|
||||
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)
|
|||
{
|
||||
struct wl18xx_priv *priv = wl->priv;
|
||||
|
||||
/* If we have VIFs requiring extra spare, indulge them */
|
||||
if (priv->extra_spare_vif_count)
|
||||
/* If we have keys requiring extra spare, indulge them */
|
||||
if (priv->extra_spare_key_count)
|
||||
return WL18XX_TX_HW_EXTRA_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,
|
|||
struct ieee80211_key_conf *key_conf)
|
||||
{
|
||||
struct wl18xx_priv *priv = wl->priv;
|
||||
bool change_spare = false;
|
||||
bool change_spare = false, special_enc;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* when adding the first or removing the last GEM/TKIP interface,
|
||||
* we have to adjust the number of spare blocks.
|
||||
*/
|
||||
change_spare = (key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
|
||||
key_conf->cipher == WLAN_CIPHER_SUITE_TKIP) &&
|
||||
((priv->extra_spare_vif_count == 0 && cmd == SET_KEY) ||
|
||||
(priv->extra_spare_vif_count == 1 && cmd == DISABLE_KEY));
|
||||
wl1271_debug(DEBUG_CRYPT, "extra spare keys before: %d",
|
||||
priv->extra_spare_key_count);
|
||||
|
||||
/* no need to change spare - just regular set_key */
|
||||
if (!change_spare)
|
||||
return wlcore_set_key(wl, cmd, vif, sta, key_conf);
|
||||
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 key,
|
||||
* we have to adjust the number of spare blocks.
|
||||
*/
|
||||
if (special_enc) {
|
||||
if (cmd == SET_KEY) {
|
||||
/* first 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--;
|
||||
}
|
||||
}
|
||||
|
||||
wl1271_debug(DEBUG_CRYPT, "extra spare keys after: %d",
|
||||
priv->extra_spare_key_count);
|
||||
|
||||
if (!change_spare)
|
||||
goto out;
|
||||
|
||||
/* 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,
|
||||
WL18XX_TX_HW_EXTRA_BLOCK_SPARE);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
priv->extra_spare_vif_count++;
|
||||
} else {
|
||||
else
|
||||
ret = wl18xx_set_host_cfg_bitmap(wl,
|
||||
WL18XX_TX_HW_BLOCK_SPARE);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
priv->extra_spare_vif_count--;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
@ -1296,6 +1364,92 @@ static u32 wl18xx_pre_pkt_send(struct wl1271 *wl,
|
|||
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 struct wlcore_ops wl18xx_ops = {
|
||||
|
@ -1305,6 +1459,8 @@ static struct wlcore_ops wl18xx_ops = {
|
|||
.plt_init = wl18xx_plt_init,
|
||||
.trigger_cmd = wl18xx_trigger_cmd,
|
||||
.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,
|
||||
.set_tx_desc_blocks = wl18xx_set_tx_desc_blocks,
|
||||
.set_tx_desc_data_len = wl18xx_set_tx_desc_data_len,
|
||||
|
@ -1320,16 +1476,26 @@ static struct wlcore_ops wl18xx_ops = {
|
|||
.ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask,
|
||||
.get_mac = wl18xx_get_mac,
|
||||
.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,
|
||||
.get_spare_blocks = wl18xx_get_spare_blocks,
|
||||
.set_key = wl18xx_set_key,
|
||||
.channel_switch = wl18xx_cmd_channel_switch,
|
||||
.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 */
|
||||
static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
|
||||
.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
|
||||
IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40,
|
||||
IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40 |
|
||||
IEEE80211_HT_CAP_GRN_FLD,
|
||||
.ht_supported = true,
|
||||
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
|
||||
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
|
||||
|
@ -1343,7 +1509,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
|
|||
/* HT cap appropriate for wide channels in 5Ghz */
|
||||
static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
|
||||
.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
|
||||
IEEE80211_HT_CAP_SUP_WIDTH_20_40,
|
||||
IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
|
||||
IEEE80211_HT_CAP_GRN_FLD,
|
||||
.ht_supported = true,
|
||||
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
|
||||
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
|
||||
|
@ -1356,7 +1523,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
|
|||
|
||||
/* HT cap appropriate for SISO 20 */
|
||||
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,
|
||||
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
|
||||
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
|
||||
|
@ -1369,7 +1537,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
|
|||
|
||||
/* HT cap appropriate for MIMO rates in 20mhz channel */
|
||||
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,
|
||||
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
|
||||
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
|
||||
|
@ -1387,7 +1556,8 @@ static int wl18xx_setup(struct wl1271 *wl)
|
|||
|
||||
wl->rtable = wl18xx_rtable;
|
||||
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->band_rate_to_idx = wl18xx_band_rate_to_idx;
|
||||
wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX;
|
||||
|
@ -1506,7 +1676,8 @@ static int wl18xx_probe(struct platform_device *pdev)
|
|||
int ret;
|
||||
|
||||
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)) {
|
||||
wl1271_error("can't allocate hw");
|
||||
ret = PTR_ERR(hw);
|
||||
|
|
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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 @@
|
|||
#include "wl18xx.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)
|
||||
{
|
||||
struct ieee80211_tx_info *info;
|
||||
|
@ -44,7 +87,6 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
|
|||
/* a zero bit indicates Tx success */
|
||||
tx_success = !(tx_stat_byte & BIT(WL18XX_TX_STATUS_STAT_BIT_IDX));
|
||||
|
||||
|
||||
skb = wl->tx_frames[id];
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
|
@ -56,11 +98,13 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
|
|||
/* update the TX status info */
|
||||
if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_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].idx = -1;
|
||||
info->status.rates[0].count = 0;
|
||||
info->status.rates[0].flags = 0;
|
||||
info->status.rates[0].count = 1; /* no data about retries */
|
||||
info->status.ack_signal = -1;
|
||||
|
||||
if (!tx_success)
|
||||
|
|
|
@ -26,10 +26,10 @@
|
|||
|
||||
/* minimum FW required for driver */
|
||||
#define WL18XX_CHIP_VER 8
|
||||
#define WL18XX_IFTYPE_VER 2
|
||||
#define WL18XX_MAJOR_VER 0
|
||||
#define WL18XX_SUBTYPE_VER 0
|
||||
#define WL18XX_MINOR_VER 100
|
||||
#define WL18XX_IFTYPE_VER 5
|
||||
#define WL18XX_MAJOR_VER WLCORE_FW_VER_IGNORE
|
||||
#define WL18XX_SUBTYPE_VER WLCORE_FW_VER_IGNORE
|
||||
#define WL18XX_MINOR_VER 28
|
||||
|
||||
#define WL18XX_CMD_MAX_SIZE 740
|
||||
|
||||
|
@ -49,8 +49,8 @@ struct wl18xx_priv {
|
|||
/* Index of last released Tx desc in FW */
|
||||
u8 last_fw_rls_idx;
|
||||
|
||||
/* number of VIFs requiring extra spare mem-blocks */
|
||||
int extra_spare_vif_count;
|
||||
/* number of keys requiring extra spare mem-blocks */
|
||||
int extra_spare_key_count;
|
||||
};
|
||||
|
||||
#define WL18XX_FW_MAX_TX_STATUS_DESC 33
|
||||
|
@ -68,7 +68,43 @@ struct wl18xx_fw_status_priv {
|
|||
*/
|
||||
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
|
||||
|
|
|
@ -1340,6 +1340,8 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
|
|||
kfree(acx);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wl1271_acx_set_ht_capabilities);
|
||||
|
||||
|
||||
int wl1271_acx_set_ht_information(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
|
@ -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->ssn = ssn;
|
||||
|
||||
ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_RX_SETUP, acx,
|
||||
sizeof(*acx));
|
||||
ret = wlcore_cmd_configure_failsafe(wl, ACX_BA_SESSION_RX_SETUP, acx,
|
||||
sizeof(*acx),
|
||||
BIT(CMD_STATUS_NO_RX_BA_SESSION));
|
||||
if (ret < 0) {
|
||||
wl1271_warning("acx ba receiver session failed: %d", ret);
|
||||
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:
|
||||
kfree(acx);
|
||||
return ret;
|
||||
|
|
|
@ -1025,7 +1025,6 @@ enum {
|
|||
ACX_CONFIG_HANGOVER = 0x0042,
|
||||
ACX_FEATURE_CFG = 0x0043,
|
||||
ACX_PROTECTION_CFG = 0x0044,
|
||||
ACX_CHECKSUM_CONFIG = 0x0045,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -84,47 +84,57 @@ static int wlcore_boot_parse_fw_ver(struct wl1271 *wl,
|
|||
static int wlcore_validate_fw_ver(struct wl1271 *wl)
|
||||
{
|
||||
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 */
|
||||
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;
|
||||
|
||||
/* always check the next digit if all previous ones are equal */
|
||||
|
||||
if (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])
|
||||
/* the firmware type must be equal */
|
||||
if ((min_ver[FW_VER_IF_TYPE] != WLCORE_FW_VER_IGNORE) &&
|
||||
(min_ver[FW_VER_IF_TYPE] != fw_ver[FW_VER_IF_TYPE]))
|
||||
goto fail;
|
||||
|
||||
if (min_ver[FW_VER_MAJOR] < fw_ver[FW_VER_MAJOR])
|
||||
goto out;
|
||||
else if (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR])
|
||||
/* the project number must be equal */
|
||||
if ((min_ver[FW_VER_SUBTYPE] != WLCORE_FW_VER_IGNORE) &&
|
||||
(min_ver[FW_VER_SUBTYPE] != fw_ver[FW_VER_SUBTYPE]))
|
||||
goto fail;
|
||||
|
||||
if (min_ver[FW_VER_SUBTYPE] < fw_ver[FW_VER_SUBTYPE])
|
||||
goto out;
|
||||
else if (min_ver[FW_VER_SUBTYPE] > fw_ver[FW_VER_SUBTYPE])
|
||||
/* the API version must be greater or equal */
|
||||
if ((min_ver[FW_VER_MAJOR] != WLCORE_FW_VER_IGNORE) &&
|
||||
(min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR]))
|
||||
goto fail;
|
||||
|
||||
if (min_ver[FW_VER_MINOR] < fw_ver[FW_VER_MINOR])
|
||||
goto out;
|
||||
else if (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR])
|
||||
/* if the API version is equal... */
|
||||
if (((min_ver[FW_VER_MAJOR] == WLCORE_FW_VER_IGNORE) ||
|
||||
(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;
|
||||
|
||||
out:
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is outdated.\n"
|
||||
"Please use at least FW %u.%u.%u.%u.%u.\n"
|
||||
"You can get more information at:\n"
|
||||
"http://wireless.kernel.org/en/users/Drivers/wl12xx",
|
||||
for (i = 0; i < NUM_FW_VER; i++)
|
||||
if (min_ver[i] == WLCORE_FW_VER_IGNORE)
|
||||
snprintf(min_fw_str, sizeof(min_fw_str),
|
||||
"%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_MAJOR], fw_ver[FW_VER_SUBTYPE],
|
||||
fw_ver[FW_VER_MINOR], min_ver[FW_VER_CHIP],
|
||||
min_ver[FW_VER_IF_TYPE], min_ver[FW_VER_MAJOR],
|
||||
min_ver[FW_VER_SUBTYPE], min_ver[FW_VER_MINOR]);
|
||||
fw_ver[FW_VER_MINOR], min_fw_str);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -491,7 +501,7 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
|
|||
if (ret < 0)
|
||||
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",
|
||||
wl->mbox_ptr[0], wl->mbox_ptr[1]);
|
||||
|
@ -508,23 +518,6 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
|
|||
*/
|
||||
|
||||
/* 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);
|
||||
if (ret < 0) {
|
||||
wl1271_error("EVENT mask setting failed");
|
||||
|
|
|
@ -48,14 +48,15 @@
|
|||
* @id: command id
|
||||
* @buf: buffer containing the command, must work with dma
|
||||
* @len: length of the buffer
|
||||
* return the cmd status code on success.
|
||||
*/
|
||||
int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
|
||||
size_t res_len)
|
||||
static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf,
|
||||
size_t len, size_t res_len)
|
||||
{
|
||||
struct wl1271_cmd_header *cmd;
|
||||
unsigned long timeout;
|
||||
u32 intr;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
u16 status;
|
||||
u16 poll_count = 0;
|
||||
|
||||
|
@ -71,7 +72,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
|
|||
|
||||
ret = wlcore_write(wl, wl->cmd_box_addr, buf, len, false);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* TODO: we just need this because one bit is in a different
|
||||
|
@ -79,19 +80,18 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
|
|||
*/
|
||||
ret = wl->ops->trigger_cmd(wl, wl->cmd_box_addr, buf, len);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
return ret;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT);
|
||||
|
||||
ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
return ret;
|
||||
|
||||
while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
wl1271_error("command complete timeout");
|
||||
ret = -ETIMEDOUT;
|
||||
goto fail;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
poll_count++;
|
||||
|
@ -102,7 +102,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
|
|||
|
||||
ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* read back the status code of the command */
|
||||
|
@ -111,33 +111,66 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
|
|||
|
||||
ret = wlcore_read(wl, wl->cmd_box_addr, cmd, res_len, false);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
return ret;
|
||||
|
||||
status = le16_to_cpu(cmd->status);
|
||||
if (status != CMD_STATUS_SUCCESS) {
|
||||
wl1271_error("command execute failure %d", status);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK,
|
||||
WL1271_ACX_INTR_CMD_COMPLETE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* send command to fw and return cmd status on success
|
||||
* valid_rets contains a bitmap of allowed error codes
|
||||
*/
|
||||
int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len,
|
||||
size_t res_len, unsigned long valid_rets)
|
||||
{
|
||||
int ret = __wlcore_cmd_send(wl, id, buf, len, res_len);
|
||||
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
/* success is always a valid status */
|
||||
valid_rets |= BIT(CMD_STATUS_SUCCESS);
|
||||
|
||||
if (ret >= MAX_COMMAND_STATUS ||
|
||||
!test_bit(ret, &valid_rets)) {
|
||||
wl1271_error("command execute failure %d", ret);
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
return ret;
|
||||
fail:
|
||||
wl12xx_queue_recovery_work(wl);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wl1271_cmd_send);
|
||||
|
||||
/*
|
||||
* wrapper for wlcore_cmd_send that accept only CMD_STATUS_SUCCESS
|
||||
* return 0 on success.
|
||||
*/
|
||||
int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
|
||||
size_t res_len)
|
||||
{
|
||||
int ret = wlcore_cmd_send_failsafe(wl, id, buf, len, res_len, 0);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Poll the mailbox event field until any of the bits in the mask is set or a
|
||||
* timeout occurs (WL1271_EVENT_TIMEOUT in msecs)
|
||||
*/
|
||||
static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
|
||||
u32 mask, bool *timeout)
|
||||
int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
|
||||
u32 mask, bool *timeout)
|
||||
{
|
||||
u32 *events_vector;
|
||||
u32 event;
|
||||
|
@ -187,20 +220,7 @@ static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
|
|||
kfree(events_vector);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
|
||||
{
|
||||
int ret;
|
||||
bool timeout = false;
|
||||
|
||||
ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask, &timeout);
|
||||
if (ret != 0 || timeout) {
|
||||
wl12xx_queue_recovery_work(wl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wlcore_cmd_wait_for_event_or_timeout);
|
||||
|
||||
int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
|
||||
u8 *role_id)
|
||||
|
@ -278,6 +298,16 @@ int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int wlcore_get_new_session_id(struct wl1271 *wl, u8 hlid)
|
||||
{
|
||||
if (wl->session_ids[hlid] >= SESSION_COUNTER_MAX)
|
||||
wl->session_ids[hlid] = 0;
|
||||
|
||||
wl->session_ids[hlid]++;
|
||||
|
||||
return wl->session_ids[hlid];
|
||||
}
|
||||
|
||||
int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
@ -285,12 +315,21 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
|
|||
if (link >= WL12XX_MAX_LINKS)
|
||||
return -EBUSY;
|
||||
|
||||
wl->session_ids[link] = wlcore_get_new_session_id(wl, link);
|
||||
|
||||
/* these bits are used by op_tx */
|
||||
spin_lock_irqsave(&wl->wl_lock, flags);
|
||||
__set_bit(link, wl->links_map);
|
||||
__set_bit(link, wlvif->links_map);
|
||||
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
||||
|
||||
/* take the last "freed packets" value from the current FW status */
|
||||
wl->links[link].prev_freed_pkts =
|
||||
wl->fw_status_2->counters.tx_lnk_free_pkts[link];
|
||||
wl->links[link].wlvif = wlvif;
|
||||
*hlid = link;
|
||||
|
||||
wl->active_link_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -307,24 +346,21 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
|
|||
__clear_bit(*hlid, wlvif->links_map);
|
||||
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
||||
|
||||
wl->links[*hlid].allocated_pkts = 0;
|
||||
wl->links[*hlid].prev_freed_pkts = 0;
|
||||
wl->links[*hlid].ba_bitmap = 0;
|
||||
memset(wl->links[*hlid].addr, 0, ETH_ALEN);
|
||||
|
||||
/*
|
||||
* At this point op_tx() will not add more packets to the queues. We
|
||||
* can purge them.
|
||||
*/
|
||||
wl1271_tx_reset_link_queues(wl, *hlid);
|
||||
wl->links[*hlid].wlvif = NULL;
|
||||
|
||||
*hlid = WL12XX_INVALID_LINK_ID;
|
||||
}
|
||||
|
||||
static int wl12xx_get_new_session_id(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif)
|
||||
{
|
||||
if (wlvif->session_counter >= SESSION_COUNTER_MAX)
|
||||
wlvif->session_counter = 0;
|
||||
|
||||
wlvif->session_counter++;
|
||||
|
||||
return wlvif->session_counter;
|
||||
wl->active_link_count--;
|
||||
WARN_ON_ONCE(wl->active_link_count < 0);
|
||||
}
|
||||
|
||||
static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
|
||||
|
@ -345,7 +381,9 @@ static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
|
|||
}
|
||||
|
||||
static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif)
|
||||
struct wl12xx_vif *wlvif,
|
||||
enum ieee80211_band band,
|
||||
int channel)
|
||||
{
|
||||
struct wl12xx_cmd_role_start *cmd;
|
||||
int ret;
|
||||
|
@ -359,9 +397,9 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
|
|||
wl1271_debug(DEBUG_CMD, "cmd role start dev %d", wlvif->dev_role_id);
|
||||
|
||||
cmd->role_id = wlvif->dev_role_id;
|
||||
if (wlvif->band == IEEE80211_BAND_5GHZ)
|
||||
if (band == IEEE80211_BAND_5GHZ)
|
||||
cmd->band = WLCORE_BAND_5GHZ;
|
||||
cmd->channel = wlvif->channel;
|
||||
cmd->channel = channel;
|
||||
|
||||
if (wlvif->dev_hlid == WL12XX_INVALID_LINK_ID) {
|
||||
ret = wl12xx_allocate_link(wl, wlvif, &wlvif->dev_hlid);
|
||||
|
@ -369,7 +407,7 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
|
|||
goto out_free;
|
||||
}
|
||||
cmd->device.hlid = wlvif->dev_hlid;
|
||||
cmd->device.session = wl12xx_get_new_session_id(wl, wlvif);
|
||||
cmd->device.session = wl->session_ids[wlvif->dev_hlid];
|
||||
|
||||
wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d",
|
||||
cmd->role_id, cmd->device.hlid, cmd->device.session);
|
||||
|
@ -420,12 +458,6 @@ static int wl12xx_cmd_role_stop_dev(struct wl1271 *wl,
|
|||
goto out_free;
|
||||
}
|
||||
|
||||
ret = wl1271_cmd_wait_for_event(wl, ROLE_STOP_COMPLETE_EVENT_ID);
|
||||
if (ret < 0) {
|
||||
wl1271_error("cmd role stop dev event completion error");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
wl12xx_free_link(wl, wlvif, &wlvif->dev_hlid);
|
||||
|
||||
out_free:
|
||||
|
@ -439,6 +471,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|||
{
|
||||
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
|
||||
struct wl12xx_cmd_role_start *cmd;
|
||||
u32 supported_rates;
|
||||
int ret;
|
||||
|
||||
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
||||
|
@ -459,7 +492,14 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|||
cmd->sta.ssid_len = wlvif->ssid_len;
|
||||
memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len);
|
||||
memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN);
|
||||
cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
|
||||
|
||||
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->sta.local_rates = cpu_to_le32(supported_rates);
|
||||
|
||||
cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
|
||||
|
||||
if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) {
|
||||
|
@ -468,7 +508,11 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|||
goto out_free;
|
||||
}
|
||||
cmd->sta.hlid = wlvif->sta.hlid;
|
||||
cmd->sta.session = wl12xx_get_new_session_id(wl, wlvif);
|
||||
cmd->sta.session = wl->session_ids[wlvif->sta.hlid];
|
||||
/*
|
||||
* We don't have the correct remote rates in this stage. the rates
|
||||
* will be reconfigured later, after authorization.
|
||||
*/
|
||||
cmd->sta.remote_rates = cpu_to_le32(wlvif->rate_set);
|
||||
|
||||
wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d "
|
||||
|
@ -482,6 +526,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|||
goto err_hlid;
|
||||
}
|
||||
|
||||
wlvif->sta.role_chan_type = wlvif->channel_type;
|
||||
goto out_free;
|
||||
|
||||
err_hlid:
|
||||
|
@ -500,7 +545,6 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|||
{
|
||||
struct wl12xx_cmd_role_stop *cmd;
|
||||
int ret;
|
||||
bool timeout = false;
|
||||
|
||||
if (WARN_ON(wlvif->sta.hlid == WL12XX_INVALID_LINK_ID))
|
||||
return -EINVAL;
|
||||
|
@ -523,17 +567,6 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|||
goto out_free;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sometimes the firmware doesn't send this event, so we just
|
||||
* time out without failing. Queue recovery for other
|
||||
* failures.
|
||||
*/
|
||||
ret = wl1271_cmd_wait_for_event_or_timeout(wl,
|
||||
ROLE_STOP_COMPLETE_EVENT_ID,
|
||||
&timeout);
|
||||
if (ret)
|
||||
wl12xx_queue_recovery_work(wl);
|
||||
|
||||
wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid);
|
||||
|
||||
out_free:
|
||||
|
@ -579,12 +612,15 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|||
cmd->ap.bss_index = WL1271_AP_BSS_INDEX;
|
||||
cmd->ap.global_hlid = wlvif->ap.global_hlid;
|
||||
cmd->ap.broadcast_hlid = wlvif->ap.bcast_hlid;
|
||||
cmd->ap.global_session_id = wl->session_ids[wlvif->ap.global_hlid];
|
||||
cmd->ap.bcast_session_id = wl->session_ids[wlvif->ap.bcast_hlid];
|
||||
cmd->ap.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
|
||||
cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int);
|
||||
cmd->ap.dtim_interval = bss_conf->dtim_period;
|
||||
cmd->ap.beacon_expiry = WL1271_AP_DEF_BEACON_EXP;
|
||||
/* FIXME: Change when adding DFS */
|
||||
cmd->ap.reset_tsf = 1; /* By default reset AP TSF */
|
||||
cmd->ap.wmm = wlvif->wmm_enabled;
|
||||
cmd->channel = wlvif->channel;
|
||||
cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
|
||||
|
||||
|
@ -599,8 +635,10 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|||
memcpy(cmd->ap.ssid, bss_conf->ssid, bss_conf->ssid_len);
|
||||
}
|
||||
|
||||
supported_rates = CONF_TX_AP_ENABLED_RATES | CONF_TX_MCS_RATES |
|
||||
supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
|
||||
wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif);
|
||||
if (wlvif->p2p)
|
||||
supported_rates &= ~CONF_TX_CCK_RATES;
|
||||
|
||||
wl1271_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x",
|
||||
supported_rates);
|
||||
|
@ -799,8 +837,11 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len)
|
|||
* @id: acx id
|
||||
* @buf: buffer containing acx, including all headers, must work with dma
|
||||
* @len: length of buf
|
||||
* @valid_rets: bitmap of valid cmd status codes (i.e. return values).
|
||||
* return the cmd status on success.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
struct acx_header *acx = buf;
|
||||
int ret;
|
||||
|
@ -812,12 +853,26 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
|
|||
/* payload length, does not include any headers */
|
||||
acx->len = cpu_to_le16(len - sizeof(*acx));
|
||||
|
||||
ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len, 0);
|
||||
ret = wlcore_cmd_send_failsafe(wl, CMD_CONFIGURE, acx, len, 0,
|
||||
valid_rets);
|
||||
if (ret < 0) {
|
||||
wl1271_warning("CONFIGURE command NOK");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* wrapper for wlcore_cmd_configure that accepts only success status.
|
||||
* return 0 on success
|
||||
*/
|
||||
int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
|
||||
{
|
||||
int ret = wlcore_cmd_configure_failsafe(wl, id, buf, len, 0);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wl1271_cmd_configure);
|
||||
|
@ -1034,8 +1089,8 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
struct sk_buff *skb;
|
||||
int ret;
|
||||
u32 rate;
|
||||
u16 template_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
|
||||
u16 template_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
|
||||
u16 template_id_2_4 = wl->scan_templ_id_2_4;
|
||||
u16 template_id_5 = wl->scan_templ_id_5;
|
||||
|
||||
skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
|
||||
ie_len);
|
||||
|
@ -1048,10 +1103,10 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
|
||||
wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len);
|
||||
|
||||
if (!sched_scan &&
|
||||
if (sched_scan &&
|
||||
(wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) {
|
||||
template_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4;
|
||||
template_id_5 = CMD_TEMPL_APP_PROBE_REQ_5;
|
||||
template_id_2_4 = wl->sched_scan_templ_id_2_4;
|
||||
template_id_5 = wl->sched_scan_templ_id_5;
|
||||
}
|
||||
|
||||
rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
|
||||
|
@ -1068,6 +1123,7 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
dev_kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wl12xx_cmd_build_probe_req);
|
||||
|
||||
struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
|
@ -1379,7 +1435,8 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid)
|
||||
int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
u8 hlid)
|
||||
{
|
||||
struct wl12xx_cmd_set_peer_state *cmd;
|
||||
int ret = 0;
|
||||
|
@ -1395,6 +1452,10 @@ int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid)
|
|||
cmd->hlid = hlid;
|
||||
cmd->state = WL1271_CMD_STA_STATE_CONNECTED;
|
||||
|
||||
/* wmm param is valid only for station role */
|
||||
if (wlvif->bss_type == BSS_TYPE_STA_BSS)
|
||||
cmd->wmm = wlvif->wmm_enabled;
|
||||
|
||||
ret = wl1271_cmd_send(wl, CMD_SET_PEER_STATE, cmd, sizeof(*cmd), 0);
|
||||
if (ret < 0) {
|
||||
wl1271_error("failed to send set peer state command");
|
||||
|
@ -1429,6 +1490,7 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
cmd->hlid = hlid;
|
||||
cmd->sp_len = sta->max_sp;
|
||||
cmd->wmm = sta->wme ? 1 : 0;
|
||||
cmd->session_id = wl->session_ids[hlid];
|
||||
|
||||
for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++)
|
||||
if (sta->wme && (sta->uapsd_queues & BIT(i)))
|
||||
|
@ -1490,9 +1552,10 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid)
|
|||
goto out_free;
|
||||
}
|
||||
|
||||
ret = wl1271_cmd_wait_for_event_or_timeout(wl,
|
||||
PEER_REMOVE_COMPLETE_EVENT_ID,
|
||||
&timeout);
|
||||
ret = wl->ops->wait_for_event(wl,
|
||||
WLCORE_EVENT_PEER_REMOVE_COMPLETE,
|
||||
&timeout);
|
||||
|
||||
/*
|
||||
* We are ok with a timeout here. The event is sometimes not sent
|
||||
* due to a firmware bug. In case of another error (like SDIO timeout)
|
||||
|
@ -1508,6 +1571,131 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int wlcore_get_reg_conf_ch_idx(enum ieee80211_band band, u16 ch)
|
||||
{
|
||||
int idx = -1;
|
||||
|
||||
switch (band) {
|
||||
case IEEE80211_BAND_5GHZ:
|
||||
if (ch >= 8 && ch <= 16)
|
||||
idx = ((ch-8)/4 + 18);
|
||||
else if (ch >= 34 && ch <= 64)
|
||||
idx = ((ch-34)/2 + 3 + 18);
|
||||
else if (ch >= 100 && ch <= 140)
|
||||
idx = ((ch-100)/4 + 15 + 18);
|
||||
else if (ch >= 149 && ch <= 165)
|
||||
idx = ((ch-149)/4 + 26 + 18);
|
||||
else
|
||||
idx = -1;
|
||||
break;
|
||||
case IEEE80211_BAND_2GHZ:
|
||||
if (ch >= 1 && ch <= 14)
|
||||
idx = ch - 1;
|
||||
else
|
||||
idx = -1;
|
||||
break;
|
||||
default:
|
||||
wl1271_error("get reg conf ch idx - unknown band: %d",
|
||||
(int)band);
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
|
||||
enum ieee80211_band band)
|
||||
{
|
||||
int ch_bit_idx = 0;
|
||||
|
||||
if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
|
||||
return;
|
||||
|
||||
ch_bit_idx = wlcore_get_reg_conf_ch_idx(band, channel);
|
||||
|
||||
if (ch_bit_idx > 0 && ch_bit_idx <= WL1271_MAX_CHANNELS)
|
||||
set_bit(ch_bit_idx, (long *)wl->reg_ch_conf_pending);
|
||||
}
|
||||
|
||||
int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl)
|
||||
{
|
||||
struct wl12xx_cmd_regdomain_dfs_config *cmd = NULL;
|
||||
int ret = 0, i, b, ch_bit_idx;
|
||||
struct ieee80211_channel *channel;
|
||||
u32 tmp_ch_bitmap[2];
|
||||
u16 ch;
|
||||
struct wiphy *wiphy = wl->hw->wiphy;
|
||||
struct ieee80211_supported_band *band;
|
||||
bool timeout = false;
|
||||
|
||||
if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
|
||||
return 0;
|
||||
|
||||
wl1271_debug(DEBUG_CMD, "cmd reg domain config");
|
||||
|
||||
memset(tmp_ch_bitmap, 0, sizeof(tmp_ch_bitmap));
|
||||
|
||||
for (b = IEEE80211_BAND_2GHZ; b <= IEEE80211_BAND_5GHZ; b++) {
|
||||
band = wiphy->bands[b];
|
||||
for (i = 0; i < band->n_channels; i++) {
|
||||
channel = &band->channels[i];
|
||||
ch = channel->hw_value;
|
||||
|
||||
if (channel->flags & (IEEE80211_CHAN_DISABLED |
|
||||
IEEE80211_CHAN_RADAR |
|
||||
IEEE80211_CHAN_PASSIVE_SCAN))
|
||||
continue;
|
||||
|
||||
ch_bit_idx = wlcore_get_reg_conf_ch_idx(b, ch);
|
||||
if (ch_bit_idx < 0)
|
||||
continue;
|
||||
|
||||
set_bit(ch_bit_idx, (long *)tmp_ch_bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
tmp_ch_bitmap[0] |= wl->reg_ch_conf_pending[0];
|
||||
tmp_ch_bitmap[1] |= wl->reg_ch_conf_pending[1];
|
||||
|
||||
if (!memcmp(tmp_ch_bitmap, wl->reg_ch_conf_last, sizeof(tmp_ch_bitmap)))
|
||||
goto out;
|
||||
|
||||
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
||||
if (!cmd) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cmd->ch_bit_map1 = cpu_to_le32(tmp_ch_bitmap[0]);
|
||||
cmd->ch_bit_map2 = cpu_to_le32(tmp_ch_bitmap[1]);
|
||||
|
||||
wl1271_debug(DEBUG_CMD,
|
||||
"cmd reg domain bitmap1: 0x%08x, bitmap2: 0x%08x",
|
||||
cmd->ch_bit_map1, cmd->ch_bit_map2);
|
||||
|
||||
ret = wl1271_cmd_send(wl, CMD_DFS_CHANNEL_CONFIG, cmd, sizeof(*cmd), 0);
|
||||
if (ret < 0) {
|
||||
wl1271_error("failed to send reg domain dfs config");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = wl->ops->wait_for_event(wl,
|
||||
WLCORE_EVENT_DFS_CONFIG_COMPLETE,
|
||||
&timeout);
|
||||
if (ret < 0 || timeout) {
|
||||
wl1271_error("reg domain conf %serror",
|
||||
timeout ? "completion " : "");
|
||||
ret = timeout ? -ETIMEDOUT : ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(wl->reg_ch_conf_last, tmp_ch_bitmap, sizeof(tmp_ch_bitmap));
|
||||
memset(wl->reg_ch_conf_pending, 0, sizeof(wl->reg_ch_conf_pending));
|
||||
|
||||
out:
|
||||
kfree(cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wl12xx_cmd_config_fwlog(struct wl1271 *wl)
|
||||
{
|
||||
struct wl12xx_cmd_config_fwlog *cmd;
|
||||
|
@ -1593,12 +1781,12 @@ int wl12xx_cmd_stop_fwlog(struct wl1271 *wl)
|
|||
}
|
||||
|
||||
static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
u8 role_id)
|
||||
u8 role_id, enum ieee80211_band band, u8 channel)
|
||||
{
|
||||
struct wl12xx_cmd_roc *cmd;
|
||||
int ret = 0;
|
||||
|
||||
wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", wlvif->channel, role_id);
|
||||
wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", channel, role_id);
|
||||
|
||||
if (WARN_ON(role_id == WL12XX_INVALID_ROLE_ID))
|
||||
return -EINVAL;
|
||||
|
@ -1610,8 +1798,8 @@ static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
}
|
||||
|
||||
cmd->role_id = role_id;
|
||||
cmd->channel = wlvif->channel;
|
||||
switch (wlvif->band) {
|
||||
cmd->channel = channel;
|
||||
switch (band) {
|
||||
case IEEE80211_BAND_2GHZ:
|
||||
cmd->band = WLCORE_BAND_2_4GHZ;
|
||||
break;
|
||||
|
@ -1666,30 +1854,18 @@ static int wl12xx_cmd_croc(struct wl1271 *wl, u8 role_id)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id)
|
||||
int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
|
||||
enum ieee80211_band band, u8 channel)
|
||||
{
|
||||
int ret = 0;
|
||||
bool is_first_roc;
|
||||
|
||||
if (WARN_ON(test_bit(role_id, wl->roc_map)))
|
||||
return 0;
|
||||
|
||||
is_first_roc = (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >=
|
||||
WL12XX_MAX_ROLES);
|
||||
|
||||
ret = wl12xx_cmd_roc(wl, wlvif, role_id);
|
||||
ret = wl12xx_cmd_roc(wl, wlvif, role_id, band, channel);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (is_first_roc) {
|
||||
ret = wl1271_cmd_wait_for_event(wl,
|
||||
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID);
|
||||
if (ret < 0) {
|
||||
wl1271_error("cmd roc event completion error");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
__set_bit(role_id, wl->roc_map);
|
||||
out:
|
||||
return ret;
|
||||
|
@ -1719,43 +1895,7 @@ int wl12xx_croc(struct wl1271 *wl, u8 role_id)
|
|||
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 */
|
||||
cmd->post_switch_tx_disable = 0; /* Enable TX on the target channel */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl)
|
||||
int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
||||
{
|
||||
struct wl12xx_cmd_stop_channel_switch *cmd;
|
||||
int ret;
|
||||
|
@ -1768,6 +1908,8 @@ int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl)
|
|||
goto out;
|
||||
}
|
||||
|
||||
cmd->role_id = wlvif->role_id;
|
||||
|
||||
ret = wl1271_cmd_send(wl, CMD_STOP_CHANNEL_SWICTH, cmd, sizeof(*cmd), 0);
|
||||
if (ret < 0) {
|
||||
wl1271_error("failed to stop channel switch command");
|
||||
|
@ -1782,7 +1924,8 @@ int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl)
|
|||
}
|
||||
|
||||
/* start dev role and roc on its channel */
|
||||
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 ret;
|
||||
|
||||
|
@ -1797,11 +1940,11 @@ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = wl12xx_cmd_role_start_dev(wl, wlvif);
|
||||
ret = wl12xx_cmd_role_start_dev(wl, wlvif, band, channel);
|
||||
if (ret < 0)
|
||||
goto out_disable;
|
||||
|
||||
ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id);
|
||||
ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id, band, channel);
|
||||
if (ret < 0)
|
||||
goto out_stop;
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@ struct acx_header;
|
|||
|
||||
int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t 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,
|
||||
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);
|
|||
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_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 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_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_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
u8 ps_mode, u16 auto_ps_timeout);
|
||||
|
@ -75,22 +80,30 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
u16 action, u8 id, u8 key_type,
|
||||
u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
|
||||
u16 tx_seq_16);
|
||||
int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid);
|
||||
int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id);
|
||||
int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
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_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
struct ieee80211_sta *sta, 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_start_fwlog(struct wl1271 *wl);
|
||||
int wl12xx_cmd_stop_fwlog(struct wl1271 *wl);
|
||||
int wl12xx_cmd_channel_switch(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
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,
|
||||
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 {
|
||||
CMD_INTERROGATE = 1, /* use this to read information elements */
|
||||
|
@ -149,8 +162,11 @@ enum wl1271_commands {
|
|||
CMD_WFD_START_DISCOVERY = 45,
|
||||
CMD_WFD_STOP_DISCOVERY = 46,
|
||||
CMD_WFD_ATTRIBUTE_CONFIG = 47,
|
||||
CMD_NOP = 48,
|
||||
CMD_LAST_COMMAND,
|
||||
CMD_GENERIC_CFG = 48,
|
||||
CMD_NOP = 49,
|
||||
|
||||
/* start of 18xx specific commands */
|
||||
CMD_DFS_CHANNEL_CONFIG = 60,
|
||||
|
||||
MAX_COMMAND_ID = 0xFFFF,
|
||||
};
|
||||
|
@ -167,8 +183,8 @@ enum cmd_templ {
|
|||
CMD_TEMPL_PS_POLL,
|
||||
CMD_TEMPL_KLV,
|
||||
CMD_TEMPL_DISCONNECT,
|
||||
CMD_TEMPL_APP_PROBE_REQ_2_4,
|
||||
CMD_TEMPL_APP_PROBE_REQ_5,
|
||||
CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY,
|
||||
CMD_TEMPL_APP_PROBE_REQ_5_LEGACY,
|
||||
CMD_TEMPL_BAR, /* for firmware internal use only */
|
||||
CMD_TEMPL_CTS, /*
|
||||
* For CTS-to-self (FastCTS) mechanism
|
||||
|
@ -179,6 +195,8 @@ enum cmd_templ {
|
|||
CMD_TEMPL_DEAUTH_AP,
|
||||
CMD_TEMPL_TEMPORARY,
|
||||
CMD_TEMPL_LINK_MEASUREMENT_REPORT,
|
||||
CMD_TEMPL_PROBE_REQ_2_4_PERIODIC,
|
||||
CMD_TEMPL_PROBE_REQ_5_PERIODIC,
|
||||
|
||||
CMD_TEMPL_MAX = 0xff
|
||||
};
|
||||
|
@ -220,7 +238,8 @@ enum {
|
|||
CMD_STATUS_FW_RESET = 22, /* Driver internal use.*/
|
||||
CMD_STATUS_TEMPLATE_OOM = 23,
|
||||
CMD_STATUS_NO_RX_BA_SESSION = 24,
|
||||
MAX_COMMAND_STATUS = 0xff
|
||||
|
||||
MAX_COMMAND_STATUS
|
||||
};
|
||||
|
||||
#define CMDMBOX_HEADER_LEN 4
|
||||
|
@ -345,7 +364,15 @@ struct wl12xx_cmd_role_start {
|
|||
|
||||
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;
|
||||
|
@ -515,7 +542,14 @@ struct wl12xx_cmd_set_peer_state {
|
|||
|
||||
u8 hlid;
|
||||
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;
|
||||
|
||||
struct wl12xx_cmd_roc {
|
||||
|
@ -558,7 +592,7 @@ struct wl12xx_cmd_add_peer {
|
|||
u8 bss_index;
|
||||
u8 sp_len;
|
||||
u8 wmm;
|
||||
u8 padding1;
|
||||
u8 session_id;
|
||||
} __packed;
|
||||
|
||||
struct wl12xx_cmd_remove_peer {
|
||||
|
@ -597,6 +631,13 @@ enum wl12xx_fwlogger_output {
|
|||
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 wl1271_cmd_header header;
|
||||
|
||||
|
@ -626,27 +667,13 @@ struct wl12xx_cmd_stop_fwlog {
|
|||
struct wl1271_cmd_header header;
|
||||
} __packed;
|
||||
|
||||
struct wl12xx_cmd_channel_switch {
|
||||
struct wl12xx_cmd_stop_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;
|
||||
|
||||
struct wl12xx_cmd_stop_channel_switch {
|
||||
struct wl1271_cmd_header header;
|
||||
} __packed;
|
||||
|
||||
/* Used to check radio status after calibration */
|
||||
#define MAX_TLV_LENGTH 500
|
||||
#define TEST_CMD_P2G_CAL 2 /* TX BiP */
|
||||
|
|
|
@ -57,20 +57,49 @@ enum {
|
|||
};
|
||||
|
||||
enum {
|
||||
CONF_HW_RATE_INDEX_1MBPS = 0,
|
||||
CONF_HW_RATE_INDEX_2MBPS = 1,
|
||||
CONF_HW_RATE_INDEX_5_5MBPS = 2,
|
||||
CONF_HW_RATE_INDEX_6MBPS = 3,
|
||||
CONF_HW_RATE_INDEX_9MBPS = 4,
|
||||
CONF_HW_RATE_INDEX_11MBPS = 5,
|
||||
CONF_HW_RATE_INDEX_12MBPS = 6,
|
||||
CONF_HW_RATE_INDEX_18MBPS = 7,
|
||||
CONF_HW_RATE_INDEX_22MBPS = 8,
|
||||
CONF_HW_RATE_INDEX_24MBPS = 9,
|
||||
CONF_HW_RATE_INDEX_36MBPS = 10,
|
||||
CONF_HW_RATE_INDEX_48MBPS = 11,
|
||||
CONF_HW_RATE_INDEX_54MBPS = 12,
|
||||
CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_54MBPS,
|
||||
CONF_HW_RATE_INDEX_1MBPS = 0,
|
||||
CONF_HW_RATE_INDEX_2MBPS = 1,
|
||||
CONF_HW_RATE_INDEX_5_5MBPS = 2,
|
||||
CONF_HW_RATE_INDEX_11MBPS = 3,
|
||||
CONF_HW_RATE_INDEX_6MBPS = 4,
|
||||
CONF_HW_RATE_INDEX_9MBPS = 5,
|
||||
CONF_HW_RATE_INDEX_12MBPS = 6,
|
||||
CONF_HW_RATE_INDEX_18MBPS = 7,
|
||||
CONF_HW_RATE_INDEX_24MBPS = 8,
|
||||
CONF_HW_RATE_INDEX_36MBPS = 9,
|
||||
CONF_HW_RATE_INDEX_48MBPS = 10,
|
||||
CONF_HW_RATE_INDEX_54MBPS = 11,
|
||||
CONF_HW_RATE_INDEX_MCS0 = 12,
|
||||
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
|
||||
|
@ -415,11 +444,11 @@ struct conf_rx_settings {
|
|||
#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
|
||||
* 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_6MBPS | CONF_HW_BIT_RATE_9MBPS | \
|
||||
CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS | \
|
||||
|
@ -677,6 +706,18 @@ struct conf_tx_settings {
|
|||
|
||||
/* Time in ms for Tx watchdog timer to expire */
|
||||
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;
|
||||
|
||||
enum {
|
||||
|
@ -1047,6 +1088,7 @@ struct conf_roam_trigger_settings {
|
|||
struct conf_scan_settings {
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
@ -1054,24 +1096,37 @@ struct conf_scan_settings {
|
|||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
u32 max_dwell_time_active;
|
||||
|
||||
/*
|
||||
* The minimum time to wait on each channel for passive scans
|
||||
/* The minimum time to wait on each channel for active 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
|
||||
*/
|
||||
u32 min_dwell_time_passive;
|
||||
u32 min_dwell_time_active_long;
|
||||
|
||||
/*
|
||||
* The maximum time to wait on each channel for passive scans
|
||||
/* The maximum time to wait on each channel for active scans
|
||||
* when it's possible to have longer scan dwell times.
|
||||
* See min_dwell_time_active_long
|
||||
*
|
||||
* 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
|
||||
|
@ -1276,12 +1331,20 @@ struct conf_hangover_settings {
|
|||
u8 window_size;
|
||||
} __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
|
||||
* version, the two LSB are the lower driver's private conf
|
||||
* version.
|
||||
*/
|
||||
#define WLCORE_CONF_VERSION (0x0002 << 16)
|
||||
#define WLCORE_CONF_VERSION (0x0005 << 16)
|
||||
#define WLCORE_CONF_MASK 0xffff0000
|
||||
#define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \
|
||||
sizeof(struct wlcore_conf))
|
||||
|
@ -1309,6 +1372,7 @@ struct wlcore_conf {
|
|||
struct conf_fwlog fwlog;
|
||||
struct conf_rate_policy_settings rate;
|
||||
struct conf_hangover_settings hangover;
|
||||
struct conf_recovery_settings recovery;
|
||||
} __packed;
|
||||
|
||||
struct wlcore_conf_file {
|
||||
|
|
|
@ -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_STR(chip.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_LONG
|
||||
|
@ -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 ||
|
||||
wlvif->bss_type == BSS_TYPE_IBSS) {
|
||||
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.ap_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,
|
|||
VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]);
|
||||
}
|
||||
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_NSTR(ssid, wlvif->ssid_len);
|
||||
VIF_STATE_PRINT_INT(band);
|
||||
|
@ -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(default_key);
|
||||
VIF_STATE_PRINT_INT(aid);
|
||||
VIF_STATE_PRINT_INT(session_counter);
|
||||
VIF_STATE_PRINT_INT(psm_entry_retry);
|
||||
VIF_STATE_PRINT_INT(power_level);
|
||||
VIF_STATE_PRINT_INT(rssi_thold);
|
||||
|
@ -993,7 +995,7 @@ static ssize_t sleep_auth_write(struct file *file,
|
|||
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_PSM_MAX);
|
||||
return -ERANGE;
|
||||
|
|
|
@ -29,34 +29,39 @@
|
|||
#include "scan.h"
|
||||
#include "wl12xx_80211.h"
|
||||
|
||||
static void wl1271_event_rssi_trigger(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
struct event_mailbox *mbox)
|
||||
void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr)
|
||||
{
|
||||
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
|
||||
struct wl12xx_vif *wlvif;
|
||||
struct ieee80211_vif *vif;
|
||||
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);
|
||||
|
||||
if (metric <= wlvif->rssi_thold)
|
||||
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
|
||||
else
|
||||
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
|
||||
/* TODO: check actual multi-role support */
|
||||
wl12xx_for_each_wlvif_sta(wl, wlvif) {
|
||||
if (metric <= wlvif->rssi_thold)
|
||||
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
|
||||
else
|
||||
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
|
||||
|
||||
if (event != wlvif->last_rssi_event)
|
||||
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)
|
||||
{
|
||||
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
|
||||
|
||||
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;
|
||||
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);
|
||||
} else {
|
||||
u8 hlid;
|
||||
|
@ -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,
|
||||
u8 enable)
|
||||
void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable)
|
||||
{
|
||||
struct wl12xx_vif *wlvif;
|
||||
|
||||
|
@ -87,201 +91,169 @@ static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl,
|
|||
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, "\tvector: 0x%x", mbox->events_vector);
|
||||
wl1271_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask);
|
||||
wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)",
|
||||
status);
|
||||
|
||||
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;
|
||||
u32 vector;
|
||||
bool disconnect_sta = false;
|
||||
unsigned long sta_bitmap = 0;
|
||||
int ret;
|
||||
|
||||
wl1271_event_mbox_dump(mbox);
|
||||
wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx allowed=0x%lx",
|
||||
__func__, roles_bitmap, allowed_bitmap);
|
||||
|
||||
vector = le32_to_cpu(mbox->events_vector);
|
||||
vector &= ~(le32_to_cpu(mbox->events_mask));
|
||||
wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector);
|
||||
wl12xx_for_each_wlvif(wl, wlvif) {
|
||||
if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
|
||||
!test_bit(wlvif->role_id , &roles_bitmap))
|
||||
continue;
|
||||
|
||||
if (vector & SCAN_COMPLETE_EVENT_ID) {
|
||||
wl1271_debug(DEBUG_EVENT, "status: 0x%x",
|
||||
mbox->scheduled_scan_status);
|
||||
|
||||
wl1271_scan_stm(wl, wl->scan_vif);
|
||||
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_REPORT_EVENT_ID) {
|
||||
wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT "
|
||||
"(status 0x%0x)", mbox->scheduled_scan_status);
|
||||
void wlcore_event_channel_switch(struct wl1271 *wl,
|
||||
unsigned long roles_bitmap,
|
||||
bool success)
|
||||
{
|
||||
struct wl12xx_vif *wlvif;
|
||||
struct ieee80211_vif *vif;
|
||||
|
||||
wl1271_scan_sched_scan_results(wl);
|
||||
wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d",
|
||||
__func__, roles_bitmap, success);
|
||||
|
||||
wl12xx_for_each_wlvif_sta(wl, wlvif) {
|
||||
if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
|
||||
!test_bit(wlvif->role_id , &roles_bitmap))
|
||||
continue;
|
||||
|
||||
if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
|
||||
&wlvif->flags))
|
||||
continue;
|
||||
|
||||
vif = wl12xx_wlvif_to_vif(wlvif);
|
||||
|
||||
ieee80211_chswitch_done(vif, success);
|
||||
cancel_delayed_work(&wlvif->channel_switch_work);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wlcore_event_channel_switch);
|
||||
|
||||
if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) {
|
||||
wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT "
|
||||
"(status 0x%0x)", mbox->scheduled_scan_status);
|
||||
if (wl->sched_scanning) {
|
||||
ieee80211_sched_scan_stopped(wl->hw);
|
||||
wl->sched_scanning = false;
|
||||
void wlcore_event_dummy_packet(struct wl1271 *wl)
|
||||
{
|
||||
wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
|
||||
wl1271_tx_dummy_packet(wl);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wlcore_event_dummy_packet);
|
||||
|
||||
static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap)
|
||||
{
|
||||
u32 num_packets = wl->conf.tx.max_tx_retries;
|
||||
struct wl12xx_vif *wlvif;
|
||||
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;
|
||||
|
||||
vif = wl12xx_wlvif_to_vif(wlvif);
|
||||
addr = wl->links[h].addr;
|
||||
|
||||
rcu_read_lock();
|
||||
sta = ieee80211_find_sta(vif, addr);
|
||||
if (sta) {
|
||||
wl1271_debug(DEBUG_EVENT, "remove sta %d", h);
|
||||
ieee80211_report_low_ack(sta, num_packets);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
|
||||
wl12xx_event_soft_gemini_sense(wl,
|
||||
mbox->soft_gemini_sense_info);
|
||||
void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap)
|
||||
{
|
||||
wl1271_debug(DEBUG_EVENT, "MAX_TX_FAILURE_EVENT_ID");
|
||||
wlcore_disconnect_sta(wl, sta_bitmap);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wlcore_event_max_tx_failure);
|
||||
|
||||
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);
|
||||
|
||||
void wlcore_event_roc_complete(struct wl1271 *wl)
|
||||
{
|
||||
wl1271_debug(DEBUG_EVENT, "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID");
|
||||
if (wl->roc_vif)
|
||||
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)
|
||||
{
|
||||
/*
|
||||
* We are HW_MONITOR device. On beacon loss - queue
|
||||
* connection loss work. Cancel it on REGAINED event.
|
||||
*/
|
||||
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.");
|
||||
struct wl12xx_vif *wlvif;
|
||||
struct ieee80211_vif *vif;
|
||||
int delay = wl->conf.conn.synch_fail_thold *
|
||||
wl->conf.conn.bss_lose_timeout;
|
||||
|
||||
wl1271_info("Beacon loss detected. roles:0x%lx", roles_bitmap);
|
||||
|
||||
wl12xx_for_each_wlvif_sta(wl, wlvif) {
|
||||
if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
|
||||
!test_bit(wlvif->role_id , &roles_bitmap))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* if the work is already queued, it should take place. We
|
||||
* don't want to delay the connection loss indication
|
||||
* any more.
|
||||
* 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, &wl->connection_loss_work,
|
||||
ieee80211_queue_delayed_work(wl->hw,
|
||||
&wlvif->connection_loss_work,
|
||||
msecs_to_jiffies(delay));
|
||||
|
||||
wl12xx_for_each_wlvif_sta(wl, wlvif) {
|
||||
vif = wl12xx_wlvif_to_vif(wlvif);
|
||||
|
||||
ieee80211_cqm_rssi_notify(
|
||||
vif,
|
||||
NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
vif = wl12xx_wlvif_to_vif(wlvif);
|
||||
ieee80211_cqm_rssi_notify(
|
||||
vif,
|
||||
NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
|
||||
if (vector & REGAINED_BSS_EVENT_ID) {
|
||||
/* TODO: check for multi-role */
|
||||
wl1271_info("Beacon regained.");
|
||||
cancel_delayed_work(&wl->connection_loss_work);
|
||||
|
||||
/* sanity check - we can't lose and gain the beacon together */
|
||||
WARN(vector & BSS_LOSE_EVENT_ID,
|
||||
"Concurrent beacon loss and gain from FW");
|
||||
}
|
||||
|
||||
if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
|
||||
/* TODO: check actual multi-role support */
|
||||
wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
|
||||
wl12xx_for_each_wlvif_sta(wl, wlvif) {
|
||||
wl1271_event_rssi_trigger(wl, wlvif, mbox);
|
||||
}
|
||||
}
|
||||
|
||||
if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) {
|
||||
u8 role_id = mbox->role_id;
|
||||
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) {
|
||||
if (role_id != 0xff && role_id != wlvif->role_id)
|
||||
continue;
|
||||
|
||||
wlvif->ba_allowed = !!mbox->rx_ba_allowed;
|
||||
if (!wlvif->ba_allowed)
|
||||
wl1271_stop_ba_event(wl, wlvif);
|
||||
}
|
||||
}
|
||||
|
||||
if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) {
|
||||
wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. "
|
||||
"status = 0x%x",
|
||||
mbox->channel_switch_status);
|
||||
/*
|
||||
* That event uses for two cases:
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
if ((vector & DUMMY_PACKET_EVENT_ID)) {
|
||||
wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
|
||||
ret = wl1271_tx_dummy_packet(wl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* "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) {
|
||||
wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID");
|
||||
sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded);
|
||||
disconnect_sta = true;
|
||||
}
|
||||
|
||||
if (vector & INACTIVE_STA_EVENT_ID) {
|
||||
wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
|
||||
sta_bitmap |= le16_to_cpu(mbox->sta_aging_status);
|
||||
disconnect_sta = true;
|
||||
}
|
||||
|
||||
if (disconnect_sta) {
|
||||
u32 num_packets = wl->conf.tx.max_tx_retries;
|
||||
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;
|
||||
|
||||
vif = wl12xx_wlvif_to_vif(wlvif);
|
||||
addr = wl->links[h].addr;
|
||||
|
||||
rcu_read_lock();
|
||||
sta = ieee80211_find_sta(vif, addr);
|
||||
if (sta) {
|
||||
wl1271_debug(DEBUG_EVENT, "remove sta %d", h);
|
||||
ieee80211_report_low_ack(sta, num_packets);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss);
|
||||
|
||||
int wl1271_event_unmask(struct wl1271 *wl)
|
||||
{
|
||||
|
@ -305,12 +277,12 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
|
|||
|
||||
/* first we read the mbox descriptor */
|
||||
ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox,
|
||||
sizeof(*wl->mbox), false);
|
||||
wl->mbox_size, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* process the descriptor */
|
||||
ret = wl1271_event_process(wl);
|
||||
ret = wl->ops->process_mailbox_events(wl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -46,33 +46,17 @@ enum {
|
|||
RSSI_SNR_TRIGGER_5_EVENT_ID = BIT(5),
|
||||
RSSI_SNR_TRIGGER_6_EVENT_ID = BIT(6),
|
||||
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,
|
||||
};
|
||||
|
||||
/* 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 {
|
||||
EVENT_ENTER_POWER_SAVE_FAIL = 0,
|
||||
EVENT_ENTER_POWER_SAVE_SUCCESS,
|
||||
|
@ -80,61 +64,24 @@ enum {
|
|||
|
||||
#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;
|
||||
|
||||
int wl1271_event_unmask(struct wl1271 *wl);
|
||||
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
|
||||
|
|
|
@ -201,4 +201,45 @@ wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len)
|
|||
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
|
||||
|
|
|
@ -41,14 +41,14 @@ int wl1271_init_templates_config(struct wl1271 *wl)
|
|||
|
||||
/* send empty templates for fw memory reservation */
|
||||
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,
|
||||
0, WL1271_RATE_AUTOMATIC);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
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,
|
||||
WL1271_RATE_AUTOMATIC);
|
||||
if (ret < 0)
|
||||
|
@ -56,14 +56,16 @@ int wl1271_init_templates_config(struct wl1271 *wl)
|
|||
|
||||
if (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL) {
|
||||
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,
|
||||
0, WL1271_RATE_AUTOMATIC);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
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,
|
||||
0, WL1271_RATE_AUTOMATIC);
|
||||
if (ret < 0)
|
||||
|
@ -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))
|
||||
supported_rates = CONF_TX_OFDM_RATES;
|
||||
else
|
||||
supported_rates = CONF_TX_AP_ENABLED_RATES;
|
||||
supported_rates = CONF_TX_ENABLED_RATES;
|
||||
|
||||
/* unconditionally enable HT rates */
|
||||
supported_rates |= CONF_TX_MCS_RATES;
|
||||
|
@ -575,9 +577,6 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
|
|||
/* Configure for power according to debugfs */
|
||||
if (sta_auth != WL1271_PSM_ILLEGAL)
|
||||
ret = wl1271_acx_sleep_auth(wl, sta_auth);
|
||||
/* Configure for power always on */
|
||||
else if (wl->quirks & WLCORE_QUIRK_NO_ELP)
|
||||
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
|
||||
/* Configure for ELP power saving */
|
||||
else
|
||||
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
|
||||
|
@ -679,6 +678,10 @@ int wl1271_hw_init(struct wl1271 *wl)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = wlcore_cmd_regdomain_config_locked(wl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Bluetooth WLAN coexistence */
|
||||
ret = wl1271_init_pta(wl);
|
||||
if (ret < 0)
|
||||
|
|
|
@ -105,13 +105,13 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr,
|
|||
{
|
||||
int ret;
|
||||
|
||||
ret = wlcore_raw_read(wl, addr, &wl->buffer_32,
|
||||
sizeof(wl->buffer_32), false);
|
||||
ret = wlcore_raw_read(wl, addr, wl->buffer_32,
|
||||
sizeof(*wl->buffer_32), false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (val)
|
||||
*val = le32_to_cpu(wl->buffer_32);
|
||||
*val = le32_to_cpu(*wl->buffer_32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -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,
|
||||
u32 val)
|
||||
{
|
||||
wl->buffer_32 = cpu_to_le32(val);
|
||||
return wlcore_raw_write(wl, addr, &wl->buffer_32,
|
||||
sizeof(wl->buffer_32), false);
|
||||
*wl->buffer_32 = cpu_to_le32(val);
|
||||
return wlcore_raw_write(wl, addr, wl->buffer_32,
|
||||
sizeof(*wl->buffer_32), false);
|
||||
}
|
||||
|
||||
static inline int __must_check wlcore_read(struct wl1271 *wl, int addr,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -151,9 +151,6 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl)
|
|||
wl12xx_queue_recovery_work(wl);
|
||||
ret = -ETIMEDOUT;
|
||||
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)
|
|||
struct ieee80211_tx_info *info;
|
||||
unsigned long flags;
|
||||
int filtered[NUM_TX_QUEUES];
|
||||
struct wl1271_link *lnk = &wl->links[hlid];
|
||||
|
||||
/* filter all frames currently in the low level queues for this hlid */
|
||||
for (i = 0; i < NUM_TX_QUEUES; i++) {
|
||||
filtered[i] = 0;
|
||||
while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
|
||||
while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
|
||||
filtered[i]++;
|
||||
|
||||
if (WARN_ON(wl12xx_is_dummy_packet(wl, skb)))
|
||||
|
@ -260,8 +258,11 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
|
|||
}
|
||||
|
||||
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];
|
||||
if (lnk->wlvif)
|
||||
lnk->wlvif->tx_queue_count[i] -= filtered[i];
|
||||
}
|
||||
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
||||
|
||||
wl1271_handle_tx_low_watermark(wl);
|
||||
|
|
|
@ -92,11 +92,16 @@ static void wl1271_rx_status(struct wl1271 *wl,
|
|||
status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED |
|
||||
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;
|
||||
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,
|
||||
|
@ -108,7 +113,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
|
|||
u8 *buf;
|
||||
u8 beacon = 0;
|
||||
u8 is_data = 0;
|
||||
u8 reserved = 0;
|
||||
u8 reserved = 0, offset_to_data = 0;
|
||||
u16 seq_num;
|
||||
u32 pkt_data_len;
|
||||
|
||||
|
@ -128,6 +133,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
|
|||
|
||||
if (rx_align == WLCORE_RX_BUF_UNALIGNED)
|
||||
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 */
|
||||
desc = (struct wl1271_rx_descriptor *) data;
|
||||
|
@ -139,19 +146,15 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
|
|||
return 0;
|
||||
}
|
||||
|
||||
switch (desc->status & WL1271_RX_DESC_STATUS_MASK) {
|
||||
/* discard corrupted packets */
|
||||
case WL1271_RX_DESC_DRIVER_RX_Q_FAIL:
|
||||
case WL1271_RX_DESC_DECRYPT_FAIL:
|
||||
wl1271_warning("corrupted packet in RX with status: 0x%x",
|
||||
desc->status & WL1271_RX_DESC_STATUS_MASK);
|
||||
return -EINVAL;
|
||||
case WL1271_RX_DESC_SUCCESS:
|
||||
case WL1271_RX_DESC_MIC_FAIL:
|
||||
break;
|
||||
default:
|
||||
wl1271_error("invalid RX descriptor status: 0x%x",
|
||||
desc->status & WL1271_RX_DESC_STATUS_MASK);
|
||||
if (desc->status & WL1271_RX_DESC_DECRYPT_FAIL) {
|
||||
hdr = (void *)(data + sizeof(*desc) + offset_to_data);
|
||||
wl1271_warning("corrupted packet in RX: status: 0x%x len: %d",
|
||||
desc->status & WL1271_RX_DESC_STATUS_MASK,
|
||||
pkt_data_len);
|
||||
wl1271_dump((DEBUG_RX|DEBUG_CMD), "PKT: ", data + sizeof(*desc),
|
||||
min(pkt_data_len,
|
||||
ieee80211_hdrlen(hdr->frame_control)));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
@ -84,12 +84,11 @@
|
|||
* Bits 3-5 - process_id tag (AP mode FW)
|
||||
* 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_DECRYPT_FAIL 0x01
|
||||
#define WL1271_RX_DESC_MIC_FAIL 0x02
|
||||
#define WL1271_RX_DESC_DRIVER_RX_Q_FAIL 0x03
|
||||
|
||||
#define RX_MEM_BLOCK_MASK 0xFF
|
||||
#define RX_BUF_SIZE_MASK 0xFFF00
|
||||
|
|
|
@ -35,7 +35,6 @@ void wl1271_scan_complete_work(struct work_struct *work)
|
|||
{
|
||||
struct delayed_work *dwork;
|
||||
struct wl1271 *wl;
|
||||
struct ieee80211_vif *vif;
|
||||
struct wl12xx_vif *wlvif;
|
||||
int ret;
|
||||
|
||||
|
@ -52,8 +51,7 @@ void wl1271_scan_complete_work(struct work_struct *work)
|
|||
if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
|
||||
goto out;
|
||||
|
||||
vif = wl->scan_vif;
|
||||
wlvif = wl12xx_vif_to_data(vif);
|
||||
wlvif = wl->scan_wlvif;
|
||||
|
||||
/*
|
||||
* Rearm the tx watchdog just before idling scan. This
|
||||
|
@ -64,7 +62,7 @@ void wl1271_scan_complete_work(struct work_struct *work)
|
|||
wl->scan.state = WL1271_SCAN_STATE_IDLE;
|
||||
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
|
||||
wl->scan.req = NULL;
|
||||
wl->scan_vif = NULL;
|
||||
wl->scan_wlvif = NULL;
|
||||
|
||||
ret = wl1271_ps_elp_wakeup(wl);
|
||||
if (ret < 0)
|
||||
|
@ -82,6 +80,8 @@ void wl1271_scan_complete_work(struct work_struct *work)
|
|||
wl12xx_queue_recovery_work(wl);
|
||||
}
|
||||
|
||||
wlcore_cmd_regdomain_config_locked(wl);
|
||||
|
||||
ieee80211_scan_completed(wl->hw, false);
|
||||
|
||||
out:
|
||||
|
@ -89,371 +89,99 @@ void wl1271_scan_complete_work(struct work_struct *work)
|
|||
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
static void wlcore_started_vifs_iter(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct conf_scan_settings *c = &wl->conf.scan;
|
||||
int i, j;
|
||||
u32 flags;
|
||||
int *count = (int *)data;
|
||||
|
||||
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->min_dwell_time_passive);
|
||||
channels[j].max_duration =
|
||||
cpu_to_le32(c->max_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;
|
||||
if (!vif->bss_conf.idle)
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
#define WL1271_NOTHING_TO_SCAN 1
|
||||
|
||||
static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif,
|
||||
enum ieee80211_band band,
|
||||
bool passive, u32 basic_rate)
|
||||
static int wlcore_count_started_vifs(struct wl1271 *wl)
|
||||
{
|
||||
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
||||
struct wl1271_cmd_scan *cmd;
|
||||
struct wl1271_cmd_trigger_scan_to *trigger;
|
||||
int ret;
|
||||
u16 scan_options = 0;
|
||||
int count = 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;
|
||||
}
|
||||
|
||||
void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
||||
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, vif, band, false, rate);
|
||||
if (ret == WL1271_NOTHING_TO_SCAN) {
|
||||
wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
|
||||
wl1271_scan_stm(wl, vif);
|
||||
}
|
||||
|
||||
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, vif, 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, vif);
|
||||
}
|
||||
|
||||
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, vif, band, false, rate);
|
||||
if (ret == WL1271_NOTHING_TO_SCAN) {
|
||||
wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
|
||||
wl1271_scan_stm(wl, vif);
|
||||
}
|
||||
|
||||
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, vif, band, true, rate);
|
||||
if (ret == WL1271_NOTHING_TO_SCAN) {
|
||||
wl->scan.state = WL1271_SCAN_STATE_DONE;
|
||||
wl1271_scan_stm(wl, vif);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
struct cfg80211_scan_request *req)
|
||||
{
|
||||
/*
|
||||
* cfg80211 should guarantee that we don't get more channels
|
||||
* than what we have registered.
|
||||
*/
|
||||
BUG_ON(req->n_channels > WL1271_MAX_CHANNELS);
|
||||
|
||||
if (wl->scan.state != WL1271_SCAN_STATE_IDLE)
|
||||
return -EBUSY;
|
||||
|
||||
wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE;
|
||||
|
||||
if (ssid_len && ssid) {
|
||||
wl->scan.ssid_len = ssid_len;
|
||||
memcpy(wl->scan.ssid, ssid, ssid_len);
|
||||
} else {
|
||||
wl->scan.ssid_len = 0;
|
||||
}
|
||||
|
||||
wl->scan_vif = vif;
|
||||
wl->scan.req = req;
|
||||
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
|
||||
|
||||
/* we assume failure so that timeout scenarios are handled correctly */
|
||||
wl->scan.failed = true;
|
||||
ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
|
||||
msecs_to_jiffies(WL1271_SCAN_TIMEOUT));
|
||||
|
||||
wl1271_scan_stm(wl, vif);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wl1271_scan_stop(struct wl1271 *wl)
|
||||
{
|
||||
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;
|
||||
ieee80211_iterate_active_interfaces_atomic(wl->hw,
|
||||
IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
wlcore_started_vifs_iter, &count);
|
||||
return count;
|
||||
}
|
||||
|
||||
static int
|
||||
wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct conn_scan_ch_params *channels,
|
||||
u32 band, bool radar, bool passive,
|
||||
int start, int max_channels,
|
||||
u8 *n_pactive_ch)
|
||||
wlcore_scan_get_channels(struct wl1271 *wl,
|
||||
struct ieee80211_channel *req_channels[],
|
||||
u32 n_channels,
|
||||
u32 n_ssids,
|
||||
struct conn_scan_ch_params *channels,
|
||||
u32 band, bool radar, bool passive,
|
||||
int start, int max_channels,
|
||||
u8 *n_pactive_ch,
|
||||
int scan_type)
|
||||
{
|
||||
struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
|
||||
int i, j;
|
||||
u32 flags;
|
||||
bool force_passive = !req->n_ssids;
|
||||
u32 min_dwell_time_active, max_dwell_time_active, delta_per_probe;
|
||||
bool force_passive = !n_ssids;
|
||||
u32 min_dwell_time_active, max_dwell_time_active;
|
||||
u32 dwell_time_passive, dwell_time_dfs;
|
||||
|
||||
if (band == IEEE80211_BAND_5GHZ)
|
||||
delta_per_probe = c->dwell_time_delta_per_probe_5;
|
||||
else
|
||||
delta_per_probe = c->dwell_time_delta_per_probe;
|
||||
/* configure dwell times according to scan type */
|
||||
if (scan_type == SCAN_TYPE_SEARCH) {
|
||||
struct conf_scan_settings *c = &wl->conf.scan;
|
||||
bool active_vif_exists = !!wlcore_count_started_vifs(wl);
|
||||
|
||||
min_dwell_time_active = c->base_dwell_time +
|
||||
req->n_ssids * c->num_probe_reqs * delta_per_probe;
|
||||
min_dwell_time_active = active_vif_exists ?
|
||||
c->min_dwell_time_active :
|
||||
c->min_dwell_time_active_long;
|
||||
max_dwell_time_active = active_vif_exists ?
|
||||
c->max_dwell_time_active :
|
||||
c->max_dwell_time_active_long;
|
||||
dwell_time_passive = c->dwell_time_passive;
|
||||
dwell_time_dfs = c->dwell_time_dfs;
|
||||
} else {
|
||||
struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
|
||||
u32 delta_per_probe;
|
||||
|
||||
max_dwell_time_active = min_dwell_time_active + c->max_dwell_time_delta;
|
||||
if (band == IEEE80211_BAND_5GHZ)
|
||||
delta_per_probe = c->dwell_time_delta_per_probe_5;
|
||||
else
|
||||
delta_per_probe = c->dwell_time_delta_per_probe;
|
||||
|
||||
min_dwell_time_active = c->base_dwell_time +
|
||||
n_ssids * c->num_probe_reqs * delta_per_probe;
|
||||
|
||||
max_dwell_time_active = min_dwell_time_active +
|
||||
c->max_dwell_time_delta;
|
||||
dwell_time_passive = c->dwell_time_passive;
|
||||
dwell_time_dfs = c->dwell_time_dfs;
|
||||
}
|
||||
min_dwell_time_active = DIV_ROUND_UP(min_dwell_time_active, 1000);
|
||||
max_dwell_time_active = DIV_ROUND_UP(max_dwell_time_active, 1000);
|
||||
dwell_time_passive = DIV_ROUND_UP(c->dwell_time_passive, 1000);
|
||||
dwell_time_dfs = DIV_ROUND_UP(c->dwell_time_dfs, 1000);
|
||||
dwell_time_passive = DIV_ROUND_UP(dwell_time_passive, 1000);
|
||||
dwell_time_dfs = DIV_ROUND_UP(dwell_time_dfs, 1000);
|
||||
|
||||
for (i = 0, j = start;
|
||||
i < req->n_channels && j < max_channels;
|
||||
i < n_channels && j < max_channels;
|
||||
i++) {
|
||||
flags = req->channels[i]->flags;
|
||||
flags = req_channels[i]->flags;
|
||||
|
||||
if (force_passive)
|
||||
flags |= IEEE80211_CHAN_PASSIVE_SCAN;
|
||||
|
||||
if ((req->channels[i]->band == band) &&
|
||||
if ((req_channels[i]->band == band) &&
|
||||
!(flags & IEEE80211_CHAN_DISABLED) &&
|
||||
(!!(flags & IEEE80211_CHAN_RADAR) == radar) &&
|
||||
/* if radar is set, we ignore the passive flag */
|
||||
(radar ||
|
||||
!!(flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive)) {
|
||||
wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
|
||||
req->channels[i]->band,
|
||||
req->channels[i]->center_freq);
|
||||
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);
|
||||
req_channels[i]->hw_value,
|
||||
req_channels[i]->flags);
|
||||
wl1271_debug(DEBUG_SCAN, "max_power %d",
|
||||
req->channels[i]->max_power);
|
||||
req_channels[i]->max_power);
|
||||
wl1271_debug(DEBUG_SCAN, "min_dwell_time %d max dwell time %d",
|
||||
min_dwell_time_active,
|
||||
max_dwell_time_active);
|
||||
|
@ -473,10 +201,11 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
|
|||
channels[j].max_duration =
|
||||
cpu_to_le16(max_dwell_time_active);
|
||||
|
||||
channels[j].tx_power_att = req->channels[i]->max_power;
|
||||
channels[j].channel = req->channels[i]->hw_value;
|
||||
channels[j].tx_power_att = req_channels[i]->max_power;
|
||||
channels[j].channel = req_channels[i]->hw_value;
|
||||
|
||||
if ((band == IEEE80211_BAND_2GHZ) &&
|
||||
if (n_pactive_ch &&
|
||||
(band == IEEE80211_BAND_2GHZ) &&
|
||||
(channels[j].channel >= 12) &&
|
||||
(channels[j].channel <= 14) &&
|
||||
(flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
|
||||
|
@ -500,51 +229,80 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
|
|||
return j - start;
|
||||
}
|
||||
|
||||
static bool
|
||||
wl1271_scan_sched_scan_channels(struct wl1271 *wl,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct wl1271_cmd_sched_scan_config *cfg)
|
||||
bool
|
||||
wlcore_set_scan_chan_params(struct wl1271 *wl,
|
||||
struct wlcore_scan_channels *cfg,
|
||||
struct ieee80211_channel *channels[],
|
||||
u32 n_channels,
|
||||
u32 n_ssids,
|
||||
int scan_type)
|
||||
{
|
||||
u8 n_pactive_ch = 0;
|
||||
|
||||
cfg->passive[0] =
|
||||
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
|
||||
IEEE80211_BAND_2GHZ,
|
||||
false, true, 0,
|
||||
MAX_CHANNELS_2GHZ,
|
||||
&n_pactive_ch);
|
||||
wlcore_scan_get_channels(wl,
|
||||
channels,
|
||||
n_channels,
|
||||
n_ssids,
|
||||
cfg->channels_2,
|
||||
IEEE80211_BAND_2GHZ,
|
||||
false, true, 0,
|
||||
MAX_CHANNELS_2GHZ,
|
||||
&n_pactive_ch,
|
||||
scan_type);
|
||||
cfg->active[0] =
|
||||
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
|
||||
IEEE80211_BAND_2GHZ,
|
||||
false, false,
|
||||
cfg->passive[0],
|
||||
MAX_CHANNELS_2GHZ,
|
||||
&n_pactive_ch);
|
||||
wlcore_scan_get_channels(wl,
|
||||
channels,
|
||||
n_channels,
|
||||
n_ssids,
|
||||
cfg->channels_2,
|
||||
IEEE80211_BAND_2GHZ,
|
||||
false, false,
|
||||
cfg->passive[0],
|
||||
MAX_CHANNELS_2GHZ,
|
||||
&n_pactive_ch,
|
||||
scan_type);
|
||||
cfg->passive[1] =
|
||||
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
|
||||
IEEE80211_BAND_5GHZ,
|
||||
false, true, 0,
|
||||
MAX_CHANNELS_5GHZ,
|
||||
&n_pactive_ch);
|
||||
wlcore_scan_get_channels(wl,
|
||||
channels,
|
||||
n_channels,
|
||||
n_ssids,
|
||||
cfg->channels_5,
|
||||
IEEE80211_BAND_5GHZ,
|
||||
false, true, 0,
|
||||
wl->max_channels_5,
|
||||
&n_pactive_ch,
|
||||
scan_type);
|
||||
cfg->dfs =
|
||||
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
|
||||
IEEE80211_BAND_5GHZ,
|
||||
true, true,
|
||||
cfg->passive[1],
|
||||
MAX_CHANNELS_5GHZ,
|
||||
&n_pactive_ch);
|
||||
wlcore_scan_get_channels(wl,
|
||||
channels,
|
||||
n_channels,
|
||||
n_ssids,
|
||||
cfg->channels_5,
|
||||
IEEE80211_BAND_5GHZ,
|
||||
true, true,
|
||||
cfg->passive[1],
|
||||
wl->max_channels_5,
|
||||
&n_pactive_ch,
|
||||
scan_type);
|
||||
cfg->active[1] =
|
||||
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
|
||||
IEEE80211_BAND_5GHZ,
|
||||
false, false,
|
||||
cfg->passive[1] + cfg->dfs,
|
||||
MAX_CHANNELS_5GHZ,
|
||||
&n_pactive_ch);
|
||||
wlcore_scan_get_channels(wl,
|
||||
channels,
|
||||
n_channels,
|
||||
n_ssids,
|
||||
cfg->channels_5,
|
||||
IEEE80211_BAND_5GHZ,
|
||||
false, false,
|
||||
cfg->passive[1] + cfg->dfs,
|
||||
wl->max_channels_5,
|
||||
&n_pactive_ch,
|
||||
scan_type);
|
||||
|
||||
/* 802.11j channels are not supported yet */
|
||||
cfg->passive[2] = 0;
|
||||
cfg->active[2] = 0;
|
||||
|
||||
cfg->n_pactive_ch = n_pactive_ch;
|
||||
cfg->passive_active = n_pactive_ch;
|
||||
|
||||
wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d",
|
||||
cfg->active[0], cfg->passive[0]);
|
||||
|
@ -556,10 +314,48 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl,
|
|||
cfg->passive[1] || cfg->active[1] || cfg->dfs ||
|
||||
cfg->passive[2] || cfg->active[2];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wlcore_set_scan_chan_params);
|
||||
|
||||
int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
struct cfg80211_scan_request *req)
|
||||
{
|
||||
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
||||
|
||||
/*
|
||||
* cfg80211 should guarantee that we don't get more channels
|
||||
* than what we have registered.
|
||||
*/
|
||||
BUG_ON(req->n_channels > WL1271_MAX_CHANNELS);
|
||||
|
||||
if (wl->scan.state != WL1271_SCAN_STATE_IDLE)
|
||||
return -EBUSY;
|
||||
|
||||
wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE;
|
||||
|
||||
if (ssid_len && ssid) {
|
||||
wl->scan.ssid_len = ssid_len;
|
||||
memcpy(wl->scan.ssid, ssid, ssid_len);
|
||||
} else {
|
||||
wl->scan.ssid_len = 0;
|
||||
}
|
||||
|
||||
wl->scan_wlvif = wlvif;
|
||||
wl->scan.req = req;
|
||||
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
|
||||
|
||||
/* we assume failure so that timeout scenarios are handled correctly */
|
||||
wl->scan.failed = true;
|
||||
ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
|
||||
msecs_to_jiffies(WL1271_SCAN_TIMEOUT));
|
||||
|
||||
wl->ops->scan_start(wl, wlvif, req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* Returns the scan type to be used or a negative value on error */
|
||||
static int
|
||||
wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
|
||||
int
|
||||
wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
struct cfg80211_sched_scan_request *req)
|
||||
{
|
||||
|
@ -662,160 +458,12 @@ wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
|
|||
return ret;
|
||||
return type;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_ssid_list);
|
||||
|
||||
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 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 = wl12xx_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);
|
||||
|
||||
if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) {
|
||||
wl1271_error("scan channel list is empty");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
void wl1271_scan_sched_scan_results(struct wl1271 *wl)
|
||||
void wlcore_scan_sched_scan_results(struct wl1271 *wl)
|
||||
{
|
||||
wl1271_debug(DEBUG_SCAN, "got periodic scan results");
|
||||
|
||||
ieee80211_sched_scan_results(wl->hw);
|
||||
}
|
||||
|
||||
void wl1271_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);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_results);
|
||||
|
|
|
@ -26,22 +26,20 @@
|
|||
|
||||
#include "wlcore.h"
|
||||
|
||||
int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
|
||||
int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
struct cfg80211_scan_request *req);
|
||||
int wl1271_scan_stop(struct wl1271 *wl);
|
||||
int wl1271_scan_build_probe_req(struct wl1271 *wl,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len, u8 band);
|
||||
void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif);
|
||||
void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
||||
void wl1271_scan_complete_work(struct work_struct *work);
|
||||
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);
|
||||
int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
||||
void wl1271_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
||||
void wl1271_scan_sched_scan_results(struct wl1271 *wl);
|
||||
void wlcore_scan_sched_scan_results(struct wl1271 *wl);
|
||||
|
||||
#define WL1271_SCAN_MAX_CHANNELS 24
|
||||
#define WL1271_SCAN_DEFAULT_TAG 1
|
||||
|
@ -66,56 +64,6 @@ enum {
|
|||
WL1271_SCAN_STATE_DONE
|
||||
};
|
||||
|
||||
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_trigger_scan_to {
|
||||
struct wl1271_cmd_header header;
|
||||
|
||||
|
@ -123,9 +71,17 @@ struct wl1271_cmd_trigger_scan_to {
|
|||
} __packed;
|
||||
|
||||
#define MAX_CHANNELS_2GHZ 14
|
||||
#define MAX_CHANNELS_5GHZ 23
|
||||
#define MAX_CHANNELS_4GHZ 4
|
||||
|
||||
/*
|
||||
* This max value here is used only for the struct definition of
|
||||
* wlcore_scan_channels. This struct is used by both 12xx
|
||||
* and 18xx (which have different max 5ghz channels value).
|
||||
* In order to make sure this is large enough, just use the
|
||||
* max possible 5ghz channels.
|
||||
*/
|
||||
#define MAX_CHANNELS_5GHZ 42
|
||||
|
||||
#define SCAN_MAX_CYCLE_INTERVALS 16
|
||||
#define SCAN_MAX_BANDS 3
|
||||
|
||||
|
@ -160,43 +116,6 @@ struct conn_scan_ch_params {
|
|||
u8 padding[3];
|
||||
} __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[MAX_CHANNELS_5GHZ];
|
||||
struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
|
||||
} __packed;
|
||||
|
||||
|
||||
#define SCHED_SCAN_MAX_SSIDS 16
|
||||
|
||||
enum {
|
||||
|
@ -220,21 +139,34 @@ struct wl1271_cmd_sched_scan_ssid_list {
|
|||
u8 padding[2];
|
||||
} __packed;
|
||||
|
||||
struct wl1271_cmd_sched_scan_start {
|
||||
struct wl1271_cmd_header header;
|
||||
struct wlcore_scan_channels {
|
||||
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 */
|
||||
|
||||
u8 tag;
|
||||
u8 role_id;
|
||||
u8 padding[2];
|
||||
} __packed;
|
||||
struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
|
||||
struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
|
||||
struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
|
||||
};
|
||||
|
||||
struct wl1271_cmd_sched_scan_stop {
|
||||
struct wl1271_cmd_header header;
|
||||
enum {
|
||||
SCAN_TYPE_SEARCH = 0,
|
||||
SCAN_TYPE_PERIODIC = 1,
|
||||
SCAN_TYPE_TRACKING = 2,
|
||||
};
|
||||
|
||||
u8 tag;
|
||||
u8 role_id;
|
||||
u8 padding[2];
|
||||
} __packed;
|
||||
bool
|
||||
wlcore_set_scan_chan_params(struct wl1271 *wl,
|
||||
struct wlcore_scan_channels *cfg,
|
||||
struct ieee80211_channel *channels[],
|
||||
u32 n_channels,
|
||||
u32 n_ssids,
|
||||
int scan_type);
|
||||
|
||||
int
|
||||
wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
struct cfg80211_sched_scan_request *req);
|
||||
|
||||
#endif /* __WL1271_SCAN_H__ */
|
||||
|
|
|
@ -326,8 +326,7 @@ static void wl1271_remove(struct sdio_func *func)
|
|||
/* Undo decrement done above in wl1271_probe */
|
||||
pm_runtime_get_noresume(&func->dev);
|
||||
|
||||
platform_device_del(glue->core);
|
||||
platform_device_put(glue->core);
|
||||
platform_device_unregister(glue->core);
|
||||
kfree(glue);
|
||||
}
|
||||
|
||||
|
|
|
@ -270,7 +270,7 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
|
|||
void *buf, size_t len, bool fixed)
|
||||
{
|
||||
struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
|
||||
struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
|
||||
struct spi_transfer t[2 * (WSPI_MAX_NUM_OF_CHUNKS + 1)];
|
||||
struct spi_message m;
|
||||
u32 commands[WSPI_MAX_NUM_OF_CHUNKS];
|
||||
u32 *cmd;
|
||||
|
@ -407,8 +407,7 @@ static int wl1271_remove(struct spi_device *spi)
|
|||
{
|
||||
struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
|
||||
|
||||
platform_device_del(glue->core);
|
||||
platform_device_put(glue->core);
|
||||
platform_device_unregister(glue->core);
|
||||
kfree(glue);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -104,7 +104,7 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
|
|||
struct wl12xx_vif *wlvif,
|
||||
u8 hlid)
|
||||
{
|
||||
bool fw_ps, single_sta;
|
||||
bool fw_ps, single_link;
|
||||
u8 tx_pkts;
|
||||
|
||||
if (WARN_ON(!test_bit(hlid, wlvif->links_map)))
|
||||
|
@ -112,15 +112,15 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
|
|||
|
||||
fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
|
||||
tx_pkts = wl->links[hlid].allocated_pkts;
|
||||
single_sta = (wl->active_sta_count == 1);
|
||||
single_link = (wl->active_link_count == 1);
|
||||
|
||||
/*
|
||||
* if in FW PS and there is enough data in FW we can put the link
|
||||
* into high-level PS and clean out its TX queues.
|
||||
* Make an exception if this is the only connected station. In this
|
||||
* case FW-memory congestion is not a problem.
|
||||
* Make an exception if this is the only connected link. In this
|
||||
* case FW-memory congestion is less of a problem.
|
||||
*/
|
||||
if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
|
||||
if (!single_link && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
|
||||
wl12xx_ps_link_start(wl, wlvif, hlid, true);
|
||||
}
|
||||
|
||||
|
@ -155,21 +155,18 @@ static u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
struct sk_buff *skb, struct ieee80211_sta *sta)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||
|
||||
if (!wlvif || wl12xx_is_dummy_packet(wl, skb))
|
||||
return wl->system_hlid;
|
||||
struct ieee80211_tx_info *control;
|
||||
|
||||
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
|
||||
return wl12xx_tx_get_hlid_ap(wl, wlvif, skb, sta);
|
||||
|
||||
if ((test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
|
||||
test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) &&
|
||||
!ieee80211_is_auth(hdr->frame_control) &&
|
||||
!ieee80211_is_assoc_req(hdr->frame_control))
|
||||
return wlvif->sta.hlid;
|
||||
else
|
||||
control = IEEE80211_SKB_CB(skb);
|
||||
if (control->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
|
||||
wl1271_debug(DEBUG_TX, "tx offchannel");
|
||||
return wlvif->dev_hlid;
|
||||
}
|
||||
|
||||
return wlvif->sta.hlid;
|
||||
}
|
||||
|
||||
unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
|
||||
|
@ -224,9 +221,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
|
||||
wl->tx_allocated_pkts[ac]++;
|
||||
|
||||
if (!wl12xx_is_dummy_packet(wl, skb) && wlvif &&
|
||||
wlvif->bss_type == BSS_TYPE_AP_BSS &&
|
||||
test_bit(hlid, wlvif->ap.sta_hlid_map))
|
||||
if (test_bit(hlid, wl->links_map))
|
||||
wl->links[hlid].allocated_pkts++;
|
||||
|
||||
ret = 0;
|
||||
|
@ -293,9 +288,14 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
|
||||
tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ;
|
||||
} else if (wlvif) {
|
||||
u8 session_id = wl->session_ids[hlid];
|
||||
|
||||
if ((wl->quirks & WLCORE_QUIRK_AP_ZERO_SESSION_ID) &&
|
||||
(wlvif->bss_type == BSS_TYPE_AP_BSS))
|
||||
session_id = 0;
|
||||
|
||||
/* configure the tx attributes */
|
||||
tx_attr = wlvif->session_counter <<
|
||||
TX_HW_ATTR_OFST_SESSION_COUNTER;
|
||||
tx_attr = session_id << TX_HW_ATTR_OFST_SESSION_COUNTER;
|
||||
}
|
||||
|
||||
desc->hlid = hlid;
|
||||
|
@ -452,20 +452,22 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
|
|||
void wl1271_handle_tx_low_watermark(struct wl1271 *wl)
|
||||
{
|
||||
int i;
|
||||
struct wl12xx_vif *wlvif;
|
||||
|
||||
for (i = 0; i < NUM_TX_QUEUES; i++) {
|
||||
if (wlcore_is_queue_stopped_by_reason(wl, i,
|
||||
WLCORE_QUEUE_STOP_REASON_WATERMARK) &&
|
||||
wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) {
|
||||
/* firmware buffer has space, restart queues */
|
||||
wlcore_wake_queue(wl, i,
|
||||
WLCORE_QUEUE_STOP_REASON_WATERMARK);
|
||||
wl12xx_for_each_wlvif(wl, wlvif) {
|
||||
for (i = 0; i < NUM_TX_QUEUES; i++) {
|
||||
if (wlcore_is_queue_stopped_by_reason(wl, wlvif, i,
|
||||
WLCORE_QUEUE_STOP_REASON_WATERMARK) &&
|
||||
wlvif->tx_queue_count[i] <=
|
||||
WL1271_TX_QUEUE_LOW_WATERMARK)
|
||||
/* firmware buffer has space, restart queues */
|
||||
wlcore_wake_queue(wl, wlvif, i,
|
||||
WLCORE_QUEUE_STOP_REASON_WATERMARK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl,
|
||||
struct sk_buff_head *queues)
|
||||
static int wlcore_select_ac(struct wl1271 *wl)
|
||||
{
|
||||
int i, q = -1, ac;
|
||||
u32 min_pkts = 0xffffffff;
|
||||
|
@ -479,45 +481,60 @@ static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl,
|
|||
*/
|
||||
for (i = 0; i < NUM_TX_QUEUES; i++) {
|
||||
ac = wl1271_tx_get_queue(i);
|
||||
if (!skb_queue_empty(&queues[ac]) &&
|
||||
(wl->tx_allocated_pkts[ac] < min_pkts)) {
|
||||
if (wl->tx_queue_count[ac] &&
|
||||
wl->tx_allocated_pkts[ac] < min_pkts) {
|
||||
q = ac;
|
||||
min_pkts = wl->tx_allocated_pkts[q];
|
||||
}
|
||||
}
|
||||
|
||||
if (q == -1)
|
||||
return NULL;
|
||||
|
||||
return &queues[q];
|
||||
return q;
|
||||
}
|
||||
|
||||
static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
|
||||
struct wl1271_link *lnk)
|
||||
static struct sk_buff *wlcore_lnk_dequeue(struct wl1271 *wl,
|
||||
struct wl1271_link *lnk, u8 q)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
struct sk_buff_head *queue;
|
||||
|
||||
queue = wl1271_select_queue(wl, lnk->tx_queue);
|
||||
if (!queue)
|
||||
return NULL;
|
||||
|
||||
skb = skb_dequeue(queue);
|
||||
skb = skb_dequeue(&lnk->tx_queue[q]);
|
||||
if (skb) {
|
||||
int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
|
||||
spin_lock_irqsave(&wl->wl_lock, flags);
|
||||
WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
|
||||
wl->tx_queue_count[q]--;
|
||||
if (lnk->wlvif) {
|
||||
WARN_ON_ONCE(lnk->wlvif->tx_queue_count[q] <= 0);
|
||||
lnk->wlvif->tx_queue_count[q]--;
|
||||
}
|
||||
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
u8 *hlid)
|
||||
static struct sk_buff *wlcore_lnk_dequeue_high_prio(struct wl1271 *wl,
|
||||
u8 hlid, u8 ac,
|
||||
u8 *low_prio_hlid)
|
||||
{
|
||||
struct wl1271_link *lnk = &wl->links[hlid];
|
||||
|
||||
if (!wlcore_hw_lnk_high_prio(wl, hlid, lnk)) {
|
||||
if (*low_prio_hlid == WL12XX_INVALID_LINK_ID &&
|
||||
!skb_queue_empty(&lnk->tx_queue[ac]) &&
|
||||
wlcore_hw_lnk_low_prio(wl, hlid, lnk))
|
||||
/* we found the first non-empty low priority queue */
|
||||
*low_prio_hlid = hlid;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return wlcore_lnk_dequeue(wl, lnk, ac);
|
||||
}
|
||||
|
||||
static struct sk_buff *wlcore_vif_dequeue_high_prio(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
u8 ac, u8 *hlid,
|
||||
u8 *low_prio_hlid)
|
||||
{
|
||||
struct sk_buff *skb = NULL;
|
||||
int i, h, start_hlid;
|
||||
|
@ -533,7 +550,8 @@ static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
|
|||
if (!test_bit(h, wlvif->links_map))
|
||||
continue;
|
||||
|
||||
skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[h]);
|
||||
skb = wlcore_lnk_dequeue_high_prio(wl, h, ac,
|
||||
low_prio_hlid);
|
||||
if (!skb)
|
||||
continue;
|
||||
|
||||
|
@ -553,42 +571,74 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid)
|
|||
unsigned long flags;
|
||||
struct wl12xx_vif *wlvif = wl->last_wlvif;
|
||||
struct sk_buff *skb = NULL;
|
||||
int ac;
|
||||
u8 low_prio_hlid = WL12XX_INVALID_LINK_ID;
|
||||
|
||||
ac = wlcore_select_ac(wl);
|
||||
if (ac < 0)
|
||||
goto out;
|
||||
|
||||
/* continue from last wlvif (round robin) */
|
||||
if (wlvif) {
|
||||
wl12xx_for_each_wlvif_continue(wl, wlvif) {
|
||||
skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
|
||||
if (skb) {
|
||||
wl->last_wlvif = wlvif;
|
||||
break;
|
||||
}
|
||||
if (!wlvif->tx_queue_count[ac])
|
||||
continue;
|
||||
|
||||
skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid,
|
||||
&low_prio_hlid);
|
||||
if (!skb)
|
||||
continue;
|
||||
|
||||
wl->last_wlvif = wlvif;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* dequeue from the system HLID before the restarting wlvif list */
|
||||
if (!skb) {
|
||||
skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
|
||||
*hlid = wl->system_hlid;
|
||||
skb = wlcore_lnk_dequeue_high_prio(wl, wl->system_hlid,
|
||||
ac, &low_prio_hlid);
|
||||
if (skb) {
|
||||
*hlid = wl->system_hlid;
|
||||
wl->last_wlvif = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* do a new pass over the wlvif list */
|
||||
/* Do a new pass over the wlvif list. But no need to continue
|
||||
* after last_wlvif. The previous pass should have found it. */
|
||||
if (!skb) {
|
||||
wl12xx_for_each_wlvif(wl, wlvif) {
|
||||
skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
|
||||
if (!wlvif->tx_queue_count[ac])
|
||||
goto next;
|
||||
|
||||
skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid,
|
||||
&low_prio_hlid);
|
||||
if (skb) {
|
||||
wl->last_wlvif = wlvif;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* No need to continue after last_wlvif. The previous
|
||||
* pass should have found it.
|
||||
*/
|
||||
next:
|
||||
if (wlvif == wl->last_wlvif)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* no high priority skbs found - but maybe a low priority one? */
|
||||
if (!skb && low_prio_hlid != WL12XX_INVALID_LINK_ID) {
|
||||
struct wl1271_link *lnk = &wl->links[low_prio_hlid];
|
||||
skb = wlcore_lnk_dequeue(wl, lnk, ac);
|
||||
|
||||
WARN_ON(!skb); /* we checked this before */
|
||||
*hlid = low_prio_hlid;
|
||||
|
||||
/* ensure proper round robin in the vif/link levels */
|
||||
wl->last_wlvif = lnk->wlvif;
|
||||
if (lnk->wlvif)
|
||||
lnk->wlvif->last_tx_hlid = low_prio_hlid;
|
||||
|
||||
}
|
||||
|
||||
if (!skb &&
|
||||
test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) {
|
||||
int q;
|
||||
|
@ -602,6 +652,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid)
|
|||
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
||||
}
|
||||
|
||||
out:
|
||||
return skb;
|
||||
}
|
||||
|
||||
|
@ -623,6 +674,8 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
|
||||
spin_lock_irqsave(&wl->wl_lock, flags);
|
||||
wl->tx_queue_count[q]++;
|
||||
if (wlvif)
|
||||
wlvif->tx_queue_count[q]++;
|
||||
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
||||
}
|
||||
|
||||
|
@ -699,7 +752,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
|
|||
bool has_data = false;
|
||||
|
||||
wlvif = NULL;
|
||||
if (!wl12xx_is_dummy_packet(wl, skb) && info->control.vif)
|
||||
if (!wl12xx_is_dummy_packet(wl, skb))
|
||||
wlvif = wl12xx_vif_to_data(info->control.vif);
|
||||
else
|
||||
hlid = wl->system_hlid;
|
||||
|
@ -972,10 +1025,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
|
|||
unsigned long flags;
|
||||
struct ieee80211_tx_info *info;
|
||||
int total[NUM_TX_QUEUES];
|
||||
struct wl1271_link *lnk = &wl->links[hlid];
|
||||
|
||||
for (i = 0; i < NUM_TX_QUEUES; i++) {
|
||||
total[i] = 0;
|
||||
while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
|
||||
while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
|
||||
wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb);
|
||||
|
||||
if (!wl12xx_is_dummy_packet(wl, skb)) {
|
||||
|
@ -990,8 +1044,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
|
|||
}
|
||||
|
||||
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] -= total[i];
|
||||
if (lnk->wlvif)
|
||||
lnk->wlvif->tx_queue_count[i] -= total[i];
|
||||
}
|
||||
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
||||
|
||||
wl1271_handle_tx_low_watermark(wl);
|
||||
|
@ -1004,16 +1061,18 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|||
|
||||
/* TX failure */
|
||||
for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) {
|
||||
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
|
||||
if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
|
||||
/* this calls wl12xx_free_link */
|
||||
wl1271_free_sta(wl, wlvif, i);
|
||||
else
|
||||
wlvif->sta.ba_rx_bitmap = 0;
|
||||
|
||||
wl->links[i].allocated_pkts = 0;
|
||||
wl->links[i].prev_freed_pkts = 0;
|
||||
} else {
|
||||
u8 hlid = i;
|
||||
wl12xx_free_link(wl, wlvif, &hlid);
|
||||
}
|
||||
}
|
||||
wlvif->last_tx_hlid = 0;
|
||||
|
||||
for (i = 0; i < NUM_TX_QUEUES; i++)
|
||||
wlvif->tx_queue_count[i] = 0;
|
||||
}
|
||||
/* caller must hold wl->mutex and TX must be stopped */
|
||||
void wl12xx_tx_reset(struct wl1271 *wl)
|
||||
|
@ -1023,7 +1082,7 @@ void wl12xx_tx_reset(struct wl1271 *wl)
|
|||
struct ieee80211_tx_info *info;
|
||||
|
||||
/* only reset the queues if something bad happened */
|
||||
if (WARN_ON_ONCE(wl1271_tx_total_queue_count(wl) != 0)) {
|
||||
if (wl1271_tx_total_queue_count(wl) != 0) {
|
||||
for (i = 0; i < WL12XX_MAX_LINKS; i++)
|
||||
wl1271_tx_reset_link_queues(wl, i);
|
||||
|
||||
|
@ -1135,45 +1194,48 @@ u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)
|
|||
|
||||
return BIT(__ffs(rate_set));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wl1271_tx_min_rate_get);
|
||||
|
||||
void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
|
||||
enum wlcore_queue_stop_reason reason)
|
||||
void wlcore_stop_queue_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
u8 queue, enum wlcore_queue_stop_reason reason)
|
||||
{
|
||||
bool stopped = !!wl->queue_stop_reasons[queue];
|
||||
int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
|
||||
bool stopped = !!wl->queue_stop_reasons[hwq];
|
||||
|
||||
/* queue should not be stopped for this reason */
|
||||
WARN_ON(test_and_set_bit(reason, &wl->queue_stop_reasons[queue]));
|
||||
WARN_ON_ONCE(test_and_set_bit(reason, &wl->queue_stop_reasons[hwq]));
|
||||
|
||||
if (stopped)
|
||||
return;
|
||||
|
||||
ieee80211_stop_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
|
||||
ieee80211_stop_queue(wl->hw, hwq);
|
||||
}
|
||||
|
||||
void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
|
||||
void wlcore_stop_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
|
||||
enum wlcore_queue_stop_reason reason)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&wl->wl_lock, flags);
|
||||
wlcore_stop_queue_locked(wl, queue, reason);
|
||||
wlcore_stop_queue_locked(wl, wlvif, queue, reason);
|
||||
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
||||
}
|
||||
|
||||
void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
|
||||
void wlcore_wake_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
|
||||
enum wlcore_queue_stop_reason reason)
|
||||
{
|
||||
unsigned long flags;
|
||||
int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
|
||||
|
||||
spin_lock_irqsave(&wl->wl_lock, flags);
|
||||
|
||||
/* queue should not be clear for this reason */
|
||||
WARN_ON(!test_and_clear_bit(reason, &wl->queue_stop_reasons[queue]));
|
||||
WARN_ON_ONCE(!test_and_clear_bit(reason, &wl->queue_stop_reasons[hwq]));
|
||||
|
||||
if (wl->queue_stop_reasons[queue])
|
||||
if (wl->queue_stop_reasons[hwq])
|
||||
goto out;
|
||||
|
||||
ieee80211_wake_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
|
||||
ieee80211_wake_queue(wl->hw, hwq);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
||||
|
@ -1183,48 +1245,74 @@ void wlcore_stop_queues(struct wl1271 *wl,
|
|||
enum wlcore_queue_stop_reason reason)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
for (i = 0; i < NUM_TX_QUEUES; i++)
|
||||
wlcore_stop_queue(wl, i, reason);
|
||||
spin_lock_irqsave(&wl->wl_lock, flags);
|
||||
|
||||
/* mark all possible queues as stopped */
|
||||
for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++)
|
||||
WARN_ON_ONCE(test_and_set_bit(reason,
|
||||
&wl->queue_stop_reasons[i]));
|
||||
|
||||
/* use the global version to make sure all vifs in mac80211 we don't
|
||||
* know are stopped.
|
||||
*/
|
||||
ieee80211_stop_queues(wl->hw);
|
||||
|
||||
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wlcore_stop_queues);
|
||||
|
||||
void wlcore_wake_queues(struct wl1271 *wl,
|
||||
enum wlcore_queue_stop_reason reason)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_TX_QUEUES; i++)
|
||||
wlcore_wake_queue(wl, i, reason);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wlcore_wake_queues);
|
||||
|
||||
void wlcore_reset_stopped_queues(struct wl1271 *wl)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&wl->wl_lock, flags);
|
||||
|
||||
for (i = 0; i < NUM_TX_QUEUES; i++) {
|
||||
if (!wl->queue_stop_reasons[i])
|
||||
continue;
|
||||
/* mark all possible queues as awake */
|
||||
for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++)
|
||||
WARN_ON_ONCE(!test_and_clear_bit(reason,
|
||||
&wl->queue_stop_reasons[i]));
|
||||
|
||||
wl->queue_stop_reasons[i] = 0;
|
||||
ieee80211_wake_queue(wl->hw,
|
||||
wl1271_tx_get_mac80211_queue(i));
|
||||
}
|
||||
/* use the global version to make sure all vifs in mac80211 we don't
|
||||
* know are woken up.
|
||||
*/
|
||||
ieee80211_wake_queues(wl->hw);
|
||||
|
||||
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
||||
}
|
||||
|
||||
bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
|
||||
enum wlcore_queue_stop_reason reason)
|
||||
bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif, u8 queue,
|
||||
enum wlcore_queue_stop_reason reason)
|
||||
{
|
||||
return test_bit(reason, &wl->queue_stop_reasons[queue]);
|
||||
unsigned long flags;
|
||||
bool stopped;
|
||||
|
||||
spin_lock_irqsave(&wl->wl_lock, flags);
|
||||
stopped = wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, queue,
|
||||
reason);
|
||||
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
||||
|
||||
return stopped;
|
||||
}
|
||||
|
||||
bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue)
|
||||
bool wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif, u8 queue,
|
||||
enum wlcore_queue_stop_reason reason)
|
||||
{
|
||||
return !!wl->queue_stop_reasons[queue];
|
||||
int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
|
||||
|
||||
WARN_ON_ONCE(!spin_is_locked(&wl->wl_lock));
|
||||
return test_bit(reason, &wl->queue_stop_reasons[hwq]);
|
||||
}
|
||||
|
||||
bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
u8 queue)
|
||||
{
|
||||
int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
|
||||
|
||||
WARN_ON_ONCE(!spin_is_locked(&wl->wl_lock));
|
||||
return !!wl->queue_stop_reasons[hwq];
|
||||
}
|
||||
|
|
|
@ -207,19 +207,22 @@ static inline int wl1271_tx_get_queue(int queue)
|
|||
}
|
||||
}
|
||||
|
||||
static inline int wl1271_tx_get_mac80211_queue(int queue)
|
||||
static inline
|
||||
int wlcore_tx_get_mac80211_queue(struct wl12xx_vif *wlvif, int queue)
|
||||
{
|
||||
int mac_queue = wlvif->hw_queue_base;
|
||||
|
||||
switch (queue) {
|
||||
case CONF_TX_AC_VO:
|
||||
return 0;
|
||||
return mac_queue + 0;
|
||||
case CONF_TX_AC_VI:
|
||||
return 1;
|
||||
return mac_queue + 1;
|
||||
case CONF_TX_AC_BE:
|
||||
return 2;
|
||||
return mac_queue + 2;
|
||||
case CONF_TX_AC_BK:
|
||||
return 3;
|
||||
return mac_queue + 3;
|
||||
default:
|
||||
return 2;
|
||||
return mac_queue + 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,20 +255,26 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids);
|
|||
unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
|
||||
unsigned int packet_length);
|
||||
void wl1271_free_tx_id(struct wl1271 *wl, int id);
|
||||
void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
|
||||
enum wlcore_queue_stop_reason reason);
|
||||
void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
|
||||
void wlcore_stop_queue_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
u8 queue, enum wlcore_queue_stop_reason reason);
|
||||
void wlcore_stop_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
|
||||
enum wlcore_queue_stop_reason reason);
|
||||
void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
|
||||
void wlcore_wake_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
|
||||
enum wlcore_queue_stop_reason reason);
|
||||
void wlcore_stop_queues(struct wl1271 *wl,
|
||||
enum wlcore_queue_stop_reason reason);
|
||||
void wlcore_wake_queues(struct wl1271 *wl,
|
||||
enum wlcore_queue_stop_reason reason);
|
||||
void wlcore_reset_stopped_queues(struct wl1271 *wl);
|
||||
bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
|
||||
bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif, u8 queue,
|
||||
enum wlcore_queue_stop_reason reason);
|
||||
bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue);
|
||||
bool
|
||||
wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
u8 queue,
|
||||
enum wlcore_queue_stop_reason reason);
|
||||
bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
u8 queue);
|
||||
|
||||
/* from main.c */
|
||||
void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
|
||||
|
|
|
@ -37,6 +37,9 @@
|
|||
*/
|
||||
#define WLCORE_NUM_MAC_ADDRESSES 3
|
||||
|
||||
/* wl12xx/wl18xx maximum transmission power (in dBm) */
|
||||
#define WLCORE_MAX_TXPWR 25
|
||||
|
||||
/* forward declaration */
|
||||
struct wl1271_tx_hw_descr;
|
||||
enum wl_rx_buf_align;
|
||||
|
@ -51,6 +54,9 @@ struct wlcore_ops {
|
|||
int (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr,
|
||||
void *buf, size_t len);
|
||||
int (*ack_event)(struct wl1271 *wl);
|
||||
int (*wait_for_event)(struct wl1271 *wl, enum wlcore_wait_event event,
|
||||
bool *timeout);
|
||||
int (*process_mailbox_events)(struct wl1271 *wl);
|
||||
u32 (*calc_tx_blocks)(struct wl1271 *wl, u32 len, u32 spare_blks);
|
||||
void (*set_tx_desc_blocks)(struct wl1271 *wl,
|
||||
struct wl1271_tx_hw_descr *desc,
|
||||
|
@ -82,12 +88,32 @@ struct wlcore_ops {
|
|||
int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir);
|
||||
int (*handle_static_data)(struct wl1271 *wl,
|
||||
struct wl1271_static_data *static_data);
|
||||
int (*scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
struct cfg80211_scan_request *req);
|
||||
int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
||||
int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies);
|
||||
void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
||||
int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
|
||||
int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key_conf);
|
||||
int (*channel_switch)(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
struct ieee80211_channel_switch *ch_switch);
|
||||
u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len);
|
||||
void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
struct ieee80211_sta *sta, u32 changed);
|
||||
int (*set_peer_cap)(struct wl1271 *wl,
|
||||
struct ieee80211_sta_ht_cap *ht_cap,
|
||||
bool allow_ht_operation,
|
||||
u32 rate_set, u8 hlid);
|
||||
bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid,
|
||||
struct wl1271_link *lnk);
|
||||
bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
|
||||
struct wl1271_link *lnk);
|
||||
};
|
||||
|
||||
enum wlcore_partitions {
|
||||
|
@ -202,6 +228,8 @@ struct wl1271 {
|
|||
unsigned long klv_templates_map[
|
||||
BITS_TO_LONGS(WLCORE_MAX_KLV_TEMPLATES)];
|
||||
|
||||
u8 session_ids[WL12XX_MAX_LINKS];
|
||||
|
||||
struct list_head wlvif_list;
|
||||
|
||||
u8 sta_count;
|
||||
|
@ -227,7 +255,8 @@ struct wl1271 {
|
|||
|
||||
/* Frames scheduled for transmission, not handled yet */
|
||||
int tx_queue_count[NUM_TX_QUEUES];
|
||||
unsigned long queue_stop_reasons[NUM_TX_QUEUES];
|
||||
unsigned long queue_stop_reasons[
|
||||
NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES];
|
||||
|
||||
/* Frames received, not handled yet by mac80211 */
|
||||
struct sk_buff_head deferred_rx_queue;
|
||||
|
@ -269,24 +298,30 @@ struct wl1271 {
|
|||
struct work_struct recovery_work;
|
||||
bool watchdog_recovery;
|
||||
|
||||
/* Reg domain last configuration */
|
||||
u32 reg_ch_conf_last[2];
|
||||
/* Reg domain pending configuration */
|
||||
u32 reg_ch_conf_pending[2];
|
||||
|
||||
/* Pointer that holds DMA-friendly block for the mailbox */
|
||||
struct event_mailbox *mbox;
|
||||
void *mbox;
|
||||
|
||||
/* The mbox event mask */
|
||||
u32 event_mask;
|
||||
|
||||
/* Mailbox pointers */
|
||||
u32 mbox_size;
|
||||
u32 mbox_ptr[2];
|
||||
|
||||
/* Are we currently scanning */
|
||||
struct ieee80211_vif *scan_vif;
|
||||
struct wl12xx_vif *scan_wlvif;
|
||||
struct wl1271_scan scan;
|
||||
struct delayed_work scan_complete_work;
|
||||
|
||||
/* Connection loss work */
|
||||
struct delayed_work connection_loss_work;
|
||||
struct ieee80211_vif *roc_vif;
|
||||
struct delayed_work roc_complete_work;
|
||||
|
||||
bool sched_scanning;
|
||||
struct wl12xx_vif *sched_vif;
|
||||
|
||||
/* The current band */
|
||||
enum ieee80211_band band;
|
||||
|
@ -299,7 +334,7 @@ struct wl1271 {
|
|||
|
||||
struct wl1271_stats stats;
|
||||
|
||||
__le32 buffer_32;
|
||||
__le32 *buffer_32;
|
||||
u32 buffer_cmd;
|
||||
u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
|
||||
|
||||
|
@ -314,6 +349,8 @@ struct wl1271 {
|
|||
|
||||
bool enable_11a;
|
||||
|
||||
int recovery_count;
|
||||
|
||||
/* Most recently reported noise in dBm */
|
||||
s8 noise;
|
||||
|
||||
|
@ -333,6 +370,12 @@ struct wl1271 {
|
|||
*/
|
||||
struct wl1271_link links[WL12XX_MAX_LINKS];
|
||||
|
||||
/* number of currently active links */
|
||||
int active_link_count;
|
||||
|
||||
/* Fast/slow links bitmap according to FW */
|
||||
u32 fw_fast_lnk_map;
|
||||
|
||||
/* AP-mode - a bitmap of links currently in PS mode according to FW */
|
||||
u32 ap_fw_ps_map;
|
||||
|
||||
|
@ -367,6 +410,12 @@ struct wl1271 {
|
|||
const char *sr_fw_name;
|
||||
const char *mr_fw_name;
|
||||
|
||||
u8 scan_templ_id_2_4;
|
||||
u8 scan_templ_id_5;
|
||||
u8 sched_scan_templ_id_2_4;
|
||||
u8 sched_scan_templ_id_5;
|
||||
u8 max_channels_5;
|
||||
|
||||
/* per-chip-family private structure */
|
||||
void *priv;
|
||||
|
||||
|
@ -408,20 +457,28 @@ struct wl1271 {
|
|||
/* the number of allocated MAC addresses in this chip */
|
||||
int num_mac_addr;
|
||||
|
||||
/* the minimum FW version required for the driver to work */
|
||||
unsigned int min_fw_ver[NUM_FW_VER];
|
||||
/* minimum FW version required for the driver to work in single-role */
|
||||
unsigned int min_sr_fw_ver[NUM_FW_VER];
|
||||
|
||||
/* minimum FW version required for the driver to work in multi-role */
|
||||
unsigned int min_mr_fw_ver[NUM_FW_VER];
|
||||
|
||||
struct completion nvs_loading_complete;
|
||||
|
||||
/* number of concurrent channels the HW supports */
|
||||
u32 num_channels;
|
||||
};
|
||||
|
||||
int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
|
||||
int wlcore_remove(struct platform_device *pdev);
|
||||
struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size);
|
||||
struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
|
||||
u32 mbox_size);
|
||||
int wlcore_free_hw(struct wl1271 *wl);
|
||||
int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key_conf);
|
||||
void wlcore_regdomain_config(struct wl1271 *wl);
|
||||
|
||||
static inline void
|
||||
wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
|
||||
|
@ -430,16 +487,27 @@ wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
|
|||
memcpy(&wl->ht_cap[band], ht_cap, sizeof(*ht_cap));
|
||||
}
|
||||
|
||||
/* Tell wlcore not to care about this element when checking the version */
|
||||
#define WLCORE_FW_VER_IGNORE -1
|
||||
|
||||
static inline void
|
||||
wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
|
||||
unsigned int iftype, unsigned int major,
|
||||
unsigned int subtype, unsigned int minor)
|
||||
unsigned int iftype_sr, unsigned int major_sr,
|
||||
unsigned int subtype_sr, unsigned int minor_sr,
|
||||
unsigned int iftype_mr, unsigned int major_mr,
|
||||
unsigned int subtype_mr, unsigned int minor_mr)
|
||||
{
|
||||
wl->min_fw_ver[FW_VER_CHIP] = chip;
|
||||
wl->min_fw_ver[FW_VER_IF_TYPE] = iftype;
|
||||
wl->min_fw_ver[FW_VER_MAJOR] = major;
|
||||
wl->min_fw_ver[FW_VER_SUBTYPE] = subtype;
|
||||
wl->min_fw_ver[FW_VER_MINOR] = minor;
|
||||
wl->min_sr_fw_ver[FW_VER_CHIP] = chip;
|
||||
wl->min_sr_fw_ver[FW_VER_IF_TYPE] = iftype_sr;
|
||||
wl->min_sr_fw_ver[FW_VER_MAJOR] = major_sr;
|
||||
wl->min_sr_fw_ver[FW_VER_SUBTYPE] = subtype_sr;
|
||||
wl->min_sr_fw_ver[FW_VER_MINOR] = minor_sr;
|
||||
|
||||
wl->min_mr_fw_ver[FW_VER_CHIP] = chip;
|
||||
wl->min_mr_fw_ver[FW_VER_IF_TYPE] = iftype_mr;
|
||||
wl->min_mr_fw_ver[FW_VER_MAJOR] = major_mr;
|
||||
wl->min_mr_fw_ver[FW_VER_SUBTYPE] = subtype_mr;
|
||||
wl->min_mr_fw_ver[FW_VER_MINOR] = minor_mr;
|
||||
}
|
||||
|
||||
/* Firmware image load chunk size */
|
||||
|
@ -450,6 +518,9 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
|
|||
/* Each RX/TX transaction requires an end-of-transaction transfer */
|
||||
#define WLCORE_QUIRK_END_OF_TRANSACTION BIT(0)
|
||||
|
||||
/* the first start_role(sta) sometimes doesn't work on wl12xx */
|
||||
#define WLCORE_QUIRK_START_STA_FAILS BIT(1)
|
||||
|
||||
/* wl127x and SPI don't support SDIO block size alignment */
|
||||
#define WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN BIT(2)
|
||||
|
||||
|
@ -462,9 +533,6 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
|
|||
/* Older firmwares use an old NVS format */
|
||||
#define WLCORE_QUIRK_LEGACY_NVS BIT(5)
|
||||
|
||||
/* Some firmwares may not support ELP */
|
||||
#define WLCORE_QUIRK_NO_ELP BIT(6)
|
||||
|
||||
/* pad only the last frame in the aggregate buffer */
|
||||
#define WLCORE_QUIRK_TX_PAD_LAST_FRAME BIT(7)
|
||||
|
||||
|
@ -477,11 +545,11 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
|
|||
/* separate probe response templates for one-shot and sched scans */
|
||||
#define WLCORE_QUIRK_DUAL_PROBE_TMPL BIT(10)
|
||||
|
||||
/* TODO: move to the lower drivers when all usages are abstracted */
|
||||
#define CHIP_ID_1271_PG10 (0x4030101)
|
||||
#define CHIP_ID_1271_PG20 (0x4030111)
|
||||
#define CHIP_ID_1283_PG10 (0x05030101)
|
||||
#define CHIP_ID_1283_PG20 (0x05030111)
|
||||
/* Firmware requires reg domain configuration for active calibration */
|
||||
#define WLCORE_QUIRK_REGDOMAIN_CONF BIT(11)
|
||||
|
||||
/* The FW only support a zero session id for AP */
|
||||
#define WLCORE_QUIRK_AP_ZERO_SESSION_ID BIT(12)
|
||||
|
||||
/* TODO: move all these common registers and values elsewhere */
|
||||
#define HW_ACCESS_ELP_CTRL_REG 0x1FFFC
|
||||
|
|
|
@ -109,17 +109,6 @@ enum {
|
|||
NUM_FW_VER
|
||||
};
|
||||
|
||||
#define FW_VER_CHIP_WL127X 6
|
||||
#define FW_VER_CHIP_WL128X 7
|
||||
|
||||
#define FW_VER_IF_TYPE_STA 1
|
||||
#define FW_VER_IF_TYPE_AP 2
|
||||
|
||||
#define FW_VER_MINOR_1_SPARE_STA_MIN 58
|
||||
#define FW_VER_MINOR_1_SPARE_AP_MIN 47
|
||||
|
||||
#define FW_VER_MINOR_FWLOG_STA_MIN 70
|
||||
|
||||
struct wl1271_chip {
|
||||
u32 id;
|
||||
char fw_ver_str[ETHTOOL_BUSINFO_LEN];
|
||||
|
@ -141,7 +130,10 @@ struct wl_fw_packet_counters {
|
|||
/* Cumulative counter of released Voice memory blocks */
|
||||
u8 tx_voice_released_blks;
|
||||
|
||||
u8 padding[3];
|
||||
/* Tx rate of the last transmitted packet */
|
||||
u8 tx_last_rate;
|
||||
|
||||
u8 padding[2];
|
||||
} __packed;
|
||||
|
||||
/* FW status registers */
|
||||
|
@ -260,6 +252,8 @@ enum wl12xx_vif_flags {
|
|||
WLVIF_FLAG_IN_USE,
|
||||
};
|
||||
|
||||
struct wl12xx_vif;
|
||||
|
||||
struct wl1271_link {
|
||||
/* AP-mode - TX queue per AC in link */
|
||||
struct sk_buff_head tx_queue[NUM_TX_QUEUES];
|
||||
|
@ -272,6 +266,9 @@ struct wl1271_link {
|
|||
|
||||
/* bitmap of TIDs where RX BA sessions are active for this link */
|
||||
u8 ba_bitmap;
|
||||
|
||||
/* The wlvif this link belongs to. Might be null for global links */
|
||||
struct wl12xx_vif *wlvif;
|
||||
};
|
||||
|
||||
#define WL1271_MAX_RX_FILTERS 5
|
||||
|
@ -315,6 +312,7 @@ struct wl12xx_rx_filter {
|
|||
|
||||
struct wl1271_station {
|
||||
u8 hlid;
|
||||
bool in_connection;
|
||||
};
|
||||
|
||||
struct wl12xx_vif {
|
||||
|
@ -332,7 +330,6 @@ struct wl12xx_vif {
|
|||
union {
|
||||
struct {
|
||||
u8 hlid;
|
||||
u8 ba_rx_bitmap;
|
||||
|
||||
u8 basic_rate_idx;
|
||||
u8 ap_rate_idx;
|
||||
|
@ -341,6 +338,8 @@ struct wl12xx_vif {
|
|||
u8 klv_template_id;
|
||||
|
||||
bool qos;
|
||||
/* channel type we started the STA role with */
|
||||
enum nl80211_channel_type role_chan_type;
|
||||
} sta;
|
||||
struct {
|
||||
u8 global_hlid;
|
||||
|
@ -362,6 +361,9 @@ struct wl12xx_vif {
|
|||
/* the hlid of the last transmitted skb */
|
||||
int last_tx_hlid;
|
||||
|
||||
/* counters of packets per AC, across all links in the vif */
|
||||
int tx_queue_count[NUM_TX_QUEUES];
|
||||
|
||||
unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
|
||||
|
||||
u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
|
||||
|
@ -396,9 +398,6 @@ struct wl12xx_vif {
|
|||
/* Our association ID */
|
||||
u16 aid;
|
||||
|
||||
/* Session counter for the chipset */
|
||||
int session_counter;
|
||||
|
||||
/* retry counter for PSM entries */
|
||||
u8 psm_entry_retry;
|
||||
|
||||
|
@ -416,11 +415,28 @@ struct wl12xx_vif {
|
|||
bool ba_support;
|
||||
bool ba_allowed;
|
||||
|
||||
bool wmm_enabled;
|
||||
|
||||
/* Rx Streaming */
|
||||
struct work_struct rx_streaming_enable_work;
|
||||
struct work_struct rx_streaming_disable_work;
|
||||
struct timer_list rx_streaming_timer;
|
||||
|
||||
struct delayed_work channel_switch_work;
|
||||
struct delayed_work connection_loss_work;
|
||||
|
||||
/* number of in connection stations */
|
||||
int inconn_count;
|
||||
|
||||
/*
|
||||
* This vif's queues are mapped to mac80211 HW queues as:
|
||||
* VO - hw_queue_base
|
||||
* VI - hw_queue_base + 1
|
||||
* BE - hw_queue_base + 2
|
||||
* BK - hw_queue_base + 3
|
||||
*/
|
||||
int hw_queue_base;
|
||||
|
||||
/*
|
||||
* This struct must be last!
|
||||
* data that has to be saved acrossed reconfigs (e.g. recovery)
|
||||
|
@ -443,6 +459,7 @@ struct wl12xx_vif {
|
|||
|
||||
static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *vif)
|
||||
{
|
||||
WARN_ON(!vif);
|
||||
return (struct wl12xx_vif *)vif->drv_priv;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue