/* * Copyright (c) 2014 genua mbh * 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 * 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 __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include "opt_iwm.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 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_mvm_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_mvm_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_mvm_fill_sf_command(sc, &sf_cmd, NULL); break; case IWM_SF_FULL_ON: iwm_mvm_fill_sf_command(sc, &sf_cmd, ni); break; case IWM_SF_INIT_OFF: iwm_mvm_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_mvm_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_mvm_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_mvm_sf_config(sc, ni, new_state); }