329 lines
9.8 KiB
C
329 lines
9.8 KiB
C
/*
|
|
* Copyright (c) 2014 genua mbh <info@genua.de>
|
|
* Copyright (c) 2014 Fixup Software Ltd.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
/*-
|
|
* Based on BSD-licensed source modules in the Linux iwlwifi driver,
|
|
* which were used as the reference documentation for this implementation.
|
|
*
|
|
* Driver version we are currently based off of is
|
|
* Linux 4.7.3 (tag id d7f6728f57e3ecbb7ef34eb7d9f564d514775d75)
|
|
*
|
|
******************************************************************************
|
|
*
|
|
* This file is provided under a dual BSD/GPLv2 license. When using or
|
|
* redistributing this file, you may do so under either license.
|
|
*
|
|
* GPL LICENSE SUMMARY
|
|
*
|
|
* Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
|
|
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of version 2 of the GNU General Public License 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 Street, Fifth Floor, Boston, MA 02110,
|
|
* USA
|
|
*
|
|
* The full GNU General Public License is included in this distribution
|
|
* in the file called COPYING.
|
|
*
|
|
* Contact Information:
|
|
* Intel Linux Wireless <linuxwifi@intel.com>
|
|
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
*
|
|
* BSD LICENSE
|
|
*
|
|
* Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
|
|
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name Intel Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include <sys/cdefs.h>
|
|
#include "opt_wlan.h"
|
|
#include "opt_iwm.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/endian.h>
|
|
#include <sys/firmware.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/module.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/rman.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/linker.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/endian.h>
|
|
#include <machine/resource.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_var.h>
|
|
#include <net/if_arp.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_media.h>
|
|
#include <net/if_types.h>
|
|
#include <net/bpf.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/if_ether.h>
|
|
#include <netinet/ip.h>
|
|
|
|
#include <net80211/ieee80211_var.h>
|
|
#include <net80211/ieee80211_regdomain.h>
|
|
#include <net80211/ieee80211_ratectl.h>
|
|
#include <net80211/ieee80211_radiotap.h>
|
|
|
|
#include <dev/iwm/if_iwmreg.h>
|
|
#include <dev/iwm/if_iwmvar.h>
|
|
#include <dev/iwm/if_iwm_config.h>
|
|
#include <dev/iwm/if_iwm_debug.h>
|
|
#include <dev/iwm/if_iwm_util.h>
|
|
#include <dev/iwm/if_iwm_sf.h>
|
|
|
|
/*
|
|
* Aging and idle timeouts for the different possible scenarios
|
|
* in default configuration
|
|
*/
|
|
static const uint32_t
|
|
sf_full_timeout_def[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = {
|
|
{
|
|
htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER_DEF),
|
|
htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER_DEF)
|
|
},
|
|
{
|
|
htole32(IWM_SF_AGG_UNICAST_AGING_TIMER_DEF),
|
|
htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER_DEF)
|
|
},
|
|
{
|
|
htole32(IWM_SF_MCAST_AGING_TIMER_DEF),
|
|
htole32(IWM_SF_MCAST_IDLE_TIMER_DEF)
|
|
},
|
|
{
|
|
htole32(IWM_SF_BA_AGING_TIMER_DEF),
|
|
htole32(IWM_SF_BA_IDLE_TIMER_DEF)
|
|
},
|
|
{
|
|
htole32(IWM_SF_TX_RE_AGING_TIMER_DEF),
|
|
htole32(IWM_SF_TX_RE_IDLE_TIMER_DEF)
|
|
},
|
|
};
|
|
|
|
/*
|
|
* Aging and idle timeouts for the different possible scenarios
|
|
* in single BSS MAC configuration.
|
|
*/
|
|
static const uint32_t
|
|
sf_full_timeout[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = {
|
|
{
|
|
htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER),
|
|
htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER)
|
|
},
|
|
{
|
|
htole32(IWM_SF_AGG_UNICAST_AGING_TIMER),
|
|
htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER)
|
|
},
|
|
{
|
|
htole32(IWM_SF_MCAST_AGING_TIMER),
|
|
htole32(IWM_SF_MCAST_IDLE_TIMER)
|
|
},
|
|
{
|
|
htole32(IWM_SF_BA_AGING_TIMER),
|
|
htole32(IWM_SF_BA_IDLE_TIMER)
|
|
},
|
|
{
|
|
htole32(IWM_SF_TX_RE_AGING_TIMER),
|
|
htole32(IWM_SF_TX_RE_IDLE_TIMER)
|
|
},
|
|
};
|
|
|
|
static void
|
|
iwm_fill_sf_command(struct iwm_softc *sc, struct iwm_sf_cfg_cmd *sf_cmd,
|
|
struct ieee80211_node *ni)
|
|
{
|
|
int i, j, watermark;
|
|
|
|
sf_cmd->watermark[IWM_SF_LONG_DELAY_ON] = htole32(IWM_SF_W_MARK_SCAN);
|
|
|
|
/*
|
|
* If we are in association flow - check antenna configuration
|
|
* capabilities of the AP station, and choose the watermark accordingly.
|
|
*/
|
|
if (ni) {
|
|
if (ni->ni_flags & IEEE80211_NODE_HT) {
|
|
watermark = IWM_SF_W_MARK_SISO;
|
|
} else {
|
|
watermark = IWM_SF_W_MARK_LEGACY;
|
|
}
|
|
/* default watermark value for unassociated mode. */
|
|
} else {
|
|
watermark = IWM_SF_W_MARK_MIMO2;
|
|
}
|
|
sf_cmd->watermark[IWM_SF_FULL_ON] = htole32(watermark);
|
|
|
|
for (i = 0; i < IWM_SF_NUM_SCENARIO; i++) {
|
|
for (j = 0; j < IWM_SF_NUM_TIMEOUT_TYPES; j++) {
|
|
sf_cmd->long_delay_timeouts[i][j] =
|
|
htole32(IWM_SF_LONG_DELAY_AGING_TIMER);
|
|
}
|
|
}
|
|
|
|
if (ni) {
|
|
_Static_assert(sizeof(sf_full_timeout) == sizeof(uint32_t) *
|
|
IWM_SF_NUM_SCENARIO * IWM_SF_NUM_TIMEOUT_TYPES,
|
|
"sf_full_timeout has wrong size");
|
|
|
|
memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
|
|
sizeof(sf_full_timeout));
|
|
} else {
|
|
_Static_assert(sizeof(sf_full_timeout_def) == sizeof(uint32_t) *
|
|
IWM_SF_NUM_SCENARIO * IWM_SF_NUM_TIMEOUT_TYPES,
|
|
"sf_full_timeout_def has wrong size");
|
|
|
|
memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def,
|
|
sizeof(sf_full_timeout_def));
|
|
}
|
|
}
|
|
|
|
static int
|
|
iwm_sf_config(struct iwm_softc *sc, struct ieee80211_node *ni,
|
|
enum iwm_sf_state new_state)
|
|
{
|
|
struct iwm_sf_cfg_cmd sf_cmd = {
|
|
.state = htole32(new_state),
|
|
};
|
|
int ret = 0;
|
|
|
|
#ifdef notyet /* only relevant for sdio variants */
|
|
if (sc->cfg->disable_dummy_notification)
|
|
sf_cmd.state |= htole32(IWM_SF_CFG_DUMMY_NOTIF_OFF);
|
|
#endif
|
|
|
|
/*
|
|
* If an associated AP sta changed its antenna configuration, the state
|
|
* will remain FULL_ON but SF parameters need to be reconsidered.
|
|
*/
|
|
if (new_state != IWM_SF_FULL_ON && sc->sf_state == new_state)
|
|
return 0;
|
|
|
|
switch (new_state) {
|
|
case IWM_SF_UNINIT:
|
|
iwm_fill_sf_command(sc, &sf_cmd, NULL);
|
|
break;
|
|
case IWM_SF_FULL_ON:
|
|
iwm_fill_sf_command(sc, &sf_cmd, ni);
|
|
break;
|
|
case IWM_SF_INIT_OFF:
|
|
iwm_fill_sf_command(sc, &sf_cmd, NULL);
|
|
break;
|
|
default:
|
|
device_printf(sc->sc_dev,
|
|
"Invalid state: %d. not sending Smart Fifo cmd\n",
|
|
new_state);
|
|
return EINVAL;
|
|
}
|
|
|
|
ret = iwm_send_cmd_pdu(sc, IWM_REPLY_SF_CFG_CMD, IWM_CMD_ASYNC,
|
|
sizeof(sf_cmd), &sf_cmd);
|
|
if (!ret)
|
|
sc->sf_state = new_state;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Update Smart fifo:
|
|
* Count bound interfaces that are not to be removed, ignoring p2p devices,
|
|
* and set new state accordingly.
|
|
*/
|
|
int
|
|
iwm_sf_update(struct iwm_softc *sc, struct ieee80211vap *changed_vif,
|
|
boolean_t remove_vif)
|
|
{
|
|
enum iwm_sf_state new_state;
|
|
struct ieee80211_node *ni = NULL;
|
|
int num_active_macs = 0;
|
|
|
|
/* If changed_vif exists and is not to be removed, add to the count */
|
|
if (changed_vif && !remove_vif)
|
|
num_active_macs++;
|
|
|
|
switch (num_active_macs) {
|
|
case 0:
|
|
/* If there are no active macs - change state to SF_INIT_OFF */
|
|
new_state = IWM_SF_INIT_OFF;
|
|
break;
|
|
case 1:
|
|
if (!changed_vif)
|
|
return EINVAL;
|
|
ni = changed_vif->iv_bss;
|
|
if (ni != NULL && IWM_NODE(ni)->in_assoc &&
|
|
changed_vif->iv_dtim_period) {
|
|
new_state = IWM_SF_FULL_ON;
|
|
} else {
|
|
new_state = IWM_SF_INIT_OFF;
|
|
}
|
|
break;
|
|
default:
|
|
/* If there are multiple active macs - change to SF_UNINIT */
|
|
new_state = IWM_SF_UNINIT;
|
|
}
|
|
return iwm_sf_config(sc, ni, new_state);
|
|
}
|