2012-01-31 09:57:21 +00:00
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
/*
|
|
|
|
* This file is part of wl1271
|
|
|
|
*
|
2010-02-18 11:25:51 +00:00
|
|
|
* Copyright (C) 2008-2010 Nokia Corporation
|
2009-08-06 13:25:28 +00:00
|
|
|
*
|
|
|
|
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
|
|
|
|
*
|
|
|
|
* 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/module.h>
|
|
|
|
#include <linux/firmware.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/spi/spi.h>
|
|
|
|
#include <linux/crc32.h>
|
|
|
|
#include <linux/etherdevice.h>
|
2009-10-08 18:56:32 +00:00
|
|
|
#include <linux/vmalloc.h>
|
2010-03-18 10:26:31 +00:00
|
|
|
#include <linux/platform_device.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
|
|
|
#include <linux/slab.h>
|
2011-03-31 08:07:01 +00:00
|
|
|
#include <linux/wl12xx.h>
|
2011-06-06 11:57:06 +00:00
|
|
|
#include <linux/sched.h>
|
2011-10-06 07:07:44 +00:00
|
|
|
#include <linux/interrupt.h>
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-11-21 17:25:24 +00:00
|
|
|
#include "wlcore.h"
|
2011-10-07 08:02:42 +00:00
|
|
|
#include "debug.h"
|
2009-08-06 13:25:28 +00:00
|
|
|
#include "wl12xx_80211.h"
|
2010-11-08 11:20:10 +00:00
|
|
|
#include "io.h"
|
|
|
|
#include "event.h"
|
|
|
|
#include "tx.h"
|
|
|
|
#include "rx.h"
|
|
|
|
#include "ps.h"
|
|
|
|
#include "init.h"
|
|
|
|
#include "debugfs.h"
|
|
|
|
#include "cmd.h"
|
|
|
|
#include "boot.h"
|
|
|
|
#include "testmode.h"
|
|
|
|
#include "scan.h"
|
2011-12-12 09:32:37 +00:00
|
|
|
#include "hw_ops.h"
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2009-12-11 13:41:01 +00:00
|
|
|
#define WL1271_BOOT_RETRIES 3
|
|
|
|
|
2011-12-05 14:12:54 +00:00
|
|
|
#define WL1271_BOOT_RETRIES 3
|
2009-10-13 09:47:44 +00:00
|
|
|
|
2011-06-06 11:57:06 +00:00
|
|
|
static char *fwlog_param;
|
2011-08-25 15:10:59 +00:00
|
|
|
static bool bug_on_recovery;
|
2011-12-08 11:06:45 +00:00
|
|
|
static bool no_recovery;
|
2011-06-06 11:57:06 +00:00
|
|
|
|
2011-04-18 11:15:28 +00:00
|
|
|
static void __wl1271_op_remove_interface(struct wl1271 *wl,
|
2011-10-05 09:55:45 +00:00
|
|
|
struct ieee80211_vif *vif,
|
2011-04-18 11:15:28 +00:00
|
|
|
bool reset_tx_queues);
|
2011-10-10 08:13:14 +00:00
|
|
|
static void wl1271_op_stop(struct ieee80211_hw *hw);
|
2011-10-05 09:56:06 +00:00
|
|
|
static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
2010-09-21 04:23:31 +00:00
|
|
|
|
2012-03-04 08:55:48 +00:00
|
|
|
static int wl12xx_set_authorized(struct wl1271 *wl,
|
|
|
|
struct wl12xx_vif *wlvif)
|
2011-06-06 10:03:12 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2011-10-05 09:55:51 +00:00
|
|
|
|
2012-03-04 08:55:48 +00:00
|
|
|
if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
|
2011-06-06 10:03:12 +00:00
|
|
|
return 0;
|
|
|
|
|
2011-10-10 08:13:04 +00:00
|
|
|
if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags))
|
2011-06-06 10:03:12 +00:00
|
|
|
return 0;
|
|
|
|
|
2011-10-05 09:55:53 +00:00
|
|
|
ret = wl12xx_cmd_set_peer_state(wl, wlvif->sta.hlid);
|
2011-06-06 10:03:12 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2011-10-05 09:55:51 +00:00
|
|
|
wl12xx_croc(wl, wlvif->role_id);
|
wl12xx: replace dummy_join with ROC/CROC commands
The ROC command asks the fw stay on the channel of the given
hlid. it currently has 2 primary functions:
1. Allow tx/rx from the device role.
In order to tx/rx packets while the stations is not associated
(e.g. auth req/resp), the device role has to be used, along
with ROC on its link.
Keep the logic similiar to the one used in dummy_join. However,
since we can't scan while we ROC, we add CROC before starting
a scan, and ROC again (if needed) on scan complete.
2. Keeping the antenna for a specific link.
We ROC until the connection was completed (after EAPOLs exchange)
in order to prevent BT coex operations from taking the antenna
and failing the connection (after this stage, psm can be used).
During association, we ROC on the station role, and then CROC
the device role, thus assuring being ROC during all the connection
process.
Delete the WL1271_FLAG_JOINED flag, and use a roc bitmap
to indicate what roles are currently ROCed.
Add wl12xx_roc/croc functions in order to wrap the roc/croc
commands while taking care of the roc bitmap.
The current ROC/CROC state-machine is a bit complicated. In
the future we'll probably want to use wpa_supplicant to control
the ROC during connection.
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
2011-08-14 10:17:17 +00:00
|
|
|
|
2011-06-06 10:03:12 +00:00
|
|
|
wl1271_info("Association completed.");
|
|
|
|
return 0;
|
|
|
|
}
|
2010-07-27 00:30:09 +00:00
|
|
|
|
2010-11-10 10:27:19 +00:00
|
|
|
static int wl1271_reg_notify(struct wiphy *wiphy,
|
2010-11-26 11:44:59 +00:00
|
|
|
struct regulatory_request *request)
|
|
|
|
{
|
2010-11-10 10:27:19 +00:00
|
|
|
struct ieee80211_supported_band *band;
|
|
|
|
struct ieee80211_channel *ch;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
band = wiphy->bands[IEEE80211_BAND_5GHZ];
|
|
|
|
for (i = 0; i < band->n_channels; i++) {
|
|
|
|
ch = &band->channels[i];
|
|
|
|
if (ch->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (ch->flags & IEEE80211_CHAN_RADAR)
|
|
|
|
ch->flags |= IEEE80211_CHAN_NO_IBSS |
|
|
|
|
IEEE80211_CHAN_PASSIVE_SCAN;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-10 08:12:59 +00:00
|
|
|
static int wl1271_set_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
|
|
bool enable)
|
2011-05-15 08:10:29 +00:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* we should hold wl->mutex */
|
2011-10-10 08:12:59 +00:00
|
|
|
ret = wl1271_acx_ps_rx_streaming(wl, wlvif, enable);
|
2011-05-15 08:10:29 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (enable)
|
2011-10-10 08:13:05 +00:00
|
|
|
set_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags);
|
2011-05-15 08:10:29 +00:00
|
|
|
else
|
2011-10-10 08:13:05 +00:00
|
|
|
clear_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags);
|
2011-05-15 08:10:29 +00:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* this function is being called when the rx_streaming interval
|
|
|
|
* has beed changed or rx_streaming should be disabled
|
|
|
|
*/
|
2011-10-10 08:12:59 +00:00
|
|
|
int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
2011-05-15 08:10:29 +00:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
int period = wl->conf.rx_streaming.interval;
|
|
|
|
|
|
|
|
/* don't reconfigure if rx_streaming is disabled */
|
2011-10-10 08:13:05 +00:00
|
|
|
if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
|
2011-05-15 08:10:29 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* reconfigure/disable according to new streaming_period */
|
|
|
|
if (period &&
|
2011-10-10 08:13:00 +00:00
|
|
|
test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
|
2011-05-15 08:10:29 +00:00
|
|
|
(wl->conf.rx_streaming.always ||
|
|
|
|
test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
|
2011-10-10 08:12:59 +00:00
|
|
|
ret = wl1271_set_rx_streaming(wl, wlvif, true);
|
2011-05-15 08:10:29 +00:00
|
|
|
else {
|
2011-10-10 08:12:59 +00:00
|
|
|
ret = wl1271_set_rx_streaming(wl, wlvif, false);
|
2011-05-15 08:10:29 +00:00
|
|
|
/* don't cancel_work_sync since we might deadlock */
|
2011-10-10 08:12:59 +00:00
|
|
|
del_timer_sync(&wlvif->rx_streaming_timer);
|
2011-05-15 08:10:29 +00:00
|
|
|
}
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wl1271_rx_streaming_enable_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
int ret;
|
2011-10-10 08:12:59 +00:00
|
|
|
struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif,
|
|
|
|
rx_streaming_enable_work);
|
|
|
|
struct wl1271 *wl = wlvif->wl;
|
2011-05-15 08:10:29 +00:00
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
2011-10-10 08:13:05 +00:00
|
|
|
if (test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags) ||
|
2011-10-10 08:13:00 +00:00
|
|
|
!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
|
2011-05-15 08:10:29 +00:00
|
|
|
(!wl->conf.rx_streaming.always &&
|
|
|
|
!test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (!wl->conf.rx_streaming.interval)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-10-10 08:12:59 +00:00
|
|
|
ret = wl1271_set_rx_streaming(wl, wlvif, true);
|
2011-05-15 08:10:29 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out_sleep;
|
|
|
|
|
|
|
|
/* stop it after some time of inactivity */
|
2011-10-10 08:12:59 +00:00
|
|
|
mod_timer(&wlvif->rx_streaming_timer,
|
2011-05-15 08:10:29 +00:00
|
|
|
jiffies + msecs_to_jiffies(wl->conf.rx_streaming.duration));
|
|
|
|
|
|
|
|
out_sleep:
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wl1271_rx_streaming_disable_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
int ret;
|
2011-10-10 08:12:59 +00:00
|
|
|
struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif,
|
|
|
|
rx_streaming_disable_work);
|
|
|
|
struct wl1271 *wl = wlvif->wl;
|
2011-05-15 08:10:29 +00:00
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
2011-10-10 08:13:05 +00:00
|
|
|
if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
|
2011-05-15 08:10:29 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-10-10 08:12:59 +00:00
|
|
|
ret = wl1271_set_rx_streaming(wl, wlvif, false);
|
2011-05-15 08:10:29 +00:00
|
|
|
if (ret)
|
|
|
|
goto out_sleep;
|
|
|
|
|
|
|
|
out_sleep:
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wl1271_rx_streaming_timer(unsigned long data)
|
|
|
|
{
|
2011-10-10 08:12:59 +00:00
|
|
|
struct wl12xx_vif *wlvif = (struct wl12xx_vif *)data;
|
|
|
|
struct wl1271 *wl = wlvif->wl;
|
|
|
|
ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_disable_work);
|
2011-05-15 08:10:29 +00:00
|
|
|
}
|
|
|
|
|
2012-03-03 20:18:00 +00:00
|
|
|
/* wl->mutex must be taken */
|
|
|
|
void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl)
|
|
|
|
{
|
|
|
|
/* if the watchdog is not armed, don't do anything */
|
|
|
|
if (wl->tx_allocated_blocks == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
cancel_delayed_work(&wl->tx_watchdog_work);
|
|
|
|
ieee80211_queue_delayed_work(wl->hw, &wl->tx_watchdog_work,
|
|
|
|
msecs_to_jiffies(wl->conf.tx.tx_watchdog_timeout));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wl12xx_tx_watchdog_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct delayed_work *dwork;
|
|
|
|
struct wl1271 *wl;
|
|
|
|
|
|
|
|
dwork = container_of(work, struct delayed_work, work);
|
|
|
|
wl = container_of(dwork, struct wl1271, tx_watchdog_work);
|
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Tx went out in the meantime - everything is ok */
|
|
|
|
if (unlikely(wl->tx_allocated_blocks == 0))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if a ROC is in progress, we might not have any Tx for a long
|
|
|
|
* time (e.g. pending Tx on the non-ROC channels)
|
|
|
|
*/
|
|
|
|
if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) {
|
|
|
|
wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to ROC",
|
|
|
|
wl->conf.tx.tx_watchdog_timeout);
|
|
|
|
wl12xx_rearm_tx_watchdog_locked(wl);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if a scan is in progress, we might not have any Tx for a long
|
|
|
|
* time
|
|
|
|
*/
|
|
|
|
if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
|
|
|
|
wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to scan",
|
|
|
|
wl->conf.tx.tx_watchdog_timeout);
|
|
|
|
wl12xx_rearm_tx_watchdog_locked(wl);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* AP might cache a frame for a long time for a sleeping station,
|
|
|
|
* so rearm the timer if there's an AP interface with stations. If
|
|
|
|
* Tx is genuinely stuck we will most hopefully discover it when all
|
|
|
|
* stations are removed due to inactivity.
|
|
|
|
*/
|
|
|
|
if (wl->active_sta_count) {
|
|
|
|
wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms. AP has "
|
|
|
|
" %d stations",
|
|
|
|
wl->conf.tx.tx_watchdog_timeout,
|
|
|
|
wl->active_sta_count);
|
|
|
|
wl12xx_rearm_tx_watchdog_locked(wl);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
wl1271_error("Tx stuck (in FW) for %d ms. Starting recovery",
|
|
|
|
wl->conf.tx.tx_watchdog_timeout);
|
|
|
|
wl12xx_queue_recovery_work(wl);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
}
|
|
|
|
|
2011-12-05 14:12:54 +00:00
|
|
|
static void wlcore_adjust_conf(struct wl1271 *wl)
|
2009-10-13 09:47:44 +00:00
|
|
|
{
|
2011-06-06 11:57:06 +00:00
|
|
|
/* Adjust settings according to optional module parameters */
|
|
|
|
if (fwlog_param) {
|
|
|
|
if (!strcmp(fwlog_param, "continuous")) {
|
|
|
|
wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
|
|
|
|
} else if (!strcmp(fwlog_param, "ondemand")) {
|
|
|
|
wl->conf.fwlog.mode = WL12XX_FWLOG_ON_DEMAND;
|
|
|
|
} else if (!strcmp(fwlog_param, "dbgpins")) {
|
|
|
|
wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
|
|
|
|
wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_DBG_PINS;
|
|
|
|
} else if (!strcmp(fwlog_param, "disable")) {
|
|
|
|
wl->conf.fwlog.mem_blocks = 0;
|
|
|
|
wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_NONE;
|
|
|
|
} else {
|
|
|
|
wl1271_error("Unknown fwlog parameter %s", fwlog_param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-10-13 09:47:39 +00:00
|
|
|
|
2011-10-10 08:13:13 +00:00
|
|
|
static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
|
|
|
|
struct wl12xx_vif *wlvif,
|
|
|
|
u8 hlid, u8 tx_pkts)
|
2011-02-22 22:22:31 +00:00
|
|
|
{
|
2011-08-25 09:43:15 +00:00
|
|
|
bool fw_ps, single_sta;
|
2011-02-22 22:22:31 +00:00
|
|
|
|
|
|
|
fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
|
2011-08-25 09:43:15 +00:00
|
|
|
single_sta = (wl->active_sta_count == 1);
|
2011-02-22 22:22:31 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Wake up from high level PS if the STA is asleep with too little
|
2011-08-14 10:17:35 +00:00
|
|
|
* packets in FW or if the STA is awake.
|
2011-02-22 22:22:31 +00:00
|
|
|
*/
|
2011-08-14 10:17:35 +00:00
|
|
|
if (!fw_ps || tx_pkts < WL1271_PS_STA_MAX_PACKETS)
|
2011-10-10 08:13:13 +00:00
|
|
|
wl12xx_ps_link_end(wl, wlvif, hlid);
|
2011-02-22 22:22:31 +00:00
|
|
|
|
2011-08-25 09:43:15 +00:00
|
|
|
/*
|
|
|
|
* Start high-level PS if the STA is asleep with enough blocks in FW.
|
|
|
|
* Make an exception if this is the only connected station. In this
|
|
|
|
* case FW-memory congestion is not a problem.
|
|
|
|
*/
|
|
|
|
else if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
|
2011-10-10 08:13:13 +00:00
|
|
|
wl12xx_ps_link_start(wl, wlvif, hlid, true);
|
2011-02-22 22:22:31 +00:00
|
|
|
}
|
|
|
|
|
2011-08-14 10:17:35 +00:00
|
|
|
static void wl12xx_irq_update_links_status(struct wl1271 *wl,
|
2011-10-05 09:56:05 +00:00
|
|
|
struct wl12xx_vif *wlvif,
|
2012-05-10 09:13:54 +00:00
|
|
|
struct wl_fw_status_2 *status)
|
2011-02-22 22:22:31 +00:00
|
|
|
{
|
2011-10-05 09:56:05 +00:00
|
|
|
struct wl1271_link *lnk;
|
2011-02-22 22:22:31 +00:00
|
|
|
u32 cur_fw_ps_map;
|
2011-08-14 10:17:35 +00:00
|
|
|
u8 hlid, cnt;
|
|
|
|
|
|
|
|
/* TODO: also use link_fast_bitmap here */
|
2011-02-22 22:22:31 +00:00
|
|
|
|
|
|
|
cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
|
|
|
|
if (wl->ap_fw_ps_map != cur_fw_ps_map) {
|
|
|
|
wl1271_debug(DEBUG_PSM,
|
|
|
|
"link ps prev 0x%x cur 0x%x changed 0x%x",
|
|
|
|
wl->ap_fw_ps_map, cur_fw_ps_map,
|
|
|
|
wl->ap_fw_ps_map ^ cur_fw_ps_map);
|
|
|
|
|
|
|
|
wl->ap_fw_ps_map = cur_fw_ps_map;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:56:05 +00:00
|
|
|
for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) {
|
|
|
|
lnk = &wl->links[hlid];
|
2011-12-12 10:08:25 +00:00
|
|
|
cnt = status->counters.tx_lnk_free_pkts[hlid] -
|
|
|
|
lnk->prev_freed_pkts;
|
2011-02-22 22:22:31 +00:00
|
|
|
|
2011-12-12 10:08:25 +00:00
|
|
|
lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[hlid];
|
2011-10-05 09:56:05 +00:00
|
|
|
lnk->allocated_pkts -= cnt;
|
2011-08-14 10:17:35 +00:00
|
|
|
|
2011-10-10 08:13:13 +00:00
|
|
|
wl12xx_irq_ps_regulate_link(wl, wlvif, hlid,
|
|
|
|
lnk->allocated_pkts);
|
2011-02-22 22:22:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-17 18:59:42 +00:00
|
|
|
static int wlcore_fw_status(struct wl1271 *wl,
|
|
|
|
struct wl_fw_status_1 *status_1,
|
|
|
|
struct wl_fw_status_2 *status_2)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
2011-10-10 08:13:13 +00:00
|
|
|
struct wl12xx_vif *wlvif;
|
2010-02-22 06:38:38 +00:00
|
|
|
struct timespec ts;
|
2011-03-06 14:32:12 +00:00
|
|
|
u32 old_tx_blk_count = wl->tx_blocks_available;
|
2011-08-14 10:17:05 +00:00
|
|
|
int avail, freed_blocks;
|
2011-08-14 10:17:32 +00:00
|
|
|
int i;
|
2011-12-12 10:08:25 +00:00
|
|
|
size_t status_len;
|
2012-06-17 18:59:42 +00:00
|
|
|
int ret;
|
2011-12-12 10:08:25 +00:00
|
|
|
|
2012-05-10 09:13:54 +00:00
|
|
|
status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
|
|
|
|
sizeof(*status_2) + wl->fw_status_priv_len;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2012-06-17 18:59:42 +00:00
|
|
|
ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1,
|
|
|
|
status_len, false);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2011-03-06 14:32:12 +00:00
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
|
|
|
|
"drv_rx_counter = %d, tx_results_counter = %d)",
|
2012-05-10 09:13:54 +00:00
|
|
|
status_1->intr,
|
|
|
|
status_1->fw_rx_counter,
|
|
|
|
status_1->drv_rx_counter,
|
|
|
|
status_1->tx_results_counter);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-08-14 10:17:32 +00:00
|
|
|
for (i = 0; i < NUM_TX_QUEUES; i++) {
|
|
|
|
/* prevent wrap-around in freed-packets counter */
|
2011-08-14 10:17:33 +00:00
|
|
|
wl->tx_allocated_pkts[i] -=
|
2012-05-10 09:13:54 +00:00
|
|
|
(status_2->counters.tx_released_pkts[i] -
|
2011-08-14 10:17:32 +00:00
|
|
|
wl->tx_pkts_freed[i]) & 0xff;
|
|
|
|
|
2012-05-10 09:13:54 +00:00
|
|
|
wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
|
2011-08-14 10:17:32 +00:00
|
|
|
}
|
|
|
|
|
2011-08-14 10:17:34 +00:00
|
|
|
/* prevent wrap-around in total blocks counter */
|
|
|
|
if (likely(wl->tx_blocks_freed <=
|
2012-05-10 09:13:54 +00:00
|
|
|
le32_to_cpu(status_2->total_released_blks)))
|
|
|
|
freed_blocks = le32_to_cpu(status_2->total_released_blks) -
|
2011-08-14 10:17:34 +00:00
|
|
|
wl->tx_blocks_freed;
|
|
|
|
else
|
|
|
|
freed_blocks = 0x100000000LL - wl->tx_blocks_freed +
|
2012-05-10 09:13:54 +00:00
|
|
|
le32_to_cpu(status_2->total_released_blks);
|
2011-08-14 10:17:34 +00:00
|
|
|
|
2012-05-10 09:13:54 +00:00
|
|
|
wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks);
|
2011-03-06 14:32:12 +00:00
|
|
|
|
2011-08-14 10:17:00 +00:00
|
|
|
wl->tx_allocated_blocks -= freed_blocks;
|
|
|
|
|
2012-03-03 20:18:00 +00:00
|
|
|
/*
|
|
|
|
* If the FW freed some blocks:
|
|
|
|
* If we still have allocated blocks - re-arm the timer, Tx is
|
|
|
|
* not stuck. Otherwise, cancel the timer (no Tx currently).
|
|
|
|
*/
|
|
|
|
if (freed_blocks) {
|
|
|
|
if (wl->tx_allocated_blocks)
|
|
|
|
wl12xx_rearm_tx_watchdog_locked(wl);
|
|
|
|
else
|
|
|
|
cancel_delayed_work(&wl->tx_watchdog_work);
|
|
|
|
}
|
|
|
|
|
2012-05-10 09:13:54 +00:00
|
|
|
avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks;
|
2011-03-06 14:32:12 +00:00
|
|
|
|
2011-08-14 10:17:05 +00:00
|
|
|
/*
|
|
|
|
* The FW might change the total number of TX memblocks before
|
|
|
|
* we get a notification about blocks being released. Thus, the
|
|
|
|
* available blocks calculation might yield a temporary result
|
|
|
|
* which is lower than the actual available blocks. Keeping in
|
|
|
|
* mind that only blocks that were allocated can be moved from
|
|
|
|
* TX to RX, tx_blocks_available should never decrease here.
|
|
|
|
*/
|
|
|
|
wl->tx_blocks_available = max((int)wl->tx_blocks_available,
|
|
|
|
avail);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-10-12 12:49:10 +00:00
|
|
|
/* if more blocks are available now, tx work can be scheduled */
|
2011-03-06 14:32:12 +00:00
|
|
|
if (wl->tx_blocks_available > old_tx_blk_count)
|
2010-10-12 12:49:10 +00:00
|
|
|
clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-08-14 10:17:05 +00:00
|
|
|
/* for AP update num of allocated TX blocks per link and ps status */
|
2011-10-10 08:13:13 +00:00
|
|
|
wl12xx_for_each_wlvif_ap(wl, wlvif) {
|
2012-05-10 09:13:54 +00:00
|
|
|
wl12xx_irq_update_links_status(wl, wlvif, status_2);
|
2011-10-10 08:13:13 +00:00
|
|
|
}
|
2011-08-14 10:17:05 +00:00
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
/* update the host-chipset time offset */
|
2010-02-22 06:38:38 +00:00
|
|
|
getnstimeofday(&ts);
|
|
|
|
wl->time_offset = (timespec_to_ns(&ts) >> 10) -
|
2012-05-10 09:13:54 +00:00
|
|
|
(s64)le32_to_cpu(status_2->fw_localtime);
|
2012-06-17 18:59:42 +00:00
|
|
|
|
|
|
|
return 0;
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
static void wl1271_flush_deferred_work(struct wl1271 *wl)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
/* Pass all received frames to the network stack */
|
|
|
|
while ((skb = skb_dequeue(&wl->deferred_rx_queue)))
|
|
|
|
ieee80211_rx_ni(wl->hw, skb);
|
|
|
|
|
|
|
|
/* Return sent skbs to the network stack */
|
|
|
|
while ((skb = skb_dequeue(&wl->deferred_tx_queue)))
|
2011-06-07 07:40:39 +00:00
|
|
|
ieee80211_tx_status_ni(wl->hw, skb);
|
2011-03-01 13:14:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void wl1271_netstack_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl =
|
|
|
|
container_of(work, struct wl1271, netstack_work);
|
|
|
|
|
|
|
|
do {
|
|
|
|
wl1271_flush_deferred_work(wl);
|
|
|
|
} while (skb_queue_len(&wl->deferred_rx_queue));
|
|
|
|
}
|
2010-02-22 06:38:37 +00:00
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
#define WL1271_IRQ_MAX_LOOPS 256
|
|
|
|
|
2011-10-06 07:46:20 +00:00
|
|
|
static irqreturn_t wl1271_irq(int irq, void *cookie)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2009-10-12 12:08:50 +00:00
|
|
|
u32 intr;
|
2010-02-22 06:38:37 +00:00
|
|
|
int loopcount = WL1271_IRQ_MAX_LOOPS;
|
2011-03-01 13:14:41 +00:00
|
|
|
struct wl1271 *wl = (struct wl1271 *)cookie;
|
|
|
|
bool done = false;
|
|
|
|
unsigned int defer_count;
|
2011-03-01 13:14:43 +00:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
/* TX might be handled here, avoid redundant work */
|
|
|
|
set_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
|
|
|
|
cancel_work_sync(&wl->tx_work);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-03-31 08:07:01 +00:00
|
|
|
/*
|
|
|
|
* In case edge triggered interrupt must be used, we cannot iterate
|
|
|
|
* more than once without introducing race conditions with the hardirq.
|
|
|
|
*/
|
|
|
|
if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
|
|
|
|
loopcount = 1;
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_IRQ, "IRQ work");
|
|
|
|
|
2010-02-22 06:38:37 +00:00
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF))
|
2009-08-06 13:25:28 +00:00
|
|
|
goto out;
|
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
2009-08-06 13:25:28 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
while (!done && loopcount--) {
|
|
|
|
/*
|
|
|
|
* In order to avoid a race with the hardirq, clear the flag
|
|
|
|
* before acknowledging the chip. Since the mutex is held,
|
|
|
|
* wl1271_ps_elp_wakeup cannot be called concurrently.
|
|
|
|
*/
|
|
|
|
clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
|
|
|
|
smp_mb__after_clear_bit();
|
2010-02-22 06:38:37 +00:00
|
|
|
|
2012-06-17 18:59:42 +00:00
|
|
|
ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl12xx_queue_recovery_work(wl);
|
|
|
|
goto out;
|
|
|
|
}
|
2011-12-12 09:32:37 +00:00
|
|
|
|
|
|
|
wlcore_hw_tx_immediate_compl(wl);
|
|
|
|
|
2012-05-10 09:13:54 +00:00
|
|
|
intr = le32_to_cpu(wl->fw_status_1->intr);
|
2012-04-23 14:35:25 +00:00
|
|
|
intr &= WLCORE_ALL_INTR_MASK;
|
2010-02-22 06:38:37 +00:00
|
|
|
if (!intr) {
|
2011-03-01 13:14:41 +00:00
|
|
|
done = true;
|
2010-02-22 06:38:37 +00:00
|
|
|
continue;
|
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-10-27 12:09:57 +00:00
|
|
|
if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
|
2012-04-23 14:35:25 +00:00
|
|
|
wl1271_error("HW watchdog interrupt received! starting recovery.");
|
|
|
|
wl->watchdog_recovery = true;
|
|
|
|
wl12xx_queue_recovery_work(wl);
|
|
|
|
|
|
|
|
/* restarting the chip. ignore any other interrupt. */
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unlikely(intr & WL1271_ACX_SW_INTR_WATCHDOG)) {
|
|
|
|
wl1271_error("SW watchdog interrupt received! "
|
2010-10-27 12:09:57 +00:00
|
|
|
"starting recovery.");
|
2012-05-16 08:34:18 +00:00
|
|
|
wl->watchdog_recovery = true;
|
2011-06-06 11:57:05 +00:00
|
|
|
wl12xx_queue_recovery_work(wl);
|
2010-10-27 12:09:57 +00:00
|
|
|
|
|
|
|
/* restarting the chip. ignore any other interrupt. */
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
if (likely(intr & WL1271_ACX_INTR_DATA)) {
|
2010-02-22 06:38:37 +00:00
|
|
|
wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
|
2009-10-13 09:47:54 +00:00
|
|
|
|
2012-06-18 09:31:16 +00:00
|
|
|
ret = wlcore_rx(wl, wl->fw_status_1);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl12xx_queue_recovery_work(wl);
|
|
|
|
goto out;
|
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-10-12 12:49:10 +00:00
|
|
|
/* Check if any tx blocks were freed */
|
2011-03-01 13:14:43 +00:00
|
|
|
spin_lock_irqsave(&wl->wl_lock, flags);
|
2010-10-12 12:49:10 +00:00
|
|
|
if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
|
2011-07-07 11:25:23 +00:00
|
|
|
wl1271_tx_total_queue_count(wl) > 0) {
|
2011-03-01 13:14:43 +00:00
|
|
|
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
2010-10-12 12:49:10 +00:00
|
|
|
/*
|
|
|
|
* In order to avoid starvation of the TX path,
|
|
|
|
* call the work function directly.
|
|
|
|
*/
|
2012-06-18 10:21:55 +00:00
|
|
|
ret = wlcore_tx_work_locked(wl);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl12xx_queue_recovery_work(wl);
|
|
|
|
goto out;
|
|
|
|
}
|
2011-03-01 13:14:43 +00:00
|
|
|
} else {
|
|
|
|
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
2010-10-12 12:49:10 +00:00
|
|
|
}
|
|
|
|
|
2011-03-01 13:14:38 +00:00
|
|
|
/* check for tx results */
|
2012-06-18 09:31:16 +00:00
|
|
|
ret = wlcore_hw_tx_delayed_compl(wl);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl12xx_queue_recovery_work(wl);
|
|
|
|
goto out;
|
|
|
|
}
|
2011-03-01 13:14:41 +00:00
|
|
|
|
|
|
|
/* Make sure the deferred queues don't get too long */
|
|
|
|
defer_count = skb_queue_len(&wl->deferred_tx_queue) +
|
|
|
|
skb_queue_len(&wl->deferred_rx_queue);
|
|
|
|
if (defer_count > WL1271_DEFERRED_QUEUE_LIMIT)
|
|
|
|
wl1271_flush_deferred_work(wl);
|
2010-02-22 06:38:37 +00:00
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-02-22 06:38:37 +00:00
|
|
|
if (intr & WL1271_ACX_INTR_EVENT_A) {
|
|
|
|
wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
|
2012-06-18 09:31:16 +00:00
|
|
|
ret = wl1271_event_handle(wl, 0);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl12xx_queue_recovery_work(wl);
|
|
|
|
goto out;
|
|
|
|
}
|
2010-02-22 06:38:37 +00:00
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-02-22 06:38:37 +00:00
|
|
|
if (intr & WL1271_ACX_INTR_EVENT_B) {
|
|
|
|
wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
|
2012-06-18 09:31:16 +00:00
|
|
|
ret = wl1271_event_handle(wl, 1);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl12xx_queue_recovery_work(wl);
|
|
|
|
goto out;
|
|
|
|
}
|
2010-02-22 06:38:37 +00:00
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-02-22 06:38:37 +00:00
|
|
|
if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
|
|
|
|
wl1271_debug(DEBUG_IRQ,
|
|
|
|
"WL1271_ACX_INTR_INIT_COMPLETE");
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-02-22 06:38:37 +00:00
|
|
|
if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
|
|
|
|
wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
|
2009-10-12 12:08:50 +00:00
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
|
|
|
|
out:
|
2011-03-01 13:14:43 +00:00
|
|
|
spin_lock_irqsave(&wl->wl_lock, flags);
|
|
|
|
/* In case TX was not handled here, queue TX work */
|
|
|
|
clear_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
|
|
|
|
if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
|
2011-07-07 11:25:23 +00:00
|
|
|
wl1271_tx_total_queue_count(wl) > 0)
|
2011-03-01 13:14:43 +00:00
|
|
|
ieee80211_queue_work(wl->hw, &wl->tx_work);
|
|
|
|
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
2011-03-01 13:14:41 +00:00
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
|
2012-02-06 11:07:52 +00:00
|
|
|
struct vif_counter_data {
|
|
|
|
u8 counter;
|
|
|
|
|
|
|
|
struct ieee80211_vif *cur_vif;
|
|
|
|
bool cur_vif_running;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void wl12xx_vif_count_iter(void *data, u8 *mac,
|
|
|
|
struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct vif_counter_data *counter = data;
|
|
|
|
|
|
|
|
counter->counter++;
|
|
|
|
if (counter->cur_vif == vif)
|
|
|
|
counter->cur_vif_running = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* caller must not hold wl->mutex, as it might deadlock */
|
|
|
|
static void wl12xx_get_vif_count(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *cur_vif,
|
|
|
|
struct vif_counter_data *data)
|
|
|
|
{
|
|
|
|
memset(data, 0, sizeof(*data));
|
|
|
|
data->cur_vif = cur_vif;
|
|
|
|
|
|
|
|
ieee80211_iterate_active_interfaces(hw,
|
|
|
|
wl12xx_vif_count_iter, data);
|
|
|
|
}
|
|
|
|
|
2012-02-06 10:47:54 +00:00
|
|
|
static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
|
|
|
const struct firmware *fw;
|
2010-10-16 19:44:57 +00:00
|
|
|
const char *fw_name;
|
2012-02-06 10:47:54 +00:00
|
|
|
enum wl12xx_fw_type fw_type;
|
2009-08-06 13:25:28 +00:00
|
|
|
int ret;
|
|
|
|
|
2012-02-06 10:47:54 +00:00
|
|
|
if (plt) {
|
|
|
|
fw_type = WL12XX_FW_TYPE_PLT;
|
2011-11-29 14:27:31 +00:00
|
|
|
fw_name = wl->plt_fw_name;
|
2012-02-06 10:47:54 +00:00
|
|
|
} else {
|
2012-02-06 11:07:52 +00:00
|
|
|
/*
|
|
|
|
* we can't call wl12xx_get_vif_count() here because
|
|
|
|
* wl->mutex is taken, so use the cached last_vif_count value
|
|
|
|
*/
|
|
|
|
if (wl->last_vif_count > 1) {
|
|
|
|
fw_type = WL12XX_FW_TYPE_MULTI;
|
2011-11-29 14:27:31 +00:00
|
|
|
fw_name = wl->mr_fw_name;
|
2012-02-06 11:07:52 +00:00
|
|
|
} else {
|
|
|
|
fw_type = WL12XX_FW_TYPE_NORMAL;
|
2011-11-29 14:27:31 +00:00
|
|
|
fw_name = wl->sr_fw_name;
|
2012-02-06 11:07:52 +00:00
|
|
|
}
|
2012-02-06 10:47:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (wl->fw_type == fw_type)
|
|
|
|
return 0;
|
2010-10-16 19:44:57 +00:00
|
|
|
|
|
|
|
wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name);
|
|
|
|
|
2011-10-06 07:07:44 +00:00
|
|
|
ret = request_firmware(&fw, fw_name, wl->dev);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
if (ret < 0) {
|
2011-11-30 14:35:09 +00:00
|
|
|
wl1271_error("could not get firmware %s: %d", fw_name, ret);
|
2009-08-06 13:25:28 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fw->size % 4) {
|
|
|
|
wl1271_error("firmware size is not multiple of 32 bits: %zu",
|
|
|
|
fw->size);
|
|
|
|
ret = -EILSEQ;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-10-16 19:44:57 +00:00
|
|
|
vfree(wl->fw);
|
2012-02-06 10:47:54 +00:00
|
|
|
wl->fw_type = WL12XX_FW_TYPE_NONE;
|
2009-08-06 13:25:28 +00:00
|
|
|
wl->fw_len = fw->size;
|
2009-10-08 18:56:32 +00:00
|
|
|
wl->fw = vmalloc(wl->fw_len);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
if (!wl->fw) {
|
|
|
|
wl1271_error("could not allocate memory for the firmware");
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(wl->fw, fw->data, wl->fw_len);
|
|
|
|
ret = 0;
|
2012-02-06 10:47:54 +00:00
|
|
|
wl->fw_type = fw_type;
|
2009-08-06 13:25:28 +00:00
|
|
|
out:
|
|
|
|
release_firmware(fw);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-05-29 15:38:05 +00:00
|
|
|
static void wl1271_fetch_nvs(struct wl1271 *wl)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
|
|
|
const struct firmware *fw;
|
|
|
|
int ret;
|
|
|
|
|
2011-10-06 07:07:44 +00:00
|
|
|
ret = request_firmware(&fw, WL12XX_NVS_NAME, wl->dev);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
if (ret < 0) {
|
2012-05-29 15:38:05 +00:00
|
|
|
wl1271_debug(DEBUG_BOOT, "could not get nvs file %s: %d",
|
|
|
|
WL12XX_NVS_NAME, ret);
|
|
|
|
return;
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
|
2011-03-06 14:32:10 +00:00
|
|
|
wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
if (!wl->nvs) {
|
|
|
|
wl1271_error("could not allocate memory for the nvs file");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-08-19 02:41:15 +00:00
|
|
|
wl->nvs_len = fw->size;
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
out:
|
|
|
|
release_firmware(fw);
|
|
|
|
}
|
|
|
|
|
2011-06-06 11:57:05 +00:00
|
|
|
void wl12xx_queue_recovery_work(struct wl1271 *wl)
|
|
|
|
{
|
2012-05-20 22:10:11 +00:00
|
|
|
/* Avoid a recursive recovery */
|
|
|
|
if (!test_and_set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
|
|
|
|
wlcore_disable_interrupts_nosync(wl);
|
2011-06-06 11:57:05 +00:00
|
|
|
ieee80211_queue_work(wl->hw, &wl->recovery_work);
|
2012-05-20 22:10:11 +00:00
|
|
|
}
|
2011-06-06 11:57:05 +00:00
|
|
|
}
|
|
|
|
|
2011-06-06 11:57:06 +00:00
|
|
|
size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
|
|
|
|
{
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
|
/* The FW log is a length-value list, find where the log end */
|
|
|
|
while (len < maxlen) {
|
|
|
|
if (memblock[len] == 0)
|
|
|
|
break;
|
|
|
|
if (len + memblock[len] + 1 > maxlen)
|
|
|
|
break;
|
|
|
|
len += memblock[len] + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure we have enough room */
|
|
|
|
len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size));
|
|
|
|
|
|
|
|
/* Fill the FW log file, consumed by the sysfs fwlog entry */
|
|
|
|
memcpy(wl->fwlog + wl->fwlog_size, memblock, len);
|
|
|
|
wl->fwlog_size += len;
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2012-06-18 08:05:39 +00:00
|
|
|
#define WLCORE_FW_LOG_END 0x2000000
|
|
|
|
|
2011-06-06 11:57:06 +00:00
|
|
|
static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
|
|
|
|
{
|
|
|
|
u32 addr;
|
2012-06-18 08:05:39 +00:00
|
|
|
u32 offset;
|
|
|
|
u32 end_of_log;
|
2011-06-06 11:57:06 +00:00
|
|
|
u8 *block;
|
2012-06-17 18:59:42 +00:00
|
|
|
int ret;
|
2011-06-06 11:57:06 +00:00
|
|
|
|
2011-11-29 14:27:31 +00:00
|
|
|
if ((wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) ||
|
2011-06-06 11:57:06 +00:00
|
|
|
(wl->conf.fwlog.mem_blocks == 0))
|
|
|
|
return;
|
|
|
|
|
|
|
|
wl1271_info("Reading FW panic log");
|
|
|
|
|
|
|
|
block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL);
|
|
|
|
if (!block)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the chip is awake and the logger isn't active.
|
2012-05-16 08:34:18 +00:00
|
|
|
* Do not send a stop fwlog command if the fw is hanged.
|
2011-06-06 11:57:06 +00:00
|
|
|
*/
|
2012-06-18 08:05:39 +00:00
|
|
|
if (wl1271_ps_elp_wakeup(wl))
|
2012-05-16 08:34:18 +00:00
|
|
|
goto out;
|
2012-06-18 08:05:39 +00:00
|
|
|
if (!wl->watchdog_recovery)
|
|
|
|
wl12xx_cmd_stop_fwlog(wl);
|
2011-06-06 11:57:06 +00:00
|
|
|
|
|
|
|
/* Read the first memory block address */
|
2012-06-17 18:59:42 +00:00
|
|
|
ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2012-06-18 08:05:39 +00:00
|
|
|
addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
|
|
|
|
if (!addr)
|
2011-06-06 11:57:06 +00:00
|
|
|
goto out;
|
|
|
|
|
2012-06-18 08:05:39 +00:00
|
|
|
if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) {
|
|
|
|
offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor);
|
|
|
|
end_of_log = WLCORE_FW_LOG_END;
|
|
|
|
} else {
|
|
|
|
offset = sizeof(addr);
|
|
|
|
end_of_log = addr;
|
|
|
|
}
|
|
|
|
|
2011-06-06 11:57:06 +00:00
|
|
|
/* Traverse the memory blocks linked list */
|
|
|
|
do {
|
|
|
|
memset(block, 0, WL12XX_HW_BLOCK_SIZE);
|
|
|
|
wl1271_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
|
|
|
|
false);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Memory blocks are linked to one another. The first 4 bytes
|
|
|
|
* of each memory block hold the hardware address of the next
|
2012-06-18 08:05:39 +00:00
|
|
|
* one. The last memory block points to the first one in
|
|
|
|
* on demand mode and is equal to 0x2000000 in continuous mode.
|
2011-06-06 11:57:06 +00:00
|
|
|
*/
|
2011-08-14 10:17:05 +00:00
|
|
|
addr = le32_to_cpup((__le32 *)block);
|
2012-06-18 08:05:39 +00:00
|
|
|
if (!wl12xx_copy_fwlog(wl, block + offset,
|
|
|
|
WL12XX_HW_BLOCK_SIZE - offset))
|
2011-06-06 11:57:06 +00:00
|
|
|
break;
|
2012-06-18 08:05:39 +00:00
|
|
|
} while (addr && (addr != end_of_log));
|
2011-06-06 11:57:06 +00:00
|
|
|
|
|
|
|
wake_up_interruptible(&wl->fwlog_waitq);
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(block);
|
|
|
|
}
|
|
|
|
|
2012-06-18 12:50:21 +00:00
|
|
|
static void wlcore_print_recovery(struct wl1271 *wl)
|
|
|
|
{
|
|
|
|
u32 pc = 0;
|
|
|
|
u32 hint_sts = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
wl1271_info("Hardware recovery in progress. FW ver: %s",
|
|
|
|
wl->chip.fw_ver_str);
|
|
|
|
|
|
|
|
/* change partitions momentarily so we can read the FW pc */
|
|
|
|
wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
|
|
|
|
|
|
|
|
ret = wlcore_read_reg(wl, REG_PC_ON_RECOVERY, &pc);
|
|
|
|
if (ret < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &hint_sts);
|
|
|
|
if (ret < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
wl1271_info("pc: 0x%x, hint_sts: 0x%08x", pc, hint_sts);
|
|
|
|
|
|
|
|
wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-09-21 04:23:31 +00:00
|
|
|
static void wl1271_recovery_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl =
|
|
|
|
container_of(work, struct wl1271, recovery_work);
|
2011-10-10 08:12:58 +00:00
|
|
|
struct wl12xx_vif *wlvif;
|
2011-10-10 08:13:13 +00:00
|
|
|
struct ieee80211_vif *vif;
|
2010-09-21 04:23:31 +00:00
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
2012-02-06 10:47:54 +00:00
|
|
|
if (wl->state != WL1271_STATE_ON || wl->plt)
|
2011-10-10 08:13:14 +00:00
|
|
|
goto out_unlock;
|
2010-09-21 04:23:31 +00:00
|
|
|
|
2011-06-06 11:57:06 +00:00
|
|
|
wl12xx_read_fwlog_panic(wl);
|
|
|
|
|
2012-06-18 12:50:21 +00:00
|
|
|
wlcore_print_recovery(wl);
|
2010-09-21 04:23:31 +00:00
|
|
|
|
2012-03-04 08:55:54 +00:00
|
|
|
BUG_ON(bug_on_recovery &&
|
|
|
|
!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
|
2011-08-25 15:10:59 +00:00
|
|
|
|
2011-12-08 11:06:45 +00:00
|
|
|
if (no_recovery) {
|
|
|
|
wl1271_info("No recovery (chosen on module load). Fw will remain stuck.");
|
|
|
|
clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2011-06-26 07:36:02 +00:00
|
|
|
/*
|
|
|
|
* Advance security sequence number to overcome potential progress
|
|
|
|
* in the firmware during recovery. This doens't hurt if the network is
|
|
|
|
* not encrypted.
|
|
|
|
*/
|
2011-10-10 08:12:58 +00:00
|
|
|
wl12xx_for_each_wlvif(wl, wlvif) {
|
2011-10-10 08:13:00 +00:00
|
|
|
if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
|
2011-10-10 08:13:02 +00:00
|
|
|
test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
|
2011-10-10 08:12:58 +00:00
|
|
|
wlvif->tx_security_seq +=
|
|
|
|
WL1271_TX_SQN_POST_RECOVERY_PADDING;
|
|
|
|
}
|
2011-06-26 07:36:02 +00:00
|
|
|
|
2011-04-18 11:15:28 +00:00
|
|
|
/* Prevent spurious TX during FW restart */
|
2012-05-18 04:46:38 +00:00
|
|
|
wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
|
2011-04-18 11:15:28 +00:00
|
|
|
|
2011-05-10 11:46:02 +00:00
|
|
|
if (wl->sched_scanning) {
|
|
|
|
ieee80211_sched_scan_stopped(wl->hw);
|
|
|
|
wl->sched_scanning = false;
|
|
|
|
}
|
|
|
|
|
2010-09-21 04:23:31 +00:00
|
|
|
/* reboot the chipset */
|
2011-10-10 08:13:13 +00:00
|
|
|
while (!list_empty(&wl->wlvif_list)) {
|
|
|
|
wlvif = list_first_entry(&wl->wlvif_list,
|
|
|
|
struct wl12xx_vif, list);
|
|
|
|
vif = wl12xx_wlvif_to_vif(wlvif);
|
|
|
|
__wl1271_op_remove_interface(wl, vif, false);
|
|
|
|
}
|
2012-05-16 08:34:18 +00:00
|
|
|
wl->watchdog_recovery = false;
|
2011-10-10 08:13:14 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
wl1271_op_stop(wl->hw);
|
2011-06-06 11:57:05 +00:00
|
|
|
|
2010-09-21 04:23:31 +00:00
|
|
|
ieee80211_restart_hw(wl->hw);
|
|
|
|
|
2011-04-18 11:15:28 +00:00
|
|
|
/*
|
|
|
|
* Its safe to enable TX now - the queues are stopped after a request
|
|
|
|
* to restart the HW.
|
|
|
|
*/
|
2012-05-18 04:46:38 +00:00
|
|
|
wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
|
2011-10-10 08:13:14 +00:00
|
|
|
return;
|
|
|
|
out_unlock:
|
2012-05-16 08:34:18 +00:00
|
|
|
wl->watchdog_recovery = false;
|
2010-09-21 04:23:31 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
}
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
static void wl1271_fw_wakeup(struct wl1271 *wl)
|
|
|
|
{
|
2011-11-29 11:38:37 +00:00
|
|
|
wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP);
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int wl1271_setup(struct wl1271 *wl)
|
|
|
|
{
|
2012-05-10 09:13:54 +00:00
|
|
|
wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
|
2012-05-10 09:14:00 +00:00
|
|
|
sizeof(*wl->fw_status_2) +
|
|
|
|
wl->fw_status_priv_len, GFP_KERNEL);
|
2012-05-10 09:13:54 +00:00
|
|
|
if (!wl->fw_status_1)
|
2009-08-06 13:25:28 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2012-05-10 09:13:54 +00:00
|
|
|
wl->fw_status_2 = (struct wl_fw_status_2 *)
|
|
|
|
(((u8 *) wl->fw_status_1) +
|
|
|
|
WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
|
|
|
|
if (!wl->tx_res_if) {
|
2012-05-10 09:13:54 +00:00
|
|
|
kfree(wl->fw_status_1);
|
2009-08-06 13:25:28 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-01-18 12:53:22 +00:00
|
|
|
static int wl12xx_set_power_on(struct wl1271 *wl)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
2012-01-18 12:53:22 +00:00
|
|
|
int ret;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2009-12-11 13:41:02 +00:00
|
|
|
msleep(WL1271_PRE_POWER_ON_SLEEP);
|
2010-09-15 23:22:04 +00:00
|
|
|
ret = wl1271_power_on(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2009-08-06 13:25:28 +00:00
|
|
|
msleep(WL1271_POWER_ON_SLEEP);
|
2010-02-18 11:25:56 +00:00
|
|
|
wl1271_io_reset(wl);
|
|
|
|
wl1271_io_init(wl);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-11-29 11:38:37 +00:00
|
|
|
wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
/* ELP module wake up */
|
|
|
|
wl1271_fw_wakeup(wl);
|
|
|
|
|
2012-01-18 12:53:22 +00:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2012-02-06 10:47:54 +00:00
|
|
|
static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt)
|
2012-01-18 12:53:22 +00:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
ret = wl12xx_set_power_on(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-11-03 06:44:42 +00:00
|
|
|
/*
|
|
|
|
* For wl127x based devices we could use the default block
|
|
|
|
* size (512 bytes), but due to a bug in the sdio driver, we
|
|
|
|
* need to set it explicitly after the chip is powered on. To
|
|
|
|
* simplify the code and since the performance impact is
|
|
|
|
* negligible, we use the same block size for all different
|
|
|
|
* chip types.
|
2012-06-04 21:02:25 +00:00
|
|
|
*
|
|
|
|
* Check if the bus supports blocksize alignment and, if it
|
|
|
|
* doesn't, make sure we don't have the quirk.
|
2011-11-03 06:44:42 +00:00
|
|
|
*/
|
2012-06-04 21:02:25 +00:00
|
|
|
if (!wl1271_set_block_size(wl))
|
|
|
|
wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-11-29 14:27:31 +00:00
|
|
|
/* TODO: make sure the lower driver has set things up correctly */
|
2011-03-06 14:32:20 +00:00
|
|
|
|
2011-11-29 14:27:31 +00:00
|
|
|
ret = wl1271_setup(wl);
|
|
|
|
if (ret < 0)
|
2009-12-11 13:41:01 +00:00
|
|
|
goto out;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2012-02-06 10:47:54 +00:00
|
|
|
ret = wl12xx_fetch_firmware(wl, plt);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int wl1271_plt_start(struct wl1271 *wl)
|
|
|
|
{
|
2009-12-11 13:41:01 +00:00
|
|
|
int retries = WL1271_BOOT_RETRIES;
|
2011-07-18 11:21:49 +00:00
|
|
|
struct wiphy *wiphy = wl->hw->wiphy;
|
2009-08-06 13:25:28 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
wl1271_notice("power up");
|
|
|
|
|
|
|
|
if (wl->state != WL1271_STATE_OFF) {
|
|
|
|
wl1271_error("cannot go into PLT state because not "
|
|
|
|
"in off state: %d", wl->state);
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-12-11 13:41:01 +00:00
|
|
|
while (retries) {
|
|
|
|
retries--;
|
2012-02-06 10:47:54 +00:00
|
|
|
ret = wl12xx_chip_wakeup(wl, true);
|
2009-12-11 13:41:01 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto power_off;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2012-05-10 09:13:49 +00:00
|
|
|
ret = wl->ops->plt_init(wl);
|
2009-12-11 13:41:01 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto power_off;
|
2009-10-13 09:47:45 +00:00
|
|
|
|
2012-02-06 10:47:54 +00:00
|
|
|
wl->plt = true;
|
|
|
|
wl->state = WL1271_STATE_ON;
|
2009-12-11 13:41:01 +00:00
|
|
|
wl1271_notice("firmware booted in PLT mode (%s)",
|
2011-01-23 06:27:22 +00:00
|
|
|
wl->chip.fw_ver_str);
|
2011-03-10 13:24:57 +00:00
|
|
|
|
2011-07-18 11:21:49 +00:00
|
|
|
/* update hw/fw version info in wiphy struct */
|
|
|
|
wiphy->hw_version = wl->chip.id;
|
|
|
|
strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
|
|
|
|
sizeof(wiphy->fw_version));
|
|
|
|
|
2009-12-11 13:41:01 +00:00
|
|
|
goto out;
|
2009-10-13 09:47:45 +00:00
|
|
|
|
2009-12-11 13:41:01 +00:00
|
|
|
power_off:
|
|
|
|
wl1271_power_off(wl);
|
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2009-12-11 13:41:01 +00:00
|
|
|
wl1271_error("firmware boot in PLT mode failed despite %d retries",
|
|
|
|
WL1271_BOOT_RETRIES);
|
2009-08-06 13:25:28 +00:00
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-01-11 07:42:39 +00:00
|
|
|
int wl1271_plt_stop(struct wl1271 *wl)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
wl1271_notice("power down");
|
|
|
|
|
2012-01-11 07:42:41 +00:00
|
|
|
/*
|
|
|
|
* Interrupts must be disabled before setting the state to OFF.
|
|
|
|
* Otherwise, the interrupt handler might be called and exit without
|
|
|
|
* reading the interrupt status.
|
|
|
|
*/
|
2012-04-11 08:03:14 +00:00
|
|
|
wlcore_disable_interrupts(wl);
|
2012-01-11 07:42:39 +00:00
|
|
|
mutex_lock(&wl->mutex);
|
2012-02-06 10:47:54 +00:00
|
|
|
if (!wl->plt) {
|
2012-01-11 07:42:39 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
2012-01-11 07:42:41 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This will not necessarily enable interrupts as interrupts
|
|
|
|
* may have been disabled when op_stop was called. It will,
|
|
|
|
* however, balance the above call to disable_interrupts().
|
|
|
|
*/
|
2012-04-11 08:03:14 +00:00
|
|
|
wlcore_enable_interrupts(wl);
|
2012-01-11 07:42:41 +00:00
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
wl1271_error("cannot power down because not in PLT "
|
|
|
|
"state: %d", wl->state);
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&wl->mutex);
|
2012-01-11 07:42:39 +00:00
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
wl1271_flush_deferred_work(wl);
|
|
|
|
cancel_work_sync(&wl->netstack_work);
|
2010-09-21 04:23:31 +00:00
|
|
|
cancel_work_sync(&wl->recovery_work);
|
2012-01-11 07:42:42 +00:00
|
|
|
cancel_delayed_work_sync(&wl->elp_work);
|
2012-03-03 20:18:00 +00:00
|
|
|
cancel_delayed_work_sync(&wl->tx_watchdog_work);
|
2012-04-26 07:35:07 +00:00
|
|
|
cancel_delayed_work_sync(&wl->connection_loss_work);
|
2012-01-11 07:42:40 +00:00
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
wl1271_power_off(wl);
|
2012-01-11 07:42:42 +00:00
|
|
|
wl->flags = 0;
|
2012-06-10 16:10:45 +00:00
|
|
|
wl->sleep_auth = WL1271_PSM_ILLEGAL;
|
2012-01-11 07:42:42 +00:00
|
|
|
wl->state = WL1271_STATE_OFF;
|
2012-02-06 10:47:54 +00:00
|
|
|
wl->plt = false;
|
2012-01-11 07:42:42 +00:00
|
|
|
wl->rx_counter = 0;
|
2012-01-11 07:42:40 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
2011-01-14 11:48:46 +00:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-02-24 13:42:06 +00:00
|
|
|
static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
2011-10-05 09:55:54 +00:00
|
|
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
|
|
|
struct ieee80211_vif *vif = info->control.vif;
|
2011-10-11 11:52:25 +00:00
|
|
|
struct wl12xx_vif *wlvif = NULL;
|
2009-12-11 13:41:06 +00:00
|
|
|
unsigned long flags;
|
2011-06-24 10:03:37 +00:00
|
|
|
int q, mapping;
|
2011-10-10 08:12:51 +00:00
|
|
|
u8 hlid;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-10-11 11:52:25 +00:00
|
|
|
if (vif)
|
|
|
|
wlvif = wl12xx_vif_to_data(vif);
|
|
|
|
|
2011-06-24 10:03:37 +00:00
|
|
|
mapping = skb_get_queue_mapping(skb);
|
|
|
|
q = wl1271_tx_get_queue(mapping);
|
2011-03-01 13:14:43 +00:00
|
|
|
|
2011-10-10 08:12:51 +00:00
|
|
|
hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
|
2011-03-01 13:14:43 +00:00
|
|
|
|
2009-12-11 13:41:06 +00:00
|
|
|
spin_lock_irqsave(&wl->wl_lock, flags);
|
2011-03-01 13:14:43 +00:00
|
|
|
|
2012-05-18 04:46:38 +00:00
|
|
|
/*
|
|
|
|
* drop the packet if the link is invalid or the queue is stopped
|
|
|
|
* for any reason but watermark. Watermark is a "soft"-stop so we
|
|
|
|
* allow these packets through.
|
|
|
|
*/
|
2011-10-10 08:12:51 +00:00
|
|
|
if (hlid == WL12XX_INVALID_LINK_ID ||
|
2012-05-18 04:46:38 +00:00
|
|
|
(wlvif && !test_bit(hlid, wlvif->links_map)) ||
|
|
|
|
(wlcore_is_queue_stopped(wl, q) &&
|
|
|
|
!wlcore_is_queue_stopped_by_reason(wl, q,
|
|
|
|
WLCORE_QUEUE_STOP_REASON_WATERMARK))) {
|
2011-10-10 08:12:51 +00:00
|
|
|
wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q);
|
2011-12-13 13:26:38 +00:00
|
|
|
ieee80211_free_txskb(hw, skb);
|
2011-10-10 08:12:51 +00:00
|
|
|
goto out;
|
2011-02-22 22:22:26 +00:00
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2012-03-04 08:55:55 +00:00
|
|
|
wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d len %d",
|
|
|
|
hlid, q, skb->len);
|
2011-10-10 08:12:51 +00:00
|
|
|
skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
|
|
|
|
|
2011-08-14 10:17:39 +00:00
|
|
|
wl->tx_queue_count[q]++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The workqueue is slow to process the tx_queue and we need stop
|
|
|
|
* the queue here, otherwise the queue will get too long.
|
|
|
|
*/
|
|
|
|
if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
|
|
|
|
wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q);
|
2012-05-18 04:46:38 +00:00
|
|
|
wlcore_stop_queue_locked(wl, q,
|
|
|
|
WLCORE_QUEUE_STOP_REASON_WATERMARK);
|
2011-08-14 10:17:39 +00:00
|
|
|
}
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
/*
|
|
|
|
* The chip specific setup must run before the first TX packet -
|
|
|
|
* before that, the tx_work will not be initialized!
|
|
|
|
*/
|
|
|
|
|
2011-03-01 13:14:43 +00:00
|
|
|
if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
|
|
|
|
!test_bit(WL1271_FLAG_TX_PENDING, &wl->flags))
|
2010-10-12 12:49:10 +00:00
|
|
|
ieee80211_queue_work(wl->hw, &wl->tx_work);
|
2011-03-01 13:14:43 +00:00
|
|
|
|
2011-08-14 10:17:38 +00:00
|
|
|
out:
|
2011-03-01 13:14:43 +00:00
|
|
|
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
|
2011-03-06 14:32:14 +00:00
|
|
|
int wl1271_tx_dummy_packet(struct wl1271 *wl)
|
|
|
|
{
|
2011-03-31 08:06:59 +00:00
|
|
|
unsigned long flags;
|
2011-08-28 12:11:57 +00:00
|
|
|
int q;
|
|
|
|
|
|
|
|
/* no need to queue a new dummy packet if one is already pending */
|
|
|
|
if (test_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
q = wl1271_tx_get_queue(skb_get_queue_mapping(wl->dummy_packet));
|
2011-03-31 08:06:59 +00:00
|
|
|
|
|
|
|
spin_lock_irqsave(&wl->wl_lock, flags);
|
|
|
|
set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
|
2011-07-07 11:25:23 +00:00
|
|
|
wl->tx_queue_count[q]++;
|
2011-03-31 08:06:59 +00:00
|
|
|
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
|
|
|
|
|
|
|
/* The FW is low on RX memory blocks, so send the dummy packet asap */
|
|
|
|
if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
|
2012-06-18 10:21:55 +00:00
|
|
|
return wlcore_tx_work_locked(wl);
|
2011-03-31 08:06:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the FW TX is busy, TX work will be scheduled by the threaded
|
|
|
|
* interrupt handler function
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The size of the dummy packet should be at least 1400 bytes. However, in
|
|
|
|
* order to minimize the number of bus transactions, aligning it to 512 bytes
|
|
|
|
* boundaries could be beneficial, performance wise
|
|
|
|
*/
|
|
|
|
#define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512))
|
|
|
|
|
2011-04-01 18:08:23 +00:00
|
|
|
static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
|
2011-03-31 08:06:59 +00:00
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
2011-03-06 14:32:14 +00:00
|
|
|
struct ieee80211_hdr_3addr *hdr;
|
2011-03-31 08:06:59 +00:00
|
|
|
unsigned int dummy_packet_size;
|
|
|
|
|
|
|
|
dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE -
|
|
|
|
sizeof(struct wl1271_tx_hw_descr) - sizeof(*hdr);
|
2011-03-06 14:32:14 +00:00
|
|
|
|
2011-03-31 08:06:59 +00:00
|
|
|
skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE);
|
2011-03-06 14:32:14 +00:00
|
|
|
if (!skb) {
|
2011-03-31 08:06:59 +00:00
|
|
|
wl1271_warning("Failed to allocate a dummy packet skb");
|
|
|
|
return NULL;
|
2011-03-06 14:32:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr));
|
|
|
|
|
|
|
|
hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr));
|
|
|
|
memset(hdr, 0, sizeof(*hdr));
|
|
|
|
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
|
2011-03-31 08:06:59 +00:00
|
|
|
IEEE80211_STYPE_NULLFUNC |
|
|
|
|
IEEE80211_FCTL_TODS);
|
2011-03-06 14:32:14 +00:00
|
|
|
|
2011-03-31 08:06:59 +00:00
|
|
|
memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size);
|
2011-03-06 14:32:14 +00:00
|
|
|
|
2011-03-21 14:35:21 +00:00
|
|
|
/* Dummy packets require the TID to be management */
|
|
|
|
skb->priority = WL1271_TID_MGMT;
|
2011-03-06 14:32:14 +00:00
|
|
|
|
2011-03-31 08:06:59 +00:00
|
|
|
/* Initialize all fields that might be used */
|
2011-04-26 21:27:44 +00:00
|
|
|
skb_set_queue_mapping(skb, 0);
|
2011-03-31 08:06:59 +00:00
|
|
|
memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
|
2011-03-06 14:32:14 +00:00
|
|
|
|
2011-03-31 08:06:59 +00:00
|
|
|
return skb;
|
2011-03-06 14:32:14 +00:00
|
|
|
}
|
|
|
|
|
2011-03-31 08:06:59 +00:00
|
|
|
|
2011-05-18 20:51:26 +00:00
|
|
|
#ifdef CONFIG_PM
|
2012-05-16 03:00:00 +00:00
|
|
|
static int
|
|
|
|
wl1271_validate_wowlan_pattern(struct cfg80211_wowlan_trig_pkt_pattern *p)
|
2012-03-14 04:32:10 +00:00
|
|
|
{
|
|
|
|
int num_fields = 0, in_field = 0, fields_size = 0;
|
|
|
|
int i, pattern_len = 0;
|
|
|
|
|
|
|
|
if (!p->mask) {
|
|
|
|
wl1271_warning("No mask in WoWLAN pattern");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The pattern is broken up into segments of bytes at different offsets
|
|
|
|
* that need to be checked by the FW filter. Each segment is called
|
|
|
|
* a field in the FW API. We verify that the total number of fields
|
|
|
|
* required for this pattern won't exceed FW limits (8)
|
|
|
|
* as well as the total fields buffer won't exceed the FW limit.
|
|
|
|
* Note that if there's a pattern which crosses Ethernet/IP header
|
|
|
|
* boundary a new field is required.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < p->pattern_len; i++) {
|
|
|
|
if (test_bit(i, (unsigned long *)p->mask)) {
|
|
|
|
if (!in_field) {
|
|
|
|
in_field = 1;
|
|
|
|
pattern_len = 1;
|
|
|
|
} else {
|
|
|
|
if (i == WL1271_RX_FILTER_ETH_HEADER_SIZE) {
|
|
|
|
num_fields++;
|
|
|
|
fields_size += pattern_len +
|
|
|
|
RX_FILTER_FIELD_OVERHEAD;
|
|
|
|
pattern_len = 1;
|
|
|
|
} else
|
|
|
|
pattern_len++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (in_field) {
|
|
|
|
in_field = 0;
|
|
|
|
fields_size += pattern_len +
|
|
|
|
RX_FILTER_FIELD_OVERHEAD;
|
|
|
|
num_fields++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (in_field) {
|
|
|
|
fields_size += pattern_len + RX_FILTER_FIELD_OVERHEAD;
|
|
|
|
num_fields++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_fields > WL1271_RX_FILTER_MAX_FIELDS) {
|
|
|
|
wl1271_warning("RX Filter too complex. Too many segments");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fields_size > WL1271_RX_FILTER_MAX_FIELDS_SIZE) {
|
|
|
|
wl1271_warning("RX filter pattern is too big");
|
|
|
|
return -E2BIG;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-14 04:32:07 +00:00
|
|
|
struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void)
|
|
|
|
{
|
|
|
|
return kzalloc(sizeof(struct wl12xx_rx_filter), GFP_KERNEL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (filter == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < filter->num_fields; i++)
|
|
|
|
kfree(filter->fields[i].pattern);
|
|
|
|
|
|
|
|
kfree(filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter,
|
|
|
|
u16 offset, u8 flags,
|
|
|
|
u8 *pattern, u8 len)
|
|
|
|
{
|
|
|
|
struct wl12xx_rx_filter_field *field;
|
|
|
|
|
|
|
|
if (filter->num_fields == WL1271_RX_FILTER_MAX_FIELDS) {
|
|
|
|
wl1271_warning("Max fields per RX filter. can't alloc another");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
field = &filter->fields[filter->num_fields];
|
|
|
|
|
|
|
|
field->pattern = kzalloc(len, GFP_KERNEL);
|
|
|
|
if (!field->pattern) {
|
|
|
|
wl1271_warning("Failed to allocate RX filter pattern");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
filter->num_fields++;
|
|
|
|
|
|
|
|
field->offset = cpu_to_le16(offset);
|
|
|
|
field->flags = flags;
|
|
|
|
field->len = len;
|
|
|
|
memcpy(field->pattern, pattern, len);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter)
|
|
|
|
{
|
|
|
|
int i, fields_size = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < filter->num_fields; i++)
|
|
|
|
fields_size += filter->fields[i].len +
|
|
|
|
sizeof(struct wl12xx_rx_filter_field) -
|
|
|
|
sizeof(u8 *);
|
|
|
|
|
|
|
|
return fields_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
|
|
|
|
u8 *buf)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct wl12xx_rx_filter_field *field;
|
|
|
|
|
|
|
|
for (i = 0; i < filter->num_fields; i++) {
|
|
|
|
field = (struct wl12xx_rx_filter_field *)buf;
|
|
|
|
|
|
|
|
field->offset = filter->fields[i].offset;
|
|
|
|
field->flags = filter->fields[i].flags;
|
|
|
|
field->len = filter->fields[i].len;
|
|
|
|
|
|
|
|
memcpy(&field->pattern, filter->fields[i].pattern, field->len);
|
|
|
|
buf += sizeof(struct wl12xx_rx_filter_field) -
|
|
|
|
sizeof(u8 *) + field->len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-14 04:32:10 +00:00
|
|
|
/*
|
|
|
|
* Allocates an RX filter returned through f
|
|
|
|
* which needs to be freed using rx_filter_free()
|
|
|
|
*/
|
2012-05-16 03:00:00 +00:00
|
|
|
static int wl1271_convert_wowlan_pattern_to_rx_filter(
|
2012-03-14 04:32:10 +00:00
|
|
|
struct cfg80211_wowlan_trig_pkt_pattern *p,
|
|
|
|
struct wl12xx_rx_filter **f)
|
|
|
|
{
|
|
|
|
int i, j, ret = 0;
|
|
|
|
struct wl12xx_rx_filter *filter;
|
|
|
|
u16 offset;
|
|
|
|
u8 flags, len;
|
|
|
|
|
|
|
|
filter = wl1271_rx_filter_alloc();
|
|
|
|
if (!filter) {
|
|
|
|
wl1271_warning("Failed to alloc rx filter");
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while (i < p->pattern_len) {
|
|
|
|
if (!test_bit(i, (unsigned long *)p->mask)) {
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = i; j < p->pattern_len; j++) {
|
|
|
|
if (!test_bit(j, (unsigned long *)p->mask))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (i < WL1271_RX_FILTER_ETH_HEADER_SIZE &&
|
|
|
|
j >= WL1271_RX_FILTER_ETH_HEADER_SIZE)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i < WL1271_RX_FILTER_ETH_HEADER_SIZE) {
|
|
|
|
offset = i;
|
|
|
|
flags = WL1271_RX_FILTER_FLAG_ETHERNET_HEADER;
|
|
|
|
} else {
|
|
|
|
offset = i - WL1271_RX_FILTER_ETH_HEADER_SIZE;
|
|
|
|
flags = WL1271_RX_FILTER_FLAG_IP_HEADER;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = j - i;
|
|
|
|
|
|
|
|
ret = wl1271_rx_filter_alloc_field(filter,
|
|
|
|
offset,
|
|
|
|
flags,
|
|
|
|
&p->pattern[i], len);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
i = j;
|
|
|
|
}
|
|
|
|
|
|
|
|
filter->action = FILTER_SIGNAL;
|
|
|
|
|
|
|
|
*f = filter;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
wl1271_rx_filter_free(filter);
|
|
|
|
*f = NULL;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wl1271_configure_wowlan(struct wl1271 *wl,
|
|
|
|
struct cfg80211_wowlan *wow)
|
|
|
|
{
|
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
if (!wow || wow->any || !wow->n_patterns) {
|
|
|
|
wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL);
|
|
|
|
wl1271_rx_filter_clear_all(wl);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WARN_ON(wow->n_patterns > WL1271_MAX_RX_FILTERS))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Validate all incoming patterns before clearing current FW state */
|
|
|
|
for (i = 0; i < wow->n_patterns; i++) {
|
|
|
|
ret = wl1271_validate_wowlan_pattern(&wow->patterns[i]);
|
|
|
|
if (ret) {
|
|
|
|
wl1271_warning("Bad wowlan pattern %d", i);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL);
|
|
|
|
wl1271_rx_filter_clear_all(wl);
|
|
|
|
|
|
|
|
/* Translate WoWLAN patterns into filters */
|
|
|
|
for (i = 0; i < wow->n_patterns; i++) {
|
|
|
|
struct cfg80211_wowlan_trig_pkt_pattern *p;
|
|
|
|
struct wl12xx_rx_filter *filter = NULL;
|
|
|
|
|
|
|
|
p = &wow->patterns[i];
|
|
|
|
|
|
|
|
ret = wl1271_convert_wowlan_pattern_to_rx_filter(p, &filter);
|
|
|
|
if (ret) {
|
|
|
|
wl1271_warning("Failed to create an RX filter from "
|
|
|
|
"wowlan pattern %d", i);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = wl1271_rx_filter_enable(wl, i, 1, filter);
|
|
|
|
|
|
|
|
wl1271_rx_filter_free(filter);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = wl1271_acx_default_rx_filter_enable(wl, 1, FILTER_DROP);
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-02-02 10:03:39 +00:00
|
|
|
static int wl1271_configure_suspend_sta(struct wl1271 *wl,
|
2012-03-14 04:32:10 +00:00
|
|
|
struct wl12xx_vif *wlvif,
|
|
|
|
struct cfg80211_wowlan *wow)
|
2012-02-02 10:03:39 +00:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
|
2012-03-13 18:03:21 +00:00
|
|
|
goto out;
|
2012-02-02 10:03:39 +00:00
|
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
2012-03-13 18:03:21 +00:00
|
|
|
goto out;
|
2012-02-02 10:03:39 +00:00
|
|
|
|
2012-03-14 04:32:10 +00:00
|
|
|
wl1271_configure_wowlan(wl, wow);
|
2012-02-02 10:03:39 +00:00
|
|
|
ret = wl1271_acx_wake_up_conditions(wl, wlvif,
|
|
|
|
wl->conf.conn.suspend_wake_up_event,
|
|
|
|
wl->conf.conn.suspend_listen_interval);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
wl1271_error("suspend: set wake up conditions failed: %d", ret);
|
|
|
|
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
|
2012-03-13 18:03:21 +00:00
|
|
|
out:
|
2012-02-02 10:03:39 +00:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
2011-05-13 08:57:13 +00:00
|
|
|
|
2011-10-05 09:55:51 +00:00
|
|
|
static int wl1271_configure_suspend_ap(struct wl1271 *wl,
|
|
|
|
struct wl12xx_vif *wlvif)
|
2011-06-06 09:21:54 +00:00
|
|
|
{
|
2011-06-27 10:06:43 +00:00
|
|
|
int ret = 0;
|
2011-06-06 09:21:54 +00:00
|
|
|
|
2011-10-10 08:13:02 +00:00
|
|
|
if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
|
2012-03-13 18:03:21 +00:00
|
|
|
goto out;
|
2011-06-27 10:06:43 +00:00
|
|
|
|
2011-06-06 09:21:54 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
2012-03-13 18:03:21 +00:00
|
|
|
goto out;
|
2011-06-06 09:21:54 +00:00
|
|
|
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
|
2011-06-06 09:21:54 +00:00
|
|
|
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
2012-03-13 18:03:21 +00:00
|
|
|
out:
|
2011-06-06 09:21:54 +00:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:43 +00:00
|
|
|
static int wl1271_configure_suspend(struct wl1271 *wl,
|
2012-03-14 04:32:10 +00:00
|
|
|
struct wl12xx_vif *wlvif,
|
|
|
|
struct cfg80211_wowlan *wow)
|
2011-06-06 09:21:54 +00:00
|
|
|
{
|
2012-02-02 10:03:39 +00:00
|
|
|
if (wlvif->bss_type == BSS_TYPE_STA_BSS)
|
2012-03-14 04:32:10 +00:00
|
|
|
return wl1271_configure_suspend_sta(wl, wlvif, wow);
|
2011-10-05 09:55:45 +00:00
|
|
|
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
|
2011-10-05 09:55:51 +00:00
|
|
|
return wl1271_configure_suspend_ap(wl, wlvif);
|
2011-06-06 09:21:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:43 +00:00
|
|
|
static void wl1271_configure_resume(struct wl1271 *wl,
|
|
|
|
struct wl12xx_vif *wlvif)
|
2011-05-13 08:57:13 +00:00
|
|
|
{
|
2012-02-02 10:03:39 +00:00
|
|
|
int ret = 0;
|
2011-10-05 09:55:45 +00:00
|
|
|
bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
|
2012-02-02 10:03:39 +00:00
|
|
|
bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
|
2011-05-13 08:57:13 +00:00
|
|
|
|
2012-02-02 10:03:39 +00:00
|
|
|
if ((!is_ap) && (!is_sta))
|
2011-05-13 08:57:13 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
2012-03-13 18:03:21 +00:00
|
|
|
return;
|
2011-05-13 08:57:13 +00:00
|
|
|
|
2012-02-02 10:03:39 +00:00
|
|
|
if (is_sta) {
|
2012-03-14 04:32:10 +00:00
|
|
|
wl1271_configure_wowlan(wl, NULL);
|
|
|
|
|
2012-02-02 10:03:39 +00:00
|
|
|
ret = wl1271_acx_wake_up_conditions(wl, wlvif,
|
|
|
|
wl->conf.conn.wake_up_event,
|
|
|
|
wl->conf.conn.listen_interval);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
wl1271_error("resume: wake up conditions failed: %d",
|
|
|
|
ret);
|
|
|
|
|
|
|
|
} else if (is_ap) {
|
|
|
|
ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
|
|
|
|
}
|
2011-05-13 08:57:13 +00:00
|
|
|
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
}
|
|
|
|
|
2011-05-13 08:57:09 +00:00
|
|
|
static int wl1271_op_suspend(struct ieee80211_hw *hw,
|
|
|
|
struct cfg80211_wowlan *wow)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
2011-10-10 08:13:13 +00:00
|
|
|
struct wl12xx_vif *wlvif;
|
2011-06-06 09:21:52 +00:00
|
|
|
int ret;
|
|
|
|
|
2011-05-13 08:57:09 +00:00
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
|
2012-03-14 04:32:10 +00:00
|
|
|
WARN_ON(!wow);
|
2011-05-13 08:57:11 +00:00
|
|
|
|
2012-02-27 22:41:33 +00:00
|
|
|
wl1271_tx_flush(wl);
|
|
|
|
|
2012-03-13 18:03:21 +00:00
|
|
|
mutex_lock(&wl->mutex);
|
2011-06-06 09:21:52 +00:00
|
|
|
wl->wow_enabled = true;
|
2011-10-10 08:13:13 +00:00
|
|
|
wl12xx_for_each_wlvif(wl, wlvif) {
|
2012-03-14 04:32:10 +00:00
|
|
|
ret = wl1271_configure_suspend(wl, wlvif, wow);
|
2011-10-10 08:13:13 +00:00
|
|
|
if (ret < 0) {
|
2012-04-16 10:57:02 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
2011-10-10 08:13:13 +00:00
|
|
|
wl1271_warning("couldn't prepare device to suspend");
|
|
|
|
return ret;
|
|
|
|
}
|
2011-06-06 09:21:52 +00:00
|
|
|
}
|
2012-03-13 18:03:21 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
2011-06-06 09:21:52 +00:00
|
|
|
/* flush any remaining work */
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
|
2011-05-13 08:57:11 +00:00
|
|
|
|
2011-06-06 09:21:52 +00:00
|
|
|
/*
|
|
|
|
* disable and re-enable interrupts in order to flush
|
|
|
|
* the threaded_irq
|
|
|
|
*/
|
2012-04-11 08:03:14 +00:00
|
|
|
wlcore_disable_interrupts(wl);
|
2011-06-06 09:21:52 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* set suspended flag to avoid triggering a new threaded_irq
|
|
|
|
* work. no need for spinlock as interrupts are disabled.
|
|
|
|
*/
|
|
|
|
set_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
|
|
|
|
|
2012-04-11 08:03:14 +00:00
|
|
|
wlcore_enable_interrupts(wl);
|
2011-06-06 09:21:52 +00:00
|
|
|
flush_work(&wl->tx_work);
|
|
|
|
flush_delayed_work(&wl->elp_work);
|
2011-05-13 08:57:11 +00:00
|
|
|
|
2011-05-13 08:57:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wl1271_op_resume(struct ieee80211_hw *hw)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
2011-10-10 08:13:13 +00:00
|
|
|
struct wl12xx_vif *wlvif;
|
2011-06-06 09:21:52 +00:00
|
|
|
unsigned long flags;
|
|
|
|
bool run_irq_work = false;
|
|
|
|
|
2011-05-13 08:57:09 +00:00
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d",
|
|
|
|
wl->wow_enabled);
|
2011-06-06 09:21:52 +00:00
|
|
|
WARN_ON(!wl->wow_enabled);
|
2011-05-13 08:57:11 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* re-enable irq_work enqueuing, and call irq_work directly if
|
|
|
|
* there is a pending work.
|
|
|
|
*/
|
2011-06-06 09:21:52 +00:00
|
|
|
spin_lock_irqsave(&wl->wl_lock, flags);
|
|
|
|
clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
|
|
|
|
if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags))
|
|
|
|
run_irq_work = true;
|
|
|
|
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
2011-05-13 08:57:13 +00:00
|
|
|
|
2011-06-06 09:21:52 +00:00
|
|
|
if (run_irq_work) {
|
|
|
|
wl1271_debug(DEBUG_MAC80211,
|
|
|
|
"run postponed irq_work directly");
|
|
|
|
wl1271_irq(0, wl);
|
2012-04-11 08:03:14 +00:00
|
|
|
wlcore_enable_interrupts(wl);
|
2011-05-13 08:57:11 +00:00
|
|
|
}
|
2012-03-13 18:03:21 +00:00
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
2011-10-10 08:13:13 +00:00
|
|
|
wl12xx_for_each_wlvif(wl, wlvif) {
|
|
|
|
wl1271_configure_resume(wl, wlvif);
|
|
|
|
}
|
2011-06-06 09:21:53 +00:00
|
|
|
wl->wow_enabled = false;
|
2012-03-13 18:03:21 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
2011-05-13 08:57:11 +00:00
|
|
|
|
2011-05-13 08:57:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-05-18 20:51:26 +00:00
|
|
|
#endif
|
2011-05-13 08:57:09 +00:00
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
static int wl1271_op_start(struct ieee80211_hw *hw)
|
2010-03-18 10:26:39 +00:00
|
|
|
{
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 start");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have to delay the booting of the hardware because
|
|
|
|
* we need to know the local MAC address before downloading and
|
|
|
|
* initializing the firmware. The MAC address cannot be changed
|
|
|
|
* after boot, and without the proper MAC address, the firmware
|
|
|
|
* will not function properly.
|
|
|
|
*
|
|
|
|
* The MAC address is first known when the corresponding interface
|
|
|
|
* is added. That is where we will initialize the hardware.
|
|
|
|
*/
|
|
|
|
|
2012-01-31 09:57:25 +00:00
|
|
|
return 0;
|
2010-03-18 10:26:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void wl1271_op_stop(struct ieee80211_hw *hw)
|
|
|
|
{
|
2011-10-10 08:12:52 +00:00
|
|
|
struct wl1271 *wl = hw->priv;
|
|
|
|
int i;
|
|
|
|
|
2010-03-18 10:26:39 +00:00
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
|
2011-10-10 08:12:52 +00:00
|
|
|
|
2012-01-11 07:42:41 +00:00
|
|
|
/*
|
|
|
|
* Interrupts must be disabled before setting the state to OFF.
|
|
|
|
* Otherwise, the interrupt handler might be called and exit without
|
|
|
|
* reading the interrupt status.
|
|
|
|
*/
|
2012-04-11 08:03:14 +00:00
|
|
|
wlcore_disable_interrupts(wl);
|
2011-10-10 08:13:06 +00:00
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
if (wl->state == WL1271_STATE_OFF) {
|
2012-05-20 22:10:11 +00:00
|
|
|
if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS,
|
|
|
|
&wl->flags))
|
|
|
|
wlcore_enable_interrupts(wl);
|
|
|
|
|
2011-10-10 08:13:06 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
2012-01-11 07:42:41 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This will not necessarily enable interrupts as interrupts
|
|
|
|
* may have been disabled when op_stop was called. It will,
|
|
|
|
* however, balance the above call to disable_interrupts().
|
|
|
|
*/
|
2012-04-11 08:03:14 +00:00
|
|
|
wlcore_enable_interrupts(wl);
|
2011-10-10 08:13:06 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-01-11 07:42:41 +00:00
|
|
|
|
2011-10-10 08:12:52 +00:00
|
|
|
/*
|
|
|
|
* this must be before the cancel_work calls below, so that the work
|
|
|
|
* functions don't perform further work.
|
|
|
|
*/
|
|
|
|
wl->state = WL1271_STATE_OFF;
|
2011-10-10 08:13:06 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
2011-10-10 08:12:52 +00:00
|
|
|
wl1271_flush_deferred_work(wl);
|
|
|
|
cancel_delayed_work_sync(&wl->scan_complete_work);
|
|
|
|
cancel_work_sync(&wl->netstack_work);
|
|
|
|
cancel_work_sync(&wl->tx_work);
|
|
|
|
cancel_delayed_work_sync(&wl->elp_work);
|
2012-03-03 20:18:00 +00:00
|
|
|
cancel_delayed_work_sync(&wl->tx_watchdog_work);
|
2012-04-26 07:35:07 +00:00
|
|
|
cancel_delayed_work_sync(&wl->connection_loss_work);
|
2011-10-10 08:12:52 +00:00
|
|
|
|
|
|
|
/* let's notify MAC80211 about the remaining pending TX frames */
|
2012-05-18 04:46:38 +00:00
|
|
|
wl12xx_tx_reset(wl);
|
2011-10-10 08:12:52 +00:00
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
wl1271_power_off(wl);
|
2012-05-20 22:10:11 +00:00
|
|
|
/*
|
|
|
|
* In case a recovery was scheduled, interrupts were disabled to avoid
|
|
|
|
* an interrupt storm. Now that the power is down, it is safe to
|
|
|
|
* re-enable interrupts to balance the disable depth
|
|
|
|
*/
|
|
|
|
if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
|
|
|
|
wlcore_enable_interrupts(wl);
|
2011-10-10 08:12:52 +00:00
|
|
|
|
|
|
|
wl->band = IEEE80211_BAND_2GHZ;
|
|
|
|
|
|
|
|
wl->rx_counter = 0;
|
|
|
|
wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
|
2012-05-10 09:13:30 +00:00
|
|
|
wl->channel_type = NL80211_CHAN_NO_HT;
|
2011-10-10 08:12:52 +00:00
|
|
|
wl->tx_blocks_available = 0;
|
|
|
|
wl->tx_allocated_blocks = 0;
|
|
|
|
wl->tx_results_count = 0;
|
|
|
|
wl->tx_packets_count = 0;
|
|
|
|
wl->time_offset = 0;
|
|
|
|
wl->ap_fw_ps_map = 0;
|
|
|
|
wl->ap_ps_map = 0;
|
|
|
|
wl->sched_scanning = false;
|
2012-06-10 16:10:45 +00:00
|
|
|
wl->sleep_auth = WL1271_PSM_ILLEGAL;
|
2011-10-10 08:12:52 +00:00
|
|
|
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));
|
|
|
|
wl->active_sta_count = 0;
|
|
|
|
|
|
|
|
/* The system link is always allocated */
|
|
|
|
__set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* this is performed after the cancel_work calls and the associated
|
|
|
|
* mutex_lock, so that wl1271_op_add_interface does not accidentally
|
|
|
|
* get executed before all these vars have been reset.
|
|
|
|
*/
|
|
|
|
wl->flags = 0;
|
|
|
|
|
|
|
|
wl->tx_blocks_freed = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_TX_QUEUES; i++) {
|
|
|
|
wl->tx_pkts_freed[i] = 0;
|
|
|
|
wl->tx_allocated_pkts[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
wl1271_debugfs_reset(wl);
|
|
|
|
|
2012-05-10 09:13:54 +00:00
|
|
|
kfree(wl->fw_status_1);
|
|
|
|
wl->fw_status_1 = NULL;
|
|
|
|
wl->fw_status_2 = NULL;
|
2011-10-10 08:12:52 +00:00
|
|
|
kfree(wl->tx_res_if);
|
|
|
|
wl->tx_res_if = NULL;
|
|
|
|
kfree(wl->target_mem_map);
|
|
|
|
wl->target_mem_map = NULL;
|
|
|
|
|
|
|
|
mutex_unlock(&wl->mutex);
|
2010-03-18 10:26:39 +00:00
|
|
|
}
|
|
|
|
|
2011-10-10 08:13:15 +00:00
|
|
|
static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx)
|
|
|
|
{
|
|
|
|
u8 policy = find_first_zero_bit(wl->rate_policies_map,
|
|
|
|
WL12XX_MAX_RATE_POLICIES);
|
|
|
|
if (policy >= WL12XX_MAX_RATE_POLICIES)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
__set_bit(policy, wl->rate_policies_map);
|
|
|
|
*idx = policy;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wl12xx_free_rate_policy(struct wl1271 *wl, u8 *idx)
|
|
|
|
{
|
|
|
|
if (WARN_ON(*idx >= WL12XX_MAX_RATE_POLICIES))
|
|
|
|
return;
|
|
|
|
|
|
|
|
__clear_bit(*idx, wl->rate_policies_map);
|
|
|
|
*idx = WL12XX_MAX_RATE_POLICIES;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:45 +00:00
|
|
|
static u8 wl12xx_get_role_type(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
2011-08-14 10:17:08 +00:00
|
|
|
{
|
2011-10-05 09:55:45 +00:00
|
|
|
switch (wlvif->bss_type) {
|
2011-08-14 10:17:08 +00:00
|
|
|
case BSS_TYPE_AP_BSS:
|
2011-10-05 09:55:47 +00:00
|
|
|
if (wlvif->p2p)
|
2011-08-28 12:23:01 +00:00
|
|
|
return WL1271_ROLE_P2P_GO;
|
|
|
|
else
|
|
|
|
return WL1271_ROLE_AP;
|
2011-08-14 10:17:08 +00:00
|
|
|
|
|
|
|
case BSS_TYPE_STA_BSS:
|
2011-10-05 09:55:47 +00:00
|
|
|
if (wlvif->p2p)
|
2011-08-28 12:23:01 +00:00
|
|
|
return WL1271_ROLE_P2P_CL;
|
|
|
|
else
|
|
|
|
return WL1271_ROLE_STA;
|
2011-08-14 10:17:08 +00:00
|
|
|
|
2011-08-14 10:17:26 +00:00
|
|
|
case BSS_TYPE_IBSS:
|
|
|
|
return WL1271_ROLE_IBSS;
|
|
|
|
|
2011-08-14 10:17:08 +00:00
|
|
|
default:
|
2011-10-05 09:55:45 +00:00
|
|
|
wl1271_error("invalid bss_type: %d", wlvif->bss_type);
|
2011-08-14 10:17:08 +00:00
|
|
|
}
|
|
|
|
return WL12XX_INVALID_ROLE_TYPE;
|
|
|
|
}
|
|
|
|
|
2011-10-10 08:12:53 +00:00
|
|
|
static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
|
2011-10-05 09:55:41 +00:00
|
|
|
{
|
2011-10-05 09:55:56 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2011-10-10 08:13:15 +00:00
|
|
|
int i;
|
2011-10-05 09:55:56 +00:00
|
|
|
|
2011-10-10 08:12:58 +00:00
|
|
|
/* clear everything but the persistent data */
|
|
|
|
memset(wlvif, 0, offsetof(struct wl12xx_vif, persistent));
|
2011-10-05 09:55:56 +00:00
|
|
|
|
|
|
|
switch (ieee80211_vif_type_p2p(vif)) {
|
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
|
|
wlvif->p2p = 1;
|
|
|
|
/* fall-through */
|
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
wlvif->bss_type = BSS_TYPE_STA_BSS;
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
wlvif->bss_type = BSS_TYPE_IBSS;
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
|
|
wlvif->p2p = 1;
|
|
|
|
/* fall-through */
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
wlvif->bss_type = BSS_TYPE_AP_BSS;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
wlvif->bss_type = MAX_BSS_TYPE;
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:51 +00:00
|
|
|
wlvif->role_id = WL12XX_INVALID_ROLE_ID;
|
2011-10-05 09:55:52 +00:00
|
|
|
wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID;
|
2011-10-05 09:55:57 +00:00
|
|
|
wlvif->dev_hlid = WL12XX_INVALID_LINK_ID;
|
2011-10-05 09:55:54 +00:00
|
|
|
|
2011-10-05 09:55:56 +00:00
|
|
|
if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
|
|
|
|
wlvif->bss_type == BSS_TYPE_IBSS) {
|
|
|
|
/* init sta/ibss data */
|
|
|
|
wlvif->sta.hlid = WL12XX_INVALID_LINK_ID;
|
2011-10-10 08:13:15 +00:00
|
|
|
wl12xx_allocate_rate_policy(wl, &wlvif->sta.basic_rate_idx);
|
|
|
|
wl12xx_allocate_rate_policy(wl, &wlvif->sta.ap_rate_idx);
|
|
|
|
wl12xx_allocate_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
|
2012-05-10 09:14:05 +00:00
|
|
|
wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
|
|
|
|
wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC;
|
|
|
|
wlvif->rate_set = CONF_TX_RATE_MASK_BASIC;
|
2011-10-05 09:55:56 +00:00
|
|
|
} else {
|
|
|
|
/* init ap data */
|
|
|
|
wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
|
|
|
|
wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID;
|
2011-10-10 08:13:15 +00:00
|
|
|
wl12xx_allocate_rate_policy(wl, &wlvif->ap.mgmt_rate_idx);
|
|
|
|
wl12xx_allocate_rate_policy(wl, &wlvif->ap.bcast_rate_idx);
|
|
|
|
for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
|
|
|
|
wl12xx_allocate_rate_policy(wl,
|
|
|
|
&wlvif->ap.ucast_rate_idx[i]);
|
2012-05-10 09:14:05 +00:00
|
|
|
wlvif->basic_rate_set = CONF_TX_AP_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;
|
|
|
|
/* TODO: this seems to be used only for STA, check it */
|
|
|
|
wlvif->rate_set = CONF_TX_AP_ENABLED_RATES;
|
2011-10-05 09:55:56 +00:00
|
|
|
}
|
2011-10-05 09:55:54 +00:00
|
|
|
|
2011-10-10 08:12:53 +00:00
|
|
|
wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
|
|
|
|
wlvif->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5;
|
2011-10-05 09:55:58 +00:00
|
|
|
wlvif->beacon_int = WL1271_DEFAULT_BEACON_INT;
|
|
|
|
|
2011-10-10 08:13:09 +00:00
|
|
|
/*
|
|
|
|
* mac80211 configures some values globally, while we treat them
|
|
|
|
* per-interface. thus, on init, we have to copy them from wl
|
|
|
|
*/
|
|
|
|
wlvif->band = wl->band;
|
2011-10-10 08:13:10 +00:00
|
|
|
wlvif->channel = wl->channel;
|
2011-10-10 08:13:11 +00:00
|
|
|
wlvif->power_level = wl->power_level;
|
2012-05-10 09:13:30 +00:00
|
|
|
wlvif->channel_type = wl->channel_type;
|
2011-10-10 08:13:09 +00:00
|
|
|
|
2011-10-10 08:12:59 +00:00
|
|
|
INIT_WORK(&wlvif->rx_streaming_enable_work,
|
|
|
|
wl1271_rx_streaming_enable_work);
|
|
|
|
INIT_WORK(&wlvif->rx_streaming_disable_work,
|
|
|
|
wl1271_rx_streaming_disable_work);
|
2011-10-10 08:12:54 +00:00
|
|
|
INIT_LIST_HEAD(&wlvif->list);
|
2011-10-05 09:56:00 +00:00
|
|
|
|
2011-10-10 08:12:59 +00:00
|
|
|
setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
|
|
|
|
(unsigned long) wlvif);
|
2011-10-05 09:55:56 +00:00
|
|
|
return 0;
|
2011-10-05 09:55:41 +00:00
|
|
|
}
|
|
|
|
|
2011-10-10 08:12:49 +00:00
|
|
|
static bool wl12xx_init_fw(struct wl1271 *wl)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
2009-12-11 13:41:01 +00:00
|
|
|
int retries = WL1271_BOOT_RETRIES;
|
2010-10-28 19:46:43 +00:00
|
|
|
bool booted = false;
|
2011-10-10 08:12:49 +00:00
|
|
|
struct wiphy *wiphy = wl->hw->wiphy;
|
|
|
|
int ret;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2009-12-11 13:41:01 +00:00
|
|
|
while (retries) {
|
|
|
|
retries--;
|
2012-02-06 10:47:54 +00:00
|
|
|
ret = wl12xx_chip_wakeup(wl, false);
|
2009-12-11 13:41:01 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto power_off;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2012-04-11 08:03:14 +00:00
|
|
|
ret = wl->ops->boot(wl);
|
2009-12-11 13:41:01 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto power_off;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-10-05 09:55:40 +00:00
|
|
|
ret = wl1271_hw_init(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto irq_disable;
|
|
|
|
|
2010-10-28 19:46:43 +00:00
|
|
|
booted = true;
|
|
|
|
break;
|
2009-10-13 09:47:45 +00:00
|
|
|
|
2009-12-11 13:41:01 +00:00
|
|
|
irq_disable:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
/* Unlocking the mutex in the middle of handling is
|
|
|
|
inherently unsafe. In this case we deem it safe to do,
|
|
|
|
because we need to let any possibly pending IRQ out of
|
|
|
|
the system (and while we are WL1271_STATE_OFF the IRQ
|
|
|
|
work function will not do anything.) Also, any other
|
|
|
|
possible concurrent operations will fail due to the
|
|
|
|
current state, hence the wl1271 struct should be safe. */
|
2012-04-11 08:03:14 +00:00
|
|
|
wlcore_disable_interrupts(wl);
|
2011-03-01 13:14:41 +00:00
|
|
|
wl1271_flush_deferred_work(wl);
|
|
|
|
cancel_work_sync(&wl->netstack_work);
|
2009-12-11 13:41:01 +00:00
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
power_off:
|
|
|
|
wl1271_power_off(wl);
|
|
|
|
}
|
2009-10-13 09:47:45 +00:00
|
|
|
|
2010-10-28 19:46:43 +00:00
|
|
|
if (!booted) {
|
|
|
|
wl1271_error("firmware boot failed despite %d retries",
|
|
|
|
WL1271_BOOT_RETRIES);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2011-01-23 06:27:22 +00:00
|
|
|
wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str);
|
2010-10-28 19:46:43 +00:00
|
|
|
|
|
|
|
/* update hw/fw version info in wiphy struct */
|
|
|
|
wiphy->hw_version = wl->chip.id;
|
2011-01-23 06:27:22 +00:00
|
|
|
strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
|
2010-10-28 19:46:43 +00:00
|
|
|
sizeof(wiphy->fw_version));
|
|
|
|
|
2010-12-03 15:05:40 +00:00
|
|
|
/*
|
|
|
|
* Now we know if 11a is supported (info from the NVS), so disable
|
|
|
|
* 11a channels if not supported
|
|
|
|
*/
|
|
|
|
if (!wl->enable_11a)
|
|
|
|
wiphy->bands[IEEE80211_BAND_5GHZ]->n_channels = 0;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "11a is %ssupported",
|
|
|
|
wl->enable_11a ? "" : "not ");
|
|
|
|
|
2011-10-10 08:12:49 +00:00
|
|
|
wl->state = WL1271_STATE_ON;
|
|
|
|
out:
|
|
|
|
return booted;
|
|
|
|
}
|
|
|
|
|
2011-12-18 18:25:43 +00:00
|
|
|
static bool wl12xx_dev_role_started(struct wl12xx_vif *wlvif)
|
|
|
|
{
|
|
|
|
return wlvif->dev_hlid != WL12XX_INVALID_LINK_ID;
|
|
|
|
}
|
|
|
|
|
2012-02-06 11:07:52 +00:00
|
|
|
/*
|
|
|
|
* Check whether a fw switch (i.e. moving from one loaded
|
|
|
|
* fw to another) is needed. This function is also responsible
|
|
|
|
* for updating wl->last_vif_count, so it must be called before
|
|
|
|
* loading a non-plt fw (so the correct fw (single-role/multi-role)
|
|
|
|
* will be used).
|
|
|
|
*/
|
|
|
|
static bool wl12xx_need_fw_change(struct wl1271 *wl,
|
|
|
|
struct vif_counter_data vif_counter_data,
|
|
|
|
bool add)
|
|
|
|
{
|
|
|
|
enum wl12xx_fw_type current_fw = wl->fw_type;
|
|
|
|
u8 vif_count = vif_counter_data.counter;
|
|
|
|
|
|
|
|
if (test_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* increase the vif count if this is a new vif */
|
|
|
|
if (add && !vif_counter_data.cur_vif_running)
|
|
|
|
vif_count++;
|
|
|
|
|
|
|
|
wl->last_vif_count = vif_count;
|
|
|
|
|
|
|
|
/* no need for fw change if the device is OFF */
|
|
|
|
if (wl->state == WL1271_STATE_OFF)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (vif_count > 1 && current_fw == WL12XX_FW_TYPE_NORMAL)
|
|
|
|
return true;
|
|
|
|
if (vif_count <= 1 && current_fw == WL12XX_FW_TYPE_MULTI)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-02-06 10:47:56 +00:00
|
|
|
/*
|
|
|
|
* Enter "forced psm". Make sure the sta is in psm against the ap,
|
|
|
|
* to make the fw switch a bit more disconnection-persistent.
|
|
|
|
*/
|
|
|
|
static void wl12xx_force_active_psm(struct wl1271 *wl)
|
|
|
|
{
|
|
|
|
struct wl12xx_vif *wlvif;
|
|
|
|
|
|
|
|
wl12xx_for_each_wlvif_sta(wl, wlvif) {
|
|
|
|
wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-10 08:12:49 +00:00
|
|
|
static int wl1271_op_add_interface(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2012-02-06 11:07:52 +00:00
|
|
|
struct vif_counter_data vif_count;
|
2011-10-10 08:12:49 +00:00
|
|
|
int ret = 0;
|
|
|
|
u8 role_type;
|
|
|
|
bool booted = false;
|
|
|
|
|
2012-01-19 08:29:58 +00:00
|
|
|
vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
|
|
|
|
IEEE80211_VIF_SUPPORTS_CQM_RSSI;
|
2012-01-19 08:29:57 +00:00
|
|
|
|
2011-10-10 08:12:49 +00:00
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
|
|
|
|
ieee80211_vif_type_p2p(vif), vif->addr);
|
|
|
|
|
2012-02-06 11:07:52 +00:00
|
|
|
wl12xx_get_vif_count(hw, vif, &vif_count);
|
|
|
|
|
2011-10-10 08:12:49 +00:00
|
|
|
mutex_lock(&wl->mutex);
|
2011-10-10 08:13:16 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_unlock;
|
|
|
|
|
2011-10-10 08:12:49 +00:00
|
|
|
/*
|
|
|
|
* in some very corner case HW recovery scenarios its possible to
|
|
|
|
* get here before __wl1271_op_remove_interface is complete, so
|
|
|
|
* opt out if that is the case.
|
|
|
|
*/
|
2011-10-10 08:13:06 +00:00
|
|
|
if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags) ||
|
|
|
|
test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) {
|
2011-10-10 08:12:49 +00:00
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2012-02-06 10:47:54 +00:00
|
|
|
|
2011-10-10 08:12:53 +00:00
|
|
|
ret = wl12xx_init_vif_data(wl, vif);
|
2011-10-10 08:12:49 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
wlvif->wl = wl;
|
|
|
|
role_type = wl12xx_get_role_type(wl, wlvif);
|
|
|
|
if (role_type == WL12XX_INVALID_ROLE_TYPE) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2012-02-06 11:07:52 +00:00
|
|
|
if (wl12xx_need_fw_change(wl, vif_count, true)) {
|
2012-02-06 10:47:56 +00:00
|
|
|
wl12xx_force_active_psm(wl);
|
2012-03-04 08:55:54 +00:00
|
|
|
set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags);
|
2012-02-06 11:07:52 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
wl1271_recovery_work(&wl->recovery_work);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-10 08:12:49 +00:00
|
|
|
/*
|
|
|
|
* TODO: after the nvs issue will be solved, move this block
|
|
|
|
* to start(), and make sure here the driver is ON.
|
|
|
|
*/
|
|
|
|
if (wl->state == WL1271_STATE_OFF) {
|
|
|
|
/*
|
|
|
|
* we still need this in order to configure the fw
|
|
|
|
* while uploading the nvs
|
|
|
|
*/
|
2011-12-23 07:32:17 +00:00
|
|
|
memcpy(wl->addresses[0].addr, vif->addr, ETH_ALEN);
|
2011-10-10 08:12:49 +00:00
|
|
|
|
|
|
|
booted = wl12xx_init_fw(wl);
|
|
|
|
if (!booted) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
|
|
|
|
wlvif->bss_type == BSS_TYPE_IBSS) {
|
|
|
|
/*
|
|
|
|
* The device role is a special role used for
|
|
|
|
* rx and tx frames prior to association (as
|
|
|
|
* the STA role can get packets only from
|
|
|
|
* its associated bssid)
|
|
|
|
*/
|
|
|
|
ret = wl12xx_cmd_role_enable(wl, vif->addr,
|
|
|
|
WL1271_ROLE_DEVICE,
|
|
|
|
&wlvif->dev_role_id);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = wl12xx_cmd_role_enable(wl, vif->addr,
|
|
|
|
role_type, &wlvif->role_id);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = wl1271_init_vif_specific(wl, vif);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-10-10 08:12:54 +00:00
|
|
|
list_add(&wlvif->list, &wl->wlvif_list);
|
2011-10-10 08:13:06 +00:00
|
|
|
set_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags);
|
2011-10-11 09:49:15 +00:00
|
|
|
|
|
|
|
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
|
|
|
|
wl->ap_count++;
|
|
|
|
else
|
|
|
|
wl->sta_count++;
|
2009-10-13 09:47:45 +00:00
|
|
|
out:
|
2011-10-10 08:13:16 +00:00
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
out_unlock:
|
2009-08-06 13:25:28 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-04-18 11:15:28 +00:00
|
|
|
static void __wl1271_op_remove_interface(struct wl1271 *wl,
|
2011-10-05 09:55:45 +00:00
|
|
|
struct ieee80211_vif *vif,
|
2011-04-18 11:15:28 +00:00
|
|
|
bool reset_tx_queues)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
2011-10-05 09:55:45 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2011-10-10 08:13:15 +00:00
|
|
|
int i, ret;
|
2012-06-10 16:10:45 +00:00
|
|
|
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-03-18 10:26:39 +00:00
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-10-10 08:13:06 +00:00
|
|
|
if (!test_and_clear_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
|
|
|
|
return;
|
|
|
|
|
2011-03-29 13:43:50 +00:00
|
|
|
/* because of hardware recovery, we may get here twice */
|
|
|
|
if (wl->state != WL1271_STATE_ON)
|
|
|
|
return;
|
|
|
|
|
2010-03-18 10:26:39 +00:00
|
|
|
wl1271_info("down");
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-10-10 08:12:52 +00:00
|
|
|
if (wl->scan.state != WL1271_SCAN_STATE_IDLE &&
|
|
|
|
wl->scan_vif == vif) {
|
2012-03-03 20:18:00 +00:00
|
|
|
/*
|
|
|
|
* Rearm the tx watchdog just before idling scan. This
|
|
|
|
* prevents just-finished scans from triggering the watchdog
|
|
|
|
*/
|
|
|
|
wl12xx_rearm_tx_watchdog_locked(wl);
|
|
|
|
|
2010-07-08 14:50:07 +00:00
|
|
|
wl->scan.state = WL1271_SCAN_STATE_IDLE;
|
2011-03-21 21:16:14 +00:00
|
|
|
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
|
2011-10-05 09:55:39 +00:00
|
|
|
wl->scan_vif = NULL;
|
2010-10-26 11:24:38 +00:00
|
|
|
wl->scan.req = NULL;
|
2010-07-29 01:54:45 +00:00
|
|
|
ieee80211_scan_completed(wl->hw, true);
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
|
2011-08-14 10:17:08 +00:00
|
|
|
if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
|
|
|
|
/* disable active roles */
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto deinit;
|
|
|
|
|
2011-12-18 18:25:44 +00:00
|
|
|
if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
|
|
|
|
wlvif->bss_type == BSS_TYPE_IBSS) {
|
|
|
|
if (wl12xx_dev_role_started(wlvif))
|
|
|
|
wl12xx_stop_dev(wl, wlvif);
|
|
|
|
|
2011-10-05 09:55:52 +00:00
|
|
|
ret = wl12xx_cmd_role_disable(wl, &wlvif->dev_role_id);
|
2011-08-14 10:17:09 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto deinit;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl12xx_cmd_role_disable(wl, &wlvif->role_id);
|
2011-08-14 10:17:08 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto deinit;
|
|
|
|
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
}
|
|
|
|
deinit:
|
2011-08-14 10:17:21 +00:00
|
|
|
/* clear all hlids (except system_hlid) */
|
2011-10-05 09:55:57 +00:00
|
|
|
wlvif->dev_hlid = WL12XX_INVALID_LINK_ID;
|
2011-10-10 08:13:15 +00:00
|
|
|
|
|
|
|
if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
|
|
|
|
wlvif->bss_type == BSS_TYPE_IBSS) {
|
|
|
|
wlvif->sta.hlid = WL12XX_INVALID_LINK_ID;
|
|
|
|
wl12xx_free_rate_policy(wl, &wlvif->sta.basic_rate_idx);
|
|
|
|
wl12xx_free_rate_policy(wl, &wlvif->sta.ap_rate_idx);
|
|
|
|
wl12xx_free_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
|
|
|
|
} else {
|
|
|
|
wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
|
|
|
|
wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID;
|
|
|
|
wl12xx_free_rate_policy(wl, &wlvif->ap.mgmt_rate_idx);
|
|
|
|
wl12xx_free_rate_policy(wl, &wlvif->ap.bcast_rate_idx);
|
|
|
|
for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
|
|
|
|
wl12xx_free_rate_policy(wl,
|
|
|
|
&wlvif->ap.ucast_rate_idx[i]);
|
2012-03-19 09:32:55 +00:00
|
|
|
wl1271_free_ap_keys(wl, wlvif);
|
2011-10-10 08:13:15 +00:00
|
|
|
}
|
2011-08-14 10:17:08 +00:00
|
|
|
|
2012-03-19 10:06:27 +00:00
|
|
|
dev_kfree_skb(wlvif->probereq);
|
|
|
|
wlvif->probereq = NULL;
|
2011-10-10 08:12:51 +00:00
|
|
|
wl12xx_tx_reset_wlvif(wl, wlvif);
|
2011-10-10 08:13:17 +00:00
|
|
|
if (wl->last_wlvif == wlvif)
|
|
|
|
wl->last_wlvif = NULL;
|
2011-10-10 08:12:54 +00:00
|
|
|
list_del(&wlvif->list);
|
2011-10-05 09:56:05 +00:00
|
|
|
memset(wlvif->ap.sta_hlid_map, 0, sizeof(wlvif->ap.sta_hlid_map));
|
2011-10-05 09:55:51 +00:00
|
|
|
wlvif->role_id = WL12XX_INVALID_ROLE_ID;
|
2011-10-05 09:55:52 +00:00
|
|
|
wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID;
|
2009-10-12 12:08:43 +00:00
|
|
|
|
2012-06-10 16:10:45 +00:00
|
|
|
if (is_ap)
|
2011-10-11 09:49:15 +00:00
|
|
|
wl->ap_count--;
|
|
|
|
else
|
|
|
|
wl->sta_count--;
|
|
|
|
|
2012-06-10 16:10:45 +00:00
|
|
|
/* Last AP, have more stations. Configure according to STA. */
|
|
|
|
if (wl->ap_count == 0 && is_ap && wl->sta_count) {
|
|
|
|
u8 sta_auth = wl->conf.conn.sta_sleep_auth;
|
|
|
|
/* Configure for power according to debugfs */
|
|
|
|
if (sta_auth != WL1271_PSM_ILLEGAL)
|
|
|
|
wl1271_acx_sleep_auth(wl, sta_auth);
|
|
|
|
/* Configure for power always on */
|
|
|
|
else if (wl->quirks & WLCORE_QUIRK_NO_ELP)
|
|
|
|
wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
|
|
|
|
/* Configure for ELP power saving */
|
|
|
|
else
|
|
|
|
wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
|
|
|
|
}
|
|
|
|
|
2011-10-10 08:12:52 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
2012-01-31 09:57:20 +00:00
|
|
|
|
2011-10-10 08:12:59 +00:00
|
|
|
del_timer_sync(&wlvif->rx_streaming_timer);
|
|
|
|
cancel_work_sync(&wlvif->rx_streaming_enable_work);
|
|
|
|
cancel_work_sync(&wlvif->rx_streaming_disable_work);
|
2010-04-09 08:07:26 +00:00
|
|
|
|
2011-10-10 08:12:52 +00:00
|
|
|
mutex_lock(&wl->mutex);
|
2010-09-21 04:23:30 +00:00
|
|
|
}
|
2010-04-09 08:07:26 +00:00
|
|
|
|
2010-09-21 04:23:30 +00:00
|
|
|
static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
2011-10-10 08:13:06 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2011-10-10 08:13:13 +00:00
|
|
|
struct wl12xx_vif *iter;
|
2012-02-06 11:07:52 +00:00
|
|
|
struct vif_counter_data vif_count;
|
|
|
|
bool cancel_recovery = true;
|
2010-09-21 04:23:30 +00:00
|
|
|
|
2012-02-06 11:07:52 +00:00
|
|
|
wl12xx_get_vif_count(hw, vif, &vif_count);
|
2010-09-21 04:23:30 +00:00
|
|
|
mutex_lock(&wl->mutex);
|
2011-10-10 08:13:06 +00:00
|
|
|
|
|
|
|
if (wl->state == WL1271_STATE_OFF ||
|
|
|
|
!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
|
|
|
|
goto out;
|
|
|
|
|
2010-11-18 13:19:02 +00:00
|
|
|
/*
|
|
|
|
* wl->vif can be null here if someone shuts down the interface
|
|
|
|
* just when hardware recovery has been started.
|
|
|
|
*/
|
2011-10-10 08:13:13 +00:00
|
|
|
wl12xx_for_each_wlvif(wl, iter) {
|
|
|
|
if (iter != wlvif)
|
|
|
|
continue;
|
|
|
|
|
2011-10-05 09:55:45 +00:00
|
|
|
__wl1271_op_remove_interface(wl, vif, true);
|
2011-10-10 08:13:13 +00:00
|
|
|
break;
|
2010-11-18 13:19:02 +00:00
|
|
|
}
|
2011-10-10 08:13:13 +00:00
|
|
|
WARN_ON(iter != wlvif);
|
2012-02-06 11:07:52 +00:00
|
|
|
if (wl12xx_need_fw_change(wl, vif_count, false)) {
|
2012-02-06 10:47:56 +00:00
|
|
|
wl12xx_force_active_psm(wl);
|
2012-03-04 08:55:54 +00:00
|
|
|
set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags);
|
2012-02-06 11:07:52 +00:00
|
|
|
wl12xx_queue_recovery_work(wl);
|
|
|
|
cancel_recovery = false;
|
|
|
|
}
|
2011-10-10 08:13:06 +00:00
|
|
|
out:
|
2010-11-18 13:19:02 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
2012-02-06 11:07:52 +00:00
|
|
|
if (cancel_recovery)
|
|
|
|
cancel_work_sync(&wl->recovery_work);
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
|
2011-12-19 10:00:03 +00:00
|
|
|
static int wl12xx_op_change_interface(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *vif,
|
|
|
|
enum nl80211_iftype new_type, bool p2p)
|
|
|
|
{
|
2012-02-06 11:07:52 +00:00
|
|
|
struct wl1271 *wl = hw->priv;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
set_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags);
|
2011-12-19 10:00:03 +00:00
|
|
|
wl1271_op_remove_interface(hw, vif);
|
|
|
|
|
2012-03-04 08:55:50 +00:00
|
|
|
vif->type = new_type;
|
2011-12-19 10:00:03 +00:00
|
|
|
vif->p2p = p2p;
|
2012-02-06 11:07:52 +00:00
|
|
|
ret = wl1271_op_add_interface(hw, vif);
|
|
|
|
|
|
|
|
clear_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags);
|
|
|
|
return ret;
|
2011-12-19 10:00:03 +00:00
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:41 +00:00
|
|
|
static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
|
|
bool set_assoc)
|
2010-04-28 06:50:01 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2011-10-05 09:55:45 +00:00
|
|
|
bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
|
2010-04-28 06:50:01 +00:00
|
|
|
|
2010-05-07 08:39:00 +00:00
|
|
|
/*
|
|
|
|
* One of the side effects of the JOIN command is that is clears
|
|
|
|
* WPA/WPA2 keys from the chipset. Performing a JOIN while associated
|
|
|
|
* to a WPA/WPA2 access point will therefore kill the data-path.
|
2011-03-30 17:18:31 +00:00
|
|
|
* Currently the only valid scenario for JOIN during association
|
|
|
|
* is on roaming, in which case we will also be given new keys.
|
|
|
|
* Keep the below message for now, unless it starts bothering
|
|
|
|
* users who really like to roam a lot :)
|
2010-05-07 08:39:00 +00:00
|
|
|
*/
|
2011-10-10 08:13:00 +00:00
|
|
|
if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
|
2010-05-07 08:39:00 +00:00
|
|
|
wl1271_info("JOIN while associated.");
|
|
|
|
|
2012-02-02 10:22:09 +00:00
|
|
|
/* clear encryption type */
|
|
|
|
wlvif->encryption_type = KEY_NONE;
|
|
|
|
|
2010-05-07 08:39:00 +00:00
|
|
|
if (set_assoc)
|
2011-10-10 08:13:00 +00:00
|
|
|
set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
|
2010-05-07 08:39:00 +00:00
|
|
|
|
2011-08-14 10:17:26 +00:00
|
|
|
if (is_ibss)
|
2011-10-05 09:55:41 +00:00
|
|
|
ret = wl12xx_cmd_role_start_ibss(wl, wlvif);
|
2011-08-14 10:17:26 +00:00
|
|
|
else
|
2011-10-05 09:55:41 +00:00
|
|
|
ret = wl12xx_cmd_role_start_sta(wl, wlvif);
|
2010-04-28 06:50:01 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-10-10 08:13:00 +00:00
|
|
|
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
|
2010-04-28 06:50:01 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The join command disable the keep-alive mode, shut down its process,
|
|
|
|
* and also clear the template config, so we need to reset it all after
|
|
|
|
* the join. The acx_aid starts the keep-alive process, and the order
|
|
|
|
* of the commands below is relevant.
|
|
|
|
*/
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_keep_alive_mode(wl, wlvif, true);
|
2010-04-28 06:50:01 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_aid(wl, wlvif, wlvif->aid);
|
2010-04-28 06:50:01 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-10-05 09:55:43 +00:00
|
|
|
ret = wl12xx_cmd_build_klv_null_data(wl, wlvif);
|
2010-04-28 06:50:01 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_keep_alive_config(wl, wlvif,
|
|
|
|
CMD_TEMPL_KLV_IDX_NULL_DATA,
|
2010-04-28 06:50:01 +00:00
|
|
|
ACX_KEEP_ALIVE_TPL_VALID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:51 +00:00
|
|
|
static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
2009-12-11 13:40:44 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2011-10-10 08:13:08 +00:00
|
|
|
if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
|
2011-10-10 08:13:13 +00:00
|
|
|
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
|
|
|
|
|
2011-09-08 10:01:33 +00:00
|
|
|
wl12xx_cmd_stop_channel_switch(wl);
|
2011-10-10 08:13:13 +00:00
|
|
|
ieee80211_chswitch_done(vif, false);
|
2011-09-08 10:01:33 +00:00
|
|
|
}
|
|
|
|
|
2009-12-11 13:40:44 +00:00
|
|
|
/* to stop listening to a channel, we disconnect */
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
|
2009-12-11 13:40:44 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-06-26 07:36:02 +00:00
|
|
|
/* reset TX security counters on a clean disconnect */
|
2011-10-10 08:12:58 +00:00
|
|
|
wlvif->tx_security_last_seq_lsb = 0;
|
|
|
|
wlvif->tx_security_seq = 0;
|
2011-06-26 07:36:02 +00:00
|
|
|
|
2009-12-11 13:40:44 +00:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:41 +00:00
|
|
|
static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
2010-04-01 08:38:20 +00:00
|
|
|
{
|
2011-10-10 08:13:09 +00:00
|
|
|
wlvif->basic_rate_set = wlvif->bitrate_masks[wlvif->band];
|
2011-10-05 09:55:42 +00:00
|
|
|
wlvif->rate_set = wlvif->basic_rate_set;
|
2010-04-01 08:38:20 +00:00
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:41 +00:00
|
|
|
static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
|
|
bool idle)
|
2010-05-24 08:18:16 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2011-12-18 18:25:41 +00:00
|
|
|
bool cur_idle = !test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
|
|
|
|
|
|
|
|
if (idle == cur_idle)
|
|
|
|
return 0;
|
2010-05-24 08:18:16 +00:00
|
|
|
|
|
|
|
if (idle) {
|
wl12xx: replace dummy_join with ROC/CROC commands
The ROC command asks the fw stay on the channel of the given
hlid. it currently has 2 primary functions:
1. Allow tx/rx from the device role.
In order to tx/rx packets while the stations is not associated
(e.g. auth req/resp), the device role has to be used, along
with ROC on its link.
Keep the logic similiar to the one used in dummy_join. However,
since we can't scan while we ROC, we add CROC before starting
a scan, and ROC again (if needed) on scan complete.
2. Keeping the antenna for a specific link.
We ROC until the connection was completed (after EAPOLs exchange)
in order to prevent BT coex operations from taking the antenna
and failing the connection (after this stage, psm can be used).
During association, we ROC on the station role, and then CROC
the device role, thus assuring being ROC during all the connection
process.
Delete the WL1271_FLAG_JOINED flag, and use a roc bitmap
to indicate what roles are currently ROCed.
Add wl12xx_roc/croc functions in order to wrap the roc/croc
commands while taking care of the roc bitmap.
The current ROC/CROC state-machine is a bit complicated. In
the future we'll probably want to use wpa_supplicant to control
the ROC during connection.
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
2011-08-14 10:17:17 +00:00
|
|
|
/* no need to croc if we weren't busy (e.g. during boot) */
|
2011-12-18 18:25:43 +00:00
|
|
|
if (wl12xx_dev_role_started(wlvif)) {
|
2011-10-11 09:55:44 +00:00
|
|
|
ret = wl12xx_stop_dev(wl, wlvif);
|
2010-05-24 08:18:16 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
2011-10-05 09:55:42 +00:00
|
|
|
wlvif->rate_set =
|
|
|
|
wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
|
|
|
|
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
|
2010-05-24 08:18:16 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
ret = wl1271_acx_keep_alive_config(
|
2011-10-05 09:55:51 +00:00
|
|
|
wl, wlvif, CMD_TEMPL_KLV_IDX_NULL_DATA,
|
2010-05-24 08:18:16 +00:00
|
|
|
ACX_KEEP_ALIVE_TPL_INVALID);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2011-12-18 18:25:41 +00:00
|
|
|
clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
|
2010-05-24 08:18:16 +00:00
|
|
|
} else {
|
2011-05-10 11:46:02 +00:00
|
|
|
/* The current firmware only supports sched_scan in idle */
|
|
|
|
if (wl->sched_scanning) {
|
2012-05-16 08:34:17 +00:00
|
|
|
wl1271_scan_sched_scan_stop(wl, wlvif);
|
2011-05-10 11:46:02 +00:00
|
|
|
ieee80211_sched_scan_stopped(wl->hw);
|
|
|
|
}
|
|
|
|
|
2011-10-11 09:55:44 +00:00
|
|
|
ret = wl12xx_start_dev(wl, wlvif);
|
2010-05-24 08:18:16 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2011-12-18 18:25:41 +00:00
|
|
|
set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
|
2010-05-24 08:18:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-10-10 08:13:12 +00:00
|
|
|
static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
|
|
struct ieee80211_conf *conf, u32 changed)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
2011-10-10 08:13:12 +00:00
|
|
|
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
|
|
|
|
int channel, ret;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
|
|
|
|
|
2010-04-01 08:38:20 +00:00
|
|
|
/* if the channel changes while joined, join again */
|
2010-05-07 08:39:00 +00:00
|
|
|
if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
|
2011-10-10 08:13:09 +00:00
|
|
|
((wlvif->band != conf->channel->band) ||
|
2012-05-10 09:13:30 +00:00
|
|
|
(wlvif->channel != channel) ||
|
|
|
|
(wlvif->channel_type != conf->channel_type))) {
|
2011-09-15 10:00:01 +00:00
|
|
|
/* send all pending packets */
|
2012-06-18 10:21:55 +00:00
|
|
|
ret = wlcore_tx_work_locked(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2011-10-10 08:13:10 +00:00
|
|
|
wlvif->band = conf->channel->band;
|
|
|
|
wlvif->channel = channel;
|
2012-05-10 09:13:30 +00:00
|
|
|
wlvif->channel_type = conf->channel_type;
|
2010-04-01 08:38:20 +00:00
|
|
|
|
2012-05-10 09:13:34 +00:00
|
|
|
if (is_ap) {
|
2012-06-12 09:45:27 +00:00
|
|
|
wl1271_set_band_rate(wl, wlvif);
|
2012-05-10 09:13:34 +00:00
|
|
|
ret = wl1271_init_ap_rates(wl, wlvif);
|
|
|
|
if (ret < 0)
|
|
|
|
wl1271_error("AP rate policy change failed %d",
|
|
|
|
ret);
|
|
|
|
} else {
|
2010-10-16 17:17:02 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2011-10-10 08:13:00 +00:00
|
|
|
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
|
2011-10-05 09:55:41 +00:00
|
|
|
wl1271_set_band_rate(wl, wlvif);
|
2010-10-16 17:17:02 +00:00
|
|
|
|
2011-10-05 09:55:43 +00:00
|
|
|
wlvif->basic_rate =
|
2011-10-05 09:55:41 +00:00
|
|
|
wl1271_tx_min_rate_get(wl,
|
|
|
|
wlvif->basic_rate_set);
|
2011-10-05 09:55:42 +00:00
|
|
|
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
|
2010-04-01 08:38:20 +00:00
|
|
|
if (ret < 0)
|
2010-10-16 17:17:02 +00:00
|
|
|
wl1271_warning("rate policy for channel "
|
2010-04-01 08:38:20 +00:00
|
|
|
"failed %d", ret);
|
2010-10-16 17:17:02 +00:00
|
|
|
|
2012-03-04 08:55:51 +00:00
|
|
|
/*
|
|
|
|
* 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);
|
2010-10-16 17:17:02 +00:00
|
|
|
if (ret < 0)
|
2012-03-04 08:55:51 +00:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = wl12xx_start_dev(wl, wlvif);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2010-10-16 17:17:02 +00:00
|
|
|
}
|
2010-04-01 08:38:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-31 09:57:25 +00:00
|
|
|
if ((changed & IEEE80211_CONF_CHANGE_PS) && !is_ap) {
|
|
|
|
|
|
|
|
if ((conf->flags & IEEE80211_CONF_PS) &&
|
|
|
|
test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
|
2012-02-02 17:06:45 +00:00
|
|
|
!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
|
2012-01-31 09:57:25 +00:00
|
|
|
|
2012-02-02 17:06:45 +00:00
|
|
|
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);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2012-01-31 09:57:25 +00:00
|
|
|
if (ret < 0)
|
2012-02-02 17:06:45 +00:00
|
|
|
wl1271_warning("enter %s ps failed %d",
|
|
|
|
ps_mode_str, ret);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2012-01-31 09:57:25 +00:00
|
|
|
} else if (!(conf->flags & IEEE80211_CONF_PS) &&
|
2012-02-02 17:06:45 +00:00
|
|
|
test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
|
2012-01-31 09:57:25 +00:00
|
|
|
|
|
|
|
wl1271_debug(DEBUG_PSM, "auto ps disabled");
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_ps_set_mode(wl, wlvif,
|
2012-01-31 09:57:23 +00:00
|
|
|
STATION_ACTIVE_MODE);
|
2012-01-31 09:57:25 +00:00
|
|
|
if (ret < 0)
|
|
|
|
wl1271_warning("exit auto ps failed %d", ret);
|
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
|
2011-10-10 08:13:11 +00:00
|
|
|
if (conf->power_level != wlvif->power_level) {
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level);
|
2009-08-06 13:25:28 +00:00
|
|
|
if (ret < 0)
|
2011-10-10 08:13:12 +00:00
|
|
|
return ret;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-10-10 08:13:11 +00:00
|
|
|
wlvif->power_level = conf->power_level;
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
|
2011-10-10 08:13:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d 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.
|
|
|
|
*/
|
2012-05-15 13:46:56 +00:00
|
|
|
if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) ||
|
|
|
|
((changed & IEEE80211_CONF_CHANGE_IDLE) &&
|
|
|
|
(conf->flags & IEEE80211_CONF_IDLE)))
|
2011-10-10 08:13:12 +00:00
|
|
|
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;
|
2012-05-10 09:13:30 +00:00
|
|
|
wl->channel_type = conf->channel_type;
|
2011-10-10 08:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (changed & IEEE80211_CONF_CHANGE_POWER)
|
|
|
|
wl->power_level = conf->power_level;
|
|
|
|
|
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* configure each interface */
|
|
|
|
wl12xx_for_each_wlvif(wl, wlvif) {
|
|
|
|
ret = wl12xx_config_vif(wl, wlvif, conf, changed);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_sleep;
|
|
|
|
}
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
out_sleep:
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-10-13 09:47:59 +00:00
|
|
|
struct wl1271_filter_params {
|
|
|
|
bool enabled;
|
|
|
|
int mc_list_length;
|
|
|
|
u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
|
|
|
|
};
|
|
|
|
|
2010-04-01 21:22:57 +00:00
|
|
|
static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
|
|
|
|
struct netdev_hw_addr_list *mc_list)
|
2009-10-08 18:56:31 +00:00
|
|
|
{
|
|
|
|
struct wl1271_filter_params *fp;
|
2010-04-01 21:22:57 +00:00
|
|
|
struct netdev_hw_addr *ha;
|
2010-04-09 08:07:27 +00:00
|
|
|
struct wl1271 *wl = hw->priv;
|
2009-10-08 18:56:31 +00:00
|
|
|
|
2010-04-09 08:07:27 +00:00
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
|
return 0;
|
2009-10-08 18:56:31 +00:00
|
|
|
|
2009-10-13 09:47:53 +00:00
|
|
|
fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
|
2009-10-08 18:56:31 +00:00
|
|
|
if (!fp) {
|
|
|
|
wl1271_error("Out of memory setting filters.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update multicast filtering parameters */
|
|
|
|
fp->mc_list_length = 0;
|
2010-04-01 21:22:57 +00:00
|
|
|
if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
|
|
|
|
fp->enabled = false;
|
|
|
|
} else {
|
|
|
|
fp->enabled = true;
|
|
|
|
netdev_hw_addr_list_for_each(ha, mc_list) {
|
2009-10-08 18:56:31 +00:00
|
|
|
memcpy(fp->mc_list[fp->mc_list_length],
|
2010-04-01 21:22:57 +00:00
|
|
|
ha->addr, ETH_ALEN);
|
2009-10-08 18:56:31 +00:00
|
|
|
fp->mc_list_length++;
|
2010-04-01 21:22:57 +00:00
|
|
|
}
|
2009-10-08 18:56:31 +00:00
|
|
|
}
|
|
|
|
|
2009-10-13 09:47:59 +00:00
|
|
|
return (u64)(unsigned long)fp;
|
2009-10-08 18:56:31 +00:00
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2009-10-13 09:47:59 +00:00
|
|
|
#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
|
|
|
|
FIF_ALLMULTI | \
|
|
|
|
FIF_FCSFAIL | \
|
|
|
|
FIF_BCN_PRBRESP_PROMISC | \
|
|
|
|
FIF_CONTROL | \
|
|
|
|
FIF_OTHER_BSS)
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
|
|
|
|
unsigned int changed,
|
2009-10-08 18:56:31 +00:00
|
|
|
unsigned int *total, u64 multicast)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
2009-10-13 09:47:59 +00:00
|
|
|
struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
|
2009-08-06 13:25:28 +00:00
|
|
|
struct wl1271 *wl = hw->priv;
|
2011-10-10 08:13:13 +00:00
|
|
|
struct wl12xx_vif *wlvif;
|
2011-10-05 09:55:45 +00:00
|
|
|
|
2009-10-13 09:47:59 +00:00
|
|
|
int ret;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-10-16 17:25:35 +00:00
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter changed %x"
|
|
|
|
" total %x", changed, *total);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2009-10-13 09:47:59 +00:00
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
2010-04-09 08:07:27 +00:00
|
|
|
*total &= WL1271_SUPPORTED_FILTERS;
|
|
|
|
changed &= WL1271_SUPPORTED_FILTERS;
|
|
|
|
|
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF))
|
2009-10-13 09:47:59 +00:00
|
|
|
goto out;
|
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
2009-10-13 09:47:59 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-10-10 08:13:13 +00:00
|
|
|
wl12xx_for_each_wlvif(wl, wlvif) {
|
|
|
|
if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
|
|
|
|
if (*total & FIF_ALLMULTI)
|
|
|
|
ret = wl1271_acx_group_address_tbl(wl, wlvif,
|
|
|
|
false,
|
|
|
|
NULL, 0);
|
|
|
|
else if (fp)
|
|
|
|
ret = wl1271_acx_group_address_tbl(wl, wlvif,
|
|
|
|
fp->enabled,
|
|
|
|
fp->mc_list,
|
|
|
|
fp->mc_list_length);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_sleep;
|
|
|
|
}
|
2010-10-16 17:25:35 +00:00
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-08-14 10:17:04 +00:00
|
|
|
/*
|
|
|
|
* the fw doesn't provide an api to configure the filters. instead,
|
|
|
|
* the filters configuration is based on the active roles / ROC
|
|
|
|
* state.
|
|
|
|
*/
|
2009-10-13 09:47:59 +00:00
|
|
|
|
|
|
|
out_sleep:
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
2010-03-18 10:26:43 +00:00
|
|
|
kfree(fp);
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
|
2011-10-05 09:56:06 +00:00
|
|
|
static int wl1271_record_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
|
|
u8 id, u8 key_type, u8 key_size,
|
|
|
|
const u8 *key, u8 hlid, u32 tx_seq_32,
|
|
|
|
u16 tx_seq_16)
|
2010-10-16 19:39:06 +00:00
|
|
|
{
|
|
|
|
struct wl1271_ap_key *ap_key;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id);
|
|
|
|
|
|
|
|
if (key_size > MAX_KEY_SIZE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find next free entry in ap_keys. Also check we are not replacing
|
|
|
|
* an existing key.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < MAX_NUM_KEYS; i++) {
|
2011-10-05 09:56:06 +00:00
|
|
|
if (wlvif->ap.recorded_keys[i] == NULL)
|
2010-10-16 19:39:06 +00:00
|
|
|
break;
|
|
|
|
|
2011-10-05 09:56:06 +00:00
|
|
|
if (wlvif->ap.recorded_keys[i]->id == id) {
|
2010-10-16 19:39:06 +00:00
|
|
|
wl1271_warning("trying to record key replacement");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == MAX_NUM_KEYS)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL);
|
|
|
|
if (!ap_key)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ap_key->id = id;
|
|
|
|
ap_key->key_type = key_type;
|
|
|
|
ap_key->key_size = key_size;
|
|
|
|
memcpy(ap_key->key, key, key_size);
|
|
|
|
ap_key->hlid = hlid;
|
|
|
|
ap_key->tx_seq_32 = tx_seq_32;
|
|
|
|
ap_key->tx_seq_16 = tx_seq_16;
|
|
|
|
|
2011-10-05 09:56:06 +00:00
|
|
|
wlvif->ap.recorded_keys[i] = ap_key;
|
2010-10-16 19:39:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:56:06 +00:00
|
|
|
static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
2010-10-16 19:39:06 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_NUM_KEYS; i++) {
|
2011-10-05 09:56:06 +00:00
|
|
|
kfree(wlvif->ap.recorded_keys[i]);
|
|
|
|
wlvif->ap.recorded_keys[i] = NULL;
|
2010-10-16 19:39:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:54 +00:00
|
|
|
static int wl1271_ap_init_hwenc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
2010-10-16 19:39:06 +00:00
|
|
|
{
|
|
|
|
int i, ret = 0;
|
|
|
|
struct wl1271_ap_key *key;
|
|
|
|
bool wep_key_added = false;
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_NUM_KEYS; i++) {
|
2011-08-14 10:17:30 +00:00
|
|
|
u8 hlid;
|
2011-10-05 09:56:06 +00:00
|
|
|
if (wlvif->ap.recorded_keys[i] == NULL)
|
2010-10-16 19:39:06 +00:00
|
|
|
break;
|
|
|
|
|
2011-10-05 09:56:06 +00:00
|
|
|
key = wlvif->ap.recorded_keys[i];
|
2011-08-14 10:17:30 +00:00
|
|
|
hlid = key->hlid;
|
|
|
|
if (hlid == WL12XX_INVALID_LINK_ID)
|
2011-10-05 09:55:54 +00:00
|
|
|
hlid = wlvif->ap.bcast_hlid;
|
2011-08-14 10:17:30 +00:00
|
|
|
|
2011-10-05 09:55:54 +00:00
|
|
|
ret = wl1271_cmd_set_ap_key(wl, wlvif, KEY_ADD_OR_REPLACE,
|
2010-10-16 19:39:06 +00:00
|
|
|
key->id, key->key_type,
|
|
|
|
key->key_size, key->key,
|
2011-08-14 10:17:30 +00:00
|
|
|
hlid, key->tx_seq_32,
|
2010-10-16 19:39:06 +00:00
|
|
|
key->tx_seq_16);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (key->key_type == KEY_WEP)
|
|
|
|
wep_key_added = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wep_key_added) {
|
2011-10-05 09:55:59 +00:00
|
|
|
ret = wl12xx_cmd_set_default_wep_key(wl, wlvif->default_key,
|
2011-10-05 09:55:54 +00:00
|
|
|
wlvif->ap.bcast_hlid);
|
2010-10-16 19:39:06 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2011-10-05 09:56:06 +00:00
|
|
|
wl1271_free_ap_keys(wl, wlvif);
|
2010-10-16 19:39:06 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:45 +00:00
|
|
|
static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
|
|
u16 action, u8 id, u8 key_type,
|
2010-10-16 19:39:06 +00:00
|
|
|
u8 key_size, const u8 *key, u32 tx_seq_32,
|
|
|
|
u16 tx_seq_16, struct ieee80211_sta *sta)
|
|
|
|
{
|
|
|
|
int ret;
|
2011-10-05 09:55:45 +00:00
|
|
|
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
|
2010-10-16 19:39:06 +00:00
|
|
|
|
|
|
|
if (is_ap) {
|
|
|
|
struct wl1271_station *wl_sta;
|
|
|
|
u8 hlid;
|
|
|
|
|
|
|
|
if (sta) {
|
|
|
|
wl_sta = (struct wl1271_station *)sta->drv_priv;
|
|
|
|
hlid = wl_sta->hlid;
|
|
|
|
} else {
|
2011-10-05 09:55:54 +00:00
|
|
|
hlid = wlvif->ap.bcast_hlid;
|
2010-10-16 19:39:06 +00:00
|
|
|
}
|
|
|
|
|
2011-10-10 08:13:02 +00:00
|
|
|
if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
|
2010-10-16 19:39:06 +00:00
|
|
|
/*
|
|
|
|
* We do not support removing keys after AP shutdown.
|
|
|
|
* Pretend we do to make mac80211 happy.
|
|
|
|
*/
|
|
|
|
if (action != KEY_ADD_OR_REPLACE)
|
|
|
|
return 0;
|
|
|
|
|
2011-10-05 09:56:06 +00:00
|
|
|
ret = wl1271_record_ap_key(wl, wlvif, id,
|
2010-10-16 19:39:06 +00:00
|
|
|
key_type, key_size,
|
|
|
|
key, hlid, tx_seq_32,
|
|
|
|
tx_seq_16);
|
|
|
|
} else {
|
2011-10-05 09:55:54 +00:00
|
|
|
ret = wl1271_cmd_set_ap_key(wl, wlvif, action,
|
2010-10-16 19:39:06 +00:00
|
|
|
id, key_type, key_size,
|
|
|
|
key, hlid, tx_seq_32,
|
|
|
|
tx_seq_16);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
} else {
|
|
|
|
const u8 *addr;
|
|
|
|
static const u8 bcast_addr[ETH_ALEN] = {
|
|
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
|
|
|
};
|
|
|
|
|
|
|
|
addr = sta ? sta->addr : bcast_addr;
|
|
|
|
|
|
|
|
if (is_zero_ether_addr(addr)) {
|
|
|
|
/* We dont support TX only encryption */
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The wl1271 does not allow to remove unicast keys - they
|
|
|
|
will be cleared automatically on next CMD_JOIN. Ignore the
|
|
|
|
request silently, as we dont want the mac80211 to emit
|
|
|
|
an error message. */
|
|
|
|
if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
|
|
|
|
return 0;
|
|
|
|
|
2011-08-14 10:17:31 +00:00
|
|
|
/* don't remove key if hlid was already deleted */
|
|
|
|
if (action == KEY_REMOVE &&
|
2011-10-05 09:55:53 +00:00
|
|
|
wlvif->sta.hlid == WL12XX_INVALID_LINK_ID)
|
2011-08-14 10:17:31 +00:00
|
|
|
return 0;
|
|
|
|
|
2011-10-05 09:55:54 +00:00
|
|
|
ret = wl1271_cmd_set_sta_key(wl, wlvif, action,
|
2010-10-16 19:39:06 +00:00
|
|
|
id, key_type, key_size,
|
|
|
|
key, addr, tx_seq_32,
|
|
|
|
tx_seq_16);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* the default WEP key needs to be configured at least once */
|
|
|
|
if (key_type == KEY_WEP) {
|
2011-08-14 10:17:07 +00:00
|
|
|
ret = wl12xx_cmd_set_default_wep_key(wl,
|
2011-10-05 09:55:59 +00:00
|
|
|
wlvif->default_key,
|
|
|
|
wlvif->sta.hlid);
|
2010-10-16 19:39:06 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-18 04:46:40 +00:00
|
|
|
static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
2009-08-06 13:25:28 +00:00
|
|
|
struct ieee80211_vif *vif,
|
|
|
|
struct ieee80211_sta *sta,
|
|
|
|
struct ieee80211_key_conf *key_conf)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
2012-05-18 04:46:40 +00:00
|
|
|
|
|
|
|
return wlcore_hw_set_key(wl, cmd, vif, sta, key_conf);
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2011-10-05 09:55:45 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2009-08-06 13:25:28 +00:00
|
|
|
int ret;
|
2009-10-08 18:56:19 +00:00
|
|
|
u32 tx_seq_32 = 0;
|
|
|
|
u16 tx_seq_16 = 0;
|
2009-08-06 13:25:28 +00:00
|
|
|
u8 key_type;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
|
|
|
|
|
2010-10-16 19:39:06 +00:00
|
|
|
wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
|
2009-08-06 13:25:28 +00:00
|
|
|
wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
|
2010-08-10 07:46:38 +00:00
|
|
|
key_conf->cipher, key_conf->keyidx,
|
2009-08-06 13:25:28 +00:00
|
|
|
key_conf->keylen, key_conf->flags);
|
|
|
|
wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
|
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
2010-10-26 11:24:39 +00:00
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
|
|
|
ret = -EAGAIN;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
2009-08-06 13:25:28 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out_unlock;
|
|
|
|
|
2010-08-10 07:46:38 +00:00
|
|
|
switch (key_conf->cipher) {
|
|
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
2009-08-06 13:25:28 +00:00
|
|
|
key_type = KEY_WEP;
|
|
|
|
|
|
|
|
key_conf->hw_key_idx = key_conf->keyidx;
|
|
|
|
break;
|
2010-08-10 07:46:38 +00:00
|
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
2009-08-06 13:25:28 +00:00
|
|
|
key_type = KEY_TKIP;
|
|
|
|
|
|
|
|
key_conf->hw_key_idx = key_conf->keyidx;
|
2011-10-10 08:12:58 +00:00
|
|
|
tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
|
|
|
|
tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
|
2009-08-06 13:25:28 +00:00
|
|
|
break;
|
2010-08-10 07:46:38 +00:00
|
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
2009-08-06 13:25:28 +00:00
|
|
|
key_type = KEY_AES;
|
|
|
|
|
2011-10-23 06:21:54 +00:00
|
|
|
key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
|
2011-10-10 08:12:58 +00:00
|
|
|
tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
|
|
|
|
tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
|
2009-08-06 13:25:28 +00:00
|
|
|
break;
|
2010-09-27 10:42:07 +00:00
|
|
|
case WL1271_CIPHER_SUITE_GEM:
|
|
|
|
key_type = KEY_GEM;
|
2011-10-10 08:12:58 +00:00
|
|
|
tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
|
|
|
|
tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
|
2010-09-27 10:42:07 +00:00
|
|
|
break;
|
2009-08-06 13:25:28 +00:00
|
|
|
default:
|
2010-08-10 07:46:38 +00:00
|
|
|
wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
ret = -EOPNOTSUPP;
|
|
|
|
goto out_sleep;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SET_KEY:
|
2011-10-05 09:55:45 +00:00
|
|
|
ret = wl1271_set_key(wl, wlvif, KEY_ADD_OR_REPLACE,
|
2010-10-16 19:39:06 +00:00
|
|
|
key_conf->keyidx, key_type,
|
|
|
|
key_conf->keylen, key_conf->key,
|
|
|
|
tx_seq_32, tx_seq_16, sta);
|
2009-08-06 13:25:28 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_error("Could not add or replace key");
|
|
|
|
goto out_sleep;
|
|
|
|
}
|
2012-02-02 10:22:09 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* reconfiguring arp response if the unicast (or common)
|
|
|
|
* encryption key type was changed
|
|
|
|
*/
|
|
|
|
if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
|
|
|
|
(sta || key_type == KEY_WEP) &&
|
|
|
|
wlvif->encryption_type != key_type) {
|
|
|
|
wlvif->encryption_type = key_type;
|
|
|
|
ret = wl1271_cmd_build_arp_rsp(wl, wlvif);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_warning("build arp rsp failed: %d", ret);
|
|
|
|
goto out_sleep;
|
|
|
|
}
|
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DISABLE_KEY:
|
2011-10-05 09:55:45 +00:00
|
|
|
ret = wl1271_set_key(wl, wlvif, KEY_REMOVE,
|
2010-10-16 19:39:06 +00:00
|
|
|
key_conf->keyidx, key_type,
|
|
|
|
key_conf->keylen, key_conf->key,
|
|
|
|
0, 0, sta);
|
2009-08-06 13:25:28 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_error("Could not remove key");
|
|
|
|
goto out_sleep;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
wl1271_error("Unsupported key cmd 0x%x", cmd);
|
|
|
|
ret = -EOPNOTSUPP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
out_sleep:
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2012-05-18 04:46:40 +00:00
|
|
|
EXPORT_SYMBOL_GPL(wlcore_set_key);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
|
2010-04-27 09:59:34 +00:00
|
|
|
struct ieee80211_vif *vif,
|
2009-08-06 13:25:28 +00:00
|
|
|
struct cfg80211_scan_request *req)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
|
|
|
int ret;
|
|
|
|
u8 *ssid = NULL;
|
2009-10-13 09:47:50 +00:00
|
|
|
size_t len = 0;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
|
|
|
|
|
|
|
|
if (req->n_ssids) {
|
|
|
|
ssid = req->ssids[0].ssid;
|
2009-10-13 09:47:50 +00:00
|
|
|
len = req->ssids[0].ssid_len;
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
2010-10-26 11:24:38 +00:00
|
|
|
if (wl->state == WL1271_STATE_OFF) {
|
|
|
|
/*
|
|
|
|
* We cannot return -EBUSY here because cfg80211 will expect
|
|
|
|
* a call to ieee80211_scan_completed if we do - in this case
|
|
|
|
* there won't be any call.
|
|
|
|
*/
|
|
|
|
ret = -EAGAIN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
2009-08-06 13:25:28 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2012-03-04 08:55:52 +00:00
|
|
|
/* fail if there is any role in ROC */
|
|
|
|
if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) {
|
2011-12-18 18:25:43 +00:00
|
|
|
/* don't allow scanning right now */
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto out_sleep;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:39 +00:00
|
|
|
ret = wl1271_scan(hw->priv, vif, ssid, len, req);
|
wl12xx: replace dummy_join with ROC/CROC commands
The ROC command asks the fw stay on the channel of the given
hlid. it currently has 2 primary functions:
1. Allow tx/rx from the device role.
In order to tx/rx packets while the stations is not associated
(e.g. auth req/resp), the device role has to be used, along
with ROC on its link.
Keep the logic similiar to the one used in dummy_join. However,
since we can't scan while we ROC, we add CROC before starting
a scan, and ROC again (if needed) on scan complete.
2. Keeping the antenna for a specific link.
We ROC until the connection was completed (after EAPOLs exchange)
in order to prevent BT coex operations from taking the antenna
and failing the connection (after this stage, psm can be used).
During association, we ROC on the station role, and then CROC
the device role, thus assuring being ROC during all the connection
process.
Delete the WL1271_FLAG_JOINED flag, and use a roc bitmap
to indicate what roles are currently ROCed.
Add wl12xx_roc/croc functions in order to wrap the roc/croc
commands while taking care of the roc bitmap.
The current ROC/CROC state-machine is a bit complicated. In
the future we'll probably want to use wpa_supplicant to control
the ROC during connection.
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
2011-08-14 10:17:17 +00:00
|
|
|
out_sleep:
|
2009-08-06 13:25:28 +00:00
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-06-27 10:06:45 +00:00
|
|
|
static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 cancel hw scan");
|
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
if (wl->state == WL1271_STATE_OFF)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (wl->scan.state != WL1271_SCAN_STATE_DONE) {
|
|
|
|
ret = wl1271_scan_stop(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_sleep;
|
|
|
|
}
|
2012-03-03 20:18:00 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Rearm the tx watchdog just before idling scan. This
|
|
|
|
* prevents just-finished scans from triggering the watchdog
|
|
|
|
*/
|
|
|
|
wl12xx_rearm_tx_watchdog_locked(wl);
|
|
|
|
|
2011-06-27 10:06:45 +00:00
|
|
|
wl->scan.state = WL1271_SCAN_STATE_IDLE;
|
|
|
|
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
|
2011-10-05 09:55:39 +00:00
|
|
|
wl->scan_vif = NULL;
|
2011-06-27 10:06:45 +00:00
|
|
|
wl->scan.req = NULL;
|
|
|
|
ieee80211_scan_completed(wl->hw, true);
|
|
|
|
|
|
|
|
out_sleep:
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
cancel_delayed_work_sync(&wl->scan_complete_work);
|
|
|
|
}
|
|
|
|
|
2011-05-10 11:46:02 +00:00
|
|
|
static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *vif,
|
|
|
|
struct cfg80211_sched_scan_request *req,
|
|
|
|
struct ieee80211_sched_scan_ies *ies)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
2011-10-05 09:55:45 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2011-05-10 11:46:02 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_start");
|
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
2012-01-11 13:22:42 +00:00
|
|
|
if (wl->state == WL1271_STATE_OFF) {
|
|
|
|
ret = -EAGAIN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2011-05-10 11:46:02 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-10-05 09:55:45 +00:00
|
|
|
ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
|
2011-05-10 11:46:02 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out_sleep;
|
|
|
|
|
2011-10-05 09:55:45 +00:00
|
|
|
ret = wl1271_scan_sched_scan_start(wl, wlvif);
|
2011-05-10 11:46:02 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out_sleep;
|
|
|
|
|
|
|
|
wl->sched_scanning = true;
|
|
|
|
|
|
|
|
out_sleep:
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
2012-05-16 08:34:17 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2011-05-10 11:46:02 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop");
|
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
2012-01-11 13:22:42 +00:00
|
|
|
if (wl->state == WL1271_STATE_OFF)
|
|
|
|
goto out;
|
|
|
|
|
2011-05-10 11:46:02 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2012-05-16 08:34:17 +00:00
|
|
|
wl1271_scan_sched_scan_stop(wl, wlvif);
|
2011-05-10 11:46:02 +00:00
|
|
|
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
}
|
|
|
|
|
2010-11-08 09:51:07 +00:00
|
|
|
static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
|
|
|
ret = -EAGAIN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
2010-11-08 09:51:07 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-04-18 11:15:21 +00:00
|
|
|
ret = wl1271_acx_frag_threshold(wl, value);
|
2010-11-08 09:51:07 +00:00
|
|
|
if (ret < 0)
|
|
|
|
wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret);
|
|
|
|
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
2011-10-10 08:13:13 +00:00
|
|
|
struct wl12xx_vif *wlvif;
|
2010-04-09 08:07:28 +00:00
|
|
|
int ret = 0;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
2010-10-26 11:24:39 +00:00
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
|
|
|
ret = -EAGAIN;
|
2010-04-09 08:07:28 +00:00
|
|
|
goto out;
|
2010-10-26 11:24:39 +00:00
|
|
|
}
|
2010-04-09 08:07:28 +00:00
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
2009-08-06 13:25:28 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-10-10 08:13:13 +00:00
|
|
|
wl12xx_for_each_wlvif(wl, wlvif) {
|
|
|
|
ret = wl1271_acx_rts_threshold(wl, wlvif, value);
|
|
|
|
if (ret < 0)
|
|
|
|
wl1271_warning("set rts threshold failed: %d", ret);
|
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:48 +00:00
|
|
|
static int wl1271_ssid_set(struct ieee80211_vif *vif, struct sk_buff *skb,
|
2010-11-24 06:16:57 +00:00
|
|
|
int offset)
|
2010-02-18 11:25:38 +00:00
|
|
|
{
|
2011-10-05 09:55:48 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2011-05-01 06:56:45 +00:00
|
|
|
u8 ssid_len;
|
|
|
|
const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
|
|
|
|
skb->len - offset);
|
2010-02-18 11:25:38 +00:00
|
|
|
|
2011-05-01 06:56:45 +00:00
|
|
|
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;
|
2010-02-18 11:25:38 +00:00
|
|
|
}
|
2010-10-16 17:07:21 +00:00
|
|
|
|
2011-10-05 09:55:48 +00:00
|
|
|
wlvif->ssid_len = ssid_len;
|
|
|
|
memcpy(wlvif->ssid, ptr+2, ssid_len);
|
2011-05-01 06:56:45 +00:00
|
|
|
return 0;
|
2010-02-18 11:25:38 +00:00
|
|
|
}
|
|
|
|
|
2011-09-15 09:07:04 +00:00
|
|
|
static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
const u8 *next, *end = skb->data + skb->len;
|
|
|
|
u8 *ie = (u8 *)cfg80211_find_ie(eid, skb->data + ieoffset,
|
|
|
|
skb->len - ieoffset);
|
|
|
|
if (!ie)
|
|
|
|
return;
|
|
|
|
len = ie[1] + 2;
|
|
|
|
next = ie + len;
|
|
|
|
memmove(ie, next, end - next);
|
|
|
|
skb_trim(skb, skb->len - len);
|
|
|
|
}
|
|
|
|
|
2011-09-15 09:07:05 +00:00
|
|
|
static void wl12xx_remove_vendor_ie(struct sk_buff *skb,
|
|
|
|
unsigned int oui, u8 oui_type,
|
|
|
|
int ieoffset)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
const u8 *next, *end = skb->data + skb->len;
|
|
|
|
u8 *ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type,
|
|
|
|
skb->data + ieoffset,
|
|
|
|
skb->len - ieoffset);
|
|
|
|
if (!ie)
|
|
|
|
return;
|
|
|
|
len = ie[1] + 2;
|
|
|
|
next = ie + len;
|
|
|
|
memmove(ie, next, end - next);
|
|
|
|
skb_trim(skb, skb->len - len);
|
|
|
|
}
|
|
|
|
|
2011-11-22 17:52:59 +00:00
|
|
|
static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl, u32 rates,
|
|
|
|
struct ieee80211_vif *vif)
|
2011-11-08 16:46:54 +00:00
|
|
|
{
|
2012-01-31 09:57:16 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2011-11-08 16:46:54 +00:00
|
|
|
struct sk_buff *skb;
|
|
|
|
int ret;
|
|
|
|
|
2011-11-22 17:52:59 +00:00
|
|
|
skb = ieee80211_proberesp_get(wl->hw, vif);
|
2011-11-08 16:46:54 +00:00
|
|
|
if (!skb)
|
2011-11-22 17:52:59 +00:00
|
|
|
return -EOPNOTSUPP;
|
2011-11-08 16:46:54 +00:00
|
|
|
|
2012-01-31 09:57:16 +00:00
|
|
|
ret = wl1271_cmd_template_set(wl, wlvif->role_id,
|
2011-11-08 16:46:54 +00:00
|
|
|
CMD_TEMPL_AP_PROBE_RESPONSE,
|
|
|
|
skb->data,
|
|
|
|
skb->len, 0,
|
|
|
|
rates);
|
|
|
|
dev_kfree_skb(skb);
|
2012-05-10 09:14:04 +00:00
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_AP, "probe response updated");
|
|
|
|
set_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags);
|
|
|
|
|
|
|
|
out:
|
2011-11-08 16:46:54 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wl1271_ap_set_probe_resp_tmpl_legacy(struct wl1271 *wl,
|
|
|
|
struct ieee80211_vif *vif,
|
|
|
|
u8 *probe_rsp_data,
|
|
|
|
size_t probe_rsp_len,
|
|
|
|
u32 rates)
|
2011-09-03 17:22:03 +00:00
|
|
|
{
|
2011-10-05 09:55:48 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
|
|
|
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
|
2011-09-03 17:22:03 +00:00
|
|
|
u8 probe_rsp_templ[WL1271_CMD_TEMPL_MAX_SIZE];
|
|
|
|
int ssid_ie_offset, ie_offset, templ_len;
|
|
|
|
const u8 *ptr;
|
|
|
|
|
|
|
|
/* no need to change probe response if the SSID is set correctly */
|
2011-10-05 09:55:48 +00:00
|
|
|
if (wlvif->ssid_len > 0)
|
2012-01-31 09:57:16 +00:00
|
|
|
return wl1271_cmd_template_set(wl, wlvif->role_id,
|
2011-09-03 17:22:03 +00:00
|
|
|
CMD_TEMPL_AP_PROBE_RESPONSE,
|
|
|
|
probe_rsp_data,
|
|
|
|
probe_rsp_len, 0,
|
|
|
|
rates);
|
|
|
|
|
|
|
|
if (probe_rsp_len + bss_conf->ssid_len > WL1271_CMD_TEMPL_MAX_SIZE) {
|
|
|
|
wl1271_error("probe_rsp template too big");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* start searching from IE offset */
|
|
|
|
ie_offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
|
|
|
|
|
|
|
|
ptr = cfg80211_find_ie(WLAN_EID_SSID, probe_rsp_data + ie_offset,
|
|
|
|
probe_rsp_len - ie_offset);
|
|
|
|
if (!ptr) {
|
|
|
|
wl1271_error("No SSID in beacon!");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssid_ie_offset = ptr - probe_rsp_data;
|
|
|
|
ptr += (ptr[1] + 2);
|
|
|
|
|
|
|
|
memcpy(probe_rsp_templ, probe_rsp_data, ssid_ie_offset);
|
|
|
|
|
|
|
|
/* insert SSID from bss_conf */
|
|
|
|
probe_rsp_templ[ssid_ie_offset] = WLAN_EID_SSID;
|
|
|
|
probe_rsp_templ[ssid_ie_offset + 1] = bss_conf->ssid_len;
|
|
|
|
memcpy(probe_rsp_templ + ssid_ie_offset + 2,
|
|
|
|
bss_conf->ssid, bss_conf->ssid_len);
|
|
|
|
templ_len = ssid_ie_offset + 2 + bss_conf->ssid_len;
|
|
|
|
|
|
|
|
memcpy(probe_rsp_templ + ssid_ie_offset + 2 + bss_conf->ssid_len,
|
|
|
|
ptr, probe_rsp_len - (ptr - probe_rsp_data));
|
|
|
|
templ_len += probe_rsp_len - (ptr - probe_rsp_data);
|
|
|
|
|
2012-01-31 09:57:16 +00:00
|
|
|
return wl1271_cmd_template_set(wl, wlvif->role_id,
|
2011-09-03 17:22:03 +00:00
|
|
|
CMD_TEMPL_AP_PROBE_RESPONSE,
|
|
|
|
probe_rsp_templ,
|
|
|
|
templ_len, 0,
|
|
|
|
rates);
|
|
|
|
}
|
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
static int wl1271_bss_erp_info_changed(struct wl1271 *wl,
|
2011-10-05 09:55:51 +00:00
|
|
|
struct ieee80211_vif *vif,
|
2009-08-06 13:25:28 +00:00
|
|
|
struct ieee80211_bss_conf *bss_conf,
|
|
|
|
u32 changed)
|
|
|
|
{
|
2011-10-05 09:55:51 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2010-10-16 17:07:21 +00:00
|
|
|
int ret = 0;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
if (changed & BSS_CHANGED_ERP_SLOT) {
|
|
|
|
if (bss_conf->use_short_slot)
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_SHORT);
|
2010-10-16 17:07:21 +00:00
|
|
|
else
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_LONG);
|
2010-10-16 17:07:21 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_warning("Set slot time failed %d", ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
if (changed & BSS_CHANGED_ERP_PREAMBLE) {
|
|
|
|
if (bss_conf->use_short_preamble)
|
2011-10-05 09:55:51 +00:00
|
|
|
wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_SHORT);
|
2010-10-16 17:07:21 +00:00
|
|
|
else
|
2011-10-05 09:55:51 +00:00
|
|
|
wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_LONG);
|
2010-10-16 17:07:21 +00:00
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
if (changed & BSS_CHANGED_ERP_CTS_PROT) {
|
|
|
|
if (bss_conf->use_cts_prot)
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_cts_protect(wl, wlvif,
|
|
|
|
CTSPROTECT_ENABLE);
|
2010-10-16 17:07:21 +00:00
|
|
|
else
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_cts_protect(wl, wlvif,
|
|
|
|
CTSPROTECT_DISABLE);
|
2010-10-16 17:07:21 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_warning("Set ctsprotect failed %d", ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2010-10-26 11:24:39 +00:00
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2012-05-10 09:14:04 +00:00
|
|
|
static int wlcore_set_beacon_template(struct wl1271 *wl,
|
|
|
|
struct ieee80211_vif *vif,
|
|
|
|
bool is_ap)
|
|
|
|
{
|
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
|
|
|
struct ieee80211_hdr *hdr;
|
|
|
|
u32 min_rate;
|
|
|
|
int ret;
|
|
|
|
int ieoffset = offsetof(struct ieee80211_mgmt,
|
|
|
|
u.beacon.variable);
|
|
|
|
struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
|
|
|
|
u16 tmpl_id;
|
|
|
|
|
|
|
|
if (!beacon) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MASTER, "beacon updated");
|
|
|
|
|
|
|
|
ret = wl1271_ssid_set(vif, beacon, ieoffset);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_kfree_skb(beacon);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
|
|
|
|
tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
|
|
|
|
CMD_TEMPL_BEACON;
|
|
|
|
ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id,
|
|
|
|
beacon->data,
|
|
|
|
beacon->len, 0,
|
|
|
|
min_rate);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_kfree_skb(beacon);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In case we already have a probe-resp beacon set explicitly
|
|
|
|
* by usermode, don't use the beacon data.
|
|
|
|
*/
|
|
|
|
if (test_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags))
|
|
|
|
goto end_bcn;
|
|
|
|
|
|
|
|
/* remove TIM ie from probe response */
|
|
|
|
wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* remove p2p ie from probe response.
|
|
|
|
* the fw reponds to probe requests that don't include
|
|
|
|
* the p2p ie. probe requests with p2p ie will be passed,
|
|
|
|
* and will be responded by the supplicant (the spec
|
|
|
|
* forbids including the p2p ie when responding to probe
|
|
|
|
* requests that didn't include it).
|
|
|
|
*/
|
|
|
|
wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA,
|
|
|
|
WLAN_OUI_TYPE_WFA_P2P, ieoffset);
|
|
|
|
|
|
|
|
hdr = (struct ieee80211_hdr *) beacon->data;
|
|
|
|
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
|
|
|
IEEE80211_STYPE_PROBE_RESP);
|
|
|
|
if (is_ap)
|
|
|
|
ret = wl1271_ap_set_probe_resp_tmpl_legacy(wl, vif,
|
|
|
|
beacon->data,
|
|
|
|
beacon->len,
|
|
|
|
min_rate);
|
|
|
|
else
|
|
|
|
ret = wl1271_cmd_template_set(wl, wlvif->role_id,
|
|
|
|
CMD_TEMPL_PROBE_RESPONSE,
|
|
|
|
beacon->data,
|
|
|
|
beacon->len, 0,
|
|
|
|
min_rate);
|
|
|
|
end_bcn:
|
|
|
|
dev_kfree_skb(beacon);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
|
|
|
|
struct ieee80211_vif *vif,
|
|
|
|
struct ieee80211_bss_conf *bss_conf,
|
|
|
|
u32 changed)
|
|
|
|
{
|
2011-10-05 09:55:41 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2011-10-05 09:55:45 +00:00
|
|
|
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
|
2010-10-16 17:07:21 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if ((changed & BSS_CHANGED_BEACON_INT)) {
|
|
|
|
wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
|
2010-03-26 10:53:25 +00:00
|
|
|
bss_conf->beacon_int);
|
|
|
|
|
2011-10-05 09:55:58 +00:00
|
|
|
wlvif->beacon_int = bss_conf->beacon_int;
|
2010-03-26 10:53:25 +00:00
|
|
|
}
|
|
|
|
|
2011-11-08 16:46:54 +00:00
|
|
|
if ((changed & BSS_CHANGED_AP_PROBE_RESP) && is_ap) {
|
|
|
|
u32 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
|
2012-05-10 09:14:04 +00:00
|
|
|
|
|
|
|
wl1271_ap_set_probe_resp_tmpl(wl, rate, vif);
|
2011-11-08 16:46:54 +00:00
|
|
|
}
|
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
if ((changed & BSS_CHANGED_BEACON)) {
|
2012-05-10 09:14:04 +00:00
|
|
|
ret = wlcore_set_beacon_template(wl, vif, is_ap);
|
2010-10-16 17:07:21 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2011-11-08 16:46:54 +00:00
|
|
|
if (ret != 0)
|
|
|
|
wl1271_error("beacon info change failed: %d", ret);
|
2010-10-16 17:07:21 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* AP mode changes */
|
|
|
|
static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
|
|
|
|
struct ieee80211_vif *vif,
|
|
|
|
struct ieee80211_bss_conf *bss_conf,
|
|
|
|
u32 changed)
|
|
|
|
{
|
2011-10-05 09:55:41 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2010-10-16 17:07:21 +00:00
|
|
|
int ret = 0;
|
2009-12-11 13:41:04 +00:00
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
if ((changed & BSS_CHANGED_BASIC_RATES)) {
|
|
|
|
u32 rates = bss_conf->basic_rates;
|
2010-03-26 10:53:24 +00:00
|
|
|
|
2011-10-05 09:55:41 +00:00
|
|
|
wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates,
|
2011-10-10 08:13:09 +00:00
|
|
|
wlvif->band);
|
2011-10-05 09:55:43 +00:00
|
|
|
wlvif->basic_rate = wl1271_tx_min_rate_get(wl,
|
2011-10-05 09:55:41 +00:00
|
|
|
wlvif->basic_rate_set);
|
2011-04-18 11:15:25 +00:00
|
|
|
|
2011-10-05 09:55:41 +00:00
|
|
|
ret = wl1271_init_ap_rates(wl, wlvif);
|
2010-10-16 17:07:21 +00:00
|
|
|
if (ret < 0) {
|
2011-04-18 11:15:25 +00:00
|
|
|
wl1271_error("AP rate policy change failed %d", ret);
|
2010-10-16 17:07:21 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2011-04-18 11:15:26 +00:00
|
|
|
|
2011-10-05 09:55:39 +00:00
|
|
|
ret = wl1271_ap_init_templates(wl, vif);
|
2011-04-18 11:15:26 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2012-05-10 09:14:04 +00:00
|
|
|
|
|
|
|
ret = wl1271_ap_set_probe_resp_tmpl(wl, wlvif->basic_rate, vif);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = wlcore_set_beacon_template(wl, vif, true);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2010-10-16 17:07:21 +00:00
|
|
|
}
|
2010-11-24 06:16:57 +00:00
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2010-02-18 11:25:38 +00:00
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
if ((changed & BSS_CHANGED_BEACON_ENABLED)) {
|
|
|
|
if (bss_conf->enable_beacon) {
|
2011-10-10 08:13:02 +00:00
|
|
|
if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
|
2011-10-05 09:55:41 +00:00
|
|
|
ret = wl12xx_cmd_role_start_ap(wl, wlvif);
|
2010-10-16 17:07:21 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2009-12-11 13:41:04 +00:00
|
|
|
|
2011-10-05 09:55:54 +00:00
|
|
|
ret = wl1271_ap_init_hwenc(wl, wlvif);
|
2010-10-16 19:39:06 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2011-08-14 10:17:37 +00:00
|
|
|
|
2011-10-10 08:13:02 +00:00
|
|
|
set_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
|
2011-08-14 10:17:37 +00:00
|
|
|
wl1271_debug(DEBUG_AP, "started AP");
|
2009-12-11 13:41:04 +00:00
|
|
|
}
|
2010-10-16 17:07:21 +00:00
|
|
|
} else {
|
2011-10-10 08:13:02 +00:00
|
|
|
if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl12xx_cmd_role_stop_ap(wl, wlvif);
|
2010-10-16 17:07:21 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2009-12-11 13:41:04 +00:00
|
|
|
|
2011-10-10 08:13:02 +00:00
|
|
|
clear_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
|
2011-11-08 16:46:54 +00:00
|
|
|
clear_bit(WLVIF_FLAG_AP_PROBE_RESP_SET,
|
|
|
|
&wlvif->flags);
|
2010-10-16 17:07:21 +00:00
|
|
|
wl1271_debug(DEBUG_AP, "stopped AP");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-12-11 13:41:04 +00:00
|
|
|
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
|
2010-10-16 17:07:21 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2011-08-14 10:17:27 +00:00
|
|
|
|
|
|
|
/* Handle HT information change */
|
|
|
|
if ((changed & BSS_CHANGED_HT) &&
|
|
|
|
(bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_set_ht_information(wl, wlvif,
|
2011-08-14 10:17:27 +00:00
|
|
|
bss_conf->ht_operation_mode);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_warning("Set ht information failed %d", ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
out:
|
|
|
|
return;
|
|
|
|
}
|
2010-02-18 11:25:51 +00:00
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
/* STA/IBSS mode changes */
|
|
|
|
static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
|
|
|
|
struct ieee80211_vif *vif,
|
|
|
|
struct ieee80211_bss_conf *bss_conf,
|
|
|
|
u32 changed)
|
|
|
|
{
|
2011-10-05 09:55:41 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2010-10-16 17:07:21 +00:00
|
|
|
bool do_join = false, set_assoc = false;
|
2011-10-05 09:55:45 +00:00
|
|
|
bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
|
2011-08-14 10:17:26 +00:00
|
|
|
bool ibss_joined = false;
|
2011-02-02 07:59:37 +00:00
|
|
|
u32 sta_rate_set = 0;
|
2010-10-16 17:07:21 +00:00
|
|
|
int ret;
|
2011-01-11 18:07:21 +00:00
|
|
|
struct ieee80211_sta *sta;
|
2011-02-12 21:24:20 +00:00
|
|
|
bool sta_exists = false;
|
|
|
|
struct ieee80211_sta_ht_cap sta_ht_cap;
|
2010-10-16 17:07:21 +00:00
|
|
|
|
|
|
|
if (is_ibss) {
|
|
|
|
ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf,
|
|
|
|
changed);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2009-12-11 13:41:04 +00:00
|
|
|
}
|
|
|
|
|
2011-08-14 10:17:26 +00:00
|
|
|
if (changed & BSS_CHANGED_IBSS) {
|
|
|
|
if (bss_conf->ibss_joined) {
|
2011-10-10 08:13:01 +00:00
|
|
|
set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags);
|
2011-08-14 10:17:26 +00:00
|
|
|
ibss_joined = true;
|
|
|
|
} else {
|
2011-10-10 08:13:01 +00:00
|
|
|
if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED,
|
2012-03-04 08:55:45 +00:00
|
|
|
&wlvif->flags))
|
2011-10-05 09:55:51 +00:00
|
|
|
wl1271_unjoin(wl, wlvif);
|
2011-08-14 10:17:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((changed & BSS_CHANGED_BEACON_INT) && ibss_joined)
|
2010-10-16 17:07:21 +00:00
|
|
|
do_join = true;
|
|
|
|
|
|
|
|
/* Need to update the SSID (for filtering etc) */
|
2011-08-14 10:17:26 +00:00
|
|
|
if ((changed & BSS_CHANGED_BEACON) && ibss_joined)
|
2010-10-16 17:07:21 +00:00
|
|
|
do_join = true;
|
|
|
|
|
2011-08-14 10:17:26 +00:00
|
|
|
if ((changed & BSS_CHANGED_BEACON_ENABLED) && ibss_joined) {
|
2010-03-26 10:53:24 +00:00
|
|
|
wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
|
|
|
|
bss_conf->enable_beacon ? "enabled" : "disabled");
|
|
|
|
|
|
|
|
do_join = true;
|
|
|
|
}
|
|
|
|
|
2012-03-04 08:55:45 +00:00
|
|
|
if (changed & BSS_CHANGED_IDLE && !is_ibss) {
|
2011-10-23 06:21:55 +00:00
|
|
|
ret = wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle);
|
|
|
|
if (ret < 0)
|
|
|
|
wl1271_warning("idle mode change failed %d", ret);
|
|
|
|
}
|
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
if ((changed & BSS_CHANGED_CQM)) {
|
2010-04-09 08:07:30 +00:00
|
|
|
bool enable = false;
|
|
|
|
if (bss_conf->cqm_rssi_thold)
|
|
|
|
enable = true;
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_rssi_snr_trigger(wl, wlvif, enable,
|
2010-04-09 08:07:30 +00:00
|
|
|
bss_conf->cqm_rssi_thold,
|
|
|
|
bss_conf->cqm_rssi_hyst);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2011-10-05 09:56:03 +00:00
|
|
|
wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
|
2010-04-09 08:07:30 +00:00
|
|
|
}
|
|
|
|
|
2012-03-12 12:53:04 +00:00
|
|
|
if (changed & BSS_CHANGED_BSSID)
|
2011-10-05 09:55:44 +00:00
|
|
|
if (!is_zero_ether_addr(bss_conf->bssid)) {
|
2011-10-05 09:55:43 +00:00
|
|
|
ret = wl12xx_cmd_build_null_data(wl, wlvif);
|
2010-12-26 08:27:50 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2010-02-18 11:25:38 +00:00
|
|
|
|
2011-10-05 09:55:39 +00:00
|
|
|
ret = wl1271_build_qos_null_data(wl, vif);
|
2010-12-26 08:27:50 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
2010-02-18 11:25:38 +00:00
|
|
|
|
2011-08-17 07:45:49 +00:00
|
|
|
if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) {
|
|
|
|
rcu_read_lock();
|
|
|
|
sta = ieee80211_find_sta(vif, bss_conf->bssid);
|
|
|
|
if (!sta)
|
|
|
|
goto sta_not_found;
|
|
|
|
|
2011-02-02 07:59:37 +00:00
|
|
|
/* 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 |=
|
2012-05-10 09:13:33 +00:00
|
|
|
(sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
|
|
|
|
(sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET);
|
2011-02-12 21:24:20 +00:00
|
|
|
sta_ht_cap = sta->ht_cap;
|
|
|
|
sta_exists = true;
|
2011-02-02 07:59:37 +00:00
|
|
|
|
2011-08-17 07:45:49 +00:00
|
|
|
sta_not_found:
|
|
|
|
rcu_read_unlock();
|
2011-02-02 07:59:37 +00:00
|
|
|
}
|
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
if ((changed & BSS_CHANGED_ASSOC)) {
|
2009-08-06 13:25:28 +00:00
|
|
|
if (bss_conf->assoc) {
|
2010-04-01 08:38:20 +00:00
|
|
|
u32 rates;
|
2010-11-24 06:16:57 +00:00
|
|
|
int ieoffset;
|
2011-10-05 09:55:50 +00:00
|
|
|
wlvif->aid = bss_conf->aid;
|
2012-05-10 09:13:30 +00:00
|
|
|
wlvif->channel_type = bss_conf->channel_type;
|
2012-03-04 08:55:53 +00:00
|
|
|
wlvif->beacon_int = bss_conf->beacon_int;
|
2012-03-12 12:53:04 +00:00
|
|
|
do_join = true;
|
2010-05-07 08:39:00 +00:00
|
|
|
set_assoc = true;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-04-01 08:38:20 +00:00
|
|
|
/*
|
|
|
|
* use basic rates from AP, and determine lowest rate
|
|
|
|
* to use with control frames.
|
|
|
|
*/
|
|
|
|
rates = bss_conf->basic_rates;
|
2011-10-05 09:55:41 +00:00
|
|
|
wlvif->basic_rate_set =
|
2011-09-19 10:51:42 +00:00
|
|
|
wl1271_tx_enabled_rates_get(wl, rates,
|
2011-10-10 08:13:09 +00:00
|
|
|
wlvif->band);
|
2011-10-05 09:55:43 +00:00
|
|
|
wlvif->basic_rate =
|
2011-10-05 09:55:41 +00:00
|
|
|
wl1271_tx_min_rate_get(wl,
|
|
|
|
wlvif->basic_rate_set);
|
2011-02-02 07:59:37 +00:00
|
|
|
if (sta_rate_set)
|
2011-10-05 09:55:42 +00:00
|
|
|
wlvif->rate_set =
|
|
|
|
wl1271_tx_enabled_rates_get(wl,
|
2011-09-19 10:51:42 +00:00
|
|
|
sta_rate_set,
|
2011-10-10 08:13:09 +00:00
|
|
|
wlvif->band);
|
2011-10-05 09:55:42 +00:00
|
|
|
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
|
2010-04-01 08:38:20 +00:00
|
|
|
if (ret < 0)
|
2010-10-16 17:07:21 +00:00
|
|
|
goto out;
|
2010-04-01 08:38:20 +00:00
|
|
|
|
2009-10-12 12:08:57 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2011-10-05 09:55:50 +00:00
|
|
|
ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
|
2009-08-06 13:25:28 +00:00
|
|
|
if (ret < 0)
|
2010-10-16 17:07:21 +00:00
|
|
|
goto out;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-03-26 10:53:28 +00:00
|
|
|
/*
|
2010-11-24 06:16:57 +00:00
|
|
|
* Get a template for hardware connection maintenance
|
2010-03-26 10:53:28 +00:00
|
|
|
*/
|
2011-10-05 09:55:49 +00:00
|
|
|
dev_kfree_skb(wlvif->probereq);
|
|
|
|
wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
|
2011-10-10 08:12:53 +00:00
|
|
|
wlvif,
|
2011-10-05 09:55:49 +00:00
|
|
|
NULL);
|
2010-11-24 06:16:57 +00:00
|
|
|
ieoffset = offsetof(struct ieee80211_mgmt,
|
|
|
|
u.probe_req.variable);
|
2011-10-05 09:55:49 +00:00
|
|
|
wl1271_ssid_set(vif, wlvif->probereq, ieoffset);
|
2010-03-26 10:53:28 +00:00
|
|
|
|
2010-03-26 10:53:23 +00:00
|
|
|
/* enable the connection monitoring feature */
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
|
2009-08-06 13:25:28 +00:00
|
|
|
if (ret < 0)
|
2010-10-16 17:07:21 +00:00
|
|
|
goto out;
|
2009-10-08 18:56:25 +00:00
|
|
|
} else {
|
|
|
|
/* use defaults when not associated */
|
2011-04-05 16:13:28 +00:00
|
|
|
bool was_assoc =
|
2011-10-10 08:13:00 +00:00
|
|
|
!!test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED,
|
|
|
|
&wlvif->flags);
|
wl12xx: replace dummy_join with ROC/CROC commands
The ROC command asks the fw stay on the channel of the given
hlid. it currently has 2 primary functions:
1. Allow tx/rx from the device role.
In order to tx/rx packets while the stations is not associated
(e.g. auth req/resp), the device role has to be used, along
with ROC on its link.
Keep the logic similiar to the one used in dummy_join. However,
since we can't scan while we ROC, we add CROC before starting
a scan, and ROC again (if needed) on scan complete.
2. Keeping the antenna for a specific link.
We ROC until the connection was completed (after EAPOLs exchange)
in order to prevent BT coex operations from taking the antenna
and failing the connection (after this stage, psm can be used).
During association, we ROC on the station role, and then CROC
the device role, thus assuring being ROC during all the connection
process.
Delete the WL1271_FLAG_JOINED flag, and use a roc bitmap
to indicate what roles are currently ROCed.
Add wl12xx_roc/croc functions in order to wrap the roc/croc
commands while taking care of the roc bitmap.
The current ROC/CROC state-machine is a bit complicated. In
the future we'll probably want to use wpa_supplicant to control
the ROC during connection.
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
2011-08-14 10:17:17 +00:00
|
|
|
bool was_ifup =
|
2011-10-10 08:13:04 +00:00
|
|
|
!!test_and_clear_bit(WLVIF_FLAG_STA_STATE_SENT,
|
|
|
|
&wlvif->flags);
|
2011-10-05 09:55:50 +00:00
|
|
|
wlvif->aid = 0;
|
2010-03-26 10:53:23 +00:00
|
|
|
|
2010-11-24 06:16:57 +00:00
|
|
|
/* free probe-request template */
|
2011-10-05 09:55:49 +00:00
|
|
|
dev_kfree_skb(wlvif->probereq);
|
|
|
|
wlvif->probereq = NULL;
|
2010-11-24 06:16:57 +00:00
|
|
|
|
2010-04-01 08:38:20 +00:00
|
|
|
/* revert back to minimum rates for the current band */
|
2011-10-05 09:55:41 +00:00
|
|
|
wl1271_set_band_rate(wl, wlvif);
|
2011-10-05 09:55:43 +00:00
|
|
|
wlvif->basic_rate =
|
2011-10-05 09:55:41 +00:00
|
|
|
wl1271_tx_min_rate_get(wl,
|
|
|
|
wlvif->basic_rate_set);
|
2011-10-05 09:55:42 +00:00
|
|
|
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
|
2010-04-01 08:38:20 +00:00
|
|
|
if (ret < 0)
|
2010-10-16 17:07:21 +00:00
|
|
|
goto out;
|
2010-04-01 08:38:20 +00:00
|
|
|
|
2010-03-26 10:53:23 +00:00
|
|
|
/* disable connection monitor features */
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
|
2010-03-26 10:53:32 +00:00
|
|
|
|
|
|
|
/* Disable the keep-alive feature */
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
|
2010-03-26 10:53:23 +00:00
|
|
|
if (ret < 0)
|
2010-10-16 17:07:21 +00:00
|
|
|
goto out;
|
2010-11-22 10:59:08 +00:00
|
|
|
|
|
|
|
/* restore the bssid filter and go to dummy bssid */
|
2011-04-05 16:13:28 +00:00
|
|
|
if (was_assoc) {
|
wl12xx: replace dummy_join with ROC/CROC commands
The ROC command asks the fw stay on the channel of the given
hlid. it currently has 2 primary functions:
1. Allow tx/rx from the device role.
In order to tx/rx packets while the stations is not associated
(e.g. auth req/resp), the device role has to be used, along
with ROC on its link.
Keep the logic similiar to the one used in dummy_join. However,
since we can't scan while we ROC, we add CROC before starting
a scan, and ROC again (if needed) on scan complete.
2. Keeping the antenna for a specific link.
We ROC until the connection was completed (after EAPOLs exchange)
in order to prevent BT coex operations from taking the antenna
and failing the connection (after this stage, psm can be used).
During association, we ROC on the station role, and then CROC
the device role, thus assuring being ROC during all the connection
process.
Delete the WL1271_FLAG_JOINED flag, and use a roc bitmap
to indicate what roles are currently ROCed.
Add wl12xx_roc/croc functions in order to wrap the roc/croc
commands while taking care of the roc bitmap.
The current ROC/CROC state-machine is a bit complicated. In
the future we'll probably want to use wpa_supplicant to control
the ROC during connection.
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
2011-08-14 10:17:17 +00:00
|
|
|
/*
|
|
|
|
* we might have to disable roc, if there was
|
|
|
|
* no IF_OPER_UP notification.
|
|
|
|
*/
|
|
|
|
if (!was_ifup) {
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl12xx_croc(wl, wlvif->role_id);
|
wl12xx: replace dummy_join with ROC/CROC commands
The ROC command asks the fw stay on the channel of the given
hlid. it currently has 2 primary functions:
1. Allow tx/rx from the device role.
In order to tx/rx packets while the stations is not associated
(e.g. auth req/resp), the device role has to be used, along
with ROC on its link.
Keep the logic similiar to the one used in dummy_join. However,
since we can't scan while we ROC, we add CROC before starting
a scan, and ROC again (if needed) on scan complete.
2. Keeping the antenna for a specific link.
We ROC until the connection was completed (after EAPOLs exchange)
in order to prevent BT coex operations from taking the antenna
and failing the connection (after this stage, psm can be used).
During association, we ROC on the station role, and then CROC
the device role, thus assuring being ROC during all the connection
process.
Delete the WL1271_FLAG_JOINED flag, and use a roc bitmap
to indicate what roles are currently ROCed.
Add wl12xx_roc/croc functions in order to wrap the roc/croc
commands while taking care of the roc bitmap.
The current ROC/CROC state-machine is a bit complicated. In
the future we'll probably want to use wpa_supplicant to control
the ROC during connection.
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
2011-08-14 10:17:17 +00:00
|
|
|
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...)
|
|
|
|
*/
|
2011-10-05 09:55:52 +00:00
|
|
|
if (test_bit(wlvif->dev_role_id, wl->roc_map)) {
|
|
|
|
ret = wl12xx_croc(wl,
|
|
|
|
wlvif->dev_role_id);
|
wl12xx: replace dummy_join with ROC/CROC commands
The ROC command asks the fw stay on the channel of the given
hlid. it currently has 2 primary functions:
1. Allow tx/rx from the device role.
In order to tx/rx packets while the stations is not associated
(e.g. auth req/resp), the device role has to be used, along
with ROC on its link.
Keep the logic similiar to the one used in dummy_join. However,
since we can't scan while we ROC, we add CROC before starting
a scan, and ROC again (if needed) on scan complete.
2. Keeping the antenna for a specific link.
We ROC until the connection was completed (after EAPOLs exchange)
in order to prevent BT coex operations from taking the antenna
and failing the connection (after this stage, psm can be used).
During association, we ROC on the station role, and then CROC
the device role, thus assuring being ROC during all the connection
process.
Delete the WL1271_FLAG_JOINED flag, and use a roc bitmap
to indicate what roles are currently ROCed.
Add wl12xx_roc/croc functions in order to wrap the roc/croc
commands while taking care of the roc bitmap.
The current ROC/CROC state-machine is a bit complicated. In
the future we'll probably want to use wpa_supplicant to control
the ROC during connection.
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
2011-08-14 10:17:17 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:51 +00:00
|
|
|
wl1271_unjoin(wl, wlvif);
|
2012-03-04 08:55:46 +00:00
|
|
|
if (!bss_conf->idle)
|
2011-10-11 09:55:44 +00:00
|
|
|
wl12xx_start_dev(wl, wlvif);
|
2011-04-05 16:13:28 +00:00
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-24 11:33:08 +00:00
|
|
|
if (changed & BSS_CHANGED_IBSS) {
|
|
|
|
wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d",
|
|
|
|
bss_conf->ibss_joined);
|
|
|
|
|
|
|
|
if (bss_conf->ibss_joined) {
|
|
|
|
u32 rates = bss_conf->basic_rates;
|
2011-10-05 09:55:41 +00:00
|
|
|
wlvif->basic_rate_set =
|
2011-09-19 10:51:42 +00:00
|
|
|
wl1271_tx_enabled_rates_get(wl, rates,
|
2011-10-10 08:13:09 +00:00
|
|
|
wlvif->band);
|
2011-10-05 09:55:43 +00:00
|
|
|
wlvif->basic_rate =
|
2011-10-05 09:55:41 +00:00
|
|
|
wl1271_tx_min_rate_get(wl,
|
|
|
|
wlvif->basic_rate_set);
|
2011-05-24 11:33:08 +00:00
|
|
|
|
2011-09-05 10:54:36 +00:00
|
|
|
/* by default, use 11b + OFDM rates */
|
2011-10-05 09:55:42 +00:00
|
|
|
wlvif->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
|
|
|
|
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
|
2011-05-24 11:33:08 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
|
2010-10-16 17:07:21 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-02-18 11:25:51 +00:00
|
|
|
if (do_join) {
|
2011-10-05 09:55:41 +00:00
|
|
|
ret = wl1271_join(wl, wlvif, set_assoc);
|
2010-02-18 11:25:51 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_warning("cmd join failed %d", ret);
|
2010-10-16 17:07:21 +00:00
|
|
|
goto out;
|
2010-02-18 11:25:51 +00:00
|
|
|
}
|
wl12xx: replace dummy_join with ROC/CROC commands
The ROC command asks the fw stay on the channel of the given
hlid. it currently has 2 primary functions:
1. Allow tx/rx from the device role.
In order to tx/rx packets while the stations is not associated
(e.g. auth req/resp), the device role has to be used, along
with ROC on its link.
Keep the logic similiar to the one used in dummy_join. However,
since we can't scan while we ROC, we add CROC before starting
a scan, and ROC again (if needed) on scan complete.
2. Keeping the antenna for a specific link.
We ROC until the connection was completed (after EAPOLs exchange)
in order to prevent BT coex operations from taking the antenna
and failing the connection (after this stage, psm can be used).
During association, we ROC on the station role, and then CROC
the device role, thus assuring being ROC during all the connection
process.
Delete the WL1271_FLAG_JOINED flag, and use a roc bitmap
to indicate what roles are currently ROCed.
Add wl12xx_roc/croc functions in order to wrap the roc/croc
commands while taking care of the roc bitmap.
The current ROC/CROC state-machine is a bit complicated. In
the future we'll probably want to use wpa_supplicant to control
the ROC during connection.
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
2011-08-14 10:17:17 +00:00
|
|
|
|
|
|
|
/* ROC until connected (after EAPOL exchange) */
|
|
|
|
if (!is_ibss) {
|
2011-10-10 08:13:09 +00:00
|
|
|
ret = wl12xx_roc(wl, wlvif, wlvif->role_id);
|
wl12xx: replace dummy_join with ROC/CROC commands
The ROC command asks the fw stay on the channel of the given
hlid. it currently has 2 primary functions:
1. Allow tx/rx from the device role.
In order to tx/rx packets while the stations is not associated
(e.g. auth req/resp), the device role has to be used, along
with ROC on its link.
Keep the logic similiar to the one used in dummy_join. However,
since we can't scan while we ROC, we add CROC before starting
a scan, and ROC again (if needed) on scan complete.
2. Keeping the antenna for a specific link.
We ROC until the connection was completed (after EAPOLs exchange)
in order to prevent BT coex operations from taking the antenna
and failing the connection (after this stage, psm can be used).
During association, we ROC on the station role, and then CROC
the device role, thus assuring being ROC during all the connection
process.
Delete the WL1271_FLAG_JOINED flag, and use a roc bitmap
to indicate what roles are currently ROCed.
Add wl12xx_roc/croc functions in order to wrap the roc/croc
commands while taking care of the roc bitmap.
The current ROC/CROC state-machine is a bit complicated. In
the future we'll probably want to use wpa_supplicant to control
the ROC during connection.
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
2011-08-14 10:17:17 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2012-03-04 08:55:48 +00:00
|
|
|
if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags))
|
|
|
|
wl12xx_set_authorized(wl, wlvif);
|
wl12xx: replace dummy_join with ROC/CROC commands
The ROC command asks the fw stay on the channel of the given
hlid. it currently has 2 primary functions:
1. Allow tx/rx from the device role.
In order to tx/rx packets while the stations is not associated
(e.g. auth req/resp), the device role has to be used, along
with ROC on its link.
Keep the logic similiar to the one used in dummy_join. However,
since we can't scan while we ROC, we add CROC before starting
a scan, and ROC again (if needed) on scan complete.
2. Keeping the antenna for a specific link.
We ROC until the connection was completed (after EAPOLs exchange)
in order to prevent BT coex operations from taking the antenna
and failing the connection (after this stage, psm can be used).
During association, we ROC on the station role, and then CROC
the device role, thus assuring being ROC during all the connection
process.
Delete the WL1271_FLAG_JOINED flag, and use a roc bitmap
to indicate what roles are currently ROCed.
Add wl12xx_roc/croc functions in order to wrap the roc/croc
commands while taking care of the roc bitmap.
The current ROC/CROC state-machine is a bit complicated. In
the future we'll probably want to use wpa_supplicant to control
the ROC during connection.
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
2011-08-14 10:17:17 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* stop device role if started (we might already be in
|
2011-12-18 18:25:43 +00:00
|
|
|
* STA/IBSS role).
|
wl12xx: replace dummy_join with ROC/CROC commands
The ROC command asks the fw stay on the channel of the given
hlid. it currently has 2 primary functions:
1. Allow tx/rx from the device role.
In order to tx/rx packets while the stations is not associated
(e.g. auth req/resp), the device role has to be used, along
with ROC on its link.
Keep the logic similiar to the one used in dummy_join. However,
since we can't scan while we ROC, we add CROC before starting
a scan, and ROC again (if needed) on scan complete.
2. Keeping the antenna for a specific link.
We ROC until the connection was completed (after EAPOLs exchange)
in order to prevent BT coex operations from taking the antenna
and failing the connection (after this stage, psm can be used).
During association, we ROC on the station role, and then CROC
the device role, thus assuring being ROC during all the connection
process.
Delete the WL1271_FLAG_JOINED flag, and use a roc bitmap
to indicate what roles are currently ROCed.
Add wl12xx_roc/croc functions in order to wrap the roc/croc
commands while taking care of the roc bitmap.
The current ROC/CROC state-machine is a bit complicated. In
the future we'll probably want to use wpa_supplicant to control
the ROC during connection.
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
2011-08-14 10:17:17 +00:00
|
|
|
*/
|
2011-12-18 18:25:43 +00:00
|
|
|
if (wl12xx_dev_role_started(wlvif)) {
|
2011-10-11 09:55:44 +00:00
|
|
|
ret = wl12xx_stop_dev(wl, wlvif);
|
wl12xx: replace dummy_join with ROC/CROC commands
The ROC command asks the fw stay on the channel of the given
hlid. it currently has 2 primary functions:
1. Allow tx/rx from the device role.
In order to tx/rx packets while the stations is not associated
(e.g. auth req/resp), the device role has to be used, along
with ROC on its link.
Keep the logic similiar to the one used in dummy_join. However,
since we can't scan while we ROC, we add CROC before starting
a scan, and ROC again (if needed) on scan complete.
2. Keeping the antenna for a specific link.
We ROC until the connection was completed (after EAPOLs exchange)
in order to prevent BT coex operations from taking the antenna
and failing the connection (after this stage, psm can be used).
During association, we ROC on the station role, and then CROC
the device role, thus assuring being ROC during all the connection
process.
Delete the WL1271_FLAG_JOINED flag, and use a roc bitmap
to indicate what roles are currently ROCed.
Add wl12xx_roc/croc functions in order to wrap the roc/croc
commands while taking care of the roc bitmap.
The current ROC/CROC state-machine is a bit complicated. In
the future we'll probably want to use wpa_supplicant to control
the ROC during connection.
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
2011-08-14 10:17:17 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
2010-03-26 10:53:32 +00:00
|
|
|
}
|
|
|
|
|
2011-08-14 10:17:27 +00:00
|
|
|
/* Handle new association with HT. Do this after join. */
|
2011-08-17 07:45:49 +00:00
|
|
|
if (sta_exists) {
|
|
|
|
if ((changed & BSS_CHANGED_HT) &&
|
|
|
|
(bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
|
2011-08-14 10:17:27 +00:00
|
|
|
ret = wl1271_acx_set_ht_capabilities(wl,
|
|
|
|
&sta_ht_cap,
|
|
|
|
true,
|
2011-10-05 09:55:53 +00:00
|
|
|
wlvif->sta.hlid);
|
2011-08-17 07:45:49 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_warning("Set ht cap true failed %d",
|
|
|
|
ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* handle new association without HT and disassociation */
|
|
|
|
else if (changed & BSS_CHANGED_ASSOC) {
|
2011-08-14 10:17:27 +00:00
|
|
|
ret = wl1271_acx_set_ht_capabilities(wl,
|
|
|
|
&sta_ht_cap,
|
|
|
|
false,
|
2011-10-05 09:55:53 +00:00
|
|
|
wlvif->sta.hlid);
|
2011-08-17 07:45:49 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_warning("Set ht cap false failed %d",
|
|
|
|
ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-14 10:17:27 +00:00
|
|
|
/* Handle HT information change. Done after join. */
|
|
|
|
if ((changed & BSS_CHANGED_HT) &&
|
2011-08-17 07:45:49 +00:00
|
|
|
(bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_set_ht_information(wl, wlvif,
|
2011-08-17 07:45:49 +00:00
|
|
|
bss_conf->ht_operation_mode);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_warning("Set ht information failed %d", ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-02 10:22:11 +00:00
|
|
|
/* Handle arp filtering. Done after join. */
|
|
|
|
if ((changed & BSS_CHANGED_ARP_FILTER) ||
|
|
|
|
(!is_ibss && (changed & BSS_CHANGED_QOS))) {
|
|
|
|
__be32 addr = bss_conf->arp_addr_list[0];
|
|
|
|
wlvif->sta.qos = bss_conf->qos;
|
|
|
|
WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS);
|
|
|
|
|
|
|
|
if (bss_conf->arp_addr_cnt == 1 &&
|
|
|
|
bss_conf->arp_filter_enabled) {
|
|
|
|
wlvif->ip_addr = addr;
|
|
|
|
/*
|
|
|
|
* The template should have been configured only upon
|
|
|
|
* association. however, it seems that the correct ip
|
|
|
|
* isn't being set (when sending), so we have to
|
|
|
|
* reconfigure the template upon every ip change.
|
|
|
|
*/
|
|
|
|
ret = wl1271_cmd_build_arp_rsp(wl, wlvif);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_warning("build arp rsp failed: %d", ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = wl1271_acx_arp_ip_filter(wl, wlvif,
|
|
|
|
(ACX_ARP_FILTER_ARP_FILTERING |
|
|
|
|
ACX_ARP_FILTER_AUTO_ARP),
|
|
|
|
addr);
|
|
|
|
} else {
|
|
|
|
wlvif->ip_addr = 0;
|
|
|
|
ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
out:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *vif,
|
|
|
|
struct ieee80211_bss_conf *bss_conf,
|
|
|
|
u32 changed)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
2011-10-05 09:55:45 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
|
|
|
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
|
2010-10-16 17:07:21 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
|
|
|
|
(int)changed);
|
|
|
|
|
2012-05-15 14:08:54 +00:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
2012-05-15 14:08:56 +00:00
|
|
|
if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) &&
|
|
|
|
!bss_conf->enable_beacon)
|
|
|
|
wl1271_tx_flush(wl);
|
|
|
|
|
2010-10-16 17:07:21 +00:00
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
|
goto out;
|
|
|
|
|
2011-10-10 08:13:06 +00:00
|
|
|
if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
|
|
|
|
goto out;
|
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
2010-10-16 17:07:21 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (is_ap)
|
|
|
|
wl1271_bss_info_changed_ap(wl, vif, bss_conf, changed);
|
|
|
|
else
|
|
|
|
wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed);
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
}
|
|
|
|
|
2011-10-02 08:15:52 +00:00
|
|
|
static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *vif, u16 queue,
|
2010-02-18 11:25:41 +00:00
|
|
|
const struct ieee80211_tx_queue_params *params)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
2011-10-05 09:55:51 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2010-03-18 10:26:38 +00:00
|
|
|
u8 ps_scheme;
|
2010-10-16 18:33:45 +00:00
|
|
|
int ret = 0;
|
2010-02-18 11:25:41 +00:00
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
|
|
|
|
|
2010-03-18 10:26:38 +00:00
|
|
|
if (params->uapsd)
|
|
|
|
ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
|
|
|
|
else
|
|
|
|
ps_scheme = CONF_PS_SCHEME_LEGACY;
|
|
|
|
|
2011-12-18 18:25:40 +00:00
|
|
|
if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
|
2011-03-23 20:22:15 +00:00
|
|
|
goto out;
|
2010-10-16 18:33:45 +00:00
|
|
|
|
2011-03-23 20:22:15 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2010-10-16 18:33:45 +00:00
|
|
|
|
2011-03-23 20:22:15 +00:00
|
|
|
/*
|
|
|
|
* the txop is confed in units of 32us by the mac80211,
|
|
|
|
* we need us
|
|
|
|
*/
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_ac_cfg(wl, wlvif, wl1271_tx_get_queue(queue),
|
2011-03-23 20:22:15 +00:00
|
|
|
params->cw_min, params->cw_max,
|
|
|
|
params->aifs, params->txop << 5);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_sleep;
|
|
|
|
|
2011-10-05 09:55:51 +00:00
|
|
|
ret = wl1271_acx_tid_cfg(wl, wlvif, wl1271_tx_get_queue(queue),
|
2011-03-23 20:22:15 +00:00
|
|
|
CONF_CHANNEL_TYPE_EDCF,
|
|
|
|
wl1271_tx_get_queue(queue),
|
|
|
|
ps_scheme, CONF_ACK_POLICY_LEGACY,
|
|
|
|
0, 0);
|
2010-02-18 11:25:47 +00:00
|
|
|
|
|
|
|
out_sleep:
|
2011-03-23 20:22:15 +00:00
|
|
|
wl1271_ps_elp_sleep(wl);
|
2010-02-18 11:25:41 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-09-21 11:06:11 +00:00
|
|
|
static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *vif)
|
2010-07-08 14:49:57 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
struct wl1271 *wl = hw->priv;
|
2012-01-31 09:57:18 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2010-07-08 14:49:57 +00:00
|
|
|
u64 mactime = ULLONG_MAX;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
|
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
2010-10-26 11:24:39 +00:00
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
|
goto out;
|
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
2010-07-08 14:49:57 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2012-01-31 09:57:18 +00:00
|
|
|
ret = wl12xx_acx_tsf_info(wl, wlvif, &mactime);
|
2010-07-08 14:49:57 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out_sleep;
|
|
|
|
|
|
|
|
out_sleep:
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
return mactime;
|
|
|
|
}
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-07-28 20:41:06 +00:00
|
|
|
static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
|
|
|
|
struct survey_info *survey)
|
|
|
|
{
|
|
|
|
struct ieee80211_conf *conf = &hw->conf;
|
2010-10-26 11:24:38 +00:00
|
|
|
|
2010-07-28 20:41:06 +00:00
|
|
|
if (idx != 0)
|
|
|
|
return -ENOENT;
|
2010-10-26 11:24:38 +00:00
|
|
|
|
2010-07-28 20:41:06 +00:00
|
|
|
survey->channel = conf->channel;
|
2012-06-13 15:56:54 +00:00
|
|
|
survey->filled = 0;
|
2010-07-28 20:41:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-22 22:22:29 +00:00
|
|
|
static int wl1271_allocate_sta(struct wl1271 *wl,
|
2011-10-05 09:56:05 +00:00
|
|
|
struct wl12xx_vif *wlvif,
|
|
|
|
struct ieee80211_sta *sta)
|
2010-10-16 18:21:23 +00:00
|
|
|
{
|
|
|
|
struct wl1271_station *wl_sta;
|
2011-10-05 09:56:05 +00:00
|
|
|
int ret;
|
2010-10-16 18:21:23 +00:00
|
|
|
|
2011-10-05 09:56:05 +00:00
|
|
|
|
|
|
|
if (wl->active_sta_count >= AP_MAX_STATIONS) {
|
2010-10-16 18:21:23 +00:00
|
|
|
wl1271_warning("could not allocate HLID - too much stations");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
wl_sta = (struct wl1271_station *)sta->drv_priv;
|
2011-10-05 09:56:05 +00:00
|
|
|
ret = wl12xx_allocate_link(wl, wlvif, &wl_sta->hlid);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_warning("could not allocate HLID - too many links");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_bit(wl_sta->hlid, wlvif->ap.sta_hlid_map);
|
2011-02-22 22:22:31 +00:00
|
|
|
memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
|
2011-08-25 09:43:15 +00:00
|
|
|
wl->active_sta_count++;
|
2010-10-16 18:21:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:56:05 +00:00
|
|
|
void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
|
2010-10-16 18:21:23 +00:00
|
|
|
{
|
2011-10-05 09:56:05 +00:00
|
|
|
if (!test_bit(hlid, wlvif->ap.sta_hlid_map))
|
2011-08-25 09:43:17 +00:00
|
|
|
return;
|
|
|
|
|
2011-10-05 09:56:05 +00:00
|
|
|
clear_bit(hlid, wlvif->ap.sta_hlid_map);
|
2011-02-22 22:22:31 +00:00
|
|
|
memset(wl->links[hlid].addr, 0, ETH_ALEN);
|
2011-08-17 07:45:49 +00:00
|
|
|
wl->links[hlid].ba_bitmap = 0;
|
2011-02-22 22:22:31 +00:00
|
|
|
__clear_bit(hlid, &wl->ap_ps_map);
|
|
|
|
__clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
|
2011-10-05 09:56:05 +00:00
|
|
|
wl12xx_free_link(wl, wlvif, &hlid);
|
2011-08-25 09:43:15 +00:00
|
|
|
wl->active_sta_count--;
|
2012-03-03 20:18:00 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* rearm the tx watchdog when the last STA is freed - give the FW a
|
|
|
|
* chance to return STA-buffered packets before complaining.
|
|
|
|
*/
|
|
|
|
if (wl->active_sta_count == 0)
|
|
|
|
wl12xx_rearm_tx_watchdog_locked(wl);
|
2010-10-16 18:21:23 +00:00
|
|
|
}
|
|
|
|
|
2012-03-04 08:55:47 +00:00
|
|
|
static int wl12xx_sta_add(struct wl1271 *wl,
|
|
|
|
struct wl12xx_vif *wlvif,
|
|
|
|
struct ieee80211_sta *sta)
|
2010-10-16 18:21:23 +00:00
|
|
|
{
|
2011-10-05 09:56:05 +00:00
|
|
|
struct wl1271_station *wl_sta;
|
2010-10-16 18:21:23 +00:00
|
|
|
int ret = 0;
|
|
|
|
u8 hlid;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
|
|
|
|
|
2011-10-05 09:56:05 +00:00
|
|
|
ret = wl1271_allocate_sta(wl, wlvif, sta);
|
2010-10-16 18:21:23 +00:00
|
|
|
if (ret < 0)
|
2012-03-04 08:55:47 +00:00
|
|
|
return ret;
|
2010-10-16 18:21:23 +00:00
|
|
|
|
2011-10-05 09:56:05 +00:00
|
|
|
wl_sta = (struct wl1271_station *)sta->drv_priv;
|
|
|
|
hlid = wl_sta->hlid;
|
|
|
|
|
2011-10-10 08:13:09 +00:00
|
|
|
ret = wl12xx_cmd_add_peer(wl, wlvif, sta, hlid);
|
2010-10-16 18:21:23 +00:00
|
|
|
if (ret < 0)
|
2012-03-04 08:55:47 +00:00
|
|
|
wl1271_free_sta(wl, wlvif, hlid);
|
2010-10-16 18:21:23 +00:00
|
|
|
|
2012-03-04 08:55:47 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2011-08-14 10:17:23 +00:00
|
|
|
|
2012-03-04 08:55:47 +00:00
|
|
|
static int wl12xx_sta_remove(struct wl1271 *wl,
|
|
|
|
struct wl12xx_vif *wlvif,
|
|
|
|
struct ieee80211_sta *sta)
|
|
|
|
{
|
|
|
|
struct wl1271_station *wl_sta;
|
|
|
|
int ret = 0, id;
|
2011-08-14 10:17:27 +00:00
|
|
|
|
2012-03-04 08:55:47 +00:00
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
|
|
|
|
|
|
|
|
wl_sta = (struct wl1271_station *)sta->drv_priv;
|
|
|
|
id = wl_sta->hlid;
|
|
|
|
if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
|
|
|
|
return -EINVAL;
|
2010-10-16 18:21:23 +00:00
|
|
|
|
2012-03-04 08:55:47 +00:00
|
|
|
ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid);
|
2011-02-22 22:22:29 +00:00
|
|
|
if (ret < 0)
|
2012-03-04 08:55:47 +00:00
|
|
|
return ret;
|
2011-02-22 22:22:29 +00:00
|
|
|
|
2012-03-04 08:55:47 +00:00
|
|
|
wl1271_free_sta(wl, wlvif, wl_sta->hlid);
|
2010-10-16 18:21:23 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-04 08:55:47 +00:00
|
|
|
static int wl12xx_update_sta_state(struct wl1271 *wl,
|
|
|
|
struct wl12xx_vif *wlvif,
|
|
|
|
struct ieee80211_sta *sta,
|
|
|
|
enum ieee80211_sta_state old_state,
|
|
|
|
enum ieee80211_sta_state new_state)
|
2010-10-16 18:21:23 +00:00
|
|
|
{
|
|
|
|
struct wl1271_station *wl_sta;
|
2012-03-04 08:55:47 +00:00
|
|
|
u8 hlid;
|
|
|
|
bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
|
|
|
|
bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
|
|
|
|
int ret;
|
2010-10-16 18:21:23 +00:00
|
|
|
|
2012-03-04 08:55:47 +00:00
|
|
|
wl_sta = (struct wl1271_station *)sta->drv_priv;
|
|
|
|
hlid = wl_sta->hlid;
|
2010-10-16 18:21:23 +00:00
|
|
|
|
2012-03-04 08:55:47 +00:00
|
|
|
/* Add station (AP mode) */
|
|
|
|
if (is_ap &&
|
|
|
|
old_state == IEEE80211_STA_NOTEXIST &&
|
|
|
|
new_state == IEEE80211_STA_NONE)
|
|
|
|
return wl12xx_sta_add(wl, wlvif, sta);
|
|
|
|
|
|
|
|
/* Remove station (AP mode) */
|
|
|
|
if (is_ap &&
|
|
|
|
old_state == IEEE80211_STA_NONE &&
|
|
|
|
new_state == IEEE80211_STA_NOTEXIST) {
|
|
|
|
/* must not fail */
|
|
|
|
wl12xx_sta_remove(wl, wlvif, sta);
|
|
|
|
return 0;
|
|
|
|
}
|
2010-10-16 18:21:23 +00:00
|
|
|
|
2012-03-04 08:55:47 +00:00
|
|
|
/* Authorize station (AP mode) */
|
|
|
|
if (is_ap &&
|
|
|
|
new_state == IEEE80211_STA_AUTHORIZED) {
|
|
|
|
ret = wl12xx_cmd_set_peer_state(wl, hlid);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2010-10-16 18:21:23 +00:00
|
|
|
|
2012-03-04 08:55:47 +00:00
|
|
|
ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true,
|
|
|
|
hlid);
|
|
|
|
return ret;
|
|
|
|
}
|
2010-10-16 18:21:23 +00:00
|
|
|
|
2012-03-04 08:55:48 +00:00
|
|
|
/* Authorize station */
|
|
|
|
if (is_sta &&
|
|
|
|
new_state == IEEE80211_STA_AUTHORIZED) {
|
|
|
|
set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
|
|
|
|
return wl12xx_set_authorized(wl, wlvif);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_sta &&
|
|
|
|
old_state == IEEE80211_STA_AUTHORIZED &&
|
|
|
|
new_state == IEEE80211_STA_ASSOC) {
|
|
|
|
clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-04 08:55:47 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wl12xx_op_sta_state(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *vif,
|
|
|
|
struct ieee80211_sta *sta,
|
|
|
|
enum ieee80211_sta_state old_state,
|
|
|
|
enum ieee80211_sta_state new_state)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 sta %d state=%d->%d",
|
|
|
|
sta->aid, old_state, new_state);
|
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
|
|
|
ret = -EBUSY;
|
2010-10-16 18:21:23 +00:00
|
|
|
goto out;
|
2012-03-04 08:55:47 +00:00
|
|
|
}
|
2010-10-16 18:21:23 +00:00
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
2010-10-16 18:21:23 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2012-03-04 08:55:47 +00:00
|
|
|
ret = wl12xx_update_sta_state(wl, wlvif, sta, old_state, new_state);
|
2010-10-16 18:21:23 +00:00
|
|
|
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
2012-03-04 08:55:47 +00:00
|
|
|
if (new_state < old_state)
|
|
|
|
return 0;
|
2010-10-16 18:21:23 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-03-21 17:26:41 +00:00
|
|
|
static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *vif,
|
|
|
|
enum ieee80211_ampdu_mlme_action action,
|
|
|
|
struct ieee80211_sta *sta, u16 tid, u16 *ssn,
|
|
|
|
u8 buf_size)
|
2011-01-23 06:27:23 +00:00
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
2011-10-05 09:55:45 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2011-01-23 06:27:23 +00:00
|
|
|
int ret;
|
2011-08-17 07:45:49 +00:00
|
|
|
u8 hlid, *ba_bitmap;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu action %d tid %d", action,
|
|
|
|
tid);
|
|
|
|
|
|
|
|
/* sanity check - the fields in FW are only 8bits wide */
|
|
|
|
if (WARN_ON(tid > 0xFF))
|
|
|
|
return -ENOTSUPP;
|
2011-01-23 06:27:23 +00:00
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
|
|
|
ret = -EAGAIN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2011-10-05 09:55:45 +00:00
|
|
|
if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
|
2011-10-05 09:55:53 +00:00
|
|
|
hlid = wlvif->sta.hlid;
|
2011-10-05 09:56:04 +00:00
|
|
|
ba_bitmap = &wlvif->sta.ba_rx_bitmap;
|
2011-10-05 09:55:45 +00:00
|
|
|
} else if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
|
2011-08-17 07:45:49 +00:00
|
|
|
struct wl1271_station *wl_sta;
|
|
|
|
|
|
|
|
wl_sta = (struct wl1271_station *)sta->drv_priv;
|
|
|
|
hlid = wl_sta->hlid;
|
|
|
|
ba_bitmap = &wl->links[hlid].ba_bitmap;
|
|
|
|
} else {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
2011-01-23 06:27:23 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-05-22 13:10:22 +00:00
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu: Rx tid %d action %d",
|
|
|
|
tid, action);
|
|
|
|
|
2011-01-23 06:27:23 +00:00
|
|
|
switch (action) {
|
|
|
|
case IEEE80211_AMPDU_RX_START:
|
2011-10-05 09:56:04 +00:00
|
|
|
if (!wlvif->ba_support || !wlvif->ba_allowed) {
|
2011-01-23 06:27:23 +00:00
|
|
|
ret = -ENOTSUPP;
|
2011-08-17 07:45:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wl->ba_rx_session_count >= RX_BA_MAX_SESSIONS) {
|
|
|
|
ret = -EBUSY;
|
|
|
|
wl1271_error("exceeded max RX BA sessions");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*ba_bitmap & BIT(tid)) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
wl1271_error("cannot enable RX BA session on active "
|
|
|
|
"tid: %d", tid);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = wl12xx_acx_set_ba_receiver_session(wl, tid, *ssn, true,
|
|
|
|
hlid);
|
|
|
|
if (!ret) {
|
|
|
|
*ba_bitmap |= BIT(tid);
|
|
|
|
wl->ba_rx_session_count++;
|
2011-01-23 06:27:23 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IEEE80211_AMPDU_RX_STOP:
|
2011-08-17 07:45:49 +00:00
|
|
|
if (!(*ba_bitmap & BIT(tid))) {
|
2012-06-06 07:48:56 +00:00
|
|
|
/*
|
|
|
|
* this happens on reconfig - so only output a debug
|
|
|
|
* message for now, and don't fail the function.
|
|
|
|
*/
|
|
|
|
wl1271_debug(DEBUG_MAC80211,
|
|
|
|
"no active RX BA session on tid: %d",
|
2011-08-17 07:45:49 +00:00
|
|
|
tid);
|
2012-06-06 07:48:56 +00:00
|
|
|
ret = 0;
|
2011-08-17 07:45:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = wl12xx_acx_set_ba_receiver_session(wl, tid, 0, false,
|
|
|
|
hlid);
|
|
|
|
if (!ret) {
|
|
|
|
*ba_bitmap &= ~BIT(tid);
|
|
|
|
wl->ba_rx_session_count--;
|
|
|
|
}
|
2011-01-23 06:27:23 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The BA initiator session management in FW independently.
|
|
|
|
* Falling break here on purpose for all TX APDU commands.
|
|
|
|
*/
|
|
|
|
case IEEE80211_AMPDU_TX_START:
|
|
|
|
case IEEE80211_AMPDU_TX_STOP:
|
|
|
|
case IEEE80211_AMPDU_TX_OPERATIONAL:
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
wl1271_error("Incorrect ampdu action id=%x\n", action);
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-09-19 10:51:42 +00:00
|
|
|
static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *vif,
|
|
|
|
const struct cfg80211_bitrate_mask *mask)
|
|
|
|
{
|
2011-10-10 08:12:53 +00:00
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
2011-09-19 10:51:42 +00:00
|
|
|
struct wl1271 *wl = hw->priv;
|
2011-10-11 09:57:39 +00:00
|
|
|
int i, ret = 0;
|
2011-09-19 10:51:42 +00:00
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 set_bitrate_mask 0x%x 0x%x",
|
|
|
|
mask->control[NL80211_BAND_2GHZ].legacy,
|
|
|
|
mask->control[NL80211_BAND_5GHZ].legacy);
|
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
for (i = 0; i < IEEE80211_NUM_BANDS; i++)
|
2011-10-10 08:12:53 +00:00
|
|
|
wlvif->bitrate_masks[i] =
|
2011-09-19 10:51:42 +00:00
|
|
|
wl1271_tx_enabled_rates_get(wl,
|
|
|
|
mask->control[i].legacy,
|
|
|
|
i);
|
2011-10-11 09:57:39 +00:00
|
|
|
|
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
|
|
|
|
!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
|
|
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
}
|
|
|
|
out:
|
2011-09-19 10:51:42 +00:00
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
2011-10-11 09:57:39 +00:00
|
|
|
return ret;
|
2011-09-19 10:51:42 +00:00
|
|
|
}
|
|
|
|
|
2011-09-08 10:01:33 +00:00
|
|
|
static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_channel_switch *ch_switch)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
2011-10-10 08:13:08 +00:00
|
|
|
struct wl12xx_vif *wlvif;
|
2011-09-08 10:01:33 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");
|
|
|
|
|
2012-02-27 22:41:33 +00:00
|
|
|
wl1271_tx_flush(wl);
|
|
|
|
|
2011-09-08 10:01:33 +00:00
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
2011-10-10 08:13:13 +00:00
|
|
|
wl12xx_for_each_wlvif_sta(wl, wlvif) {
|
|
|
|
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
|
|
|
|
ieee80211_chswitch_done(vif, false);
|
|
|
|
}
|
|
|
|
goto out;
|
2011-09-08 10:01:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-10-10 08:13:08 +00:00
|
|
|
/* TODO: change mac80211 to pass vif as param */
|
|
|
|
wl12xx_for_each_wlvif_sta(wl, wlvif) {
|
2012-01-31 09:57:19 +00:00
|
|
|
ret = wl12xx_cmd_channel_switch(wl, wlvif, ch_switch);
|
2011-09-08 10:01:33 +00:00
|
|
|
|
2011-10-10 08:13:08 +00:00
|
|
|
if (!ret)
|
|
|
|
set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
|
|
|
|
}
|
2011-09-08 10:01:33 +00:00
|
|
|
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
}
|
|
|
|
|
2011-04-26 20:35:39 +00:00
|
|
|
static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = hw->priv;
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* packets are considered pending if in the TX queue or the FW */
|
2011-07-07 11:25:23 +00:00
|
|
|
ret = (wl1271_tx_total_queue_count(wl) > 0) || (wl->tx_frames_cnt > 0);
|
2011-04-26 20:35:39 +00:00
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
/* can't be const, mac80211 writes to this */
|
|
|
|
static struct ieee80211_rate wl1271_rates[] = {
|
|
|
|
{ .bitrate = 10,
|
2009-10-13 09:47:39 +00:00
|
|
|
.hw_value = CONF_HW_BIT_RATE_1MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
|
2009-08-06 13:25:28 +00:00
|
|
|
{ .bitrate = 20,
|
2009-10-13 09:47:39 +00:00
|
|
|
.hw_value = CONF_HW_BIT_RATE_2MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_2MBPS,
|
2009-08-06 13:25:28 +00:00
|
|
|
.flags = IEEE80211_RATE_SHORT_PREAMBLE },
|
|
|
|
{ .bitrate = 55,
|
2009-10-13 09:47:39 +00:00
|
|
|
.hw_value = CONF_HW_BIT_RATE_5_5MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
|
2009-08-06 13:25:28 +00:00
|
|
|
.flags = IEEE80211_RATE_SHORT_PREAMBLE },
|
|
|
|
{ .bitrate = 110,
|
2009-10-13 09:47:39 +00:00
|
|
|
.hw_value = CONF_HW_BIT_RATE_11MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_11MBPS,
|
2009-08-06 13:25:28 +00:00
|
|
|
.flags = IEEE80211_RATE_SHORT_PREAMBLE },
|
|
|
|
{ .bitrate = 60,
|
2009-10-13 09:47:39 +00:00
|
|
|
.hw_value = CONF_HW_BIT_RATE_6MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
|
2009-08-06 13:25:28 +00:00
|
|
|
{ .bitrate = 90,
|
2009-10-13 09:47:39 +00:00
|
|
|
.hw_value = CONF_HW_BIT_RATE_9MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
|
2009-08-06 13:25:28 +00:00
|
|
|
{ .bitrate = 120,
|
2009-10-13 09:47:39 +00:00
|
|
|
.hw_value = CONF_HW_BIT_RATE_12MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
|
2009-08-06 13:25:28 +00:00
|
|
|
{ .bitrate = 180,
|
2009-10-13 09:47:39 +00:00
|
|
|
.hw_value = CONF_HW_BIT_RATE_18MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
|
2009-08-06 13:25:28 +00:00
|
|
|
{ .bitrate = 240,
|
2009-10-13 09:47:39 +00:00
|
|
|
.hw_value = CONF_HW_BIT_RATE_24MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
|
2009-08-06 13:25:28 +00:00
|
|
|
{ .bitrate = 360,
|
2009-10-13 09:47:39 +00:00
|
|
|
.hw_value = CONF_HW_BIT_RATE_36MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
|
2009-08-06 13:25:28 +00:00
|
|
|
{ .bitrate = 480,
|
2009-10-13 09:47:39 +00:00
|
|
|
.hw_value = CONF_HW_BIT_RATE_48MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
|
2009-08-06 13:25:28 +00:00
|
|
|
{ .bitrate = 540,
|
2009-10-13 09:47:39 +00:00
|
|
|
.hw_value = CONF_HW_BIT_RATE_54MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
|
2009-08-06 13:25:28 +00:00
|
|
|
};
|
|
|
|
|
2010-11-10 10:27:20 +00:00
|
|
|
/* can't be const, mac80211 writes to this */
|
2009-08-06 13:25:28 +00:00
|
|
|
static struct ieee80211_channel wl1271_channels[] = {
|
2009-12-11 13:40:46 +00:00
|
|
|
{ .hw_value = 1, .center_freq = 2412, .max_power = 25 },
|
2010-08-10 06:22:02 +00:00
|
|
|
{ .hw_value = 2, .center_freq = 2417, .max_power = 25 },
|
2010-11-10 10:27:20 +00:00
|
|
|
{ .hw_value = 3, .center_freq = 2422, .max_power = 25 },
|
|
|
|
{ .hw_value = 4, .center_freq = 2427, .max_power = 25 },
|
|
|
|
{ .hw_value = 5, .center_freq = 2432, .max_power = 25 },
|
2010-08-10 06:22:02 +00:00
|
|
|
{ .hw_value = 6, .center_freq = 2437, .max_power = 25 },
|
2010-11-10 10:27:20 +00:00
|
|
|
{ .hw_value = 7, .center_freq = 2442, .max_power = 25 },
|
|
|
|
{ .hw_value = 8, .center_freq = 2447, .max_power = 25 },
|
|
|
|
{ .hw_value = 9, .center_freq = 2452, .max_power = 25 },
|
2010-08-10 06:22:02 +00:00
|
|
|
{ .hw_value = 10, .center_freq = 2457, .max_power = 25 },
|
2010-11-10 10:27:20 +00:00
|
|
|
{ .hw_value = 11, .center_freq = 2462, .max_power = 25 },
|
|
|
|
{ .hw_value = 12, .center_freq = 2467, .max_power = 25 },
|
|
|
|
{ .hw_value = 13, .center_freq = 2472, .max_power = 25 },
|
2011-01-18 19:39:52 +00:00
|
|
|
{ .hw_value = 14, .center_freq = 2484, .max_power = 25 },
|
2009-08-06 13:25:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* can't be const, mac80211 writes to this */
|
|
|
|
static struct ieee80211_supported_band wl1271_band_2ghz = {
|
|
|
|
.channels = wl1271_channels,
|
|
|
|
.n_channels = ARRAY_SIZE(wl1271_channels),
|
|
|
|
.bitrates = wl1271_rates,
|
|
|
|
.n_bitrates = ARRAY_SIZE(wl1271_rates),
|
|
|
|
};
|
|
|
|
|
2009-10-13 09:47:48 +00:00
|
|
|
/* 5 GHz data rates for WL1273 */
|
|
|
|
static struct ieee80211_rate wl1271_rates_5ghz[] = {
|
|
|
|
{ .bitrate = 60,
|
|
|
|
.hw_value = CONF_HW_BIT_RATE_6MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
|
|
|
|
{ .bitrate = 90,
|
|
|
|
.hw_value = CONF_HW_BIT_RATE_9MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
|
|
|
|
{ .bitrate = 120,
|
|
|
|
.hw_value = CONF_HW_BIT_RATE_12MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
|
|
|
|
{ .bitrate = 180,
|
|
|
|
.hw_value = CONF_HW_BIT_RATE_18MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
|
|
|
|
{ .bitrate = 240,
|
|
|
|
.hw_value = CONF_HW_BIT_RATE_24MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
|
|
|
|
{ .bitrate = 360,
|
|
|
|
.hw_value = CONF_HW_BIT_RATE_36MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
|
|
|
|
{ .bitrate = 480,
|
|
|
|
.hw_value = CONF_HW_BIT_RATE_48MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
|
|
|
|
{ .bitrate = 540,
|
|
|
|
.hw_value = CONF_HW_BIT_RATE_54MBPS,
|
|
|
|
.hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
|
|
|
|
};
|
|
|
|
|
2010-11-10 10:27:20 +00:00
|
|
|
/* 5 GHz band channels for WL1273 */
|
2009-10-13 09:47:48 +00:00
|
|
|
static struct ieee80211_channel wl1271_channels_5ghz[] = {
|
2011-06-27 19:06:33 +00:00
|
|
|
{ .hw_value = 7, .center_freq = 5035, .max_power = 25 },
|
|
|
|
{ .hw_value = 8, .center_freq = 5040, .max_power = 25 },
|
|
|
|
{ .hw_value = 9, .center_freq = 5045, .max_power = 25 },
|
|
|
|
{ .hw_value = 11, .center_freq = 5055, .max_power = 25 },
|
|
|
|
{ .hw_value = 12, .center_freq = 5060, .max_power = 25 },
|
|
|
|
{ .hw_value = 16, .center_freq = 5080, .max_power = 25 },
|
|
|
|
{ .hw_value = 34, .center_freq = 5170, .max_power = 25 },
|
|
|
|
{ .hw_value = 36, .center_freq = 5180, .max_power = 25 },
|
|
|
|
{ .hw_value = 38, .center_freq = 5190, .max_power = 25 },
|
|
|
|
{ .hw_value = 40, .center_freq = 5200, .max_power = 25 },
|
|
|
|
{ .hw_value = 42, .center_freq = 5210, .max_power = 25 },
|
|
|
|
{ .hw_value = 44, .center_freq = 5220, .max_power = 25 },
|
|
|
|
{ .hw_value = 46, .center_freq = 5230, .max_power = 25 },
|
|
|
|
{ .hw_value = 48, .center_freq = 5240, .max_power = 25 },
|
|
|
|
{ .hw_value = 52, .center_freq = 5260, .max_power = 25 },
|
|
|
|
{ .hw_value = 56, .center_freq = 5280, .max_power = 25 },
|
|
|
|
{ .hw_value = 60, .center_freq = 5300, .max_power = 25 },
|
|
|
|
{ .hw_value = 64, .center_freq = 5320, .max_power = 25 },
|
|
|
|
{ .hw_value = 100, .center_freq = 5500, .max_power = 25 },
|
|
|
|
{ .hw_value = 104, .center_freq = 5520, .max_power = 25 },
|
|
|
|
{ .hw_value = 108, .center_freq = 5540, .max_power = 25 },
|
|
|
|
{ .hw_value = 112, .center_freq = 5560, .max_power = 25 },
|
|
|
|
{ .hw_value = 116, .center_freq = 5580, .max_power = 25 },
|
|
|
|
{ .hw_value = 120, .center_freq = 5600, .max_power = 25 },
|
|
|
|
{ .hw_value = 124, .center_freq = 5620, .max_power = 25 },
|
|
|
|
{ .hw_value = 128, .center_freq = 5640, .max_power = 25 },
|
|
|
|
{ .hw_value = 132, .center_freq = 5660, .max_power = 25 },
|
|
|
|
{ .hw_value = 136, .center_freq = 5680, .max_power = 25 },
|
|
|
|
{ .hw_value = 140, .center_freq = 5700, .max_power = 25 },
|
|
|
|
{ .hw_value = 149, .center_freq = 5745, .max_power = 25 },
|
|
|
|
{ .hw_value = 153, .center_freq = 5765, .max_power = 25 },
|
|
|
|
{ .hw_value = 157, .center_freq = 5785, .max_power = 25 },
|
|
|
|
{ .hw_value = 161, .center_freq = 5805, .max_power = 25 },
|
|
|
|
{ .hw_value = 165, .center_freq = 5825, .max_power = 25 },
|
2009-10-13 09:47:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct ieee80211_supported_band wl1271_band_5ghz = {
|
|
|
|
.channels = wl1271_channels_5ghz,
|
|
|
|
.n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
|
|
|
|
.bitrates = wl1271_rates_5ghz,
|
|
|
|
.n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
|
2010-03-26 10:53:11 +00:00
|
|
|
};
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
static const struct ieee80211_ops wl1271_ops = {
|
|
|
|
.start = wl1271_op_start,
|
|
|
|
.stop = wl1271_op_stop,
|
|
|
|
.add_interface = wl1271_op_add_interface,
|
|
|
|
.remove_interface = wl1271_op_remove_interface,
|
2011-12-19 10:00:03 +00:00
|
|
|
.change_interface = wl12xx_op_change_interface,
|
2011-05-18 20:51:26 +00:00
|
|
|
#ifdef CONFIG_PM
|
2011-05-13 08:57:09 +00:00
|
|
|
.suspend = wl1271_op_suspend,
|
|
|
|
.resume = wl1271_op_resume,
|
2011-05-18 20:51:26 +00:00
|
|
|
#endif
|
2009-08-06 13:25:28 +00:00
|
|
|
.config = wl1271_op_config,
|
2009-10-08 18:56:31 +00:00
|
|
|
.prepare_multicast = wl1271_op_prepare_multicast,
|
2009-08-06 13:25:28 +00:00
|
|
|
.configure_filter = wl1271_op_configure_filter,
|
|
|
|
.tx = wl1271_op_tx,
|
2012-05-18 04:46:40 +00:00
|
|
|
.set_key = wlcore_op_set_key,
|
2009-08-06 13:25:28 +00:00
|
|
|
.hw_scan = wl1271_op_hw_scan,
|
2011-06-27 10:06:45 +00:00
|
|
|
.cancel_hw_scan = wl1271_op_cancel_hw_scan,
|
2011-05-10 11:46:02 +00:00
|
|
|
.sched_scan_start = wl1271_op_sched_scan_start,
|
|
|
|
.sched_scan_stop = wl1271_op_sched_scan_stop,
|
2009-08-06 13:25:28 +00:00
|
|
|
.bss_info_changed = wl1271_op_bss_info_changed,
|
2010-11-08 09:51:07 +00:00
|
|
|
.set_frag_threshold = wl1271_op_set_frag_threshold,
|
2009-08-06 13:25:28 +00:00
|
|
|
.set_rts_threshold = wl1271_op_set_rts_threshold,
|
2010-02-18 11:25:41 +00:00
|
|
|
.conf_tx = wl1271_op_conf_tx,
|
2010-07-08 14:49:57 +00:00
|
|
|
.get_tsf = wl1271_op_get_tsf,
|
2010-07-28 20:41:06 +00:00
|
|
|
.get_survey = wl1271_op_get_survey,
|
2012-03-04 08:55:47 +00:00
|
|
|
.sta_state = wl12xx_op_sta_state,
|
2011-01-23 06:27:23 +00:00
|
|
|
.ampdu_action = wl1271_op_ampdu_action,
|
2011-04-26 20:35:39 +00:00
|
|
|
.tx_frames_pending = wl1271_tx_frames_pending,
|
2011-09-19 10:51:42 +00:00
|
|
|
.set_bitrate_mask = wl12xx_set_bitrate_mask,
|
2011-09-08 10:01:33 +00:00
|
|
|
.channel_switch = wl12xx_op_channel_switch,
|
2010-02-18 11:25:53 +00:00
|
|
|
CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
|
2009-08-06 13:25:28 +00:00
|
|
|
};
|
|
|
|
|
2010-03-26 10:53:11 +00:00
|
|
|
|
2011-12-07 22:43:48 +00:00
|
|
|
u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band)
|
2010-03-26 10:53:11 +00:00
|
|
|
{
|
|
|
|
u8 idx;
|
|
|
|
|
2011-12-07 22:43:48 +00:00
|
|
|
BUG_ON(band >= 2);
|
2010-03-26 10:53:11 +00:00
|
|
|
|
2011-12-07 22:43:48 +00:00
|
|
|
if (unlikely(rate >= wl->hw_tx_rate_tbl_size)) {
|
2010-03-26 10:53:11 +00:00
|
|
|
wl1271_error("Illegal RX rate from HW: %d", rate);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-12-07 22:43:48 +00:00
|
|
|
idx = wl->band_rate_to_idx[band][rate];
|
2010-03-26 10:53:11 +00:00
|
|
|
if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
|
|
|
|
wl1271_error("Unsupported RX rate from HW: %d", rate);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
2010-03-18 10:26:32 +00:00
|
|
|
static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = dev_get_drvdata(dev);
|
|
|
|
ssize_t len;
|
|
|
|
|
2010-08-10 04:38:35 +00:00
|
|
|
len = PAGE_SIZE;
|
2010-03-18 10:26:32 +00:00
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
|
|
|
|
wl->sg_enabled);
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = dev_get_drvdata(dev);
|
|
|
|
unsigned long res;
|
|
|
|
int ret;
|
|
|
|
|
2011-04-01 14:49:54 +00:00
|
|
|
ret = kstrtoul(buf, 10, &res);
|
2010-03-18 10:26:32 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_warning("incorrect value written to bt_coex_mode");
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
|
|
res = !!res;
|
|
|
|
|
|
|
|
if (res == wl->sg_enabled)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
wl->sg_enabled = res;
|
|
|
|
|
|
|
|
if (wl->state == WL1271_STATE_OFF)
|
|
|
|
goto out;
|
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
2010-03-18 10:26:32 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
wl1271_acx_sg_enable(wl, wl->sg_enabled);
|
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
|
|
|
|
wl1271_sysfs_show_bt_coex_state,
|
|
|
|
wl1271_sysfs_store_bt_coex_state);
|
|
|
|
|
2010-05-07 08:38:58 +00:00
|
|
|
static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = dev_get_drvdata(dev);
|
|
|
|
ssize_t len;
|
|
|
|
|
2010-08-10 04:38:35 +00:00
|
|
|
len = PAGE_SIZE;
|
2010-05-07 08:38:58 +00:00
|
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
if (wl->hw_pg_ver >= 0)
|
|
|
|
len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
|
|
|
|
else
|
|
|
|
len = snprintf(buf, len, "n/a\n");
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2011-07-18 11:21:49 +00:00
|
|
|
static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
|
2010-05-07 08:38:58 +00:00
|
|
|
wl1271_sysfs_show_hw_pg_ver, NULL);
|
|
|
|
|
2011-06-06 11:57:06 +00:00
|
|
|
static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
|
|
|
|
struct bin_attribute *bin_attr,
|
|
|
|
char *buffer, loff_t pos, size_t count)
|
|
|
|
{
|
|
|
|
struct device *dev = container_of(kobj, struct device, kobj);
|
|
|
|
struct wl1271 *wl = dev_get_drvdata(dev);
|
|
|
|
ssize_t len;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = mutex_lock_interruptible(&wl->mutex);
|
|
|
|
if (ret < 0)
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
/* Let only one thread read the log at a time, blocking others */
|
|
|
|
while (wl->fwlog_size == 0) {
|
|
|
|
DEFINE_WAIT(wait);
|
|
|
|
|
|
|
|
prepare_to_wait_exclusive(&wl->fwlog_waitq,
|
|
|
|
&wait,
|
|
|
|
TASK_INTERRUPTIBLE);
|
|
|
|
|
|
|
|
if (wl->fwlog_size != 0) {
|
|
|
|
finish_wait(&wl->fwlog_waitq, &wait);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
schedule();
|
|
|
|
finish_wait(&wl->fwlog_waitq, &wait);
|
|
|
|
|
|
|
|
if (signal_pending(current))
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
ret = mutex_lock_interruptible(&wl->mutex);
|
|
|
|
if (ret < 0)
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the fwlog is still valid */
|
|
|
|
if (wl->fwlog_size < 0) {
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Seeking is not supported - old logs are not kept. Disregard pos. */
|
|
|
|
len = min(count, (size_t)wl->fwlog_size);
|
|
|
|
wl->fwlog_size -= len;
|
|
|
|
memcpy(buffer, wl->fwlog, len);
|
|
|
|
|
|
|
|
/* Make room for new messages */
|
|
|
|
memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
|
|
|
|
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bin_attribute fwlog_attr = {
|
|
|
|
.attr = {.name = "fwlog", .mode = S_IRUSR},
|
|
|
|
.read = wl1271_sysfs_read_fwlog,
|
|
|
|
};
|
|
|
|
|
2012-05-16 03:00:00 +00:00
|
|
|
static void wl1271_connection_loss_work(struct work_struct *work)
|
2012-04-26 07:35:07 +00:00
|
|
|
{
|
|
|
|
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 == WL1271_STATE_OFF))
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2011-12-23 07:32:17 +00:00
|
|
|
static void wl12xx_derive_mac_addresses(struct wl1271 *wl,
|
|
|
|
u32 oui, u32 nic, int n)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_PROBE, "base address: oui %06x nic %06x, n %d",
|
|
|
|
oui, nic, n);
|
|
|
|
|
|
|
|
if (nic + n - 1 > 0xffffff)
|
|
|
|
wl1271_warning("NIC part of the MAC address wraps around!");
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
wl->addresses[i].addr[0] = (u8)(oui >> 16);
|
|
|
|
wl->addresses[i].addr[1] = (u8)(oui >> 8);
|
|
|
|
wl->addresses[i].addr[2] = (u8) oui;
|
|
|
|
wl->addresses[i].addr[3] = (u8)(nic >> 16);
|
|
|
|
wl->addresses[i].addr[4] = (u8)(nic >> 8);
|
|
|
|
wl->addresses[i].addr[5] = (u8) nic;
|
|
|
|
nic++;
|
|
|
|
}
|
|
|
|
|
|
|
|
wl->hw->wiphy->n_addresses = n;
|
|
|
|
wl->hw->wiphy->addresses = wl->addresses;
|
|
|
|
}
|
|
|
|
|
2012-01-18 12:53:22 +00:00
|
|
|
static int wl12xx_get_hw_info(struct wl1271 *wl)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = wl12xx_set_power_on(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2012-06-18 12:50:21 +00:00
|
|
|
ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &wl->chip.id);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2012-01-18 12:53:22 +00:00
|
|
|
|
2011-11-29 11:38:37 +00:00
|
|
|
wl->fuse_oui_addr = 0;
|
|
|
|
wl->fuse_nic_addr = 0;
|
2012-01-18 12:53:22 +00:00
|
|
|
|
2012-06-18 12:50:21 +00:00
|
|
|
ret = wl->ops->get_pg_ver(wl, &wl->hw_pg_ver);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2012-01-18 12:53:22 +00:00
|
|
|
|
2012-04-11 08:07:28 +00:00
|
|
|
if (wl->ops->get_mac)
|
2012-06-18 12:50:21 +00:00
|
|
|
ret = wl->ops->get_mac(wl);
|
2011-12-23 07:32:17 +00:00
|
|
|
|
2012-01-18 12:53:22 +00:00
|
|
|
out:
|
2012-06-18 12:50:21 +00:00
|
|
|
wl1271_power_off(wl);
|
2012-01-18 12:53:22 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-10-06 07:46:20 +00:00
|
|
|
static int wl1271_register_hw(struct wl1271 *wl)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2011-12-23 07:32:17 +00:00
|
|
|
u32 oui_addr = 0, nic_addr = 0;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
if (wl->mac80211_registered)
|
|
|
|
return 0;
|
|
|
|
|
2012-05-29 15:38:05 +00:00
|
|
|
wl1271_fetch_nvs(wl);
|
|
|
|
if (wl->nvs != NULL) {
|
2011-03-06 14:32:10 +00:00
|
|
|
/* NOTE: The wl->nvs->nvs element must be first, in
|
|
|
|
* order to simplify the casting, we assume it is at
|
|
|
|
* the beginning of the wl->nvs structure.
|
|
|
|
*/
|
|
|
|
u8 *nvs_ptr = (u8 *)wl->nvs;
|
2010-10-16 19:49:52 +00:00
|
|
|
|
2011-12-23 07:32:17 +00:00
|
|
|
oui_addr =
|
|
|
|
(nvs_ptr[11] << 16) + (nvs_ptr[10] << 8) + nvs_ptr[6];
|
|
|
|
nic_addr =
|
|
|
|
(nvs_ptr[5] << 16) + (nvs_ptr[4] << 8) + nvs_ptr[3];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if the MAC address is zeroed in the NVS derive from fuse */
|
|
|
|
if (oui_addr == 0 && nic_addr == 0) {
|
|
|
|
oui_addr = wl->fuse_oui_addr;
|
|
|
|
/* fuse has the BD_ADDR, the WLAN addresses are the next two */
|
|
|
|
nic_addr = wl->fuse_nic_addr + 1;
|
2010-10-16 19:49:52 +00:00
|
|
|
}
|
|
|
|
|
2011-12-23 07:32:17 +00:00
|
|
|
wl12xx_derive_mac_addresses(wl, oui_addr, nic_addr, 2);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
ret = ieee80211_register_hw(wl->hw);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_error("unable to register mac80211 hw: %d", ret);
|
2012-01-18 12:53:22 +00:00
|
|
|
goto out;
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
wl->mac80211_registered = true;
|
|
|
|
|
2010-11-24 10:53:16 +00:00
|
|
|
wl1271_debugfs_init(wl);
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
wl1271_notice("loaded");
|
|
|
|
|
2012-01-18 12:53:22 +00:00
|
|
|
out:
|
|
|
|
return ret;
|
2009-08-06 13:25:28 +00:00
|
|
|
}
|
|
|
|
|
2011-10-06 07:46:20 +00:00
|
|
|
static void wl1271_unregister_hw(struct wl1271 *wl)
|
2010-03-18 10:26:46 +00:00
|
|
|
{
|
2012-02-06 10:47:54 +00:00
|
|
|
if (wl->plt)
|
2012-01-11 07:42:39 +00:00
|
|
|
wl1271_plt_stop(wl);
|
2011-01-14 11:48:46 +00:00
|
|
|
|
2010-03-18 10:26:46 +00:00
|
|
|
ieee80211_unregister_hw(wl->hw);
|
|
|
|
wl->mac80211_registered = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-06-13 17:29:16 +00:00
|
|
|
static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
|
|
|
|
{
|
|
|
|
.max = 2,
|
|
|
|
.types = BIT(NL80211_IFTYPE_STATION),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.max = 1,
|
|
|
|
.types = BIT(NL80211_IFTYPE_AP) |
|
|
|
|
BIT(NL80211_IFTYPE_P2P_GO) |
|
|
|
|
BIT(NL80211_IFTYPE_P2P_CLIENT),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct ieee80211_iface_combination
|
|
|
|
wlcore_iface_combinations[] = {
|
|
|
|
{
|
|
|
|
.num_different_channels = 1,
|
|
|
|
.max_interfaces = 2,
|
|
|
|
.limits = wlcore_iface_limits,
|
|
|
|
.n_limits = ARRAY_SIZE(wlcore_iface_limits),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-10-06 07:46:20 +00:00
|
|
|
static int wl1271_init_ieee80211(struct wl1271 *wl)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
2010-09-27 10:42:07 +00:00
|
|
|
static const u32 cipher_suites[] = {
|
|
|
|
WLAN_CIPHER_SUITE_WEP40,
|
|
|
|
WLAN_CIPHER_SUITE_WEP104,
|
|
|
|
WLAN_CIPHER_SUITE_TKIP,
|
|
|
|
WLAN_CIPHER_SUITE_CCMP,
|
|
|
|
WL1271_CIPHER_SUITE_GEM,
|
|
|
|
};
|
|
|
|
|
2012-05-18 04:46:36 +00:00
|
|
|
/* The tx descriptor buffer */
|
|
|
|
wl->hw->extra_tx_headroom = sizeof(struct wl1271_tx_hw_descr);
|
|
|
|
|
|
|
|
if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
|
|
|
|
wl->hw->extra_tx_headroom += WL1271_EXTRA_SPACE_TKIP;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
/* unit us */
|
|
|
|
/* FIXME: find a proper value */
|
|
|
|
wl->hw->channel_change_time = 10000;
|
2010-04-01 08:38:22 +00:00
|
|
|
wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
|
|
|
wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
|
2010-02-22 06:38:41 +00:00
|
|
|
IEEE80211_HW_SUPPORTS_PS |
|
2012-01-31 09:57:21 +00:00
|
|
|
IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
|
2010-03-18 10:26:38 +00:00
|
|
|
IEEE80211_HW_SUPPORTS_UAPSD |
|
2010-03-26 10:53:30 +00:00
|
|
|
IEEE80211_HW_HAS_RATE_CONTROL |
|
2010-04-09 08:07:30 +00:00
|
|
|
IEEE80211_HW_CONNECTION_MONITOR |
|
2011-05-02 09:37:33 +00:00
|
|
|
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
|
2011-05-11 09:12:56 +00:00
|
|
|
IEEE80211_HW_SPECTRUM_MGMT |
|
2011-08-30 06:34:01 +00:00
|
|
|
IEEE80211_HW_AP_LINK_PS |
|
|
|
|
IEEE80211_HW_AMPDU_AGGREGATION |
|
2012-02-02 11:15:35 +00:00
|
|
|
IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
|
|
|
|
IEEE80211_HW_SCAN_WHILE_IDLE;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-09-27 10:42:07 +00:00
|
|
|
wl->hw->wiphy->cipher_suites = cipher_suites;
|
|
|
|
wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
|
|
|
|
|
2009-12-11 13:41:04 +00:00
|
|
|
wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
|
2011-08-28 12:23:01 +00:00
|
|
|
BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) |
|
|
|
|
BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO);
|
2009-08-06 13:25:28 +00:00
|
|
|
wl->hw->wiphy->max_scan_ssids = 1;
|
2011-09-02 11:28:22 +00:00
|
|
|
wl->hw->wiphy->max_sched_scan_ssids = 16;
|
|
|
|
wl->hw->wiphy->max_match_sets = 16;
|
2010-12-09 14:54:59 +00:00
|
|
|
/*
|
|
|
|
* Maximum length of elements in scanning probe request templates
|
|
|
|
* should be the maximum length possible for a template, without
|
|
|
|
* the IEEE80211 header of the template
|
|
|
|
*/
|
2012-02-02 11:54:27 +00:00
|
|
|
wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
|
2010-12-09 14:54:59 +00:00
|
|
|
sizeof(struct ieee80211_header);
|
2011-01-11 17:25:18 +00:00
|
|
|
|
2012-02-02 11:54:27 +00:00
|
|
|
wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
|
2011-09-27 13:22:35 +00:00
|
|
|
sizeof(struct ieee80211_header);
|
|
|
|
|
2012-03-26 16:47:18 +00:00
|
|
|
wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
|
|
|
|
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
|
2011-08-25 11:26:54 +00:00
|
|
|
|
2011-03-21 21:16:14 +00:00
|
|
|
/* make sure all our channels fit in the scanned_ch bitmask */
|
|
|
|
BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
|
|
|
|
ARRAY_SIZE(wl1271_channels_5ghz) >
|
|
|
|
WL1271_MAX_CHANNELS);
|
2011-01-11 17:25:18 +00:00
|
|
|
/*
|
|
|
|
* We keep local copies of the band structs because we need to
|
|
|
|
* modify them on a per-device basis.
|
|
|
|
*/
|
|
|
|
memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz,
|
|
|
|
sizeof(wl1271_band_2ghz));
|
2012-05-15 14:09:00 +00:00
|
|
|
memcpy(&wl->bands[IEEE80211_BAND_2GHZ].ht_cap,
|
|
|
|
&wl->ht_cap[IEEE80211_BAND_2GHZ],
|
|
|
|
sizeof(*wl->ht_cap));
|
2011-01-11 17:25:18 +00:00
|
|
|
memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz,
|
|
|
|
sizeof(wl1271_band_5ghz));
|
2012-05-15 14:09:00 +00:00
|
|
|
memcpy(&wl->bands[IEEE80211_BAND_5GHZ].ht_cap,
|
|
|
|
&wl->ht_cap[IEEE80211_BAND_5GHZ],
|
|
|
|
sizeof(*wl->ht_cap));
|
2011-01-11 17:25:18 +00:00
|
|
|
|
|
|
|
wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
|
|
|
|
&wl->bands[IEEE80211_BAND_2GHZ];
|
|
|
|
wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
|
|
|
|
&wl->bands[IEEE80211_BAND_5GHZ];
|
2009-10-13 09:47:48 +00:00
|
|
|
|
2010-03-18 10:26:33 +00:00
|
|
|
wl->hw->queues = 4;
|
2010-03-26 10:53:12 +00:00
|
|
|
wl->hw->max_rates = 1;
|
2010-03-18 10:26:33 +00:00
|
|
|
|
2010-11-10 10:27:19 +00:00
|
|
|
wl->hw->wiphy->reg_notifier = wl1271_reg_notify;
|
|
|
|
|
2011-11-08 16:46:55 +00:00
|
|
|
/* the FW answers probe-requests in AP-mode */
|
|
|
|
wl->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
|
|
|
|
wl->hw->wiphy->probe_resp_offload =
|
|
|
|
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
|
|
|
|
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
|
|
|
|
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
|
|
|
|
|
2012-06-13 17:29:16 +00:00
|
|
|
/* allowed interface combinations */
|
|
|
|
wl->hw->wiphy->iface_combinations = wlcore_iface_combinations;
|
|
|
|
wl->hw->wiphy->n_iface_combinations =
|
|
|
|
ARRAY_SIZE(wlcore_iface_combinations);
|
|
|
|
|
2011-10-06 07:07:44 +00:00
|
|
|
SET_IEEE80211_DEV(wl->hw, wl->dev);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-10-16 18:21:23 +00:00
|
|
|
wl->hw->sta_data_size = sizeof(struct wl1271_station);
|
2011-10-05 09:55:41 +00:00
|
|
|
wl->hw->vif_data_size = sizeof(struct wl12xx_vif);
|
2010-10-16 18:21:23 +00:00
|
|
|
|
2012-01-05 22:05:51 +00:00
|
|
|
wl->hw->max_rx_aggregation_subframes = wl->conf.ht.rx_ba_win_size;
|
2011-01-12 13:27:03 +00:00
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define WL1271_DEFAULT_CHANNEL 0
|
2010-02-18 11:25:57 +00:00
|
|
|
|
2011-12-07 19:09:03 +00:00
|
|
|
struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size)
|
2009-08-06 13:25:28 +00:00
|
|
|
{
|
|
|
|
struct ieee80211_hw *hw;
|
|
|
|
struct wl1271 *wl;
|
2011-02-22 22:22:26 +00:00
|
|
|
int i, j, ret;
|
2010-09-30 11:28:27 +00:00
|
|
|
unsigned int order;
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-10-05 09:56:05 +00:00
|
|
|
BUILD_BUG_ON(AP_MAX_STATIONS > WL12XX_MAX_LINKS);
|
2011-09-22 06:52:05 +00:00
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
|
|
|
|
if (!hw) {
|
|
|
|
wl1271_error("could not alloc ieee80211_hw");
|
2010-03-18 10:26:31 +00:00
|
|
|
ret = -ENOMEM;
|
2010-03-18 10:26:46 +00:00
|
|
|
goto err_hw_alloc;
|
|
|
|
}
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
wl = hw->priv;
|
|
|
|
memset(wl, 0, sizeof(*wl));
|
|
|
|
|
2011-12-07 19:09:03 +00:00
|
|
|
wl->priv = kzalloc(priv_size, GFP_KERNEL);
|
|
|
|
if (!wl->priv) {
|
|
|
|
wl1271_error("could not alloc wl priv");
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err_priv_alloc;
|
|
|
|
}
|
|
|
|
|
2011-10-10 08:12:54 +00:00
|
|
|
INIT_LIST_HEAD(&wl->wlvif_list);
|
2009-10-13 09:47:55 +00:00
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
wl->hw = hw;
|
|
|
|
|
2011-02-22 22:22:26 +00:00
|
|
|
for (i = 0; i < NUM_TX_QUEUES; i++)
|
2011-10-05 09:56:05 +00:00
|
|
|
for (j = 0; j < WL12XX_MAX_LINKS; j++)
|
2011-02-22 22:22:26 +00:00
|
|
|
skb_queue_head_init(&wl->links[j].tx_queue[i]);
|
|
|
|
|
2011-03-01 13:14:41 +00:00
|
|
|
skb_queue_head_init(&wl->deferred_rx_queue);
|
|
|
|
skb_queue_head_init(&wl->deferred_tx_queue);
|
|
|
|
|
2009-10-08 18:56:21 +00:00
|
|
|
INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
|
2011-03-01 13:14:41 +00:00
|
|
|
INIT_WORK(&wl->netstack_work, wl1271_netstack_work);
|
2010-09-30 08:43:28 +00:00
|
|
|
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);
|
2012-03-03 20:18:00 +00:00
|
|
|
INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work);
|
2012-04-26 07:35:07 +00:00
|
|
|
INIT_DELAYED_WORK(&wl->connection_loss_work,
|
|
|
|
wl1271_connection_loss_work);
|
2011-05-15 08:10:29 +00:00
|
|
|
|
2011-06-07 09:50:46 +00:00
|
|
|
wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
|
|
|
|
if (!wl->freezable_wq) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err_hw;
|
|
|
|
}
|
|
|
|
|
2009-08-06 13:25:28 +00:00
|
|
|
wl->channel = WL1271_DEFAULT_CHANNEL;
|
|
|
|
wl->rx_counter = 0;
|
|
|
|
wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
|
2009-10-08 18:56:24 +00:00
|
|
|
wl->band = IEEE80211_BAND_2GHZ;
|
2012-05-10 09:13:30 +00:00
|
|
|
wl->channel_type = NL80211_CHAN_NO_HT;
|
2009-12-11 13:41:06 +00:00
|
|
|
wl->flags = 0;
|
2010-03-18 10:26:32 +00:00
|
|
|
wl->sg_enabled = true;
|
2012-06-10 14:09:22 +00:00
|
|
|
wl->sleep_auth = WL1271_PSM_ILLEGAL;
|
2010-05-07 08:38:58 +00:00
|
|
|
wl->hw_pg_ver = -1;
|
2011-02-22 22:22:31 +00:00
|
|
|
wl->ap_ps_map = 0;
|
|
|
|
wl->ap_fw_ps_map = 0;
|
2011-03-01 13:14:39 +00:00
|
|
|
wl->quirks = 0;
|
2011-03-31 08:07:01 +00:00
|
|
|
wl->platform_quirks = 0;
|
2011-05-10 11:46:02 +00:00
|
|
|
wl->sched_scanning = false;
|
2011-08-14 10:17:15 +00:00
|
|
|
wl->system_hlid = WL12XX_SYSTEM_HLID;
|
2011-08-25 09:43:15 +00:00
|
|
|
wl->active_sta_count = 0;
|
2011-06-06 11:57:06 +00:00
|
|
|
wl->fwlog_size = 0;
|
|
|
|
init_waitqueue_head(&wl->fwlog_waitq);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2011-08-14 10:17:15 +00:00
|
|
|
/* The system link is always allocated */
|
|
|
|
__set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
|
|
|
|
|
2010-10-12 14:20:06 +00:00
|
|
|
memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
|
2011-12-07 19:21:51 +00:00
|
|
|
for (i = 0; i < wl->num_tx_desc; i++)
|
2009-08-06 13:25:28 +00:00
|
|
|
wl->tx_frames[i] = NULL;
|
|
|
|
|
|
|
|
spin_lock_init(&wl->wl_lock);
|
|
|
|
|
|
|
|
wl->state = WL1271_STATE_OFF;
|
2012-02-06 10:47:54 +00:00
|
|
|
wl->fw_type = WL12XX_FW_TYPE_NONE;
|
2009-08-06 13:25:28 +00:00
|
|
|
mutex_init(&wl->mutex);
|
2012-05-18 04:46:39 +00:00
|
|
|
mutex_init(&wl->flush_mutex);
|
2009-08-06 13:25:28 +00:00
|
|
|
|
2010-09-30 11:28:27 +00:00
|
|
|
order = get_order(WL1271_AGGR_BUFFER_SIZE);
|
|
|
|
wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
|
|
|
|
if (!wl->aggr_buf) {
|
|
|
|
ret = -ENOMEM;
|
2011-06-07 09:50:46 +00:00
|
|
|
goto err_wq;
|
2010-09-30 11:28:27 +00:00
|
|
|
}
|
|
|
|
|
2011-03-31 08:06:59 +00:00
|
|
|
wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
|
|
|
|
if (!wl->dummy_packet) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err_aggr;
|
|
|
|
}
|
|
|
|
|
2011-06-06 11:57:06 +00:00
|
|
|
/* Allocate one page for the FW log */
|
|
|
|
wl->fwlog = (u8 *)get_zeroed_page(GFP_KERNEL);
|
|
|
|
if (!wl->fwlog) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err_dummy_packet;
|
|
|
|
}
|
|
|
|
|
2012-05-03 07:31:02 +00:00
|
|
|
wl->mbox = kmalloc(sizeof(*wl->mbox), GFP_KERNEL | GFP_DMA);
|
2012-03-17 17:41:53 +00:00
|
|
|
if (!wl->mbox) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err_fwlog;
|
|
|
|
}
|
|
|
|
|
2010-02-18 11:25:57 +00:00
|
|
|
return hw;
|
2010-03-18 10:26:31 +00:00
|
|
|
|
2012-03-17 17:41:53 +00:00
|
|
|
err_fwlog:
|
|
|
|
free_page((unsigned long)wl->fwlog);
|
|
|
|
|
2011-03-31 08:06:59 +00:00
|
|
|
err_dummy_packet:
|
|
|
|
dev_kfree_skb(wl->dummy_packet);
|
|
|
|
|
2010-09-30 11:28:27 +00:00
|
|
|
err_aggr:
|
|
|
|
free_pages((unsigned long)wl->aggr_buf, order);
|
|
|
|
|
2011-06-07 09:50:46 +00:00
|
|
|
err_wq:
|
|
|
|
destroy_workqueue(wl->freezable_wq);
|
|
|
|
|
2010-03-18 10:26:31 +00:00
|
|
|
err_hw:
|
2010-03-18 10:26:46 +00:00
|
|
|
wl1271_debugfs_exit(wl);
|
2011-12-07 19:09:03 +00:00
|
|
|
kfree(wl->priv);
|
|
|
|
|
|
|
|
err_priv_alloc:
|
2010-03-18 10:26:46 +00:00
|
|
|
ieee80211_free_hw(hw);
|
|
|
|
|
|
|
|
err_hw_alloc:
|
2010-03-18 10:26:31 +00:00
|
|
|
|
|
|
|
return ERR_PTR(ret);
|
2010-02-18 11:25:57 +00:00
|
|
|
}
|
2011-11-21 16:55:51 +00:00
|
|
|
EXPORT_SYMBOL_GPL(wlcore_alloc_hw);
|
2010-02-18 11:25:57 +00:00
|
|
|
|
2011-11-21 16:55:51 +00:00
|
|
|
int wlcore_free_hw(struct wl1271 *wl)
|
2010-02-18 11:25:57 +00:00
|
|
|
{
|
2011-06-06 11:57:06 +00:00
|
|
|
/* Unblock any fwlog readers */
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
wl->fwlog_size = -1;
|
|
|
|
wake_up_interruptible_all(&wl->fwlog_waitq);
|
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
2011-10-06 10:05:25 +00:00
|
|
|
device_remove_bin_file(wl->dev, &fwlog_attr);
|
2011-07-18 11:21:49 +00:00
|
|
|
|
2011-10-06 10:05:25 +00:00
|
|
|
device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
|
2011-07-18 11:21:49 +00:00
|
|
|
|
2011-10-06 10:05:25 +00:00
|
|
|
device_remove_file(wl->dev, &dev_attr_bt_coex_state);
|
2011-06-06 11:57:06 +00:00
|
|
|
free_page((unsigned long)wl->fwlog);
|
2011-03-31 08:06:59 +00:00
|
|
|
dev_kfree_skb(wl->dummy_packet);
|
2010-09-30 11:28:27 +00:00
|
|
|
free_pages((unsigned long)wl->aggr_buf,
|
|
|
|
get_order(WL1271_AGGR_BUFFER_SIZE));
|
2010-02-18 11:25:57 +00:00
|
|
|
|
|
|
|
wl1271_debugfs_exit(wl);
|
|
|
|
|
|
|
|
vfree(wl->fw);
|
|
|
|
wl->fw = NULL;
|
2012-02-06 10:47:54 +00:00
|
|
|
wl->fw_type = WL12XX_FW_TYPE_NONE;
|
2010-02-18 11:25:57 +00:00
|
|
|
kfree(wl->nvs);
|
|
|
|
wl->nvs = NULL;
|
|
|
|
|
2012-05-10 09:13:54 +00:00
|
|
|
kfree(wl->fw_status_1);
|
2010-02-18 11:25:57 +00:00
|
|
|
kfree(wl->tx_res_if);
|
2011-06-07 09:50:46 +00:00
|
|
|
destroy_workqueue(wl->freezable_wq);
|
2010-02-18 11:25:57 +00:00
|
|
|
|
2011-12-07 19:09:03 +00:00
|
|
|
kfree(wl->priv);
|
2010-02-18 11:25:57 +00:00
|
|
|
ieee80211_free_hw(wl->hw);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2011-11-21 16:55:51 +00:00
|
|
|
EXPORT_SYMBOL_GPL(wlcore_free_hw);
|
2010-02-22 06:38:26 +00:00
|
|
|
|
2011-10-06 07:07:44 +00:00
|
|
|
static irqreturn_t wl12xx_hardirq(int irq, void *cookie)
|
|
|
|
{
|
|
|
|
struct wl1271 *wl = cookie;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
wl1271_debug(DEBUG_IRQ, "IRQ");
|
|
|
|
|
|
|
|
/* complete the ELP completion */
|
|
|
|
spin_lock_irqsave(&wl->wl_lock, flags);
|
|
|
|
set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
|
|
|
|
if (wl->elp_compl) {
|
|
|
|
complete(wl->elp_compl);
|
|
|
|
wl->elp_compl = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
|
|
|
|
/* don't enqueue a work right now. mark it as pending */
|
|
|
|
set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
|
|
|
|
wl1271_debug(DEBUG_IRQ, "should not enqueue work");
|
|
|
|
disable_irq_nosync(wl->irq);
|
|
|
|
pm_wakeup_event(wl->dev, 0);
|
|
|
|
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&wl->wl_lock, flags);
|
|
|
|
|
|
|
|
return IRQ_WAKE_THREAD;
|
|
|
|
}
|
|
|
|
|
2011-11-21 16:55:51 +00:00
|
|
|
int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
|
2011-10-05 11:12:55 +00:00
|
|
|
{
|
2011-10-06 07:07:44 +00:00
|
|
|
struct wl12xx_platform_data *pdata = pdev->dev.platform_data;
|
|
|
|
unsigned long irqflags;
|
2011-11-21 16:55:51 +00:00
|
|
|
int ret;
|
2011-10-06 07:07:44 +00:00
|
|
|
|
2011-11-21 18:37:14 +00:00
|
|
|
if (!wl->ops || !wl->ptable) {
|
2011-11-21 17:25:24 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out_free_hw;
|
2011-10-06 07:07:44 +00:00
|
|
|
}
|
|
|
|
|
2011-12-07 19:21:51 +00:00
|
|
|
BUG_ON(wl->num_tx_desc > WLCORE_MAX_TX_DESCRIPTORS);
|
|
|
|
|
2011-12-05 14:12:54 +00:00
|
|
|
/* adjust some runtime configuration parameters */
|
|
|
|
wlcore_adjust_conf(wl);
|
|
|
|
|
2011-10-06 07:07:44 +00:00
|
|
|
wl->irq = platform_get_irq(pdev, 0);
|
|
|
|
wl->platform_quirks = pdata->platform_quirks;
|
|
|
|
wl->set_power = pdata->set_power;
|
|
|
|
wl->dev = &pdev->dev;
|
|
|
|
wl->if_ops = pdata->ops;
|
|
|
|
|
|
|
|
platform_set_drvdata(pdev, wl);
|
|
|
|
|
|
|
|
if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
|
|
|
|
irqflags = IRQF_TRIGGER_RISING;
|
|
|
|
else
|
|
|
|
irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
|
|
|
|
|
|
|
|
ret = request_threaded_irq(wl->irq, wl12xx_hardirq, wl1271_irq,
|
|
|
|
irqflags,
|
|
|
|
pdev->name, wl);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_error("request_irq() failed: %d", ret);
|
|
|
|
goto out_free_hw;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = enable_irq_wake(wl->irq);
|
|
|
|
if (!ret) {
|
|
|
|
wl->irq_wake_enabled = true;
|
|
|
|
device_init_wakeup(wl->dev, 1);
|
2012-03-14 04:32:10 +00:00
|
|
|
if (pdata->pwr_in_suspend) {
|
2011-11-21 16:55:51 +00:00
|
|
|
wl->hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
|
2012-03-14 04:32:10 +00:00
|
|
|
wl->hw->wiphy->wowlan.n_patterns =
|
|
|
|
WL1271_MAX_RX_FILTERS;
|
|
|
|
wl->hw->wiphy->wowlan.pattern_min_len = 1;
|
|
|
|
wl->hw->wiphy->wowlan.pattern_max_len =
|
|
|
|
WL1271_RX_FILTER_MAX_PATTERN_SIZE;
|
|
|
|
}
|
2011-10-06 07:07:44 +00:00
|
|
|
}
|
|
|
|
disable_irq(wl->irq);
|
|
|
|
|
2012-05-10 09:14:02 +00:00
|
|
|
ret = wl12xx_get_hw_info(wl);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_error("couldn't get hw info");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = wl->ops->identify_chip(wl);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2011-10-06 07:07:44 +00:00
|
|
|
ret = wl1271_init_ieee80211(wl);
|
|
|
|
if (ret)
|
|
|
|
goto out_irq;
|
|
|
|
|
|
|
|
ret = wl1271_register_hw(wl);
|
|
|
|
if (ret)
|
|
|
|
goto out_irq;
|
|
|
|
|
2011-10-06 10:05:25 +00:00
|
|
|
/* Create sysfs file to control bt coex state */
|
|
|
|
ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_error("failed to create sysfs file bt_coex_state");
|
|
|
|
goto out_irq;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create sysfs file to get HW PG version */
|
|
|
|
ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_error("failed to create sysfs file hw_pg_ver");
|
|
|
|
goto out_bt_coex_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create sysfs file for the FW log */
|
|
|
|
ret = device_create_bin_file(wl->dev, &fwlog_attr);
|
|
|
|
if (ret < 0) {
|
|
|
|
wl1271_error("failed to create sysfs file fwlog");
|
|
|
|
goto out_hw_pg_ver;
|
|
|
|
}
|
|
|
|
|
2011-11-21 16:55:51 +00:00
|
|
|
goto out;
|
2011-10-06 07:07:44 +00:00
|
|
|
|
2011-10-06 10:05:25 +00:00
|
|
|
out_hw_pg_ver:
|
|
|
|
device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
|
|
|
|
|
|
|
|
out_bt_coex_state:
|
|
|
|
device_remove_file(wl->dev, &dev_attr_bt_coex_state);
|
|
|
|
|
2011-10-06 07:07:44 +00:00
|
|
|
out_irq:
|
|
|
|
free_irq(wl->irq, wl);
|
|
|
|
|
|
|
|
out_free_hw:
|
2011-11-21 16:55:51 +00:00
|
|
|
wlcore_free_hw(wl);
|
2011-10-06 07:07:44 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
2011-10-05 11:12:55 +00:00
|
|
|
}
|
2011-11-20 21:32:10 +00:00
|
|
|
EXPORT_SYMBOL_GPL(wlcore_probe);
|
2011-10-05 11:12:55 +00:00
|
|
|
|
2011-11-20 21:32:10 +00:00
|
|
|
int __devexit wlcore_remove(struct platform_device *pdev)
|
2011-10-05 11:12:55 +00:00
|
|
|
{
|
2011-10-06 07:07:44 +00:00
|
|
|
struct wl1271 *wl = platform_get_drvdata(pdev);
|
|
|
|
|
|
|
|
if (wl->irq_wake_enabled) {
|
|
|
|
device_init_wakeup(wl->dev, 0);
|
|
|
|
disable_irq_wake(wl->irq);
|
|
|
|
}
|
|
|
|
wl1271_unregister_hw(wl);
|
|
|
|
free_irq(wl->irq, wl);
|
2011-11-21 16:55:51 +00:00
|
|
|
wlcore_free_hw(wl);
|
2011-10-06 07:07:44 +00:00
|
|
|
|
2011-10-05 11:12:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-11-20 21:32:10 +00:00
|
|
|
EXPORT_SYMBOL_GPL(wlcore_remove);
|
2011-10-05 11:12:55 +00:00
|
|
|
|
2011-01-12 09:33:29 +00:00
|
|
|
u32 wl12xx_debug_level = DEBUG_NONE;
|
2010-12-12 10:15:35 +00:00
|
|
|
EXPORT_SYMBOL_GPL(wl12xx_debug_level);
|
2011-01-12 09:33:29 +00:00
|
|
|
module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
|
2010-12-12 10:15:35 +00:00
|
|
|
MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
|
|
|
|
|
2011-06-06 11:57:06 +00:00
|
|
|
module_param_named(fwlog, fwlog_param, charp, 0);
|
2012-02-07 10:37:33 +00:00
|
|
|
MODULE_PARM_DESC(fwlog,
|
2011-06-06 11:57:06 +00:00
|
|
|
"FW logger options: continuous, ondemand, dbgpins or disable");
|
|
|
|
|
2011-08-25 15:10:59 +00:00
|
|
|
module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
|
|
|
|
|
2011-12-08 11:06:45 +00:00
|
|
|
module_param(no_recovery, bool, S_IRUSR | S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck.");
|
|
|
|
|
2010-02-22 06:38:26 +00:00
|
|
|
MODULE_LICENSE("GPL");
|
2011-02-22 12:19:28 +00:00
|
|
|
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
|
2010-02-22 06:38:26 +00:00
|
|
|
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
|