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_scan.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 
   90 /*-
   91  * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
   92  *
   93  * Permission to use, copy, modify, and distribute this software for any
   94  * purpose with or without fee is hereby granted, provided that the above
   95  * copyright notice and this permission notice appear in all copies.
   96  *
   97  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   98  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   99  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  100  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  101  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  102  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  103  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  104  */
  105 #include <sys/cdefs.h>
  106 __FBSDID("$FreeBSD$");
  107 
  108 #include "opt_wlan.h"
  109 #include "opt_iwm.h"
  110 
  111 #include <sys/param.h>
  112 #include <sys/bus.h>
  113 #include <sys/conf.h>
  114 #include <sys/endian.h>
  115 #include <sys/firmware.h>
  116 #include <sys/kernel.h>
  117 #include <sys/malloc.h>
  118 #include <sys/mbuf.h>
  119 #include <sys/mutex.h>
  120 #include <sys/module.h>
  121 #include <sys/proc.h>
  122 #include <sys/rman.h>
  123 #include <sys/socket.h>
  124 #include <sys/sockio.h>
  125 #include <sys/sysctl.h>
  126 #include <sys/linker.h>
  127 
  128 #include <machine/bus.h>
  129 #include <machine/endian.h>
  130 #include <machine/resource.h>
  131 
  132 #include <dev/pci/pcivar.h>
  133 #include <dev/pci/pcireg.h>
  134 
  135 #include <net/bpf.h>
  136 
  137 #include <net/if.h>
  138 #include <net/if_var.h>
  139 #include <net/if_arp.h>
  140 #include <net/if_dl.h>
  141 #include <net/if_media.h>
  142 #include <net/if_types.h>
  143 
  144 #include <netinet/in.h>
  145 #include <netinet/in_systm.h>
  146 #include <netinet/if_ether.h>
  147 #include <netinet/ip.h>
  148 
  149 #include <net80211/ieee80211_var.h>
  150 #include <net80211/ieee80211_regdomain.h>
  151 #include <net80211/ieee80211_ratectl.h>
  152 #include <net80211/ieee80211_radiotap.h>
  153 
  154 #include <dev/iwm/if_iwmreg.h>
  155 #include <dev/iwm/if_iwmvar.h>
  156 #include <dev/iwm/if_iwm_debug.h>
  157 #include <dev/iwm/if_iwm_notif_wait.h>
  158 #include <dev/iwm/if_iwm_util.h>
  159 #include <dev/iwm/if_iwm_scan.h>
  160 
  161 /*
  162  * BEGIN mvm/scan.c
  163  */
  164 
  165 #define IWM_DENSE_EBS_SCAN_RATIO 5
  166 #define IWM_SPARSE_EBS_SCAN_RATIO 1
  167 
  168 static uint16_t
  169 iwm_scan_rx_chain(struct iwm_softc *sc)
  170 {
  171         uint16_t rx_chain;
  172         uint8_t rx_ant;
  173 
  174         rx_ant = iwm_get_valid_rx_ant(sc);
  175         rx_chain = rx_ant << IWM_PHY_RX_CHAIN_VALID_POS;
  176         rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
  177         rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_SEL_POS;
  178         rx_chain |= 0x1 << IWM_PHY_RX_CHAIN_DRIVER_FORCE_POS;
  179         return htole16(rx_chain);
  180 }
  181 
  182 static uint32_t
  183 iwm_scan_rxon_flags(struct ieee80211_channel *c)
  184 {
  185         if (IEEE80211_IS_CHAN_2GHZ(c))
  186                 return htole32(IWM_PHY_BAND_24);
  187         else
  188                 return htole32(IWM_PHY_BAND_5);
  189 }
  190 
  191 static uint32_t
  192 iwm_scan_rate_n_flags(struct iwm_softc *sc, int flags, int no_cck)
  193 {
  194         uint32_t tx_ant;
  195         int i, ind;
  196 
  197         for (i = 0, ind = sc->sc_scan_last_antenna;
  198             i < IWM_RATE_MCS_ANT_NUM; i++) {
  199                 ind = (ind + 1) % IWM_RATE_MCS_ANT_NUM;
  200                 if (iwm_get_valid_tx_ant(sc) & (1 << ind)) {
  201                         sc->sc_scan_last_antenna = ind;
  202                         break;
  203                 }
  204         }
  205         tx_ant = (1 << sc->sc_scan_last_antenna) << IWM_RATE_MCS_ANT_POS;
  206 
  207         if ((flags & IEEE80211_CHAN_2GHZ) && !no_cck)
  208                 return htole32(IWM_RATE_1M_PLCP | IWM_RATE_MCS_CCK_MSK |
  209                                    tx_ant);
  210         else
  211                 return htole32(IWM_RATE_6M_PLCP | tx_ant);
  212 }
  213 
  214 static inline boolean_t
  215 iwm_rrm_scan_needed(struct iwm_softc *sc)
  216 {
  217         /* require rrm scan whenever the fw supports it */
  218         return iwm_fw_has_capa(sc, IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT);
  219 }
  220 
  221 #ifdef IWM_DEBUG
  222 static const char *
  223 iwm_ebs_status_str(enum iwm_scan_ebs_status status)
  224 {
  225         switch (status) {
  226         case IWM_SCAN_EBS_SUCCESS:
  227                 return "successful";
  228         case IWM_SCAN_EBS_INACTIVE:
  229                 return "inactive";
  230         case IWM_SCAN_EBS_FAILED:
  231         case IWM_SCAN_EBS_CHAN_NOT_FOUND:
  232         default:
  233                 return "failed";
  234         }
  235 }
  236 
  237 static const char *
  238 iwm_offload_status_str(enum iwm_scan_offload_complete_status status)
  239 {
  240         return (status == IWM_SCAN_OFFLOAD_ABORTED) ? "aborted" : "completed";
  241 }
  242 #endif
  243 
  244 void
  245 iwm_rx_lmac_scan_complete_notif(struct iwm_softc *sc,
  246     struct iwm_rx_packet *pkt)
  247 {
  248         struct iwm_periodic_scan_complete *scan_notif = (void *)pkt->data;
  249 
  250         /* If this happens, the firmware has mistakenly sent an LMAC
  251          * notification during UMAC scans -- warn and ignore it.
  252          */
  253         if (iwm_fw_has_capa(sc, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) {
  254                 device_printf(sc->sc_dev,
  255                     "%s: Mistakenly got LMAC notification during UMAC scan\n",
  256                     __func__);
  257                 return;
  258         }
  259 
  260         IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Regular scan %s, EBS status %s (FW)\n",
  261             iwm_offload_status_str(scan_notif->status),
  262             iwm_ebs_status_str(scan_notif->ebs_status));
  263 
  264         sc->last_ebs_successful =
  265                         scan_notif->ebs_status == IWM_SCAN_EBS_SUCCESS ||
  266                         scan_notif->ebs_status == IWM_SCAN_EBS_INACTIVE;
  267 
  268 }
  269 
  270 void
  271 iwm_rx_umac_scan_complete_notif(struct iwm_softc *sc,
  272     struct iwm_rx_packet *pkt)
  273 {
  274         struct iwm_umac_scan_complete *notif = (void *)pkt->data;
  275 
  276         IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
  277             "Scan completed, uid %u, status %s, EBS status %s\n",
  278             le32toh(notif->uid),
  279             iwm_offload_status_str(notif->status),
  280             iwm_ebs_status_str(notif->ebs_status));
  281 
  282         if (notif->ebs_status != IWM_SCAN_EBS_SUCCESS &&
  283             notif->ebs_status != IWM_SCAN_EBS_INACTIVE)
  284                 sc->last_ebs_successful = FALSE;
  285 }
  286 
  287 static int
  288 iwm_scan_skip_channel(struct ieee80211_channel *c)
  289 {
  290         if (IEEE80211_IS_CHAN_2GHZ(c) && IEEE80211_IS_CHAN_B(c))
  291                 return 0;
  292         else if (IEEE80211_IS_CHAN_5GHZ(c) && IEEE80211_IS_CHAN_A(c))
  293                 return 0;
  294         else
  295                 return 1;
  296 }
  297 
  298 static uint8_t
  299 iwm_lmac_scan_fill_channels(struct iwm_softc *sc,
  300     struct iwm_scan_channel_cfg_lmac *chan, int n_ssids)
  301 {
  302         struct ieee80211com *ic = &sc->sc_ic;
  303         struct ieee80211_scan_state *ss = ic->ic_scan;
  304         struct ieee80211_channel *c;
  305         uint8_t nchan;
  306         int j;
  307 
  308         for (nchan = j = 0;
  309             j < ss->ss_last && nchan < sc->sc_fw.ucode_capa.n_scan_channels;
  310             j++) {
  311                 c = ss->ss_chans[j];
  312                 /*
  313                  * Catch other channels, in case we have 900MHz channels or
  314                  * something in the chanlist.
  315                  */
  316                 if (!IEEE80211_IS_CHAN_2GHZ(c) && !IEEE80211_IS_CHAN_5GHZ(c)) {
  317                         IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM,
  318                             "%s: skipping channel (freq=%d, ieee=%d, flags=0x%08x)\n",
  319                             __func__, c->ic_freq, c->ic_ieee, c->ic_flags);
  320                         continue;
  321                 }
  322 
  323                 IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM,
  324                     "Adding channel %d (%d Mhz) to the list\n",
  325                     nchan, c->ic_freq);
  326                 chan->channel_num = htole16(ieee80211_mhz2ieee(c->ic_freq, 0));
  327                 chan->iter_count = htole16(1);
  328                 chan->iter_interval = htole32(0);
  329                 chan->flags = htole32(IWM_UNIFIED_SCAN_CHANNEL_PARTIAL);
  330                 chan->flags |= htole32(IWM_SCAN_CHANNEL_NSSIDS(n_ssids));
  331                 /* XXX IEEE80211_SCAN_NOBCAST flag is never set. */
  332                 if (!IEEE80211_IS_CHAN_PASSIVE(c) &&
  333                     (!(ss->ss_flags & IEEE80211_SCAN_NOBCAST) || n_ssids != 0))
  334                         chan->flags |= htole32(IWM_SCAN_CHANNEL_TYPE_ACTIVE);
  335                 chan++;
  336                 nchan++;
  337         }
  338 
  339         return nchan;
  340 }
  341 
  342 static uint8_t
  343 iwm_umac_scan_fill_channels(struct iwm_softc *sc,
  344     struct iwm_scan_channel_cfg_umac *chan, int n_ssids)
  345 {
  346         struct ieee80211com *ic = &sc->sc_ic;
  347         struct ieee80211_scan_state *ss = ic->ic_scan;
  348         struct ieee80211_channel *c;
  349         uint8_t nchan;
  350         int j;
  351 
  352         for (nchan = j = 0;
  353             j < ss->ss_last && nchan < sc->sc_fw.ucode_capa.n_scan_channels;
  354             j++) {
  355                 c = ss->ss_chans[j];
  356                 /*
  357                  * Catch other channels, in case we have 900MHz channels or
  358                  * something in the chanlist.
  359                  */
  360                 if (!IEEE80211_IS_CHAN_2GHZ(c) && !IEEE80211_IS_CHAN_5GHZ(c)) {
  361                         IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM,
  362                             "%s: skipping channel (freq=%d, ieee=%d, flags=0x%08x)\n",
  363                             __func__, c->ic_freq, c->ic_ieee, c->ic_flags);
  364                         continue;
  365                 }
  366 
  367                 IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM,
  368                     "Adding channel %d (%d Mhz) to the list\n",
  369                     nchan, c->ic_freq);
  370                 chan->channel_num = ieee80211_mhz2ieee(c->ic_freq, 0);
  371                 chan->iter_count = 1;
  372                 chan->iter_interval = htole16(0);
  373                 chan->flags = htole32(IWM_SCAN_CHANNEL_UMAC_NSSIDS(n_ssids));
  374                 chan++;
  375                 nchan++;
  376         }
  377 
  378         return nchan;
  379 }
  380 
  381 static int
  382 iwm_fill_probe_req(struct iwm_softc *sc, struct iwm_scan_probe_req *preq)
  383 {
  384         struct ieee80211com *ic = &sc->sc_ic;
  385         struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
  386         struct ieee80211_frame *wh = (struct ieee80211_frame *)preq->buf;
  387         struct ieee80211_rateset *rs;
  388         size_t remain = sizeof(preq->buf);
  389         uint8_t *frm, *pos;
  390 
  391         memset(preq, 0, sizeof(*preq));
  392 
  393         /* Ensure enough space for header and SSID IE. */
  394         if (remain < sizeof(*wh) + 2)
  395                 return ENOBUFS;
  396 
  397         /*
  398          * Build a probe request frame.  Most of the following code is a
  399          * copy & paste of what is done in net80211.
  400          */
  401         wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
  402             IEEE80211_FC0_SUBTYPE_PROBE_REQ;
  403         wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
  404         IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr);
  405         IEEE80211_ADDR_COPY(wh->i_addr2, vap ? vap->iv_myaddr : ic->ic_macaddr);
  406         IEEE80211_ADDR_COPY(wh->i_addr3, ieee80211broadcastaddr);
  407         *(uint16_t *)&wh->i_dur[0] = 0; /* filled by HW */
  408         *(uint16_t *)&wh->i_seq[0] = 0; /* filled by HW */
  409 
  410         frm = (uint8_t *)(wh + 1);
  411         frm = ieee80211_add_ssid(frm, NULL, 0);
  412 
  413         /* Tell the firmware where the MAC header is. */
  414         preq->mac_header.offset = 0;
  415         preq->mac_header.len = htole16(frm - (uint8_t *)wh);
  416         remain -= frm - (uint8_t *)wh;
  417 
  418         /* Fill in 2GHz IEs and tell firmware where they are. */
  419         rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
  420         if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
  421                 if (remain < 4 + rs->rs_nrates)
  422                         return ENOBUFS;
  423         } else if (remain < 2 + rs->rs_nrates) {
  424                 return ENOBUFS;
  425         }
  426         preq->band_data[0].offset = htole16(frm - (uint8_t *)wh);
  427         pos = frm;
  428         frm = ieee80211_add_rates(frm, rs);
  429         if (rs->rs_nrates > IEEE80211_RATE_SIZE)
  430                 frm = ieee80211_add_xrates(frm, rs);
  431         preq->band_data[0].len = htole16(frm - pos);
  432         remain -= frm - pos;
  433 
  434         if (iwm_rrm_scan_needed(sc)) {
  435                 if (remain < 3)
  436                         return ENOBUFS;
  437                 *frm++ = IEEE80211_ELEMID_DSPARMS;
  438                 *frm++ = 1;
  439                 *frm++ = 0;
  440                 remain -= 3;
  441         }
  442 
  443         if (sc->nvm_data->sku_cap_band_52GHz_enable) {
  444                 /* Fill in 5GHz IEs. */
  445                 rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
  446                 if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
  447                         if (remain < 4 + rs->rs_nrates)
  448                                 return ENOBUFS;
  449                 } else if (remain < 2 + rs->rs_nrates) {
  450                         return ENOBUFS;
  451                 }
  452                 preq->band_data[1].offset = htole16(frm - (uint8_t *)wh);
  453                 pos = frm;
  454                 frm = ieee80211_add_rates(frm, rs);
  455                 if (rs->rs_nrates > IEEE80211_RATE_SIZE)
  456                         frm = ieee80211_add_xrates(frm, rs);
  457                 preq->band_data[1].len = htole16(frm - pos);
  458                 remain -= frm - pos;
  459         }
  460 
  461         /* Send 11n IEs on both 2GHz and 5GHz bands. */
  462         preq->common_data.offset = htole16(frm - (uint8_t *)wh);
  463         pos = frm;
  464 #if 0
  465         if (ic->ic_flags & IEEE80211_F_HTON) {
  466                 if (remain < 28)
  467                         return ENOBUFS;
  468                 frm = ieee80211_add_htcaps(frm, ic);
  469                 /* XXX add WME info? */
  470         }
  471 #endif
  472         preq->common_data.len = htole16(frm - pos);
  473 
  474         return 0;
  475 }
  476 
  477 int
  478 iwm_config_umac_scan(struct iwm_softc *sc)
  479 {
  480         struct ieee80211com *ic = &sc->sc_ic;
  481         struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
  482 
  483         struct iwm_scan_config *scan_config;
  484         int ret, j, nchan;
  485         size_t cmd_size;
  486         struct ieee80211_channel *c;
  487         struct iwm_host_cmd hcmd = {
  488                 .id = iwm_cmd_id(IWM_SCAN_CFG_CMD, IWM_ALWAYS_LONG_GROUP, 0),
  489                 .flags = IWM_CMD_SYNC,
  490         };
  491         static const uint32_t rates = (IWM_SCAN_CONFIG_RATE_1M |
  492             IWM_SCAN_CONFIG_RATE_2M | IWM_SCAN_CONFIG_RATE_5M |
  493             IWM_SCAN_CONFIG_RATE_11M | IWM_SCAN_CONFIG_RATE_6M |
  494             IWM_SCAN_CONFIG_RATE_9M | IWM_SCAN_CONFIG_RATE_12M |
  495             IWM_SCAN_CONFIG_RATE_18M | IWM_SCAN_CONFIG_RATE_24M |
  496             IWM_SCAN_CONFIG_RATE_36M | IWM_SCAN_CONFIG_RATE_48M |
  497             IWM_SCAN_CONFIG_RATE_54M);
  498 
  499         cmd_size = sizeof(*scan_config) + sc->sc_fw.ucode_capa.n_scan_channels;
  500 
  501         scan_config = malloc(cmd_size, M_DEVBUF, M_NOWAIT | M_ZERO);
  502         if (scan_config == NULL)
  503                 return ENOMEM;
  504 
  505         scan_config->tx_chains = htole32(iwm_get_valid_tx_ant(sc));
  506         scan_config->rx_chains = htole32(iwm_get_valid_rx_ant(sc));
  507         scan_config->legacy_rates = htole32(rates |
  508             IWM_SCAN_CONFIG_SUPPORTED_RATE(rates));
  509 
  510         /* These timings correspond to iwlwifi's UNASSOC scan. */
  511         scan_config->dwell_active = 10;
  512         scan_config->dwell_passive = 110;
  513         scan_config->dwell_fragmented = 44;
  514         scan_config->dwell_extended = 90;
  515         scan_config->out_of_channel_time = htole32(0);
  516         scan_config->suspend_time = htole32(0);
  517 
  518         IEEE80211_ADDR_COPY(scan_config->mac_addr,
  519             vap ? vap->iv_myaddr : ic->ic_macaddr);
  520 
  521         scan_config->bcast_sta_id = sc->sc_aux_sta.sta_id;
  522         scan_config->channel_flags = IWM_CHANNEL_FLAG_EBS |
  523             IWM_CHANNEL_FLAG_ACCURATE_EBS | IWM_CHANNEL_FLAG_EBS_ADD |
  524             IWM_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE;
  525 
  526         for (nchan = j = 0;
  527             j < ic->ic_nchans && nchan < sc->sc_fw.ucode_capa.n_scan_channels;
  528             j++) {
  529                 c = &ic->ic_channels[j];
  530                 /* For 2GHz, only populate 11b channels */
  531                 /* For 5GHz, only populate 11a channels */
  532                 /*
  533                  * Catch other channels, in case we have 900MHz channels or
  534                  * something in the chanlist.
  535                  */
  536                 if (iwm_scan_skip_channel(c))
  537                         continue;
  538                 scan_config->channel_array[nchan++] =
  539                     ieee80211_mhz2ieee(c->ic_freq, 0);
  540         }
  541 
  542         scan_config->flags = htole32(IWM_SCAN_CONFIG_FLAG_ACTIVATE |
  543             IWM_SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS |
  544             IWM_SCAN_CONFIG_FLAG_SET_TX_CHAINS |
  545             IWM_SCAN_CONFIG_FLAG_SET_RX_CHAINS |
  546             IWM_SCAN_CONFIG_FLAG_SET_AUX_STA_ID |
  547             IWM_SCAN_CONFIG_FLAG_SET_ALL_TIMES |
  548             IWM_SCAN_CONFIG_FLAG_SET_LEGACY_RATES |
  549             IWM_SCAN_CONFIG_FLAG_SET_MAC_ADDR |
  550             IWM_SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS|
  551             IWM_SCAN_CONFIG_N_CHANNELS(nchan) |
  552             IWM_SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED);
  553 
  554         hcmd.data[0] = scan_config;
  555         hcmd.len[0] = cmd_size;
  556 
  557         IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Sending UMAC scan config\n");
  558 
  559         ret = iwm_send_cmd(sc, &hcmd);
  560         if (!ret)
  561                 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
  562                     "UMAC scan config was sent successfully\n");
  563 
  564         free(scan_config, M_DEVBUF);
  565         return ret;
  566 }
  567 
  568 static boolean_t
  569 iwm_scan_use_ebs(struct iwm_softc *sc)
  570 {
  571         const struct iwm_ucode_capabilities *capa = &sc->sc_fw.ucode_capa;
  572 
  573         /* We can only use EBS if:
  574          *      1. the feature is supported;
  575          *      2. the last EBS was successful;
  576          *      3. if only single scan, the single scan EBS API is supported;
  577          *      4. it's not a p2p find operation.
  578          */
  579         return ((capa->flags & IWM_UCODE_TLV_FLAGS_EBS_SUPPORT) &&
  580                 sc->last_ebs_successful);
  581 }
  582 
  583 static int
  584 iwm_scan_size(struct iwm_softc *sc)
  585 {
  586         int base_size;
  587 
  588         if (iwm_fw_has_capa(sc, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) {
  589                 if (iwm_fw_has_api(sc, IWM_UCODE_TLV_API_ADAPTIVE_DWELL))
  590                         base_size = IWM_SCAN_REQ_UMAC_SIZE_V7;
  591                 else
  592                         base_size = IWM_SCAN_REQ_UMAC_SIZE_V1;
  593 
  594                 return base_size +
  595                     sizeof(struct iwm_scan_channel_cfg_umac) *
  596                     sc->sc_fw.ucode_capa.n_scan_channels +
  597                     sizeof(struct iwm_scan_req_umac_tail);
  598         } else {
  599                 return sizeof(struct iwm_scan_req_lmac) +
  600                     sizeof(struct iwm_scan_channel_cfg_lmac) *
  601                     sc->sc_fw.ucode_capa.n_scan_channels +
  602                     sizeof(struct iwm_scan_probe_req);
  603         }
  604 }
  605 
  606 int
  607 iwm_umac_scan(struct iwm_softc *sc)
  608 {
  609         struct iwm_host_cmd hcmd = {
  610                 .id = iwm_cmd_id(IWM_SCAN_REQ_UMAC, IWM_ALWAYS_LONG_GROUP, 0),
  611                 .len = { 0, },
  612                 .data = { NULL, },
  613                 .flags = IWM_CMD_SYNC,
  614         };
  615         struct ieee80211_scan_state *ss = sc->sc_ic.ic_scan;
  616         struct iwm_scan_req_umac *req;
  617         struct iwm_scan_req_umac_tail *tail;
  618         size_t req_len;
  619         uint16_t general_flags;
  620         uint8_t channel_flags, i, nssid;
  621         int ret;
  622 
  623         req_len = iwm_scan_size(sc);
  624         if (req_len > IWM_MAX_CMD_PAYLOAD_SIZE)
  625                 return ENOMEM;
  626         req = malloc(req_len, M_DEVBUF, M_NOWAIT | M_ZERO);
  627         if (req == NULL)
  628                 return ENOMEM;
  629 
  630         hcmd.len[0] = (uint16_t)req_len;
  631         hcmd.data[0] = (void *)req;
  632 
  633         IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Handling ieee80211 scan request\n");
  634 
  635         nssid = MIN(ss->ss_nssid, IWM_PROBE_OPTION_MAX);
  636 
  637         general_flags = IWM_UMAC_SCAN_GEN_FLAGS_PASS_ALL |
  638             IWM_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE;
  639         if (!iwm_fw_has_api(sc, IWM_UCODE_TLV_API_ADAPTIVE_DWELL))
  640                 general_flags |= IWM_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL;
  641         if (iwm_rrm_scan_needed(sc))
  642                 general_flags |= IWM_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED;
  643         if (nssid != 0)
  644                 general_flags |= IWM_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT;
  645         else
  646                 general_flags |= IWM_UMAC_SCAN_GEN_FLAGS_PASSIVE;
  647 
  648         channel_flags = 0;
  649         if (iwm_scan_use_ebs(sc))
  650                 channel_flags = IWM_SCAN_CHANNEL_FLAG_EBS |
  651                     IWM_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
  652                     IWM_SCAN_CHANNEL_FLAG_CACHE_ADD;
  653 
  654         req->general_flags = htole16(general_flags);
  655         req->ooc_priority = htole32(IWM_SCAN_PRIORITY_HIGH);
  656 
  657         /* These timings correspond to iwlwifi's UNASSOC scan. */
  658         if (iwm_fw_has_api(sc, IWM_UCODE_TLV_API_ADAPTIVE_DWELL)) {
  659                 req->v7.active_dwell = 10;
  660                 req->v7.passive_dwell = 110;
  661                 req->v7.fragmented_dwell = 44;
  662                 req->v7.adwell_default_n_aps_social = 10;
  663                 req->v7.adwell_default_n_aps = 2;
  664                 req->v7.adwell_max_budget = htole16(300);
  665                 req->v7.scan_priority = htole32(IWM_SCAN_PRIORITY_HIGH);
  666                 req->v7.channel.flags = channel_flags;
  667                 req->v7.channel.count = iwm_umac_scan_fill_channels(sc,
  668                     (struct iwm_scan_channel_cfg_umac *)req->v7.data, nssid);
  669 
  670                 tail = (void *)((char *)&req->v7.data +
  671                     sizeof(struct iwm_scan_channel_cfg_umac) *
  672                     sc->sc_fw.ucode_capa.n_scan_channels);
  673         } else {
  674                 req->v1.active_dwell = 10;
  675                 req->v1.passive_dwell = 110;
  676                 req->v1.fragmented_dwell = 44;
  677                 req->v1.extended_dwell = 90;
  678                 req->v1.scan_priority = htole32(IWM_SCAN_PRIORITY_HIGH);
  679                 req->v1.channel.flags = channel_flags;
  680                 req->v1.channel.count = iwm_umac_scan_fill_channels(sc,
  681                     (struct iwm_scan_channel_cfg_umac *)req->v1.data, nssid);
  682 
  683                 tail = (void *)((char *)&req->v1.data +
  684                     sizeof(struct iwm_scan_channel_cfg_umac) *
  685                     sc->sc_fw.ucode_capa.n_scan_channels);
  686         }
  687 
  688         /* Check if we're doing an active directed scan. */
  689         for (i = 0; i < nssid; i++) {
  690                 tail->direct_scan[i].id = IEEE80211_ELEMID_SSID;
  691                 tail->direct_scan[i].len = MIN(ss->ss_ssid[i].len,
  692                     IEEE80211_NWID_LEN);
  693                 memcpy(tail->direct_scan[i].ssid, ss->ss_ssid[i].ssid,
  694                     tail->direct_scan[i].len);
  695                 /* XXX debug */
  696         }
  697 
  698         ret = iwm_fill_probe_req(sc, &tail->preq);
  699         if (ret) {
  700                 free(req, M_DEVBUF);
  701                 return ret;
  702         }
  703 
  704         /* Specify the scan plan: We'll do one iteration. */
  705         tail->schedule[0].interval = 0;
  706         tail->schedule[0].iter_count = 1;
  707 
  708         ret = iwm_send_cmd(sc, &hcmd);
  709         if (!ret)
  710                 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
  711                     "Scan request was sent successfully\n");
  712         free(req, M_DEVBUF);
  713         return ret;
  714 }
  715 
  716 int
  717 iwm_lmac_scan(struct iwm_softc *sc)
  718 {
  719         struct iwm_host_cmd hcmd = {
  720                 .id = IWM_SCAN_OFFLOAD_REQUEST_CMD,
  721                 .len = { 0, },
  722                 .data = { NULL, },
  723                 .flags = IWM_CMD_SYNC,
  724         };
  725         struct ieee80211_scan_state *ss = sc->sc_ic.ic_scan;
  726         struct iwm_scan_req_lmac *req;
  727         size_t req_len;
  728         uint8_t i, nssid;
  729         int ret;
  730 
  731         IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
  732             "Handling ieee80211 scan request\n");
  733 
  734         req_len = iwm_scan_size(sc);
  735         if (req_len > IWM_MAX_CMD_PAYLOAD_SIZE)
  736                 return ENOMEM;
  737         req = malloc(req_len, M_DEVBUF, M_NOWAIT | M_ZERO);
  738         if (req == NULL)
  739                 return ENOMEM;
  740 
  741         hcmd.len[0] = (uint16_t)req_len;
  742         hcmd.data[0] = (void *)req;
  743 
  744         /* These timings correspond to iwlwifi's UNASSOC scan. */
  745         req->active_dwell = 10;
  746         req->passive_dwell = 110;
  747         req->fragmented_dwell = 44;
  748         req->extended_dwell = 90;
  749         req->max_out_time = 0;
  750         req->suspend_time = 0;
  751 
  752         req->scan_prio = htole32(IWM_SCAN_PRIORITY_HIGH);
  753         req->rx_chain_select = iwm_scan_rx_chain(sc);
  754         req->iter_num = htole32(1);
  755         req->delay = 0;
  756 
  757         req->scan_flags = htole32(IWM_LMAC_SCAN_FLAG_PASS_ALL |
  758             IWM_LMAC_SCAN_FLAG_ITER_COMPLETE |
  759             IWM_LMAC_SCAN_FLAG_EXTENDED_DWELL);
  760         if (iwm_rrm_scan_needed(sc))
  761                 req->scan_flags |= htole32(IWM_LMAC_SCAN_FLAGS_RRM_ENABLED);
  762 
  763         req->flags = iwm_scan_rxon_flags(sc->sc_ic.ic_scan->ss_chans[0]);
  764 
  765         req->filter_flags =
  766             htole32(IWM_MAC_FILTER_ACCEPT_GRP | IWM_MAC_FILTER_IN_BEACON);
  767 
  768         /* Tx flags 2 GHz. */
  769         req->tx_cmd[0].tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL |
  770             IWM_TX_CMD_FLG_BT_DIS);
  771         req->tx_cmd[0].rate_n_flags =
  772             iwm_scan_rate_n_flags(sc, IEEE80211_CHAN_2GHZ, 1/*XXX*/);
  773         req->tx_cmd[0].sta_id = sc->sc_aux_sta.sta_id;
  774 
  775         /* Tx flags 5 GHz. */
  776         req->tx_cmd[1].tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL |
  777             IWM_TX_CMD_FLG_BT_DIS);
  778         req->tx_cmd[1].rate_n_flags =
  779             iwm_scan_rate_n_flags(sc, IEEE80211_CHAN_5GHZ, 1/*XXX*/);
  780         req->tx_cmd[1].sta_id = sc->sc_aux_sta.sta_id;
  781 
  782         /* Check if we're doing an active directed scan. */
  783         nssid = MIN(ss->ss_nssid, IWM_PROBE_OPTION_MAX);
  784         for (i = 0; i < nssid; i++) {
  785                 req->direct_scan[i].id = IEEE80211_ELEMID_SSID;
  786                 req->direct_scan[i].len = MIN(ss->ss_ssid[i].len,
  787                     IEEE80211_NWID_LEN);
  788                 memcpy(req->direct_scan[i].ssid, ss->ss_ssid[i].ssid,
  789                     req->direct_scan[i].len);
  790                 /* XXX debug */
  791         }
  792         if (nssid != 0) {
  793                 req->scan_flags |=
  794                     htole32(IWM_LMAC_SCAN_FLAG_PRE_CONNECTION);
  795         } else
  796                 req->scan_flags |= htole32(IWM_LMAC_SCAN_FLAG_PASSIVE);
  797 
  798         req->n_channels = iwm_lmac_scan_fill_channels(sc,
  799             (struct iwm_scan_channel_cfg_lmac *)req->data, nssid);
  800 
  801         ret = iwm_fill_probe_req(sc,
  802                             (struct iwm_scan_probe_req *)(req->data +
  803                             (sizeof(struct iwm_scan_channel_cfg_lmac) *
  804                             sc->sc_fw.ucode_capa.n_scan_channels)));
  805         if (ret) {
  806                 free(req, M_DEVBUF);
  807                 return ret;
  808         }
  809 
  810         /* Specify the scan plan: We'll do one iteration. */
  811         req->schedule[0].iterations = 1;
  812         req->schedule[0].full_scan_mul = 1;
  813 
  814         if (iwm_scan_use_ebs(sc)) {
  815                 req->channel_opt[0].flags =
  816                         htole16(IWM_SCAN_CHANNEL_FLAG_EBS |
  817                                 IWM_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
  818                                 IWM_SCAN_CHANNEL_FLAG_CACHE_ADD);
  819                 req->channel_opt[0].non_ebs_ratio =
  820                         htole16(IWM_DENSE_EBS_SCAN_RATIO);
  821                 req->channel_opt[1].flags =
  822                         htole16(IWM_SCAN_CHANNEL_FLAG_EBS |
  823                                 IWM_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
  824                                 IWM_SCAN_CHANNEL_FLAG_CACHE_ADD);
  825                 req->channel_opt[1].non_ebs_ratio =
  826                         htole16(IWM_SPARSE_EBS_SCAN_RATIO);
  827         }
  828 
  829         ret = iwm_send_cmd(sc, &hcmd);
  830         if (!ret) {
  831                 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
  832                     "Scan request was sent successfully\n");
  833         }
  834         free(req, M_DEVBUF);
  835         return ret;
  836 }
  837 
  838 static int
  839 iwm_lmac_scan_abort(struct iwm_softc *sc)
  840 {
  841         int ret;
  842         struct iwm_host_cmd hcmd = {
  843                 .id = IWM_SCAN_OFFLOAD_ABORT_CMD,
  844                 .len = { 0, },
  845                 .data = { NULL, },
  846                 .flags = IWM_CMD_SYNC,
  847         };
  848         uint32_t status;
  849 
  850         ret = iwm_send_cmd_status(sc, &hcmd, &status);
  851         if (ret)
  852                 return ret;
  853 
  854         if (status != IWM_CAN_ABORT_STATUS) {
  855                 /*
  856                  * The scan abort will return 1 for success or
  857                  * 2 for "failure".  A failure condition can be
  858                  * due to simply not being in an active scan which
  859                  * can occur if we send the scan abort before the
  860                  * microcode has notified us that a scan is completed.
  861                  */
  862                 IWM_DPRINTF(sc, IWM_DEBUG_SCAN,
  863                     "SCAN OFFLOAD ABORT ret %d.\n", status);
  864                 ret = ENOENT;
  865         }
  866 
  867         return ret;
  868 }
  869 
  870 static int
  871 iwm_umac_scan_abort(struct iwm_softc *sc)
  872 {
  873         struct iwm_umac_scan_abort cmd = {};
  874         int uid, ret;
  875 
  876         uid = 0;
  877         cmd.uid = htole32(uid);
  878 
  879         IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Sending scan abort, uid %u\n", uid);
  880 
  881         ret = iwm_send_cmd_pdu(sc,
  882                                    iwm_cmd_id(IWM_SCAN_ABORT_UMAC,
  883                                               IWM_ALWAYS_LONG_GROUP, 0),
  884                                    0, sizeof(cmd), &cmd);
  885 
  886         return ret;
  887 }
  888 
  889 int
  890 iwm_scan_stop_wait(struct iwm_softc *sc)
  891 {
  892         struct iwm_notification_wait wait_scan_done;
  893         static const uint16_t scan_done_notif[] = { IWM_SCAN_COMPLETE_UMAC,
  894                                                    IWM_SCAN_OFFLOAD_COMPLETE, };
  895         int ret;
  896 
  897         iwm_init_notification_wait(sc->sc_notif_wait, &wait_scan_done,
  898                                    scan_done_notif, nitems(scan_done_notif),
  899                                    NULL, NULL);
  900 
  901         IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Preparing to stop scan\n");
  902 
  903         if (iwm_fw_has_capa(sc, IWM_UCODE_TLV_CAPA_UMAC_SCAN))
  904                 ret = iwm_umac_scan_abort(sc);
  905         else
  906                 ret = iwm_lmac_scan_abort(sc);
  907 
  908         if (ret) {
  909                 IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "couldn't stop scan\n");
  910                 iwm_remove_notification(sc->sc_notif_wait, &wait_scan_done);
  911                 return ret;
  912         }
  913 
  914         IWM_UNLOCK(sc);
  915         ret = iwm_wait_notification(sc->sc_notif_wait, &wait_scan_done, hz);
  916         IWM_LOCK(sc);
  917 
  918         return ret;
  919 }

Cache object: a2e9dedfcfaffdc5f9f2be3b19420d47


[ 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.