Commit aaabee8b authored by Luciano Coelho's avatar Luciano Coelho

Merge branch 'wl12xx-next' into for-linville

Conflicts:
	drivers/net/wireless/ti/wlcore/main.c
parents 795e9364 2f244561
menuconfig WL1251
tristate "TI wl1251 driver support"
depends on MAC80211 && EXPERIMENTAL && GENERIC_HARDIRQS
depends on MAC80211 && GENERIC_HARDIRQS
select FW_LOADER
select CRC7
---help---
......
wl12xx-objs = main.o cmd.o acx.o debugfs.o
wl12xx-objs = main.o cmd.o acx.o debugfs.o scan.o event.o
obj-$(CONFIG_WL12XX) += wl12xx.o
......@@ -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__ */
/*
* 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)
wlcore_event_sched_scan_report(wl,
mbox->scheduled_scan_status);
if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID)
wlcore_event_sched_scan_completed(wl,
mbox->scheduled_scan_status);
if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
wlcore_event_soft_gemini_sense(wl,
mbox->soft_gemini_sense_info);
if (vector & BSS_LOSE_EVENT_ID)
wlcore_event_beacon_loss(wl, 0xff);
if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID)
wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric);
if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)
wlcore_event_ba_rx_constraint(wl,
BIT(mbox->role_id),
mbox->rx_ba_allowed);
if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID)
wlcore_event_channel_switch(wl, 0xff,
mbox->channel_switch_status);
if (vector & DUMMY_PACKET_EVENT_ID)
wlcore_event_dummy_packet(wl);
/*
* "TX retries exceeded" has a different meaning according to mode.
* In AP mode the offending station is disconnected.
*/
if (vector & MAX_TX_RETRY_EVENT_ID)
wlcore_event_max_tx_failure(wl,
le16_to_cpu(mbox->sta_tx_retry_exceeded));
if (vector & INACTIVE_STA_EVENT_ID)
wlcore_event_inactive_sta(wl,
le16_to_cpu(mbox->sta_aging_status));
if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
wlcore_event_roc_complete(wl);
return 0;
}
/*
* This file is part of wl12xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL12XX_EVENT_H__
#define __WL12XX_EVENT_H__
#include "../wlcore/wlcore.h"
enum {
MEASUREMENT_START_EVENT_ID = BIT(8),
MEASUREMENT_COMPLETE_EVENT_ID = BIT(9),
SCAN_COMPLETE_EVENT_ID = BIT(10),
WFD_DISCOVERY_COMPLETE_EVENT_ID = BIT(11),
AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12),
RESERVED1 = BIT(13),
PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14),
ROLE_STOP_COMPLETE_EVENT_ID = BIT(15),
RADAR_DETECTED_EVENT_ID = BIT(16),
CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17),
BSS_LOSE_EVENT_ID = BIT(18),
REGAINED_BSS_EVENT_ID = BIT(19),
MAX_TX_RETRY_EVENT_ID = BIT(20),
DUMMY_PACKET_EVENT_ID = BIT(21),
SOFT_GEMINI_SENSE_EVENT_ID = BIT(22),
CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID = BIT(23),
SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24),
PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25),
INACTIVE_STA_EVENT_ID = BIT(26),
PEER_REMOVE_COMPLETE_EVENT_ID = BIT(27),
PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(28),
PERIODIC_SCAN_REPORT_EVENT_ID = BIT(29),
BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(30),
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(31),
};
struct wl12xx_event_mailbox {
__le32 events_vector;
__le32 events_mask;
__le32 reserved_1;
__le32 reserved_2;
u8 number_of_scan_results;
u8 scan_tag;
u8 completed_scan_status;
u8 reserved_3;
u8 soft_gemini_sense_info;
u8 soft_gemini_protective_info;
s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
u8 change_auto_mode_timeout;
u8 scheduled_scan_status;
u8 reserved4;
/* tuned channel (roc) */
u8 roc_channel;
__le16 hlid_removed_bitmap;
/* bitmap of aged stations (by HLID) */
__le16 sta_aging_status;
/* bitmap of stations (by HLID) which exceeded max tx retries */
__le16 sta_tx_retry_exceeded;
/* discovery completed results */
u8 discovery_tag;
u8 number_of_preq_results;
u8 number_of_prsp_results;
u8 reserved_5;
/* rx ba constraint */
u8 role_id; /* 0xFF means any role. */
u8 rx_ba_allowed;
u8 reserved_6[2];
/* Channel switch results */
u8 channel_switch_role_id;
u8 channel_switch_status;
u8 reserved_7[2];
u8 ps_poll_delivery_failure_role_ids;
u8 stopped_role_ids;
u8 started_role_ids;
u8 reserved_8[9];
} __packed;
int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
bool *timeout);
int wl12xx_process_mailbox_events(struct wl1271 *wl);
#endif
......@@ -38,6 +38,8 @@
#include "reg.h"
#include "cmd.h"
#include "acx.h"
#include "scan.h"
#include "event.h"
#include "debugfs.h"
static char *fref_param;
......@@ -265,8 +267,8 @@ 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,
.dwell_time_passive = 100000,
.dwell_time_dfs = 150000,
.num_probe_reqs = 2,
.split_scan_timeout = 50000,
},
......@@ -368,6 +370,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,7 +607,7 @@ 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;
......@@ -631,13 +637,14 @@ 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;
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 +653,21 @@ 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;
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 +677,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 +694,28 @@ 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_set_min_fw_ver(wl, WL128X_CHIP_VER, WL128X_IFTYPE_VER,
WL128X_MAJOR_VER, WL128X_SUBTYPE_VER,
WL128X_MINOR_VER);
WLCORE_QUIRK_TKIP_HEADER_SPACE |
WLCORE_QUIRK_START_STA_FAILS;
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 +1088,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 +1119,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 +1173,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 +1240,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 +1299,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 +1313,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 +1377,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 +1432,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 +1448,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 +1504,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
......@@ -1594,11 +1616,12 @@ 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,8 +1638,13 @@ 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,
};
......@@ -1641,6 +1669,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;
......@@ -1703,7 +1732,8 @@ static int __devinit 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);
......
/*
* This file is part of wl12xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/ieee80211.h>
#include "scan.h"
#include "../wlcore/debug.h"
#include "../wlcore/tx.h"
static int wl1271_get_scan_channels(struct wl1271 *wl,
struct cfg80211_scan_request *req,
struct basic_scan_channel_params *channels,
enum ieee80211_band band, bool passive)
{
struct conf_scan_settings *c = &wl->conf.scan;
int i, j;
u32 flags;
for (i = 0, j = 0;
i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS;
i++) {
flags = req->channels[i]->flags;
if (!test_bit(i, wl->scan.scanned_ch) &&
!(flags & IEEE80211_CHAN_DISABLED) &&
(req->channels[i]->band == band) &&
/*
* In passive scans, we scan all remaining
* channels, even if not marked as such.
* In active scans, we only scan channels not
* marked as passive.
*/
(passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) {
wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
req->channels[i]->band,
req->channels[i]->center_freq);
wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
req->channels[i]->hw_value,
req->channels[i]->flags);
wl1271_debug(DEBUG_SCAN,
"max_antenna_gain %d, max_power %d",
req->channels[i]->max_antenna_gain,
req->channels[i]->max_power);
wl1271_debug(DEBUG_SCAN, "beacon_found %d",
req->channels[i]->beacon_found);
if (!passive) {
channels[j].min_duration =
cpu_to_le32(c->min_dwell_time_active);
channels[j].max_duration =
cpu_to_le32(c->max_dwell_time_active);
} else {
channels[j].min_duration =
cpu_to_le32(c->dwell_time_passive);
channels[j].max_duration =
cpu_to_le32(c->dwell_time_passive);
}
channels[j].early_termination = 0;
channels[j].tx_power_att = req->channels[i]->max_power;
channels[j].channel = req->channels[i]->hw_value;
memset(&channels[j].bssid_lsb, 0xff, 4);
memset(&channels[j].bssid_msb, 0xff, 2);
/* Mark the channels we already used */
set_bit(i, wl->scan.scanned_ch);
j++;
}
}
return j;
}
#define WL1271_NOTHING_TO_SCAN 1
static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum ieee80211_band band,
bool passive, u32 basic_rate)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct wl1271_cmd_scan *cmd;
struct wl1271_cmd_trigger_scan_to *trigger;
int ret;
u16 scan_options = 0;
/* skip active scans if we don't have SSIDs */
if (!passive && wl->scan.req->n_ssids == 0)
return WL1271_NOTHING_TO_SCAN;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
if (!cmd || !trigger) {
ret = -ENOMEM;
goto out;
}
if (wl->conf.scan.split_scan_timeout)
scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN;
if (passive)
scan_options |= WL1271_SCAN_OPT_PASSIVE;
cmd->params.role_id = wlvif->role_id;
if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) {
ret = -EINVAL;
goto out;
}
cmd->params.scan_options = cpu_to_le16(scan_options);
cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
cmd->channels,
band, passive);
if (cmd->params.n_ch == 0) {
ret = WL1271_NOTHING_TO_SCAN;
goto out;
}
cmd->params.tx_rate = cpu_to_le32(basic_rate);
cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
cmd->params.tid_trigger = CONF_TX_AC_ANY_TID;
cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
if (band == IEEE80211_BAND_2GHZ)
cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ;
else
cmd->params.band = WL1271_SCAN_BAND_5_GHZ;
if (wl->scan.ssid_len && wl->scan.ssid) {
cmd->params.ssid_len = wl->scan.ssid_len;
memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
}
memcpy(cmd->addr, vif->addr, ETH_ALEN);
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
cmd->params.role_id, band,
wl->scan.ssid, wl->scan.ssid_len,
wl->scan.req->ie,
wl->scan.req->ie_len, false);
if (ret < 0) {
wl1271_error("PROBE request template failed");
goto out;
}
trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout);
ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
sizeof(*trigger), 0);
if (ret < 0) {
wl1271_error("trigger scan to failed for hw scan");
goto out;
}
wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("SCAN failed");
goto out;
}
out:
kfree(cmd);
kfree(trigger);
return ret;
}
int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct wl1271_cmd_header *cmd = NULL;
int ret = 0;
if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE))
return -EINVAL;
wl1271_debug(DEBUG_CMD, "cmd scan stop");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd,
sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("cmd stop_scan failed");
goto out;
}
out:
kfree(cmd);
return ret;
}
void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret = 0;
enum ieee80211_band band;
u32 rate, mask;
switch (wl->scan.state) {
case WL1271_SCAN_STATE_IDLE:
break;
case WL1271_SCAN_STATE_2GHZ_ACTIVE:
band = IEEE80211_BAND_2GHZ;
mask = wlvif->bitrate_masks[band];
if (wl->scan.req->no_cck) {
mask &= ~CONF_TX_CCK_RATES;
if (!mask)
mask = CONF_TX_RATE_MASK_BASIC_P2P;
}
rate = wl1271_tx_min_rate_get(wl, mask);
ret = wl1271_scan_send(wl, wlvif, band, false, rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
wl1271_scan_stm(wl, wlvif);
}
break;
case WL1271_SCAN_STATE_2GHZ_PASSIVE:
band = IEEE80211_BAND_2GHZ;
mask = wlvif->bitrate_masks[band];
if (wl->scan.req->no_cck) {
mask &= ~CONF_TX_CCK_RATES;
if (!mask)
mask = CONF_TX_RATE_MASK_BASIC_P2P;
}
rate = wl1271_tx_min_rate_get(wl, mask);
ret = wl1271_scan_send(wl, wlvif, band, true, rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
if (wl->enable_11a)
wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
else
wl->scan.state = WL1271_SCAN_STATE_DONE;
wl1271_scan_stm(wl, wlvif);
}
break;
case WL1271_SCAN_STATE_5GHZ_ACTIVE:
band = IEEE80211_BAND_5GHZ;
rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
ret = wl1271_scan_send(wl, wlvif, band, false, rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
wl1271_scan_stm(wl, wlvif);
}
break;
case WL1271_SCAN_STATE_5GHZ_PASSIVE:
band = IEEE80211_BAND_5GHZ;
rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
ret = wl1271_scan_send(wl, wlvif, band, true, rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
wl->scan.state = WL1271_SCAN_STATE_DONE;
wl1271_scan_stm(wl, wlvif);
}
break;
case WL1271_SCAN_STATE_DONE:
wl->scan.failed = false;
cancel_delayed_work(&wl->scan_complete_work);
ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
msecs_to_jiffies(0));
break;
default:
wl1271_error("invalid scan state");
break;
}
if (ret < 0) {
cancel_delayed_work(&wl->scan_complete_work);
ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
msecs_to_jiffies(0));
}
}
static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd,
struct wlcore_scan_channels *cmd_channels)
{
memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive));
memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active));
cmd->dfs = cmd_channels->dfs;
cmd->n_pactive_ch = cmd_channels->passive_active;
memcpy(cmd->channels_2, cmd_channels->channels_2,
sizeof(cmd->channels_2));
memcpy(cmd->channels_5, cmd_channels->channels_5,
sizeof(cmd->channels_2));
/* channels_4 are not supported, so no need to copy them */
}
int wl1271_scan_sched_scan_config(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies)
{
struct wl1271_cmd_sched_scan_config *cfg = NULL;
struct wlcore_scan_channels *cfg_channels = NULL;
struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
int i, ret;
bool force_passive = !req->n_ssids;
wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
cfg->role_id = wlvif->role_id;
cfg->rssi_threshold = c->rssi_threshold;
cfg->snr_threshold = c->snr_threshold;
cfg->n_probe_reqs = c->num_probe_reqs;
/* cycles set to 0 it means infinite (until manually stopped) */
cfg->cycles = 0;
/* report APs when at least 1 is found */
cfg->report_after = 1;
/* don't stop scanning automatically when something is found */
cfg->terminate = 0;
cfg->tag = WL1271_SCAN_DEFAULT_TAG;
/* don't filter on BSS type */
cfg->bss_type = SCAN_BSS_TYPE_ANY;
/* currently NL80211 supports only a single interval */
for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++)
cfg->intervals[i] = cpu_to_le32(req->interval);
cfg->ssid_len = 0;
ret = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req);
if (ret < 0)
goto out;
cfg->filter_type = ret;
wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type);
cfg_channels = kzalloc(sizeof(*cfg_channels), GFP_KERNEL);
if (!cfg_channels) {
ret = -ENOMEM;
goto out;
}
if (!wlcore_set_scan_chan_params(wl, cfg_channels, req->channels,
req->n_channels, req->n_ssids,
SCAN_TYPE_PERIODIC)) {
wl1271_error("scan channel list is empty");
ret = -EINVAL;
goto out;
}
wl12xx_adjust_channels(cfg, cfg_channels);
if (!force_passive && cfg->active[0]) {
u8 band = IEEE80211_BAND_2GHZ;
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
wlvif->role_id, band,
req->ssids[0].ssid,
req->ssids[0].ssid_len,
ies->ie[band],
ies->len[band], true);
if (ret < 0) {
wl1271_error("2.4GHz PROBE request template failed");
goto out;
}
}
if (!force_passive && cfg->active[1]) {
u8 band = IEEE80211_BAND_5GHZ;
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
wlvif->role_id, band,
req->ssids[0].ssid,
req->ssids[0].ssid_len,
ies->ie[band],
ies->len[band], true);
if (ret < 0) {
wl1271_error("5GHz PROBE request template failed");
goto out;
}
}
wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg));
ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg,
sizeof(*cfg), 0);
if (ret < 0) {
wl1271_error("SCAN configuration failed");
goto out;
}
out:
kfree(cfg_channels);
kfree(cfg);
return ret;
}
int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct wl1271_cmd_sched_scan_start *start;
int ret = 0;
wl1271_debug(DEBUG_CMD, "cmd periodic scan start");
if (wlvif->bss_type != BSS_TYPE_STA_BSS)
return -EOPNOTSUPP;
if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) &&
test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
return -EBUSY;
start = kzalloc(sizeof(*start), GFP_KERNEL);
if (!start)
return -ENOMEM;
start->role_id = wlvif->role_id;
start->tag = WL1271_SCAN_DEFAULT_TAG;
ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start,
sizeof(*start), 0);
if (ret < 0) {
wl1271_error("failed to send scan start command");
goto out_free;
}
out_free:
kfree(start);
return ret;
}
int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies)
{
int ret;
ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
if (ret < 0)
return ret;
return wl1271_scan_sched_scan_start(wl, wlvif);
}
void wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct wl1271_cmd_sched_scan_stop *stop;
int ret = 0;
wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
/* FIXME: what to do if alloc'ing to stop fails? */
stop = kzalloc(sizeof(*stop), GFP_KERNEL);
if (!stop) {
wl1271_error("failed to alloc memory to send sched scan stop");
return;
}
stop->role_id = wlvif->role_id;
stop->tag = WL1271_SCAN_DEFAULT_TAG;
ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop,
sizeof(*stop), 0);
if (ret < 0) {
wl1271_error("failed to send sched scan stop command");
goto out_free;
}
out_free:
kfree(stop);
}
int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_scan_request *req)
{
wl1271_scan_stm(wl, wlvif);
return 0;
}
void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
wl1271_scan_stm(wl, wlvif);
}
/*
* This file is part of wl12xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL12XX_SCAN_H__
#define __WL12XX_SCAN_H__
#include "../wlcore/wlcore.h"
#include "../wlcore/cmd.h"
#include "../wlcore/scan.h"
#define WL12XX_MAX_CHANNELS_5GHZ 23
struct basic_scan_params {
/* Scan option flags (WL1271_SCAN_OPT_*) */
__le16 scan_options;
u8 role_id;
/* Number of scan channels in the list (maximum 30) */
u8 n_ch;
/* This field indicates the number of probe requests to send
per channel for an active scan */
u8 n_probe_reqs;
u8 tid_trigger;
u8 ssid_len;
u8 use_ssid_list;
/* Rate bit field for sending the probes */
__le32 tx_rate;
u8 ssid[IEEE80211_MAX_SSID_LEN];
/* Band to scan */
u8 band;
u8 scan_tag;
u8 padding2[2];
} __packed;
struct basic_scan_channel_params {
/* Duration in TU to wait for frames on a channel for active scan */
__le32 min_duration;
__le32 max_duration;
__le32 bssid_lsb;
__le16 bssid_msb;
u8 early_termination;
u8 tx_power_att;
u8 channel;
/* FW internal use only! */
u8 dfs_candidate;
u8 activity_detected;
u8 pad;
} __packed;
struct wl1271_cmd_scan {
struct wl1271_cmd_header header;
struct basic_scan_params params;
struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
/* src mac address */
u8 addr[ETH_ALEN];
u8 padding[2];
} __packed;
struct wl1271_cmd_sched_scan_config {
struct wl1271_cmd_header header;
__le32 intervals[SCAN_MAX_CYCLE_INTERVALS];
s8 rssi_threshold; /* for filtering (in dBm) */
s8 snr_threshold; /* for filtering (in dB) */
u8 cycles; /* maximum number of scan cycles */
u8 report_after; /* report when this number of results are received */
u8 terminate; /* stop scanning after reporting */
u8 tag;
u8 bss_type; /* for filtering */
u8 filter_type;
u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 n_probe_reqs; /* Number of probes requests per channel */
u8 passive[SCAN_MAX_BANDS];
u8 active[SCAN_MAX_BANDS];
u8 dfs;
u8 n_pactive_ch; /* number of pactive (passive until fw detects energy)
channels in BG band */
u8 role_id;
u8 padding[1];
struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
struct conn_scan_ch_params channels_5[WL12XX_MAX_CHANNELS_5GHZ];
struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
} __packed;
struct wl1271_cmd_sched_scan_start {
struct wl1271_cmd_header header;
u8 tag;
u8 role_id;
u8 padding[2];
} __packed;
struct wl1271_cmd_sched_scan_stop {
struct wl1271_cmd_header header;
u8 tag;
u8 role_id;
u8 padding[2];
} __packed;
int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_scan_request *req);
int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies);
void wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
#endif
......@@ -24,19 +24,37 @@
#include "conf.h"
/* minimum FW required for driver for wl127x */
/* WiLink 6/7 chip IDs */
#define CHIP_ID_127X_PG10 (0x04030101)
#define CHIP_ID_127X_PG20 (0x04030111)
#define CHIP_ID_128X_PG10 (0x05030101)
#define CHIP_ID_128X_PG20 (0x05030111)
/* FW chip version for wl127x */
#define WL127X_CHIP_VER 6
#define WL127X_IFTYPE_VER 3
#define WL127X_MAJOR_VER 10
#define WL127X_SUBTYPE_VER 2
#define WL127X_MINOR_VER 115
/* 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
/* minimum FW required for driver for wl128x */
/* 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)
......
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,34 @@ 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;
}
......@@ -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,24 @@ 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];
};
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);
#endif /* __WL18XX_ACX_H__ */
/*
* This file is part of wl18xx
*
* Copyright (C) 2011 Texas Instruments Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "../wlcore/cmd.h"
#include "../wlcore/debug.h"
#include "../wlcore/hw_ops.h"
#include "cmd.h"
int wl18xx_cmd_channel_switch(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct ieee80211_channel_switch *ch_switch)
{
struct wl18xx_cmd_channel_switch *cmd;
u32 supported_rates;
int ret;
wl1271_debug(DEBUG_ACX, "cmd channel switch");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
cmd->role_id = wlvif->role_id;
cmd->channel = ch_switch->channel->hw_value;
cmd->switch_time = ch_switch->count;
cmd->stop_tx = ch_switch->block_tx;
switch (ch_switch->channel->band) {
case IEEE80211_BAND_2GHZ:
cmd->band = WLCORE_BAND_2_4GHZ;
break;
case IEEE80211_BAND_5GHZ:
cmd->band = WLCORE_BAND_5GHZ;
break;
default:
wl1271_error("invalid channel switch band: %d",
ch_switch->channel->band);
ret = -EINVAL;
goto out_free;
}
supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
wlcore_hw_sta_get_ap_rate_mask(wl, wlvif);
if (wlvif->p2p)
supported_rates &= ~CONF_TX_CCK_RATES;
cmd->local_supported_rates = cpu_to_le32(supported_rates);
cmd->channel_type = wlvif->channel_type;
ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("failed to send channel switch command");
goto out_free;
}
out_free:
kfree(cmd);
out:
return ret;
}
/*
* This file is part of wl18xx
*
* Copyright (C) 2011 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL18XX_CMD_H__
#define __WL18XX_CMD_H__
#include "../wlcore/wlcore.h"
#include "../wlcore/acx.h"
struct wl18xx_cmd_channel_switch {
struct wl1271_cmd_header header;
u8 role_id;
/* The new serving channel */
u8 channel;
/* Relative time of the serving channel switch in TBTT units */
u8 switch_time;
/* Stop the role TX, should expect it after radar detection */
u8 stop_tx;
__le32 local_supported_rates;
u8 channel_type;
u8 band;
u8 padding[2];
} __packed;
int wl18xx_cmd_channel_switch(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct ieee80211_channel_switch *ch_switch);
#endif
/*
* 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_COMPLETE_EVENT_ID)
wlcore_event_sched_scan_completed(wl, 1);
if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID)
wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric);
if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)
wlcore_event_ba_rx_constraint(wl,
le16_to_cpu(mbox->rx_ba_role_id_bitmap),
le16_to_cpu(mbox->rx_ba_allowed_bitmap));
if (vector & BSS_LOSS_EVENT_ID)
wlcore_event_beacon_loss(wl,
le16_to_cpu(mbox->bss_loss_bitmap));
if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID)
wlcore_event_channel_switch(wl,
le16_to_cpu(mbox->channel_switch_role_id_bitmap),
true);
if (vector & DUMMY_PACKET_EVENT_ID)
wlcore_event_dummy_packet(wl);
/*
* "TX retries exceeded" has a different meaning according to mode.
* In AP mode the offending station is disconnected.
*/
if (vector & MAX_TX_FAILURE_EVENT_ID)
wlcore_event_max_tx_failure(wl,
le32_to_cpu(mbox->tx_retry_exceeded_bitmap));
if (vector & INACTIVE_STA_EVENT_ID)
wlcore_event_inactive_sta(wl,
le32_to_cpu(mbox->inactive_sta_bitmap));
if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
wlcore_event_roc_complete(wl);
return 0;
}
/*
* This file is part of wl18xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL18XX_EVENT_H__
#define __WL18XX_EVENT_H__
#include "../wlcore/wlcore.h"
enum {
SCAN_COMPLETE_EVENT_ID = BIT(8),
RADAR_DETECTED_EVENT_ID = BIT(9),
CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(10),
BSS_LOSS_EVENT_ID = BIT(11),
MAX_TX_FAILURE_EVENT_ID = BIT(12),
DUMMY_PACKET_EVENT_ID = BIT(13),
INACTIVE_STA_EVENT_ID = BIT(14),
PEER_REMOVE_COMPLETE_EVENT_ID = BIT(15),
PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(16),
BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(17),
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(18),
DFS_CHANNELS_CONFIG_COMPLETE_EVENT = BIT(19),
};
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
......@@ -391,8 +394,8 @@ 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,
.dwell_time_passive = 100000,
.dwell_time_dfs = 150000,
.num_probe_reqs = 2,
.split_scan_timeout = 50000,
},
......@@ -489,6 +492,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 = {
......@@ -595,7 +602,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)
{
......@@ -612,11 +619,15 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN |
WLCORE_QUIRK_TX_PAD_LAST_FRAME;
wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER, WL18XX_IFTYPE_VER,
WL18XX_MAJOR_VER, WL18XX_SUBTYPE_VER,
WL18XX_MINOR_VER);
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,
/* 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 +641,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 +859,19 @@ 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 |
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;
......@@ -1296,6 +1325,43 @@ 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_setup(struct wl1271 *wl);
static struct wlcore_ops wl18xx_ops = {
......@@ -1305,6 +1371,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,10 +1388,16 @@ 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,
};
/* HT cap appropriate for wide channels in 2Ghz */
......@@ -1388,6 +1462,7 @@ 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_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 +1581,8 @@ static int __devinit 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);
......
/*
* 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[0].ssid,
req->ssids[0].ssid_len,
req->ie,
req->ie_len,
false);
if (ret < 0) {
wl1271_error("2.4GHz PROBE request template failed");
goto out;
}
}
if (cmd->active[1]) {
u8 band = IEEE80211_BAND_5GHZ;
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
cmd->role_id, band,
req->ssids[0].ssid,
req->ssids[0].ssid_len,
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;
if (cmd->active[0]) {
u8 band = IEEE80211_BAND_2GHZ;
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
cmd->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 (cmd->active[1]) {
u8 band = IEEE80211_BAND_5GHZ;
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
cmd->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: ", cmd, sizeof(*cmd));
ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("SCAN failed");
goto out;
}
out:
kfree(cmd_channels);
kfree(cmd);
return ret;
}
int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies)
{
return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies);
}
static int __wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 scan_type)
{
struct wl18xx_cmd_scan_stop *stop;
int ret;
wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
stop = kzalloc(sizeof(*stop), GFP_KERNEL);
if (!stop) {
wl1271_error("failed to alloc memory to send sched scan stop");
return -ENOMEM;
}
stop->role_id = wlvif->role_id;
stop->scan_type = scan_type;
ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, stop, sizeof(*stop), 0);
if (ret < 0) {
wl1271_error("failed to send sched scan stop command");
goto out_free;
}
out_free:
kfree(stop);
return ret;
}
void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
__wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_PERIODIC);
}
int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_scan_request *req)
{
return wl18xx_scan_send(wl, wlvif, req);
}
int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
return __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_SEARCH);
}
/*
* This file is part of wl18xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL18XX_SCAN_H__
#define __WL18XX_SCAN_H__
#include "../wlcore/wlcore.h"
#include "../wlcore/cmd.h"
#include "../wlcore/scan.h"
struct tracking_ch_params {
struct conn_scan_ch_params channel;
__le32 bssid_lsb;
__le16 bssid_msb;
u8 padding[2];
} __packed;
/* probe request rate */
enum
{
WL18XX_SCAN_RATE_1 = 0,
WL18XX_SCAN_RATE_5_5 = 1,
WL18XX_SCAN_RATE_6 = 2,
};
#define WL18XX_MAX_CHANNELS_5GHZ 32
struct wl18xx_cmd_scan_params {
struct wl1271_cmd_header header;
u8 role_id;
u8 scan_type;
s8 rssi_threshold; /* for filtering (in dBm) */
s8 snr_threshold; /* for filtering (in dB) */
u8 bss_type; /* for filtering */
u8 ssid_from_list; /* use ssid from configured ssid list */
u8 filter; /* forward only results with matching ssids */
/*
* add broadcast ssid in addition to the configured ssids.
* the driver should add dummy entry for it (?).
*/
u8 add_broadcast;
u8 urgency;
u8 protect; /* ??? */
u8 n_probe_reqs; /* Number of probes requests per channel */
u8 terminate_after; /* early terminate scan operation */
u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */
u8 active[SCAN_MAX_BANDS]; /* number of active scan channels */
u8 dfs; /* number of dfs channels in 5ghz */
u8 passive_active; /* number of passive before active channels 2.4ghz */
__le16 short_cycles_sec;
__le16 long_cycles_sec;
u8 short_cycles_count;
u8 total_cycles; /* 0 - infinite */
u8 rate;
u8 padding[1];
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 padding1[2];
} __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
......@@ -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
......
......@@ -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");
......
......@@ -131,13 +131,14 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
wl12xx_queue_recovery_work(wl);
return ret;
}
EXPORT_SYMBOL_GPL(wl1271_cmd_send);
/*
* 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 +188,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 +266,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,6 +283,8 @@ 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);
......@@ -316,17 +316,6 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
*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;
}
static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
{
switch (nl_channel_type) {
......@@ -345,7 +334,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 +350,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 +360,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 +411,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 +424,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 +445,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,8 +461,13 @@ 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.remote_rates = cpu_to_le32(wlvif->rate_set);
cmd->sta.session = wl->session_ids[wlvif->sta.hlid];
/*
* We don't have the correct remote rates in this stage, and there
* is no way to update them later, so use our supported rates instead.
* The fw will take the configured rate policies into account anyway.
*/
cmd->sta.remote_rates = cpu_to_le32(supported_rates);
wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d "
"basic_rate_set: 0x%x, remote_rates: 0x%x",
......@@ -482,6 +480,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 +499,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 +521,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 +566,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 +589,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);
......@@ -1034,8 +1026,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, ie_len);
......@@ -1046,10 +1038,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]);
......@@ -1066,6 +1058,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,
......@@ -1377,7 +1370,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;
......@@ -1393,6 +1387,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");
......@@ -1427,6 +1425,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)))
......@@ -1488,9 +1487,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)
......@@ -1506,6 +1506,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;
......@@ -1591,12 +1716,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;
......@@ -1608,8 +1733,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;
......@@ -1664,30 +1789,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;
......@@ -1717,43 +1830,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;
......@@ -1766,6 +1843,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");
......@@ -1780,7 +1859,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;
......@@ -1795,11 +1875,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;
......
......@@ -39,7 +39,8 @@ 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);
......@@ -75,22 +76,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 +158,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 +179,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 +191,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
};
......@@ -345,7 +359,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 +537,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 +587,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 +626,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 +662,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 */
......
......@@ -415,11 +415,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 | \
......@@ -1059,19 +1059,11 @@ struct conf_scan_settings {
*/
u32 max_dwell_time_active;
/*
* The minimum time to wait on each channel for passive scans
*
* Range: u32 tu/1000
*/
u32 min_dwell_time_passive;
/* time to wait on the channel for passive scans (in TU/1000) */
u32 dwell_time_passive;
/*
* The maximum time to wait on each channel for passive scans
*
* Range: u32 tu/1000
*/
u32 max_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 +1268,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 (0x0004 << 16)
#define WLCORE_CONF_MASK 0xffff0000
#define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \
sizeof(struct wlcore_conf))
......@@ -1309,6 +1309,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
......@@ -589,7 +589,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 +992,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,25 +29,29 @@
#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;
if (event != wlvif->last_rssi_event)
ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL);
wlvif->last_rssi_event = event;
/* 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;
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)
{
......@@ -74,8 +78,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 +90,179 @@ 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_report(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_REPORT_EVENT (status 0x%0x)",
status);
wl1271_scan_sched_scan_results(wl);
}
EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_report);
static int wl1271_event_process(struct wl1271 *wl)
void wlcore_event_sched_scan_completed(struct wl1271 *wl,
u8 status)
{
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_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)",
status);
wl1271_event_mbox_dump(mbox);
if (wl->sched_vif) {
ieee80211_sched_scan_stopped(wl->hw);
wl->sched_vif = NULL;
}
}
EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_completed);
vector = le32_to_cpu(mbox->events_vector);
vector &= ~(le32_to_cpu(mbox->events_mask));
wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector);
void wlcore_event_ba_rx_constraint(struct wl1271 *wl,
unsigned long roles_bitmap,
unsigned long allowed_bitmap)
{
struct wl12xx_vif *wlvif;
if (vector & SCAN_COMPLETE_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "status: 0x%x",
mbox->scheduled_scan_status);
wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx allowed=0x%lx",
__func__, roles_bitmap, allowed_bitmap);
wl1271_scan_stm(wl, wl->scan_vif);
}
wl12xx_for_each_wlvif(wl, wlvif) {
if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
!test_bit(wlvif->role_id , &roles_bitmap))
continue;
if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT "
"(status 0x%0x)", mbox->scheduled_scan_status);
wl1271_scan_sched_scan_results(wl);
wlvif->ba_allowed = !!test_bit(wlvif->role_id,
&allowed_bitmap);
if (!wlvif->ba_allowed)
wl1271_stop_ba_event(wl, wlvif);
}
}
EXPORT_SYMBOL_GPL(wlcore_event_ba_rx_constraint);
if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) {
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_channel_switch(struct wl1271 *wl,
unsigned long roles_bitmap,
bool success)
{
struct wl12xx_vif *wlvif;
struct ieee80211_vif *vif;
if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
wl12xx_event_soft_gemini_sense(wl,
mbox->soft_gemini_sense_info);
wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d",
__func__, roles_bitmap, success);
/*
* 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.");
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.
*/
ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work,
msecs_to_jiffies(delay));
if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
&wlvif->flags))
continue;
wl12xx_for_each_wlvif_sta(wl, wlvif) {
vif = wl12xx_wlvif_to_vif(wlvif);
vif = wl12xx_wlvif_to_vif(wlvif);
ieee80211_cqm_rssi_notify(
vif,
NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
GFP_KERNEL);
}
ieee80211_chswitch_done(vif, success);
cancel_delayed_work(&wlvif->channel_switch_work);
}
}
EXPORT_SYMBOL_GPL(wlcore_event_channel_switch);
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");
}
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);
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);
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;
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);
vif = wl12xx_wlvif_to_vif(wlvif);
addr = wl->links[h].addr;
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);
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 & 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);
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);
ieee80211_chswitch_done(vif, success);
}
}
void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap)
{
wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
wlcore_disconnect_sta(wl, sta_bitmap);
}
EXPORT_SYMBOL_GPL(wlcore_event_inactive_sta);
if ((vector & DUMMY_PACKET_EVENT_ID)) {
wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
ret = wl1271_tx_dummy_packet(wl);
if (ret < 0)
return ret;
}
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)
{
/*
* "TX retries exceeded" has a different meaning according to mode.
* In AP mode the offending station is disconnected.
* We are HW_MONITOR device. On beacon loss - queue
* connection loss work. Cancel it on REGAINED event.
*/
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;
}
struct wl12xx_vif *wlvif;
struct ieee80211_vif *vif;
int delay = wl->conf.conn.synch_fail_thold *
wl->conf.conn.bss_lose_timeout;
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;
}
wl1271_info("Beacon loss detected. roles:0x%lx", roles_bitmap);
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;
wl12xx_for_each_wlvif_sta(wl, wlvif) {
if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
!test_bit(wlvif->role_id , &roles_bitmap))
continue;
vif = wl12xx_wlvif_to_vif(wlvif);
addr = wl->links[h].addr;
/*
* if the work is already queued, it should take place.
* We don't want to delay the connection loss
* indication any more.
*/
ieee80211_queue_delayed_work(wl->hw,
&wlvif->connection_loss_work,
msecs_to_jiffies(delay));
rcu_read_lock();
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();
}
vif = wl12xx_wlvif_to_vif(wlvif);
ieee80211_cqm_rssi_notify(
vif,
NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
GFP_KERNEL);
}
return 0;
}
EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss);
int wl1271_event_unmask(struct wl1271 *wl)
{
......@@ -305,12 +286,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,26 @@ 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_report(struct wl1271 *wl,
u8 status);
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,12 @@ 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);
}
#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;
......@@ -679,6 +681,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)
......
......@@ -56,8 +56,8 @@
#define WL1271_BOOT_RETRIES 3
static char *fwlog_param;
static bool bug_on_recovery;
static bool no_recovery;
static int bug_on_recovery = -1;
static int no_recovery = -1;
static void __wl1271_op_remove_interface(struct wl1271 *wl,
struct ieee80211_vif *vif,
......@@ -79,12 +79,10 @@ static int wl12xx_set_authorized(struct wl1271 *wl,
if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags))
return 0;
ret = wl12xx_cmd_set_peer_state(wl, wlvif->sta.hlid);
ret = wl12xx_cmd_set_peer_state(wl, wlvif, wlvif->sta.hlid);
if (ret < 0)
return ret;
wl12xx_croc(wl, wlvif->role_id);
wl1271_info("Association completed.");
return 0;
}
......@@ -95,6 +93,8 @@ static int wl1271_reg_notify(struct wiphy *wiphy,
struct ieee80211_supported_band *band;
struct ieee80211_channel *ch;
int i;
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct wl1271 *wl = hw->priv;
band = wiphy->bands[IEEE80211_BAND_5GHZ];
for (i = 0; i < band->n_channels; i++) {
......@@ -108,6 +108,9 @@ static int wl1271_reg_notify(struct wiphy *wiphy,
}
if (likely(wl->state == WLCORE_STATE_ON))
wlcore_regdomain_config(wl);
return 0;
}
......@@ -303,6 +306,7 @@ static void wl12xx_tx_watchdog_work(struct work_struct *work)
static void wlcore_adjust_conf(struct wl1271 *wl)
{
/* Adjust settings according to optional module parameters */
if (fwlog_param) {
if (!strcmp(fwlog_param, "continuous")) {
wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
......@@ -318,6 +322,12 @@ static void wlcore_adjust_conf(struct wl1271 *wl)
wl1271_error("Unknown fwlog parameter %s", fwlog_param);
}
}
if (bug_on_recovery != -1)
wl->conf.recovery.bug_on_recovery = (u8) bug_on_recovery;
if (no_recovery != -1)
wl->conf.recovery.no_recovery = (u8) no_recovery;
}
static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
......@@ -802,11 +812,13 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
/*
* Make sure the chip is awake and the logger isn't active.
* Do not send a stop fwlog command if the fw is hanged.
* Do not send a stop fwlog command if the fw is hanged or if
* dbgpins are used (due to some fw bug).
*/
if (wl1271_ps_elp_wakeup(wl))
goto out;
if (!wl->watchdog_recovery)
if (!wl->watchdog_recovery &&
wl->conf.fwlog.output != WL12XX_FWLOG_OUTPUT_DBG_PINS)
wl12xx_cmd_stop_fwlog(wl);
/* Read the first memory block address */
......@@ -874,7 +886,8 @@ static void wlcore_print_recovery(struct wl1271 *wl)
if (ret < 0)
return;
wl1271_info("pc: 0x%x, hint_sts: 0x%08x", pc, hint_sts);
wl1271_info("pc: 0x%x, hint_sts: 0x%08x count: %d",
pc, hint_sts, ++wl->recovery_count);
wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
}
......@@ -897,10 +910,10 @@ static void wl1271_recovery_work(struct work_struct *work)
wlcore_print_recovery(wl);
}
BUG_ON(bug_on_recovery &&
BUG_ON(wl->conf.recovery.bug_on_recovery &&
!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
if (no_recovery) {
if (wl->conf.recovery.no_recovery) {
wl1271_info("No recovery (chosen on module load). Fw will remain stuck.");
goto out_unlock;
}
......@@ -920,11 +933,6 @@ static void wl1271_recovery_work(struct work_struct *work)
/* Prevent spurious TX during FW restart */
wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
if (wl->sched_scanning) {
ieee80211_sched_scan_stopped(wl->hw);
wl->sched_scanning = false;
}
/* reboot the chipset */
while (!list_empty(&wl->wlvif_list)) {
wlvif = list_first_entry(&wl->wlvif_list,
......@@ -1141,7 +1149,6 @@ int wl1271_plt_stop(struct wl1271 *wl)
cancel_work_sync(&wl->recovery_work);
cancel_delayed_work_sync(&wl->elp_work);
cancel_delayed_work_sync(&wl->tx_watchdog_work);
cancel_delayed_work_sync(&wl->connection_loss_work);
mutex_lock(&wl->mutex);
wl1271_power_off(wl);
......@@ -1843,7 +1850,6 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
cancel_work_sync(&wl->tx_work);
cancel_delayed_work_sync(&wl->elp_work);
cancel_delayed_work_sync(&wl->tx_watchdog_work);
cancel_delayed_work_sync(&wl->connection_loss_work);
/* let's notify MAC80211 about the remaining pending TX frames */
wl12xx_tx_reset(wl);
......@@ -1870,11 +1876,11 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
wl->time_offset = 0;
wl->ap_fw_ps_map = 0;
wl->ap_ps_map = 0;
wl->sched_scanning = false;
wl->sleep_auth = WL1271_PSM_ILLEGAL;
memset(wl->roles_map, 0, sizeof(wl->roles_map));
memset(wl->links_map, 0, sizeof(wl->links_map));
memset(wl->roc_map, 0, sizeof(wl->roc_map));
memset(wl->session_ids, 0, sizeof(wl->session_ids));
wl->active_sta_count = 0;
/* The system link is always allocated */
......@@ -1903,6 +1909,12 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
wl->tx_res_if = NULL;
kfree(wl->target_mem_map);
wl->target_mem_map = NULL;
/*
* FW channels must be re-calibrated after recovery,
* clear the last Reg-Domain channel configuration.
*/
memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last));
}
static void wlcore_op_stop(struct ieee80211_hw *hw)
......@@ -1918,6 +1930,71 @@ static void wlcore_op_stop(struct ieee80211_hw *hw)
mutex_unlock(&wl->mutex);
}
static void wlcore_channel_switch_work(struct work_struct *work)
{
struct delayed_work *dwork;
struct wl1271 *wl;
struct ieee80211_vif *vif;
struct wl12xx_vif *wlvif;
int ret;
dwork = container_of(work, struct delayed_work, work);
wlvif = container_of(dwork, struct wl12xx_vif, channel_switch_work);
wl = wlvif->wl;
wl1271_info("channel switch failed (role_id: %d).", wlvif->role_id);
mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON))
goto out;
/* check the channel switch is still ongoing */
if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags))
goto out;
vif = wl12xx_wlvif_to_vif(wlvif);
ieee80211_chswitch_done(vif, false);
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
wl12xx_cmd_stop_channel_switch(wl, wlvif);
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
}
static void wlcore_connection_loss_work(struct work_struct *work)
{
struct delayed_work *dwork;
struct wl1271 *wl;
struct ieee80211_vif *vif;
struct wl12xx_vif *wlvif;
dwork = container_of(work, struct delayed_work, work);
wlvif = container_of(dwork, struct wl12xx_vif, connection_loss_work);
wl = wlvif->wl;
wl1271_info("Connection loss work (role_id: %d).", wlvif->role_id);
mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON))
goto out;
/* Call mac80211 connection loss */
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
goto out;
vif = wl12xx_wlvif_to_vif(wlvif);
ieee80211_connection_loss(vif);
out:
mutex_unlock(&wl->mutex);
}
static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx)
{
u8 policy = find_first_zero_bit(wl->rate_policies_map,
......@@ -2037,15 +2114,15 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
wl12xx_allocate_rate_policy(wl,
&wlvif->ap.ucast_rate_idx[i]);
wlvif->basic_rate_set = CONF_TX_AP_ENABLED_RATES;
wlvif->basic_rate_set = CONF_TX_ENABLED_RATES;
/*
* TODO: check if basic_rate shouldn't be
* wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
* instead (the same thing for STA above).
*/
wlvif->basic_rate = CONF_TX_AP_ENABLED_RATES;
wlvif->basic_rate = CONF_TX_ENABLED_RATES;
/* TODO: this seems to be used only for STA, check it */
wlvif->rate_set = CONF_TX_AP_ENABLED_RATES;
wlvif->rate_set = CONF_TX_ENABLED_RATES;
}
wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
......@@ -2065,6 +2142,10 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
wl1271_rx_streaming_enable_work);
INIT_WORK(&wlvif->rx_streaming_disable_work,
wl1271_rx_streaming_disable_work);
INIT_DELAYED_WORK(&wlvif->channel_switch_work,
wlcore_channel_switch_work);
INIT_DELAYED_WORK(&wlvif->connection_loss_work,
wlcore_connection_loss_work);
INIT_LIST_HEAD(&wlvif->list);
setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
......@@ -2314,7 +2395,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
wl1271_info("down");
if (wl->scan.state != WL1271_SCAN_STATE_IDLE &&
wl->scan_vif == vif) {
wl->scan_wlvif == wlvif) {
/*
* Rearm the tx watchdog just before idling scan. This
* prevents just-finished scans from triggering the watchdog
......@@ -2323,11 +2404,16 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
wl->scan.state = WL1271_SCAN_STATE_IDLE;
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
wl->scan_vif = NULL;
wl->scan_wlvif = NULL;
wl->scan.req = NULL;
ieee80211_scan_completed(wl->hw, true);
}
if (wl->sched_vif == wlvif) {
ieee80211_sched_scan_stopped(wl->hw);
wl->sched_vif = NULL;
}
if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
/* disable active roles */
ret = wl1271_ps_elp_wakeup(wl);
......@@ -2410,6 +2496,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
del_timer_sync(&wlvif->rx_streaming_timer);
cancel_work_sync(&wlvif->rx_streaming_enable_work);
cancel_work_sync(&wlvif->rx_streaming_disable_work);
cancel_delayed_work_sync(&wlvif->connection_loss_work);
mutex_lock(&wl->mutex);
}
......@@ -2468,8 +2555,7 @@ static int wl12xx_op_change_interface(struct ieee80211_hw *hw,
return ret;
}
static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool set_assoc)
static int wlcore_join(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
......@@ -2489,18 +2575,111 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
/* clear encryption type */
wlvif->encryption_type = KEY_NONE;
if (set_assoc)
set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
if (is_ibss)
ret = wl12xx_cmd_role_start_ibss(wl, wlvif);
else
else {
if (wl->quirks & WLCORE_QUIRK_START_STA_FAILS) {
/*
* TODO: this is an ugly workaround for wl12xx fw
* bug - we are not able to tx/rx after the first
* start_sta, so make dummy start+stop calls,
* and then call start_sta again.
* this should be fixed in the fw.
*/
wl12xx_cmd_role_start_sta(wl, wlvif);
wl12xx_cmd_role_stop_sta(wl, wlvif);
}
ret = wl12xx_cmd_role_start_sta(wl, wlvif);
}
return ret;
}
static int wl1271_ssid_set(struct wl12xx_vif *wlvif, struct sk_buff *skb,
int offset)
{
u8 ssid_len;
const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
skb->len - offset);
if (!ptr) {
wl1271_error("No SSID in IEs!");
return -ENOENT;
}
ssid_len = ptr[1];
if (ssid_len > IEEE80211_MAX_SSID_LEN) {
wl1271_error("SSID is too long!");
return -EINVAL;
}
wlvif->ssid_len = ssid_len;
memcpy(wlvif->ssid, ptr+2, ssid_len);
return 0;
}
static int wlcore_set_ssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct sk_buff *skb;
int ieoffset;
/* we currently only support setting the ssid from the ap probe req */
if (wlvif->bss_type != BSS_TYPE_STA_BSS)
return -EINVAL;
skb = ieee80211_ap_probereq_get(wl->hw, vif);
if (!skb)
return -EINVAL;
ieoffset = offsetof(struct ieee80211_mgmt,
u.probe_req.variable);
wl1271_ssid_set(wlvif, skb, ieoffset);
dev_kfree_skb(skb);
return 0;
}
static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct ieee80211_bss_conf *bss_conf,
u32 sta_rate_set)
{
int ieoffset;
int ret;
wlvif->aid = bss_conf->aid;
wlvif->channel_type = cfg80211_get_chandef_type(&bss_conf->chandef);
wlvif->beacon_int = bss_conf->beacon_int;
wlvif->wmm_enabled = bss_conf->qos;
set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
/*
* with wl1271, we don't need to update the
* beacon_int and dtim_period, because the firmware
* updates it by itself when the first beacon is
* received after a join.
*/
ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
if (ret < 0)
goto out;
return ret;
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
goto out;
/*
* Get a template for hardware connection maintenance
*/
dev_kfree_skb(wlvif->probereq);
wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
wlvif,
NULL);
ieoffset = offsetof(struct ieee80211_mgmt,
u.probe_req.variable);
wl1271_ssid_set(wlvif, wlvif->probereq, ieoffset);
/* enable the connection monitoring feature */
ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
if (ret < 0)
return ret;
/*
* The join command disable the keep-alive mode, shut down its process,
......@@ -2510,35 +2689,83 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
*/
ret = wl1271_acx_keep_alive_mode(wl, wlvif, true);
if (ret < 0)
goto out;
return ret;
ret = wl1271_acx_aid(wl, wlvif, wlvif->aid);
if (ret < 0)
goto out;
return ret;
ret = wl12xx_cmd_build_klv_null_data(wl, wlvif);
if (ret < 0)
goto out;
return ret;
ret = wl1271_acx_keep_alive_config(wl, wlvif,
wlvif->sta.klv_template_id,
ACX_KEEP_ALIVE_TPL_VALID);
if (ret < 0)
goto out;
return ret;
/*
* The default fw psm configuration is AUTO, while mac80211 default
* setting is off (ACTIVE), so sync the fw with the correct value.
*/
ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE);
if (ret < 0)
return ret;
if (sta_rate_set) {
wlvif->rate_set =
wl1271_tx_enabled_rates_get(wl,
sta_rate_set,
wlvif->band);
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
if (ret < 0)
return ret;
}
out:
return ret;
}
static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
bool sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
/* make sure we are connected (sta) joined */
if (sta &&
!test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
return false;
/* make sure we are joined (ibss) */
if (!sta &&
test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))
return false;
if (sta) {
/* use defaults when not associated */
wlvif->aid = 0;
/* free probe-request template */
dev_kfree_skb(wlvif->probereq);
wlvif->probereq = NULL;
/* disable connection monitor features */
ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
if (ret < 0)
return ret;
/* Disable the keep-alive feature */
ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
if (ret < 0)
return ret;
}
if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
wl12xx_cmd_stop_channel_switch(wl);
wl12xx_cmd_stop_channel_switch(wl, wlvif);
ieee80211_chswitch_done(vif, false);
cancel_delayed_work(&wlvif->channel_switch_work);
}
/* invalidate keep-alive template */
......@@ -2546,17 +2773,11 @@ static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
wlvif->sta.klv_template_id,
ACX_KEEP_ALIVE_TPL_INVALID);
/* to stop listening to a channel, we disconnect */
ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
if (ret < 0)
goto out;
/* reset TX security counters on a clean disconnect */
wlvif->tx_security_last_seq_lsb = 0;
wlvif->tx_security_seq = 0;
out:
return ret;
return 0;
}
static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
......@@ -2565,147 +2786,10 @@ static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
wlvif->rate_set = wlvif->basic_rate_set;
}
static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool idle)
{
int ret;
bool cur_idle = !test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
if (idle == cur_idle)
return 0;
if (idle) {
/* no need to croc if we weren't busy (e.g. during boot) */
if (wl12xx_dev_role_started(wlvif)) {
ret = wl12xx_stop_dev(wl, wlvif);
if (ret < 0)
goto out;
}
wlvif->rate_set =
wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
if (ret < 0)
goto out;
clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
} else {
/* The current firmware only supports sched_scan in idle */
if (wl->sched_scanning) {
wl1271_scan_sched_scan_stop(wl, wlvif);
ieee80211_sched_scan_stopped(wl->hw);
}
ret = wl12xx_start_dev(wl, wlvif);
if (ret < 0)
goto out;
set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
}
out:
return ret;
}
static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct ieee80211_conf *conf, u32 changed)
{
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
int channel, ret;
channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
/* if the channel changes while joined, join again */
if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
((wlvif->band != conf->channel->band) ||
(wlvif->channel != channel) ||
(wlvif->channel_type != conf->channel_type))) {
/* send all pending packets */
ret = wlcore_tx_work_locked(wl);
if (ret < 0)
return ret;
wlvif->band = conf->channel->band;
wlvif->channel = channel;
wlvif->channel_type = conf->channel_type;
if (is_ap) {
wl1271_set_band_rate(wl, wlvif);
ret = wl1271_init_ap_rates(wl, wlvif);
if (ret < 0)
wl1271_error("AP rate policy change failed %d",
ret);
} else {
/*
* FIXME: the mac80211 should really provide a fixed
* rate to use here. for now, just use the smallest
* possible rate for the band as a fixed rate for
* association frames and other control messages.
*/
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
wl1271_set_band_rate(wl, wlvif);
wlvif->basic_rate =
wl1271_tx_min_rate_get(wl,
wlvif->basic_rate_set);
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
if (ret < 0)
wl1271_warning("rate policy for channel "
"failed %d", ret);
/*
* change the ROC channel. do it only if we are
* not idle. otherwise, CROC will be called
* anyway.
*/
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED,
&wlvif->flags) &&
wl12xx_dev_role_started(wlvif) &&
!(conf->flags & IEEE80211_CONF_IDLE)) {
ret = wl12xx_stop_dev(wl, wlvif);
if (ret < 0)
return ret;
ret = wl12xx_start_dev(wl, wlvif);
if (ret < 0)
return ret;
}
}
}
if ((changed & IEEE80211_CONF_CHANGE_PS) && !is_ap) {
if ((conf->flags & IEEE80211_CONF_PS) &&
test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
int ps_mode;
char *ps_mode_str;
if (wl->conf.conn.forced_ps) {
ps_mode = STATION_POWER_SAVE_MODE;
ps_mode_str = "forced";
} else {
ps_mode = STATION_AUTO_PS_MODE;
ps_mode_str = "auto";
}
wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str);
ret = wl1271_ps_set_mode(wl, wlvif, ps_mode);
if (ret < 0)
wl1271_warning("enter %s ps failed %d",
ps_mode_str, ret);
} else if (!(conf->flags & IEEE80211_CONF_PS) &&
test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
wl1271_debug(DEBUG_PSM, "auto ps disabled");
ret = wl1271_ps_set_mode(wl, wlvif,
STATION_ACTIVE_MODE);
if (ret < 0)
wl1271_warning("exit auto ps failed %d", ret);
}
}
int ret;
if (conf->power_level != wlvif->power_level) {
ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level);
......@@ -2723,37 +2807,17 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif;
struct ieee80211_conf *conf = &hw->conf;
int channel, ret = 0;
channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
int ret = 0;
wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
wl1271_debug(DEBUG_MAC80211, "mac80211 config psm %s power %d %s"
" changed 0x%x",
channel,
conf->flags & IEEE80211_CONF_PS ? "on" : "off",
conf->power_level,
conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
changed);
/*
* mac80211 will go to idle nearly immediately after transmitting some
* frames, such as the deauth. To make sure those frames reach the air,
* wait here until the TX queue is fully flushed.
*/
if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) ||
((changed & IEEE80211_CONF_CHANGE_IDLE) &&
(conf->flags & IEEE80211_CONF_IDLE)))
wl1271_tx_flush(wl);
mutex_lock(&wl->mutex);
/* we support configuring the channel and band even while off */
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
wl->band = conf->channel->band;
wl->channel = channel;
wl->channel_type = conf->channel_type;
}
if (changed & IEEE80211_CONF_CHANGE_POWER)
wl->power_level = conf->power_level;
......@@ -3202,6 +3266,29 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
}
EXPORT_SYMBOL_GPL(wlcore_set_key);
void wlcore_regdomain_config(struct wl1271 *wl)
{
int ret;
if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
return;
mutex_lock(&wl->mutex);
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
ret = wlcore_cmd_regdomain_config_locked(wl);
if (ret < 0) {
wl12xx_queue_recovery_work(wl);
goto out;
}
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
}
static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req)
......@@ -3241,7 +3328,7 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
goto out_sleep;
}
ret = wl1271_scan(hw->priv, vif, ssid, len, req);
ret = wlcore_scan(hw->priv, vif, ssid, len, req);
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
......@@ -3254,6 +3341,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret;
wl1271_debug(DEBUG_MAC80211, "mac80211 cancel hw scan");
......@@ -3271,7 +3359,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
goto out;
if (wl->scan.state != WL1271_SCAN_STATE_DONE) {
ret = wl1271_scan_stop(wl);
ret = wl->ops->scan_stop(wl, wlvif);
if (ret < 0)
goto out_sleep;
}
......@@ -3284,7 +3372,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
wl->scan.state = WL1271_SCAN_STATE_IDLE;
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
wl->scan_vif = NULL;
wl->scan_wlvif = NULL;
wl->scan.req = NULL;
ieee80211_scan_completed(wl->hw, true);
......@@ -3318,15 +3406,11 @@ static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
if (ret < 0)
goto out;
ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
if (ret < 0)
goto out_sleep;
ret = wl1271_scan_sched_scan_start(wl, wlvif);
ret = wl->ops->sched_scan_start(wl, wlvif, req, ies);
if (ret < 0)
goto out_sleep;
wl->sched_scanning = true;
wl->sched_vif = wlvif;
out_sleep:
wl1271_ps_elp_sleep(wl);
......@@ -3353,7 +3437,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
if (ret < 0)
goto out;
wl1271_scan_sched_scan_stop(wl, wlvif);
wl->ops->sched_scan_stop(wl, wlvif);
wl1271_ps_elp_sleep(wl);
out:
......@@ -3418,30 +3502,6 @@ static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
return ret;
}
static int wl1271_ssid_set(struct ieee80211_vif *vif, struct sk_buff *skb,
int offset)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
u8 ssid_len;
const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
skb->len - offset);
if (!ptr) {
wl1271_error("No SSID in IEs!");
return -ENOENT;
}
ssid_len = ptr[1];
if (ssid_len > IEEE80211_MAX_SSID_LEN) {
wl1271_error("SSID is too long!");
return -EINVAL;
}
wlvif->ssid_len = ssid_len;
memcpy(wlvif->ssid, ptr+2, ssid_len);
return 0;
}
static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset)
{
int len;
......@@ -3622,7 +3682,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl,
wl1271_debug(DEBUG_MASTER, "beacon updated");
ret = wl1271_ssid_set(vif, beacon, ieoffset);
ret = wl1271_ssid_set(wlvif, beacon, ieoffset);
if (ret < 0) {
dev_kfree_skb(beacon);
goto out;
......@@ -3639,6 +3699,12 @@ static int wlcore_set_beacon_template(struct wl1271 *wl,
goto out;
}
wlvif->wmm_enabled =
cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
WLAN_OUI_TYPE_MICROSOFT_WMM,
beacon->data + ieoffset,
beacon->len - ieoffset);
/*
* In case we already have a probe-resp beacon set explicitly
* by usermode, don't use the beacon data.
......@@ -3692,7 +3758,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
int ret = 0;
if ((changed & BSS_CHANGED_BEACON_INT)) {
if (changed & BSS_CHANGED_BEACON_INT) {
wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
bss_conf->beacon_int);
......@@ -3705,7 +3771,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
wl1271_ap_set_probe_resp_tmpl(wl, rate, vif);
}
if ((changed & BSS_CHANGED_BEACON)) {
if (changed & BSS_CHANGED_BEACON) {
ret = wlcore_set_beacon_template(wl, vif, is_ap);
if (ret < 0)
goto out;
......@@ -3726,7 +3792,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret = 0;
if ((changed & BSS_CHANGED_BASIC_RATES)) {
if (changed & BSS_CHANGED_BASIC_RATES) {
u32 rates = bss_conf->basic_rates;
wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates,
......@@ -3757,7 +3823,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
if (ret < 0)
goto out;
if ((changed & BSS_CHANGED_BEACON_ENABLED)) {
if (changed & BSS_CHANGED_BEACON_ENABLED) {
if (bss_conf->enable_beacon) {
if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
ret = wl12xx_cmd_role_start_ap(wl, wlvif);
......@@ -3804,6 +3870,79 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
return;
}
static int wlcore_set_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct ieee80211_bss_conf *bss_conf,
u32 sta_rate_set)
{
u32 rates;
int ret;
wl1271_debug(DEBUG_MAC80211,
"changed_bssid: %pM, aid: %d, bcn_int: %d, brates: 0x%x sta_rate_set: 0x%x",
bss_conf->bssid, bss_conf->aid,
bss_conf->beacon_int,
bss_conf->basic_rates, sta_rate_set);
wlvif->beacon_int = bss_conf->beacon_int;
rates = bss_conf->basic_rates;
wlvif->basic_rate_set =
wl1271_tx_enabled_rates_get(wl, rates,
wlvif->band);
wlvif->basic_rate =
wl1271_tx_min_rate_get(wl,
wlvif->basic_rate_set);
if (sta_rate_set)
wlvif->rate_set =
wl1271_tx_enabled_rates_get(wl,
sta_rate_set,
wlvif->band);
/* we only support sched_scan while not connected */
if (wl->sched_vif == wlvif)
wl->ops->sched_scan_stop(wl, wlvif);
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
if (ret < 0)
return ret;
ret = wl12xx_cmd_build_null_data(wl, wlvif);
if (ret < 0)
return ret;
ret = wl1271_build_qos_null_data(wl, wl12xx_wlvif_to_vif(wlvif));
if (ret < 0)
return ret;
wlcore_set_ssid(wl, wlvif);
set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
return 0;
}
static int wlcore_clear_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
/* revert back to minimum rates for the current band */
wl1271_set_band_rate(wl, wlvif);
wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
if (ret < 0)
return ret;
if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) {
ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
if (ret < 0)
return ret;
}
clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
return 0;
}
/* STA/IBSS mode changes */
static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
struct ieee80211_vif *vif,
......@@ -3811,7 +3950,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
u32 changed)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
bool do_join = false, set_assoc = false;
bool do_join = false;
bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
bool ibss_joined = false;
u32 sta_rate_set = 0;
......@@ -3832,9 +3971,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags);
ibss_joined = true;
} else {
if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED,
&wlvif->flags))
wl1271_unjoin(wl, wlvif);
wlcore_unset_assoc(wl, wlvif);
wl12xx_cmd_role_stop_sta(wl, wlvif);
}
}
......@@ -3852,13 +3990,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
do_join = true;
}
if (changed & BSS_CHANGED_IDLE && !is_ibss) {
ret = wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle);
if (ret < 0)
wl1271_warning("idle mode change failed %d", ret);
}
if ((changed & BSS_CHANGED_CQM)) {
if (changed & BSS_CHANGED_CQM) {
bool enable = false;
if (bss_conf->cqm_rssi_thold)
enable = true;
......@@ -3870,150 +4002,39 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
}
if (changed & BSS_CHANGED_BSSID)
if (!is_zero_ether_addr(bss_conf->bssid)) {
ret = wl12xx_cmd_build_null_data(wl, wlvif);
if (ret < 0)
goto out;
ret = wl1271_build_qos_null_data(wl, vif);
if (ret < 0)
goto out;
}
if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) {
if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT |
BSS_CHANGED_ASSOC)) {
rcu_read_lock();
sta = ieee80211_find_sta(vif, bss_conf->bssid);
if (!sta)
goto sta_not_found;
/* save the supp_rates of the ap */
sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band];
if (sta->ht_cap.ht_supported)
sta_rate_set |=
(sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
(sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET);
sta_ht_cap = sta->ht_cap;
sta_exists = true;
sta_not_found:
if (sta) {
u8 *rx_mask = sta->ht_cap.mcs.rx_mask;
/* save the supp_rates of the ap */
sta_rate_set = sta->supp_rates[wlvif->band];
if (sta->ht_cap.ht_supported)
sta_rate_set |=
(rx_mask[0] << HW_HT_RATES_OFFSET) |
(rx_mask[1] << HW_MIMO_RATES_OFFSET);
sta_ht_cap = sta->ht_cap;
sta_exists = true;
}
rcu_read_unlock();
}
if ((changed & BSS_CHANGED_ASSOC)) {
if (bss_conf->assoc) {
u32 rates;
int ieoffset;
wlvif->aid = bss_conf->aid;
wlvif->channel_type =
cfg80211_get_chandef_type(&bss_conf->chandef);
wlvif->beacon_int = bss_conf->beacon_int;
do_join = true;
set_assoc = true;
/*
* use basic rates from AP, and determine lowest rate
* to use with control frames.
*/
rates = bss_conf->basic_rates;
wlvif->basic_rate_set =
wl1271_tx_enabled_rates_get(wl, rates,
wlvif->band);
wlvif->basic_rate =
wl1271_tx_min_rate_get(wl,
wlvif->basic_rate_set);
if (sta_rate_set)
wlvif->rate_set =
wl1271_tx_enabled_rates_get(wl,
sta_rate_set,
wlvif->band);
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
if (ret < 0)
goto out;
/*
* with wl1271, we don't need to update the
* beacon_int and dtim_period, because the firmware
* updates it by itself when the first beacon is
* received after a join.
*/
ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
if (changed & BSS_CHANGED_BSSID) {
if (!is_zero_ether_addr(bss_conf->bssid)) {
ret = wlcore_set_bssid(wl, wlvif, bss_conf,
sta_rate_set);
if (ret < 0)
goto out;
/*
* Get a template for hardware connection maintenance
*/
dev_kfree_skb(wlvif->probereq);
wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
wlvif,
NULL);
ieoffset = offsetof(struct ieee80211_mgmt,
u.probe_req.variable);
wl1271_ssid_set(vif, wlvif->probereq, ieoffset);
/* enable the connection monitoring feature */
ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
if (ret < 0)
goto out;
/* Need to update the BSSID (for filtering etc) */
do_join = true;
} else {
/* use defaults when not associated */
bool was_assoc =
!!test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED,
&wlvif->flags);
bool was_ifup =
!!test_and_clear_bit(WLVIF_FLAG_STA_STATE_SENT,
&wlvif->flags);
wlvif->aid = 0;
/* free probe-request template */
dev_kfree_skb(wlvif->probereq);
wlvif->probereq = NULL;
/* revert back to minimum rates for the current band */
wl1271_set_band_rate(wl, wlvif);
wlvif->basic_rate =
wl1271_tx_min_rate_get(wl,
wlvif->basic_rate_set);
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
if (ret < 0)
goto out;
/* disable connection monitor features */
ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
/* Disable the keep-alive feature */
ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
ret = wlcore_clear_bssid(wl, wlvif);
if (ret < 0)
goto out;
/* restore the bssid filter and go to dummy bssid */
if (was_assoc) {
/*
* we might have to disable roc, if there was
* no IF_OPER_UP notification.
*/
if (!was_ifup) {
ret = wl12xx_croc(wl, wlvif->role_id);
if (ret < 0)
goto out;
}
/*
* (we also need to disable roc in case of
* roaming on the same channel. until we will
* have a better flow...)
*/
if (test_bit(wlvif->dev_role_id, wl->roc_map)) {
ret = wl12xx_croc(wl,
wlvif->dev_role_id);
if (ret < 0)
goto out;
}
wl1271_unjoin(wl, wlvif);
if (!bss_conf->idle)
wl12xx_start_dev(wl, wlvif);
}
}
}
......@@ -4043,71 +4064,86 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
goto out;
if (do_join) {
ret = wl1271_join(wl, wlvif, set_assoc);
ret = wlcore_join(wl, wlvif);
if (ret < 0) {
wl1271_warning("cmd join failed %d", ret);
goto out;
}
}
/* ROC until connected (after EAPOL exchange) */
if (!is_ibss) {
ret = wl12xx_roc(wl, wlvif, wlvif->role_id);
if (changed & BSS_CHANGED_ASSOC) {
if (bss_conf->assoc) {
ret = wlcore_set_assoc(wl, wlvif, bss_conf,
sta_rate_set);
if (ret < 0)
goto out;
if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags))
wl12xx_set_authorized(wl, wlvif);
} else {
wlcore_unset_assoc(wl, wlvif);
}
/*
* stop device role if started (we might already be in
* STA/IBSS role).
*/
if (wl12xx_dev_role_started(wlvif)) {
ret = wl12xx_stop_dev(wl, wlvif);
}
if (changed & BSS_CHANGED_PS) {
if ((bss_conf->ps) &&
test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
int ps_mode;
char *ps_mode_str;
if (wl->conf.conn.forced_ps) {
ps_mode = STATION_POWER_SAVE_MODE;
ps_mode_str = "forced";
} else {
ps_mode = STATION_AUTO_PS_MODE;
ps_mode_str = "auto";
}
wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str);
ret = wl1271_ps_set_mode(wl, wlvif, ps_mode);
if (ret < 0)
goto out;
wl1271_warning("enter %s ps failed %d",
ps_mode_str, ret);
} else if (!bss_conf->ps &&
test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
wl1271_debug(DEBUG_PSM, "auto ps disabled");
ret = wl1271_ps_set_mode(wl, wlvif,
STATION_ACTIVE_MODE);
if (ret < 0)
wl1271_warning("exit auto ps failed %d", ret);
}
}
/* Handle new association with HT. Do this after join. */
if (sta_exists) {
if ((changed & BSS_CHANGED_HT) &&
(bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) {
ret = wl1271_acx_set_ht_capabilities(wl,
&sta_ht_cap,
true,
wlvif->sta.hlid);
if (ret < 0) {
wl1271_warning("Set ht cap true failed %d",
ret);
goto out;
}
if (sta_exists &&
(changed & BSS_CHANGED_HT)) {
bool enabled =
bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
ret = wl1271_acx_set_ht_capabilities(wl,
&sta_ht_cap,
enabled,
wlvif->sta.hlid);
if (ret < 0) {
wl1271_warning("Set ht cap failed %d", ret);
goto out;
}
/* handle new association without HT and disassociation */
else if (changed & BSS_CHANGED_ASSOC) {
ret = wl1271_acx_set_ht_capabilities(wl,
&sta_ht_cap,
false,
wlvif->sta.hlid);
if (enabled) {
ret = wl1271_acx_set_ht_information(wl, wlvif,
bss_conf->ht_operation_mode);
if (ret < 0) {
wl1271_warning("Set ht cap false failed %d",
wl1271_warning("Set ht information failed %d",
ret);
goto out;
}
}
}
/* Handle HT information change. Done after join. */
if ((changed & BSS_CHANGED_HT) &&
(bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) {
ret = wl1271_acx_set_ht_information(wl, wlvif,
bss_conf->ht_operation_mode);
if (ret < 0) {
wl1271_warning("Set ht information failed %d", ret);
goto out;
}
}
/* Handle arp filtering. Done after join. */
if ((changed & BSS_CHANGED_ARP_FILTER) ||
(!is_ibss && (changed & BSS_CHANGED_QOS))) {
......@@ -4157,15 +4193,15 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
int ret;
wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
(int)changed);
wl1271_debug(DEBUG_MAC80211, "mac80211 bss info role %d changed 0x%x",
wlvif->role_id, (int)changed);
/*
* make sure to cancel pending disconnections if our association
* state changed
*/
if (!is_ap && (changed & BSS_CHANGED_ASSOC))
cancel_delayed_work_sync(&wl->connection_loss_work);
cancel_delayed_work_sync(&wlvif->connection_loss_work);
if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) &&
!bss_conf->enable_beacon)
......@@ -4194,6 +4230,76 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
mutex_unlock(&wl->mutex);
}
static int wlcore_op_add_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
wl1271_debug(DEBUG_MAC80211, "mac80211 add chanctx %d (type %d)",
ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
cfg80211_get_chandef_type(&ctx->def));
return 0;
}
static void wlcore_op_remove_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
wl1271_debug(DEBUG_MAC80211, "mac80211 remove chanctx %d (type %d)",
ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
cfg80211_get_chandef_type(&ctx->def));
}
static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx,
u32 changed)
{
wl1271_debug(DEBUG_MAC80211,
"mac80211 change chanctx %d (type %d) changed 0x%x",
ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
cfg80211_get_chandef_type(&ctx->def), changed);
}
static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_chanctx_conf *ctx)
{
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int channel = ieee80211_frequency_to_channel(
ctx->def.chan->center_freq);
wl1271_debug(DEBUG_MAC80211,
"mac80211 assign chanctx (role %d) %d (type %d)",
wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def));
mutex_lock(&wl->mutex);
wlvif->band = ctx->def.chan->band;
wlvif->channel = channel;
wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def);
/* update default rates according to the band */
wl1271_set_band_rate(wl, wlvif);
mutex_unlock(&wl->mutex);
return 0;
}
static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_chanctx_conf *ctx)
{
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
wl1271_debug(DEBUG_MAC80211,
"mac80211 unassign chanctx (role %d) %d (type %d)",
wlvif->role_id,
ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
cfg80211_get_chandef_type(&ctx->def));
wl1271_tx_flush(wl);
}
static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
......@@ -4382,6 +4488,45 @@ static int wl12xx_sta_remove(struct wl1271 *wl,
return ret;
}
static void wlcore_roc_if_possible(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
if (find_first_bit(wl->roc_map,
WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)
return;
if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID))
return;
wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel);
}
static void wlcore_update_inconn_sta(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct wl1271_station *wl_sta,
bool in_connection)
{
if (in_connection) {
if (WARN_ON(wl_sta->in_connection))
return;
wl_sta->in_connection = true;
if (!wlvif->inconn_count++)
wlcore_roc_if_possible(wl, wlvif);
} else {
if (!wl_sta->in_connection)
return;
wl_sta->in_connection = false;
wlvif->inconn_count--;
if (WARN_ON(wlvif->inconn_count < 0))
return;
if (!wlvif->inconn_count)
if (test_bit(wlvif->role_id, wl->roc_map))
wl12xx_croc(wl, wlvif->role_id);
}
}
static int wl12xx_update_sta_state(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct ieee80211_sta *sta,
......@@ -4400,8 +4545,13 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
/* Add station (AP mode) */
if (is_ap &&
old_state == IEEE80211_STA_NOTEXIST &&
new_state == IEEE80211_STA_NONE)
return wl12xx_sta_add(wl, wlvif, sta);
new_state == IEEE80211_STA_NONE) {
ret = wl12xx_sta_add(wl, wlvif, sta);
if (ret)
return ret;
wlcore_update_inconn_sta(wl, wlvif, wl_sta, true);
}
/* Remove station (AP mode) */
if (is_ap &&
......@@ -4409,35 +4559,59 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
new_state == IEEE80211_STA_NOTEXIST) {
/* must not fail */
wl12xx_sta_remove(wl, wlvif, sta);
return 0;
wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
}
/* Authorize station (AP mode) */
if (is_ap &&
new_state == IEEE80211_STA_AUTHORIZED) {
ret = wl12xx_cmd_set_peer_state(wl, hlid);
ret = wl12xx_cmd_set_peer_state(wl, wlvif, hlid);
if (ret < 0)
return ret;
ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true,
hlid);
return ret;
if (ret)
return ret;
wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
}
/* Authorize station */
if (is_sta &&
new_state == IEEE80211_STA_AUTHORIZED) {
set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
return wl12xx_set_authorized(wl, wlvif);
ret = wl12xx_set_authorized(wl, wlvif);
if (ret)
return ret;
}
if (is_sta &&
old_state == IEEE80211_STA_AUTHORIZED &&
new_state == IEEE80211_STA_ASSOC) {
clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
return 0;
clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags);
}
/* clear ROCs on failure or authorization */
if (is_sta &&
(new_state == IEEE80211_STA_AUTHORIZED ||
new_state == IEEE80211_STA_NOTEXIST)) {
if (test_bit(wlvif->role_id, wl->roc_map))
wl12xx_croc(wl, wlvif->role_id);
}
if (is_sta &&
old_state == IEEE80211_STA_NOTEXIST &&
new_state == IEEE80211_STA_NONE) {
if (find_first_bit(wl->roc_map,
WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES) {
WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID);
wl12xx_roc(wl, wlvif, wlvif->role_id,
wlvif->band, wlvif->channel);
}
}
return 0;
}
......@@ -4665,12 +4839,23 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
/* TODO: change mac80211 to pass vif as param */
wl12xx_for_each_wlvif_sta(wl, wlvif) {
ret = wl12xx_cmd_channel_switch(wl, wlvif, ch_switch);
unsigned long delay_usec;
ret = wl->ops->channel_switch(wl, wlvif, ch_switch);
if (ret)
goto out_sleep;
if (!ret)
set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
/* indicate failure 5 seconds after channel switch time */
delay_usec = ieee80211_tu_to_usec(wlvif->beacon_int) *
ch_switch->count;
ieee80211_queue_delayed_work(hw, &wlvif->channel_switch_work,
usecs_to_jiffies(delay_usec) +
msecs_to_jiffies(5000));
}
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
......@@ -4684,6 +4869,144 @@ static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop)
wl1271_tx_flush(wl);
}
static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel *chan,
int duration)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct wl1271 *wl = hw->priv;
int channel, ret = 0;
channel = ieee80211_frequency_to_channel(chan->center_freq);
wl1271_debug(DEBUG_MAC80211, "mac80211 roc %d (%d)",
channel, wlvif->role_id);
mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON))
goto out;
/* return EBUSY if we can't ROC right now */
if (WARN_ON(wl->roc_vif ||
find_first_bit(wl->roc_map,
WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)) {
ret = -EBUSY;
goto out;
}
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
ret = wl12xx_start_dev(wl, wlvif, chan->band, channel);
if (ret < 0)
goto out_sleep;
wl->roc_vif = vif;
ieee80211_queue_delayed_work(hw, &wl->roc_complete_work,
msecs_to_jiffies(duration));
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return ret;
}
static int __wlcore_roc_completed(struct wl1271 *wl)
{
struct wl12xx_vif *wlvif;
int ret;
/* already completed */
if (unlikely(!wl->roc_vif))
return 0;
wlvif = wl12xx_vif_to_data(wl->roc_vif);
if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
return -EBUSY;
ret = wl12xx_stop_dev(wl, wlvif);
if (ret < 0)
return ret;
wl->roc_vif = NULL;
return 0;
}
static int wlcore_roc_completed(struct wl1271 *wl)
{
int ret;
wl1271_debug(DEBUG_MAC80211, "roc complete");
mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON)) {
ret = -EBUSY;
goto out;
}
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
ret = __wlcore_roc_completed(wl);
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return ret;
}
static void wlcore_roc_complete_work(struct work_struct *work)
{
struct delayed_work *dwork;
struct wl1271 *wl;
int ret;
dwork = container_of(work, struct delayed_work, work);
wl = container_of(dwork, struct wl1271, roc_complete_work);
ret = wlcore_roc_completed(wl);
if (!ret)
ieee80211_remain_on_channel_expired(wl->hw);
}
static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
{
struct wl1271 *wl = hw->priv;
wl1271_debug(DEBUG_MAC80211, "mac80211 croc");
/* TODO: per-vif */
wl1271_tx_flush(wl);
/*
* we can't just flush_work here, because it might deadlock
* (as we might get called from the same workqueue)
*/
cancel_delayed_work_sync(&wl->roc_complete_work);
wlcore_roc_completed(wl);
return 0;
}
static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
u32 changed)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct wl1271 *wl = hw->priv;
wlcore_hw_sta_rc_update(wl, wlvif, sta, changed);
}
static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
{
struct wl1271 *wl = hw->priv;
......@@ -4875,6 +5198,14 @@ static const struct ieee80211_ops wl1271_ops = {
.set_bitrate_mask = wl12xx_set_bitrate_mask,
.channel_switch = wl12xx_op_channel_switch,
.flush = wlcore_op_flush,
.remain_on_channel = wlcore_op_remain_on_channel,
.cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel,
.add_chanctx = wlcore_op_add_chanctx,
.remove_chanctx = wlcore_op_remove_chanctx,
.change_chanctx = wlcore_op_change_chanctx,
.assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
.unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
.sta_rc_update = wlcore_op_sta_rc_update,
CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
};
......@@ -5044,34 +5375,6 @@ static struct bin_attribute fwlog_attr = {
.read = wl1271_sysfs_read_fwlog,
};
static void wl1271_connection_loss_work(struct work_struct *work)
{
struct delayed_work *dwork;
struct wl1271 *wl;
struct ieee80211_vif *vif;
struct wl12xx_vif *wlvif;
dwork = container_of(work, struct delayed_work, work);
wl = container_of(dwork, struct wl1271, connection_loss_work);
wl1271_info("Connection loss work.");
mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON))
goto out;
/* Call mac80211 connection loss */
wl12xx_for_each_wlvif_sta(wl, wlvif) {
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
goto out;
vif = wl12xx_wlvif_to_vif(wlvif);
ieee80211_connection_loss(vif);
}
out:
mutex_unlock(&wl->mutex);
}
static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic)
{
int i;
......@@ -5117,7 +5420,7 @@ static int wl12xx_get_hw_info(struct wl1271 *wl)
ret = wl12xx_set_power_on(wl);
if (ret < 0)
goto out;
return ret;
ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &wl->chip.id);
if (ret < 0)
......@@ -5207,10 +5510,9 @@ static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
},
};
static const struct ieee80211_iface_combination
static struct ieee80211_iface_combination
wlcore_iface_combinations[] = {
{
.num_different_channels = 1,
.max_interfaces = 3,
.limits = wlcore_iface_limits,
.n_limits = ARRAY_SIZE(wlcore_iface_limits),
......@@ -5271,6 +5573,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
sizeof(struct ieee80211_header);
wl->hw->wiphy->max_remain_on_channel_duration = 5000;
wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
......@@ -5311,6 +5615,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
/* allowed interface combinations */
wlcore_iface_combinations[0].num_different_channels = wl->num_channels;
wl->hw->wiphy->iface_combinations = wlcore_iface_combinations;
wl->hw->wiphy->n_iface_combinations =
ARRAY_SIZE(wlcore_iface_combinations);
......@@ -5327,7 +5632,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
#define WL1271_DEFAULT_CHANNEL 0
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)
{
struct ieee80211_hw *hw;
struct wl1271 *wl;
......@@ -5369,9 +5675,8 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
INIT_WORK(&wl->tx_work, wl1271_tx_work);
INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
INIT_DELAYED_WORK(&wl->roc_complete_work, wlcore_roc_complete_work);
INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work);
INIT_DELAYED_WORK(&wl->connection_loss_work,
wl1271_connection_loss_work);
wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
if (!wl->freezable_wq) {
......@@ -5387,12 +5692,12 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
wl->flags = 0;
wl->sg_enabled = true;
wl->sleep_auth = WL1271_PSM_ILLEGAL;
wl->recovery_count = 0;
wl->hw_pg_ver = -1;
wl->ap_ps_map = 0;
wl->ap_fw_ps_map = 0;
wl->quirks = 0;
wl->platform_quirks = 0;
wl->sched_scanning = false;
wl->system_hlid = WL12XX_SYSTEM_HLID;
wl->active_sta_count = 0;
wl->fwlog_size = 0;
......@@ -5434,7 +5739,8 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
goto err_dummy_packet;
}
wl->mbox = kmalloc(sizeof(*wl->mbox), GFP_KERNEL | GFP_DMA);
wl->mbox_size = mbox_size;
wl->mbox = kmalloc(wl->mbox_size, GFP_KERNEL | GFP_DMA);
if (!wl->mbox) {
ret = -ENOMEM;
goto err_fwlog;
......@@ -5480,6 +5786,7 @@ int wlcore_free_hw(struct wl1271 *wl)
device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
device_remove_file(wl->dev, &dev_attr_bt_coex_state);
kfree(wl->mbox);
free_page((unsigned long)wl->fwlog);
dev_kfree_skb(wl->dummy_packet);
free_pages((unsigned long)wl->aggr_buf, get_order(wl->aggr_buf_size));
......@@ -5712,10 +6019,10 @@ module_param_named(fwlog, fwlog_param, charp, 0);
MODULE_PARM_DESC(fwlog,
"FW logger options: continuous, ondemand, dbgpins or disable");
module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR);
module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
module_param(no_recovery, bool, S_IRUSR | S_IWUSR);
module_param(no_recovery, int, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck.");
MODULE_LICENSE("GPL");
......
......@@ -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;
}
}
......
......@@ -97,6 +97,10 @@ static void wl1271_rx_status(struct wl1271 *wl,
wl1271_warning("Michael MIC error");
}
}
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,
......
......@@ -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,75 @@ 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 int
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_scan_settings *c = &wl->conf.scan;
int i, j;
u32 flags;
bool force_passive = !n_ssids;
u32 min_dwell_time_active, max_dwell_time_active;
u32 dwell_time_passive, dwell_time_dfs;
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;
}
#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)
{
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;
/* 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;
/* configure dwell times according to scan type */
if (scan_type == SCAN_TYPE_SEARCH) {
struct conf_scan_settings *c = &wl->conf.scan;
if (ssid_len && ssid) {
wl->scan.ssid_len = ssid_len;
memcpy(wl->scan.ssid, ssid, ssid_len);
min_dwell_time_active = c->min_dwell_time_active;
max_dwell_time_active = c->max_dwell_time_active;
dwell_time_passive = c->dwell_time_passive;
dwell_time_dfs = c->dwell_time_dfs;
} 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);
struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
u32 delta_per_probe;
return 0;
}
int wl1271_scan_stop(struct wl1271 *wl)
{
struct wl1271_cmd_header *cmd = NULL;
int ret = 0;
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;
if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE))
return -EINVAL;
min_dwell_time_active = c->base_dwell_time +
n_ssids * c->num_probe_reqs * delta_per_probe;
wl1271_debug(DEBUG_CMD, "cmd scan stop");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
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;
}
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;
}
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)
{
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;
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;
min_dwell_time_active = c->base_dwell_time +
req->n_ssids * c->num_probe_reqs * delta_per_probe;
max_dwell_time_active = min_dwell_time_active + c->max_dwell_time_delta;
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 +177,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 +205,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 +290,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,128 +434,7 @@ wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
return ret;
return type;
}
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;
}
EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_ssid_list);
void wl1271_scan_sched_scan_results(struct wl1271 *wl)
{
......@@ -791,31 +442,3 @@ void wl1271_scan_sched_scan_results(struct wl1271 *wl)
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);
}
......@@ -26,21 +26,19 @@
#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);
#define WL1271_SCAN_MAX_CHANNELS 24
......@@ -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 wl1271_cmd_sched_scan_stop {
struct wl1271_cmd_header header;
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];
};
u8 tag;
u8 role_id;
u8 padding[2];
} __packed;
enum {
SCAN_TYPE_SEARCH = 0,
SCAN_TYPE_PERIODIC = 1,
SCAN_TYPE_TRACKING = 2,
};
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 __devexit 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 __devexit 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;
......
......@@ -155,7 +155,7 @@ 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;
struct ieee80211_tx_info *control;
if (!wlvif || wl12xx_is_dummy_packet(wl, skb))
return wl->system_hlid;
......@@ -163,13 +163,13 @@ u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
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,
......@@ -294,7 +294,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ;
} else if (wlvif) {
/* configure the tx attributes */
tx_attr = wlvif->session_counter <<
tx_attr = wl->session_ids[hlid] <<
TX_HW_ATTR_OFST_SESSION_COUNTER;
}
......@@ -1135,6 +1135,7 @@ 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)
......
......@@ -51,6 +51,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 +85,24 @@ 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);
};
enum wlcore_partitions {
......@@ -202,6 +217,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;
......@@ -269,24 +286,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;
......@@ -314,6 +337,8 @@ struct wl1271 {
bool enable_11a;
int recovery_count;
/* Most recently reported noise in dBm */
s8 noise;
......@@ -367,6 +392,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 +439,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 __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
int __devexit 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 +469,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 +500,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)
......@@ -477,11 +530,9 @@ 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)
/* 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];
......@@ -315,6 +304,7 @@ struct wl12xx_rx_filter {
struct wl1271_station {
u8 hlid;
bool in_connection;
};
struct wl12xx_vif {
......@@ -341,6 +331,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;
......@@ -396,9 +388,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 +405,19 @@ 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 struct must be last!
* data that has to be saved acrossed reconfigs (e.g. recovery)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment