The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/iwm/if_iwm_power.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */
    2 
    3 /*
    4  * Copyright (c) 2014 genua mbh <info@genua.de>
    5  * Copyright (c) 2014 Fixup Software Ltd.
    6  *
    7  * Permission to use, copy, modify, and distribute this software for any
    8  * purpose with or without fee is hereby granted, provided that the above
    9  * copyright notice and this permission notice appear in all copies.
   10  *
   11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   18  */
   19 
   20 /*-
   21  * Based on BSD-licensed source modules in the Linux iwlwifi driver,
   22  * which were used as the reference documentation for this implementation.
   23  *
   24  * Driver version we are currently based off of is
   25  * Linux 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd)
   26  *
   27  ***********************************************************************
   28  *
   29  * This file is provided under a dual BSD/GPLv2 license.  When using or
   30  * redistributing this file, you may do so under either license.
   31  *
   32  * GPL LICENSE SUMMARY
   33  *
   34  * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
   35  *
   36  * This program is free software; you can redistribute it and/or modify
   37  * it under the terms of version 2 of the GNU General Public License as
   38  * published by the Free Software Foundation.
   39  *
   40  * This program is distributed in the hope that it will be useful, but
   41  * WITHOUT ANY WARRANTY; without even the implied warranty of
   42  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   43  * General Public License for more details.
   44  *
   45  * You should have received a copy of the GNU General Public License
   46  * along with this program; if not, write to the Free Software
   47  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
   48  * USA
   49  *
   50  * The full GNU General Public License is included in this distribution
   51  * in the file called COPYING.
   52  *
   53  * Contact Information:
   54  *  Intel Linux Wireless <ilw@linux.intel.com>
   55  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
   56  *
   57  *
   58  * BSD LICENSE
   59  *
   60  * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
   61  * All rights reserved.
   62  *
   63  * Redistribution and use in source and binary forms, with or without
   64  * modification, are permitted provided that the following conditions
   65  * are met:
   66  *
   67  *  * Redistributions of source code must retain the above copyright
   68  *    notice, this list of conditions and the following disclaimer.
   69  *  * Redistributions in binary form must reproduce the above copyright
   70  *    notice, this list of conditions and the following disclaimer in
   71  *    the documentation and/or other materials provided with the
   72  *    distribution.
   73  *  * Neither the name Intel Corporation nor the names of its
   74  *    contributors may be used to endorse or promote products derived
   75  *    from this software without specific prior written permission.
   76  *
   77  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   78  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   79  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   80  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   81  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   82  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   83  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   84  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   85  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   86  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   87  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   88  */
   89 #include <sys/cdefs.h>
   90 __FBSDID("$FreeBSD$");
   91 
   92 #include "opt_wlan.h"
   93 #include "opt_iwm.h"
   94 
   95 #include <sys/param.h>
   96 #include <sys/bus.h>
   97 #include <sys/conf.h>
   98 #include <sys/endian.h>
   99 #include <sys/firmware.h>
  100 #include <sys/kernel.h>
  101 #include <sys/malloc.h>
  102 #include <sys/mbuf.h>
  103 #include <sys/mutex.h>
  104 #include <sys/module.h>
  105 #include <sys/proc.h>
  106 #include <sys/rman.h>
  107 #include <sys/socket.h>
  108 #include <sys/sockio.h>
  109 #include <sys/sysctl.h>
  110 #include <sys/linker.h>
  111 
  112 #include <machine/bus.h>
  113 #include <machine/endian.h>
  114 #include <machine/resource.h>
  115 
  116 #include <dev/pci/pcivar.h>
  117 #include <dev/pci/pcireg.h>
  118 
  119 #include <net/bpf.h>
  120 
  121 #include <net/if.h>
  122 #include <net/if_var.h>
  123 #include <net/if_arp.h>
  124 #include <net/if_dl.h>
  125 #include <net/if_media.h>
  126 #include <net/if_types.h>
  127 
  128 #include <netinet/in.h>
  129 #include <netinet/in_systm.h>
  130 #include <netinet/if_ether.h>
  131 #include <netinet/ip.h>
  132 
  133 #include <net80211/ieee80211_var.h>
  134 #include <net80211/ieee80211_regdomain.h>
  135 #include <net80211/ieee80211_ratectl.h>
  136 #include <net80211/ieee80211_radiotap.h>
  137 
  138 #include <dev/iwm/if_iwmreg.h>
  139 #include <dev/iwm/if_iwmvar.h>
  140 #include <dev/iwm/if_iwm_debug.h>
  141 #include <dev/iwm/if_iwm_constants.h>
  142 #include <dev/iwm/if_iwm_util.h>
  143 #include <dev/iwm/if_iwm_power.h>
  144 
  145 static int iwm_power_scheme = IWM_POWER_SCHEME_BPS;
  146 
  147 TUNABLE_INT("hw.iwm.power_scheme", &iwm_power_scheme);
  148 
  149 /*
  150  * BEGIN mvm/power.c
  151  */
  152 
  153 #define IWM_POWER_KEEP_ALIVE_PERIOD_SEC    25
  154 
  155 static int
  156 iwm_beacon_filter_send_cmd(struct iwm_softc *sc,
  157         struct iwm_beacon_filter_cmd *cmd)
  158 {
  159         int ret;
  160 
  161         ret = iwm_send_cmd_pdu(sc, IWM_REPLY_BEACON_FILTERING_CMD,
  162             0, sizeof(struct iwm_beacon_filter_cmd), cmd);
  163 
  164         if (!ret) {
  165                 IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  166                     "ba_enable_beacon_abort is: %d\n",
  167                     le32toh(cmd->ba_enable_beacon_abort));
  168                 IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  169                     "ba_escape_timer is: %d\n",
  170                     le32toh(cmd->ba_escape_timer));
  171                 IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  172                     "bf_debug_flag is: %d\n",
  173                     le32toh(cmd->bf_debug_flag));
  174                 IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  175                     "bf_enable_beacon_filter is: %d\n",
  176                     le32toh(cmd->bf_enable_beacon_filter));
  177                 IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  178                     "bf_energy_delta is: %d\n",
  179                     le32toh(cmd->bf_energy_delta));
  180                 IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  181                     "bf_escape_timer is: %d\n",
  182                     le32toh(cmd->bf_escape_timer));
  183                 IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  184                     "bf_roaming_energy_delta is: %d\n",
  185                     le32toh(cmd->bf_roaming_energy_delta));
  186                 IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  187                     "bf_roaming_state is: %d\n",
  188                     le32toh(cmd->bf_roaming_state));
  189                 IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  190                     "bf_temp_threshold is: %d\n",
  191                     le32toh(cmd->bf_temp_threshold));
  192                 IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  193                     "bf_temp_fast_filter is: %d\n",
  194                     le32toh(cmd->bf_temp_fast_filter));
  195                 IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  196                     "bf_temp_slow_filter is: %d\n",
  197                     le32toh(cmd->bf_temp_slow_filter));
  198         }
  199         return ret;
  200 }
  201 
  202 static void
  203 iwm_beacon_filter_set_cqm_params(struct iwm_softc *sc,
  204         struct iwm_vap *ivp, struct iwm_beacon_filter_cmd *cmd)
  205 {
  206         cmd->ba_enable_beacon_abort = htole32(sc->sc_bf.ba_enabled);
  207 }
  208 
  209 static void
  210 iwm_power_log(struct iwm_softc *sc, struct iwm_mac_power_cmd *cmd)
  211 {
  212         IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  213             "Sending power table command on mac id 0x%X for "
  214             "power level %d, flags = 0x%X\n",
  215             cmd->id_and_color, iwm_power_scheme, le16toh(cmd->flags));
  216         IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  217             "Keep alive = %u sec\n", le16toh(cmd->keep_alive_seconds));
  218 
  219         if (!(cmd->flags & htole16(IWM_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) {
  220                 IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  221                     "Disable power management\n");
  222                 return;
  223         }
  224 
  225         IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  226             "Rx timeout = %u usec\n", le32toh(cmd->rx_data_timeout));
  227         IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  228             "Tx timeout = %u usec\n", le32toh(cmd->tx_data_timeout));
  229         if (cmd->flags & htole16(IWM_POWER_FLAGS_SKIP_OVER_DTIM_MSK))
  230                 IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  231                     "DTIM periods to skip = %u\n", cmd->skip_dtim_periods);
  232 }
  233 
  234 static boolean_t
  235 iwm_power_is_radar(struct iwm_softc *sc)
  236 {
  237         struct ieee80211com *ic = &sc->sc_ic;
  238         struct ieee80211_channel *chan;
  239         boolean_t radar_detect = FALSE;
  240 
  241         chan = ic->ic_bsschan;
  242         if (chan == IEEE80211_CHAN_ANYC ||
  243             (chan->ic_flags & IEEE80211_CHAN_DFS) != 0) {
  244                 radar_detect = TRUE;
  245         }
  246 
  247         return radar_detect;
  248 }
  249 
  250 static void
  251 iwm_power_config_skip_dtim(struct iwm_softc *sc,
  252         struct iwm_mac_power_cmd *cmd)
  253 {
  254         struct ieee80211com *ic = &sc->sc_ic;
  255         struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
  256         int dtimper = vap->iv_dtim_period ?: 1;
  257         int skip;
  258 
  259         /* disable, in case we're supposed to override */
  260         cmd->skip_dtim_periods = 0;
  261         cmd->flags &= ~htole16(IWM_POWER_FLAGS_SKIP_OVER_DTIM_MSK);
  262 
  263         if (iwm_power_is_radar(sc))
  264                 return;
  265 
  266         if (dtimper >= 10)
  267                 return;
  268 
  269         /* TODO: check that multicast wake lock is off */
  270 
  271         if (iwm_power_scheme != IWM_POWER_SCHEME_LP)
  272                 return;
  273         skip = 2;
  274 
  275         /* the firmware really expects "look at every X DTIMs", so add 1 */
  276         cmd->skip_dtim_periods = 1 + skip;
  277         cmd->flags |= htole16(IWM_POWER_FLAGS_SKIP_OVER_DTIM_MSK);
  278 }
  279 
  280 static void
  281 iwm_power_build_cmd(struct iwm_softc *sc, struct iwm_vap *ivp,
  282         struct iwm_mac_power_cmd *cmd)
  283 {
  284         struct ieee80211vap *vap = &ivp->iv_vap;
  285         struct ieee80211_node *ni = vap->iv_bss;
  286         int dtimper, dtimper_msec;
  287         int keep_alive;
  288         boolean_t bss_conf_ps = FALSE;
  289 
  290         cmd->id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(ivp->id,
  291             ivp->color));
  292         dtimper = vap->iv_dtim_period ?: 1;
  293 
  294         /*
  295          * Regardless of power management state the driver must set
  296          * keep alive period. FW will use it for sending keep alive NDPs
  297          * immediately after association. Check that keep alive period
  298          * is at least 3 * DTIM
  299          */
  300         dtimper_msec = dtimper * ni->ni_intval;
  301         keep_alive
  302             = imax(3 * dtimper_msec, 1000 * IWM_POWER_KEEP_ALIVE_PERIOD_SEC);
  303         keep_alive = roundup(keep_alive, 1000) / 1000;
  304         cmd->keep_alive_seconds = htole16(keep_alive);
  305 
  306         if (sc->sc_ps_disabled)
  307                 return;
  308 
  309         cmd->flags |= htole16(IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK);
  310 
  311         if (IWM_NODE(ni)->in_assoc &&
  312             (vap->iv_flags & IEEE80211_F_PMGTON) != 0) {
  313                 bss_conf_ps = TRUE;
  314         }
  315         if (!bss_conf_ps)
  316                 return;
  317 
  318         cmd->flags |= htole16(IWM_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
  319 
  320         iwm_power_config_skip_dtim(sc, cmd);
  321 
  322         cmd->rx_data_timeout =
  323                 htole32(IWM_DEFAULT_PS_RX_DATA_TIMEOUT);
  324         cmd->tx_data_timeout =
  325                 htole32(IWM_DEFAULT_PS_TX_DATA_TIMEOUT);
  326 }
  327 
  328 static int
  329 iwm_power_send_cmd(struct iwm_softc *sc, struct iwm_vap *ivp)
  330 {
  331         struct iwm_mac_power_cmd cmd = {};
  332 
  333         iwm_power_build_cmd(sc, ivp, &cmd);
  334         iwm_power_log(sc, &cmd);
  335 
  336         return iwm_send_cmd_pdu(sc, IWM_MAC_PM_POWER_TABLE, 0,
  337             sizeof(cmd), &cmd);
  338 }
  339 
  340 static int
  341 _iwm_enable_beacon_filter(struct iwm_softc *sc, struct iwm_vap *ivp,
  342         struct iwm_beacon_filter_cmd *cmd)
  343 {
  344         int ret;
  345 
  346         iwm_beacon_filter_set_cqm_params(sc, ivp, cmd);
  347         ret = iwm_beacon_filter_send_cmd(sc, cmd);
  348 
  349         if (!ret)
  350                 sc->sc_bf.bf_enabled = 1;
  351 
  352         return ret;
  353 }
  354 
  355 int
  356 iwm_enable_beacon_filter(struct iwm_softc *sc, struct iwm_vap *ivp)
  357 {
  358         struct iwm_beacon_filter_cmd cmd = {
  359                 IWM_BF_CMD_CONFIG_DEFAULTS,
  360                 .bf_enable_beacon_filter = htole32(1),
  361         };
  362 
  363         return _iwm_enable_beacon_filter(sc, ivp, &cmd);
  364 }
  365 
  366 int
  367 iwm_disable_beacon_filter(struct iwm_softc *sc)
  368 {
  369         struct iwm_beacon_filter_cmd cmd = {};
  370         int ret;
  371 
  372         ret = iwm_beacon_filter_send_cmd(sc, &cmd);
  373         if (ret == 0)
  374                 sc->sc_bf.bf_enabled = 0;
  375 
  376         return ret;
  377 }
  378 
  379 static int
  380 iwm_power_set_ps(struct iwm_softc *sc)
  381 {
  382         struct ieee80211vap *vap;
  383         boolean_t disable_ps;
  384         int ret;
  385 
  386         /* disable PS if CAM */
  387         disable_ps = (iwm_power_scheme == IWM_POWER_SCHEME_CAM);
  388         /* ...or if any of the vifs require PS to be off */
  389         TAILQ_FOREACH(vap, &sc->sc_ic.ic_vaps, iv_next) {
  390                 struct iwm_vap *ivp = IWM_VAP(vap);
  391                 if (ivp->phy_ctxt != NULL && ivp->ps_disabled)
  392                         disable_ps = TRUE;
  393         }
  394 
  395         /* update device power state if it has changed */
  396         if (sc->sc_ps_disabled != disable_ps) {
  397                 boolean_t old_ps_disabled = sc->sc_ps_disabled;
  398 
  399                 sc->sc_ps_disabled = disable_ps;
  400                 ret = iwm_power_update_device(sc);
  401                 if (ret) {
  402                         sc->sc_ps_disabled = old_ps_disabled;
  403                         return ret;
  404                 }
  405         }
  406 
  407         return 0;
  408 }
  409 
  410 static int
  411 iwm_power_set_ba(struct iwm_softc *sc, struct iwm_vap *ivp)
  412 {
  413         struct iwm_beacon_filter_cmd cmd = {
  414                 IWM_BF_CMD_CONFIG_DEFAULTS,
  415                 .bf_enable_beacon_filter = htole32(1),
  416         };
  417         struct ieee80211vap *vap = &ivp->iv_vap;
  418         struct ieee80211_node *ni = vap->iv_bss;
  419         boolean_t bss_conf_ps = FALSE;
  420 
  421         if (!sc->sc_bf.bf_enabled)
  422                 return 0;
  423 
  424         if (ni != NULL && IWM_NODE(ni)->in_assoc &&
  425             (vap->iv_flags & IEEE80211_F_PMGTON) != 0) {
  426                 bss_conf_ps = TRUE;
  427         }
  428         sc->sc_bf.ba_enabled = !sc->sc_ps_disabled && bss_conf_ps;
  429 
  430         return _iwm_enable_beacon_filter(sc, ivp, &cmd);
  431 }
  432 
  433 int
  434 iwm_power_update_ps(struct iwm_softc *sc)
  435 {
  436         struct ieee80211vap *vap = TAILQ_FIRST(&sc->sc_ic.ic_vaps);
  437         int ret;
  438 
  439         ret = iwm_power_set_ps(sc);
  440         if (ret)
  441                 return ret;
  442 
  443         if (vap != NULL)
  444                 return iwm_power_set_ba(sc, IWM_VAP(vap));
  445 
  446         return 0;
  447 }
  448 
  449 int
  450 iwm_power_update_mac(struct iwm_softc *sc)
  451 {
  452         struct ieee80211vap *vap = TAILQ_FIRST(&sc->sc_ic.ic_vaps);
  453         int ret;
  454 
  455         ret = iwm_power_set_ps(sc);
  456         if (ret)
  457                 return ret;
  458 
  459         if (vap != NULL) {
  460                 ret = iwm_power_send_cmd(sc, IWM_VAP(vap));
  461                 if (ret)
  462                         return ret;
  463         }
  464 
  465         if (vap != NULL)
  466                 return iwm_power_set_ba(sc, IWM_VAP(vap));
  467 
  468         return 0;
  469 }
  470 
  471 int
  472 iwm_power_update_device(struct iwm_softc *sc)
  473 {
  474         struct iwm_device_power_cmd cmd = {
  475                 .flags = 0,
  476         };
  477 
  478         if (iwm_power_scheme == IWM_POWER_SCHEME_CAM)
  479                 sc->sc_ps_disabled = TRUE;
  480 
  481         if (!sc->sc_ps_disabled)
  482                 cmd.flags |= htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
  483 
  484         IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD,
  485             "Sending device power command with flags = 0x%X\n", cmd.flags);
  486 
  487         return iwm_send_cmd_pdu(sc,
  488             IWM_POWER_TABLE_CMD, 0, sizeof(cmd), &cmd);
  489 }

Cache object: 75c17e3c5c84ea69bb86ee0d5afcbaf3


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.