1d4886179SRui Paulo /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ 2d4886179SRui Paulo 3d4886179SRui Paulo /* 4d4886179SRui Paulo * Copyright (c) 2014 genua mbh <info@genua.de> 5d4886179SRui Paulo * Copyright (c) 2014 Fixup Software Ltd. 6d4886179SRui Paulo * 7d4886179SRui Paulo * Permission to use, copy, modify, and distribute this software for any 8d4886179SRui Paulo * purpose with or without fee is hereby granted, provided that the above 9d4886179SRui Paulo * copyright notice and this permission notice appear in all copies. 10d4886179SRui Paulo * 11d4886179SRui Paulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12d4886179SRui Paulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13d4886179SRui Paulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14d4886179SRui Paulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15d4886179SRui Paulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16d4886179SRui Paulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17d4886179SRui Paulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18d4886179SRui Paulo */ 19d4886179SRui Paulo 20d4886179SRui Paulo /*- 21d4886179SRui Paulo * Based on BSD-licensed source modules in the Linux iwlwifi driver, 22d4886179SRui Paulo * which were used as the reference documentation for this implementation. 23d4886179SRui Paulo * 24d4886179SRui Paulo * Driver version we are currently based off of is 25d4886179SRui Paulo * Linux 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd) 26d4886179SRui Paulo * 27d4886179SRui Paulo *********************************************************************** 28d4886179SRui Paulo * 29d4886179SRui Paulo * This file is provided under a dual BSD/GPLv2 license. When using or 30d4886179SRui Paulo * redistributing this file, you may do so under either license. 31d4886179SRui Paulo * 32d4886179SRui Paulo * GPL LICENSE SUMMARY 33d4886179SRui Paulo * 34d4886179SRui Paulo * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. 35d4886179SRui Paulo * 36d4886179SRui Paulo * This program is free software; you can redistribute it and/or modify 37d4886179SRui Paulo * it under the terms of version 2 of the GNU General Public License as 38d4886179SRui Paulo * published by the Free Software Foundation. 39d4886179SRui Paulo * 40d4886179SRui Paulo * This program is distributed in the hope that it will be useful, but 41d4886179SRui Paulo * WITHOUT ANY WARRANTY; without even the implied warranty of 42d4886179SRui Paulo * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 43d4886179SRui Paulo * General Public License for more details. 44d4886179SRui Paulo * 45d4886179SRui Paulo * You should have received a copy of the GNU General Public License 46d4886179SRui Paulo * along with this program; if not, write to the Free Software 47d4886179SRui Paulo * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, 48d4886179SRui Paulo * USA 49d4886179SRui Paulo * 50d4886179SRui Paulo * The full GNU General Public License is included in this distribution 51d4886179SRui Paulo * in the file called COPYING. 52d4886179SRui Paulo * 53d4886179SRui Paulo * Contact Information: 54d4886179SRui Paulo * Intel Linux Wireless <ilw@linux.intel.com> 55d4886179SRui Paulo * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 56d4886179SRui Paulo * 57d4886179SRui Paulo * 58d4886179SRui Paulo * BSD LICENSE 59d4886179SRui Paulo * 60d4886179SRui Paulo * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. 61d4886179SRui Paulo * All rights reserved. 62d4886179SRui Paulo * 63d4886179SRui Paulo * Redistribution and use in source and binary forms, with or without 64d4886179SRui Paulo * modification, are permitted provided that the following conditions 65d4886179SRui Paulo * are met: 66d4886179SRui Paulo * 67d4886179SRui Paulo * * Redistributions of source code must retain the above copyright 68d4886179SRui Paulo * notice, this list of conditions and the following disclaimer. 69d4886179SRui Paulo * * Redistributions in binary form must reproduce the above copyright 70d4886179SRui Paulo * notice, this list of conditions and the following disclaimer in 71d4886179SRui Paulo * the documentation and/or other materials provided with the 72d4886179SRui Paulo * distribution. 73d4886179SRui Paulo * * Neither the name Intel Corporation nor the names of its 74d4886179SRui Paulo * contributors may be used to endorse or promote products derived 75d4886179SRui Paulo * from this software without specific prior written permission. 76d4886179SRui Paulo * 77d4886179SRui Paulo * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 78d4886179SRui Paulo * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 79d4886179SRui Paulo * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 80d4886179SRui Paulo * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 81d4886179SRui Paulo * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 82d4886179SRui Paulo * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 83d4886179SRui Paulo * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 84d4886179SRui Paulo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 85d4886179SRui Paulo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 86d4886179SRui Paulo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 87d4886179SRui Paulo * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 88d4886179SRui Paulo */ 89d4886179SRui Paulo 90d4886179SRui Paulo /*- 91d4886179SRui Paulo * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr> 92d4886179SRui Paulo * 93d4886179SRui Paulo * Permission to use, copy, modify, and distribute this software for any 94d4886179SRui Paulo * purpose with or without fee is hereby granted, provided that the above 95d4886179SRui Paulo * copyright notice and this permission notice appear in all copies. 96d4886179SRui Paulo * 97d4886179SRui Paulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 98d4886179SRui Paulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 99d4886179SRui Paulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 100d4886179SRui Paulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 101d4886179SRui Paulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 102d4886179SRui Paulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 103d4886179SRui Paulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 104d4886179SRui Paulo */ 105d4886179SRui Paulo #include <sys/cdefs.h> 106d4886179SRui Paulo __FBSDID("$FreeBSD$"); 107d4886179SRui Paulo 108b789292fSAndriy Voskoboinyk #include "opt_wlan.h" 109b789292fSAndriy Voskoboinyk 110d4886179SRui Paulo #include <sys/param.h> 111d4886179SRui Paulo #include <sys/bus.h> 112d4886179SRui Paulo #include <sys/conf.h> 113d4886179SRui Paulo #include <sys/endian.h> 114d4886179SRui Paulo #include <sys/firmware.h> 115d4886179SRui Paulo #include <sys/kernel.h> 116d4886179SRui Paulo #include <sys/malloc.h> 117d4886179SRui Paulo #include <sys/mbuf.h> 118d4886179SRui Paulo #include <sys/mutex.h> 119d4886179SRui Paulo #include <sys/module.h> 120d4886179SRui Paulo #include <sys/proc.h> 121d4886179SRui Paulo #include <sys/rman.h> 122d4886179SRui Paulo #include <sys/socket.h> 123d4886179SRui Paulo #include <sys/sockio.h> 124d4886179SRui Paulo #include <sys/sysctl.h> 125d4886179SRui Paulo #include <sys/linker.h> 126d4886179SRui Paulo 127d4886179SRui Paulo #include <machine/bus.h> 128d4886179SRui Paulo #include <machine/endian.h> 129d4886179SRui Paulo #include <machine/resource.h> 130d4886179SRui Paulo 131d4886179SRui Paulo #include <dev/pci/pcivar.h> 132d4886179SRui Paulo #include <dev/pci/pcireg.h> 133d4886179SRui Paulo 134d4886179SRui Paulo #include <net/bpf.h> 135d4886179SRui Paulo 136d4886179SRui Paulo #include <net/if.h> 137d4886179SRui Paulo #include <net/if_var.h> 138d4886179SRui Paulo #include <net/if_arp.h> 139d4886179SRui Paulo #include <net/if_dl.h> 140d4886179SRui Paulo #include <net/if_media.h> 141d4886179SRui Paulo #include <net/if_types.h> 142d4886179SRui Paulo 143d4886179SRui Paulo #include <netinet/in.h> 144d4886179SRui Paulo #include <netinet/in_systm.h> 145d4886179SRui Paulo #include <netinet/if_ether.h> 146d4886179SRui Paulo #include <netinet/ip.h> 147d4886179SRui Paulo 148d4886179SRui Paulo #include <net80211/ieee80211_var.h> 149d4886179SRui Paulo #include <net80211/ieee80211_regdomain.h> 150d4886179SRui Paulo #include <net80211/ieee80211_ratectl.h> 151d4886179SRui Paulo #include <net80211/ieee80211_radiotap.h> 152d4886179SRui Paulo 15330a0fd92SAdrian Chadd #include "if_iwmreg.h" 15430a0fd92SAdrian Chadd #include "if_iwmvar.h" 15530a0fd92SAdrian Chadd #include "if_iwm_debug.h" 15630a0fd92SAdrian Chadd #include "if_iwm_util.h" 15730a0fd92SAdrian Chadd #include "if_iwm_phy_db.h" 15830a0fd92SAdrian Chadd 15930a0fd92SAdrian Chadd #define CHANNEL_NUM_SIZE 4 /* num of channels in calib_ch size */ 16030a0fd92SAdrian Chadd 16130a0fd92SAdrian Chadd struct iwm_phy_db_entry { 16230a0fd92SAdrian Chadd uint16_t size; 16330a0fd92SAdrian Chadd uint8_t *data; 16430a0fd92SAdrian Chadd }; 16530a0fd92SAdrian Chadd 16630a0fd92SAdrian Chadd /** 16730a0fd92SAdrian Chadd * struct iwm_phy_db - stores phy configuration and calibration data. 16830a0fd92SAdrian Chadd * 16930a0fd92SAdrian Chadd * @cfg: phy configuration. 17030a0fd92SAdrian Chadd * @calib_nch: non channel specific calibration data. 17130a0fd92SAdrian Chadd * @calib_ch: channel specific calibration data. 17230a0fd92SAdrian Chadd * @n_group_papd: number of entries in papd channel group. 17330a0fd92SAdrian Chadd * @calib_ch_group_papd: calibration data related to papd channel group. 17430a0fd92SAdrian Chadd * @n_group_txp: number of entries in tx power channel group. 17530a0fd92SAdrian Chadd * @calib_ch_group_txp: calibration data related to tx power chanel group. 17630a0fd92SAdrian Chadd */ 17730a0fd92SAdrian Chadd struct iwm_phy_db { 17830a0fd92SAdrian Chadd struct iwm_phy_db_entry cfg; 17930a0fd92SAdrian Chadd struct iwm_phy_db_entry calib_nch; 18030a0fd92SAdrian Chadd int n_group_papd; 18130a0fd92SAdrian Chadd struct iwm_phy_db_entry *calib_ch_group_papd; 18230a0fd92SAdrian Chadd int n_group_txp; 18330a0fd92SAdrian Chadd struct iwm_phy_db_entry *calib_ch_group_txp; 18430a0fd92SAdrian Chadd 18530a0fd92SAdrian Chadd struct iwm_softc *sc; 18630a0fd92SAdrian Chadd }; 18730a0fd92SAdrian Chadd 18830a0fd92SAdrian Chadd enum iwm_phy_db_section_type { 18930a0fd92SAdrian Chadd IWM_PHY_DB_CFG = 1, 19030a0fd92SAdrian Chadd IWM_PHY_DB_CALIB_NCH, 19130a0fd92SAdrian Chadd IWM_PHY_DB_UNUSED, 19230a0fd92SAdrian Chadd IWM_PHY_DB_CALIB_CHG_PAPD, 19330a0fd92SAdrian Chadd IWM_PHY_DB_CALIB_CHG_TXP, 19430a0fd92SAdrian Chadd IWM_PHY_DB_MAX 19530a0fd92SAdrian Chadd }; 19630a0fd92SAdrian Chadd 19730a0fd92SAdrian Chadd #define PHY_DB_CMD 0x6c /* TEMP API - The actual is 0x8c */ 198d4886179SRui Paulo 199d4886179SRui Paulo /* 20030a0fd92SAdrian Chadd * phy db - configure operational ucode 201d4886179SRui Paulo */ 20230a0fd92SAdrian Chadd struct iwm_phy_db_cmd { 20330a0fd92SAdrian Chadd uint16_t type; 20430a0fd92SAdrian Chadd uint16_t length; 20530a0fd92SAdrian Chadd uint8_t data[]; 20630a0fd92SAdrian Chadd } __packed; 20730a0fd92SAdrian Chadd 20830a0fd92SAdrian Chadd /* for parsing of tx power channel group data that comes from the firmware*/ 20930a0fd92SAdrian Chadd struct iwm_phy_db_chg_txp { 21030a0fd92SAdrian Chadd uint32_t space; 21130a0fd92SAdrian Chadd uint16_t max_channel_idx; 21230a0fd92SAdrian Chadd } __packed; 21330a0fd92SAdrian Chadd 21430a0fd92SAdrian Chadd /* 21530a0fd92SAdrian Chadd * phy db - Receive phy db chunk after calibrations 21630a0fd92SAdrian Chadd */ 21730a0fd92SAdrian Chadd struct iwm_calib_res_notif_phy_db { 21830a0fd92SAdrian Chadd uint16_t type; 21930a0fd92SAdrian Chadd uint16_t length; 22030a0fd92SAdrian Chadd uint8_t data[]; 22130a0fd92SAdrian Chadd } __packed; 22230a0fd92SAdrian Chadd 22330a0fd92SAdrian Chadd struct iwm_phy_db * 22430a0fd92SAdrian Chadd iwm_phy_db_init(struct iwm_softc *sc) 22530a0fd92SAdrian Chadd { 22630a0fd92SAdrian Chadd struct iwm_phy_db *phy_db = malloc(sizeof(struct iwm_phy_db), 22730a0fd92SAdrian Chadd M_DEVBUF, M_NOWAIT | M_ZERO); 22830a0fd92SAdrian Chadd 22930a0fd92SAdrian Chadd if (!phy_db) 23030a0fd92SAdrian Chadd return phy_db; 23130a0fd92SAdrian Chadd 23230a0fd92SAdrian Chadd phy_db->sc = sc; 23330a0fd92SAdrian Chadd 23430a0fd92SAdrian Chadd phy_db->n_group_txp = -1; 23530a0fd92SAdrian Chadd phy_db->n_group_papd = -1; 23630a0fd92SAdrian Chadd 23730a0fd92SAdrian Chadd /* TODO: add default values of the phy db. */ 23830a0fd92SAdrian Chadd return phy_db; 23930a0fd92SAdrian Chadd } 24030a0fd92SAdrian Chadd 241d4886179SRui Paulo /* 242d4886179SRui Paulo * get phy db section: returns a pointer to a phy db section specified by 243d4886179SRui Paulo * type and channel group id. 244d4886179SRui Paulo */ 245d4886179SRui Paulo static struct iwm_phy_db_entry * 24630a0fd92SAdrian Chadd iwm_phy_db_get_section(struct iwm_phy_db *phy_db, 24730a0fd92SAdrian Chadd enum iwm_phy_db_section_type type, 24830a0fd92SAdrian Chadd uint16_t chg_id) 249d4886179SRui Paulo { 25030a0fd92SAdrian Chadd if (!phy_db || type >= IWM_PHY_DB_MAX) 251d4886179SRui Paulo return NULL; 252d4886179SRui Paulo 253d4886179SRui Paulo switch (type) { 254d4886179SRui Paulo case IWM_PHY_DB_CFG: 255d4886179SRui Paulo return &phy_db->cfg; 256d4886179SRui Paulo case IWM_PHY_DB_CALIB_NCH: 257d4886179SRui Paulo return &phy_db->calib_nch; 258d4886179SRui Paulo case IWM_PHY_DB_CALIB_CHG_PAPD: 25930a0fd92SAdrian Chadd if (chg_id >= phy_db->n_group_papd) 260d4886179SRui Paulo return NULL; 261d4886179SRui Paulo return &phy_db->calib_ch_group_papd[chg_id]; 262d4886179SRui Paulo case IWM_PHY_DB_CALIB_CHG_TXP: 26330a0fd92SAdrian Chadd if (chg_id >= phy_db->n_group_txp) 264d4886179SRui Paulo return NULL; 265d4886179SRui Paulo return &phy_db->calib_ch_group_txp[chg_id]; 266d4886179SRui Paulo default: 267d4886179SRui Paulo return NULL; 268d4886179SRui Paulo } 269d4886179SRui Paulo return NULL; 270d4886179SRui Paulo } 271d4886179SRui Paulo 27230a0fd92SAdrian Chadd static void 27330a0fd92SAdrian Chadd iwm_phy_db_free_section(struct iwm_phy_db *phy_db, 27430a0fd92SAdrian Chadd enum iwm_phy_db_section_type type, uint16_t chg_id) 27530a0fd92SAdrian Chadd { 27630a0fd92SAdrian Chadd struct iwm_phy_db_entry *entry = 27730a0fd92SAdrian Chadd iwm_phy_db_get_section(phy_db, type, chg_id); 27830a0fd92SAdrian Chadd if (!entry) 27930a0fd92SAdrian Chadd return; 28030a0fd92SAdrian Chadd 28130a0fd92SAdrian Chadd if (entry->data != NULL) 28230a0fd92SAdrian Chadd free(entry->data, M_DEVBUF); 28330a0fd92SAdrian Chadd entry->data = NULL; 28430a0fd92SAdrian Chadd entry->size = 0; 28530a0fd92SAdrian Chadd } 28630a0fd92SAdrian Chadd 28730a0fd92SAdrian Chadd void 28830a0fd92SAdrian Chadd iwm_phy_db_free(struct iwm_phy_db *phy_db) 28930a0fd92SAdrian Chadd { 29030a0fd92SAdrian Chadd int i; 29130a0fd92SAdrian Chadd 29230a0fd92SAdrian Chadd if (!phy_db) 29330a0fd92SAdrian Chadd return; 29430a0fd92SAdrian Chadd 29530a0fd92SAdrian Chadd iwm_phy_db_free_section(phy_db, IWM_PHY_DB_CFG, 0); 29630a0fd92SAdrian Chadd iwm_phy_db_free_section(phy_db, IWM_PHY_DB_CALIB_NCH, 0); 29730a0fd92SAdrian Chadd 29830a0fd92SAdrian Chadd for (i = 0; i < phy_db->n_group_papd; i++) 29930a0fd92SAdrian Chadd iwm_phy_db_free_section(phy_db, IWM_PHY_DB_CALIB_CHG_PAPD, i); 30030a0fd92SAdrian Chadd if (phy_db->calib_ch_group_papd != NULL) 30130a0fd92SAdrian Chadd free(phy_db->calib_ch_group_papd, M_DEVBUF); 30230a0fd92SAdrian Chadd 30330a0fd92SAdrian Chadd for (i = 0; i < phy_db->n_group_txp; i++) 30430a0fd92SAdrian Chadd iwm_phy_db_free_section(phy_db, IWM_PHY_DB_CALIB_CHG_TXP, i); 30530a0fd92SAdrian Chadd if (phy_db->calib_ch_group_txp != NULL) 30630a0fd92SAdrian Chadd free(phy_db->calib_ch_group_txp, M_DEVBUF); 30730a0fd92SAdrian Chadd 30830a0fd92SAdrian Chadd free(phy_db, M_DEVBUF); 30930a0fd92SAdrian Chadd } 31030a0fd92SAdrian Chadd 311d4886179SRui Paulo int 31230a0fd92SAdrian Chadd iwm_phy_db_set_section(struct iwm_phy_db *phy_db, 313*42155115SAdrian Chadd struct iwm_rx_packet *pkt) 314d4886179SRui Paulo { 315*42155115SAdrian Chadd struct iwm_calib_res_notif_phy_db *phy_db_notif = 316*42155115SAdrian Chadd (struct iwm_calib_res_notif_phy_db *)pkt->data; 317d4886179SRui Paulo enum iwm_phy_db_section_type type = le16toh(phy_db_notif->type); 318d4886179SRui Paulo uint16_t size = le16toh(phy_db_notif->length); 319d4886179SRui Paulo struct iwm_phy_db_entry *entry; 320d4886179SRui Paulo uint16_t chg_id = 0; 321d4886179SRui Paulo 32230a0fd92SAdrian Chadd if (!phy_db) 32330a0fd92SAdrian Chadd return EINVAL; 324d4886179SRui Paulo 32530a0fd92SAdrian Chadd if (type == IWM_PHY_DB_CALIB_CHG_PAPD) { 32630a0fd92SAdrian Chadd chg_id = le16toh(*(uint16_t *)phy_db_notif->data); 32730a0fd92SAdrian Chadd if (phy_db && !phy_db->calib_ch_group_papd) { 32830a0fd92SAdrian Chadd /* 32930a0fd92SAdrian Chadd * Firmware sends the largest index first, so we can use 33030a0fd92SAdrian Chadd * it to know how much we should allocate. 33130a0fd92SAdrian Chadd */ 33230a0fd92SAdrian Chadd phy_db->calib_ch_group_papd = malloc( 33330a0fd92SAdrian Chadd (chg_id + 1) * sizeof(struct iwm_phy_db_entry), 33430a0fd92SAdrian Chadd M_DEVBUF, M_NOWAIT | M_ZERO); 33530a0fd92SAdrian Chadd if (!phy_db->calib_ch_group_papd) 33630a0fd92SAdrian Chadd return ENOMEM; 33730a0fd92SAdrian Chadd phy_db->n_group_papd = chg_id + 1; 33830a0fd92SAdrian Chadd } 33930a0fd92SAdrian Chadd } else if (type == IWM_PHY_DB_CALIB_CHG_TXP) { 34030a0fd92SAdrian Chadd chg_id = le16toh(*(uint16_t *)phy_db_notif->data); 34130a0fd92SAdrian Chadd if (phy_db && !phy_db->calib_ch_group_txp) { 34230a0fd92SAdrian Chadd /* 34330a0fd92SAdrian Chadd * Firmware sends the largest index first, so we can use 34430a0fd92SAdrian Chadd * it to know how much we should allocate. 34530a0fd92SAdrian Chadd */ 34630a0fd92SAdrian Chadd phy_db->calib_ch_group_txp = malloc( 34730a0fd92SAdrian Chadd (chg_id + 1) * sizeof(struct iwm_phy_db_entry), 34830a0fd92SAdrian Chadd M_DEVBUF, M_NOWAIT | M_ZERO); 34930a0fd92SAdrian Chadd if (!phy_db->calib_ch_group_txp) 35030a0fd92SAdrian Chadd return ENOMEM; 35130a0fd92SAdrian Chadd phy_db->n_group_txp = chg_id + 1; 35230a0fd92SAdrian Chadd } 35330a0fd92SAdrian Chadd } 35430a0fd92SAdrian Chadd 35530a0fd92SAdrian Chadd entry = iwm_phy_db_get_section(phy_db, type, chg_id); 356d4886179SRui Paulo if (!entry) 357d4886179SRui Paulo return EINVAL; 358d4886179SRui Paulo 35930a0fd92SAdrian Chadd if (entry->data != NULL) 360d4886179SRui Paulo free(entry->data, M_DEVBUF); 361d4886179SRui Paulo entry->data = malloc(size, M_DEVBUF, M_NOWAIT); 362d4886179SRui Paulo if (!entry->data) { 363d4886179SRui Paulo entry->size = 0; 364d4886179SRui Paulo return ENOMEM; 365d4886179SRui Paulo } 366d4886179SRui Paulo memcpy(entry->data, phy_db_notif->data, size); 36730a0fd92SAdrian Chadd 368d4886179SRui Paulo entry->size = size; 369d4886179SRui Paulo 37030a0fd92SAdrian Chadd IWM_DPRINTF(phy_db->sc, IWM_DEBUG_RESET, 37130a0fd92SAdrian Chadd "%s(%d): [PHYDB]SET: Type %d , Size: %d\n", 37230a0fd92SAdrian Chadd __func__, __LINE__, type, size); 373d4886179SRui Paulo 374d4886179SRui Paulo return 0; 375d4886179SRui Paulo } 376d4886179SRui Paulo 377d4886179SRui Paulo static int 37830a0fd92SAdrian Chadd is_valid_channel(uint16_t ch_id) 379d4886179SRui Paulo { 380d4886179SRui Paulo if (ch_id <= 14 || 381d4886179SRui Paulo (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) || 382d4886179SRui Paulo (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) || 383d4886179SRui Paulo (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1)) 384d4886179SRui Paulo return 1; 385d4886179SRui Paulo return 0; 386d4886179SRui Paulo } 387d4886179SRui Paulo 388d4886179SRui Paulo static uint8_t 38930a0fd92SAdrian Chadd ch_id_to_ch_index(uint16_t ch_id) 390d4886179SRui Paulo { 39130a0fd92SAdrian Chadd if (!is_valid_channel(ch_id)) 392d4886179SRui Paulo return 0xff; 393d4886179SRui Paulo 394d4886179SRui Paulo if (ch_id <= 14) 395d4886179SRui Paulo return ch_id - 1; 396d4886179SRui Paulo if (ch_id <= 64) 397d4886179SRui Paulo return (ch_id + 20) / 4; 398d4886179SRui Paulo if (ch_id <= 140) 399d4886179SRui Paulo return (ch_id - 12) / 4; 400d4886179SRui Paulo return (ch_id - 13) / 4; 401d4886179SRui Paulo } 402d4886179SRui Paulo 403d4886179SRui Paulo 404d4886179SRui Paulo static uint16_t 40530a0fd92SAdrian Chadd channel_id_to_papd(uint16_t ch_id) 406d4886179SRui Paulo { 40730a0fd92SAdrian Chadd if (!is_valid_channel(ch_id)) 408d4886179SRui Paulo return 0xff; 409d4886179SRui Paulo 410d4886179SRui Paulo if (1 <= ch_id && ch_id <= 14) 411d4886179SRui Paulo return 0; 412d4886179SRui Paulo if (36 <= ch_id && ch_id <= 64) 413d4886179SRui Paulo return 1; 414d4886179SRui Paulo if (100 <= ch_id && ch_id <= 140) 415d4886179SRui Paulo return 2; 416d4886179SRui Paulo return 3; 417d4886179SRui Paulo } 418d4886179SRui Paulo 419d4886179SRui Paulo static uint16_t 42030a0fd92SAdrian Chadd channel_id_to_txp(struct iwm_phy_db *phy_db, uint16_t ch_id) 421d4886179SRui Paulo { 422d4886179SRui Paulo struct iwm_phy_db_chg_txp *txp_chg; 423d4886179SRui Paulo int i; 42430a0fd92SAdrian Chadd uint8_t ch_index = ch_id_to_ch_index(ch_id); 425d4886179SRui Paulo if (ch_index == 0xff) 426d4886179SRui Paulo return 0xff; 427d4886179SRui Paulo 42830a0fd92SAdrian Chadd for (i = 0; i < phy_db->n_group_txp; i++) { 429d4886179SRui Paulo txp_chg = (void *)phy_db->calib_ch_group_txp[i].data; 430d4886179SRui Paulo if (!txp_chg) 431d4886179SRui Paulo return 0xff; 432d4886179SRui Paulo /* 433d4886179SRui Paulo * Looking for the first channel group that its max channel is 434d4886179SRui Paulo * higher then wanted channel. 435d4886179SRui Paulo */ 436d4886179SRui Paulo if (le16toh(txp_chg->max_channel_idx) >= ch_index) 437d4886179SRui Paulo return i; 438d4886179SRui Paulo } 439d4886179SRui Paulo return 0xff; 440d4886179SRui Paulo } 441d4886179SRui Paulo 442d4886179SRui Paulo static int 44330a0fd92SAdrian Chadd iwm_phy_db_get_section_data(struct iwm_phy_db *phy_db, 44430a0fd92SAdrian Chadd uint32_t type, uint8_t **data, uint16_t *size, 44530a0fd92SAdrian Chadd uint16_t ch_id) 446d4886179SRui Paulo { 447d4886179SRui Paulo struct iwm_phy_db_entry *entry; 448d4886179SRui Paulo uint16_t ch_group_id = 0; 449d4886179SRui Paulo 45030a0fd92SAdrian Chadd if (!phy_db) 45130a0fd92SAdrian Chadd return EINVAL; 45230a0fd92SAdrian Chadd 453d4886179SRui Paulo /* find wanted channel group */ 454d4886179SRui Paulo if (type == IWM_PHY_DB_CALIB_CHG_PAPD) 45530a0fd92SAdrian Chadd ch_group_id = channel_id_to_papd(ch_id); 456d4886179SRui Paulo else if (type == IWM_PHY_DB_CALIB_CHG_TXP) 45730a0fd92SAdrian Chadd ch_group_id = channel_id_to_txp(phy_db, ch_id); 458d4886179SRui Paulo 45930a0fd92SAdrian Chadd entry = iwm_phy_db_get_section(phy_db, type, ch_group_id); 460d4886179SRui Paulo if (!entry) 461d4886179SRui Paulo return EINVAL; 462d4886179SRui Paulo 463d4886179SRui Paulo *data = entry->data; 464d4886179SRui Paulo *size = entry->size; 465d4886179SRui Paulo 46630a0fd92SAdrian Chadd IWM_DPRINTF(phy_db->sc, IWM_DEBUG_RESET, 467d4886179SRui Paulo "%s(%d): [PHYDB] GET: Type %d , Size: %d\n", 468d4886179SRui Paulo __func__, __LINE__, type, *size); 469d4886179SRui Paulo 470d4886179SRui Paulo return 0; 471d4886179SRui Paulo } 472d4886179SRui Paulo 473d4886179SRui Paulo static int 47430a0fd92SAdrian Chadd iwm_send_phy_db_cmd(struct iwm_phy_db *phy_db, uint16_t type, 475d4886179SRui Paulo uint16_t length, void *data) 476d4886179SRui Paulo { 477d4886179SRui Paulo struct iwm_phy_db_cmd phy_db_cmd; 478d4886179SRui Paulo struct iwm_host_cmd cmd = { 47930a0fd92SAdrian Chadd .id = PHY_DB_CMD, 480d4886179SRui Paulo }; 481d4886179SRui Paulo 48230a0fd92SAdrian Chadd IWM_DPRINTF(phy_db->sc, IWM_DEBUG_RESET, 483d4886179SRui Paulo "Sending PHY-DB hcmd of type %d, of length %d\n", 484d4886179SRui Paulo type, length); 485d4886179SRui Paulo 486d4886179SRui Paulo /* Set phy db cmd variables */ 48730a0fd92SAdrian Chadd phy_db_cmd.type = htole16(type); 48830a0fd92SAdrian Chadd phy_db_cmd.length = htole16(length); 489d4886179SRui Paulo 490d4886179SRui Paulo /* Set hcmd variables */ 491d4886179SRui Paulo cmd.data[0] = &phy_db_cmd; 492d4886179SRui Paulo cmd.len[0] = sizeof(struct iwm_phy_db_cmd); 493d4886179SRui Paulo cmd.data[1] = data; 494d4886179SRui Paulo cmd.len[1] = length; 49530a0fd92SAdrian Chadd #ifdef notyet 49630a0fd92SAdrian Chadd cmd.dataflags[1] = IWM_HCMD_DFL_NOCOPY; 49730a0fd92SAdrian Chadd #endif 498d4886179SRui Paulo 49930a0fd92SAdrian Chadd return iwm_send_cmd(phy_db->sc, &cmd); 500d4886179SRui Paulo } 501d4886179SRui Paulo 502d4886179SRui Paulo static int 50330a0fd92SAdrian Chadd iwm_phy_db_send_all_channel_groups(struct iwm_phy_db *phy_db, 50430a0fd92SAdrian Chadd enum iwm_phy_db_section_type type, 50530a0fd92SAdrian Chadd uint8_t max_ch_groups) 506d4886179SRui Paulo { 507d4886179SRui Paulo uint16_t i; 508d4886179SRui Paulo int err; 509d4886179SRui Paulo struct iwm_phy_db_entry *entry; 510d4886179SRui Paulo 51130a0fd92SAdrian Chadd /* Send all the channel specific groups to operational fw */ 512d4886179SRui Paulo for (i = 0; i < max_ch_groups; i++) { 51330a0fd92SAdrian Chadd entry = iwm_phy_db_get_section(phy_db, 51430a0fd92SAdrian Chadd type, 51530a0fd92SAdrian Chadd i); 516d4886179SRui Paulo if (!entry) 517d4886179SRui Paulo return EINVAL; 518d4886179SRui Paulo 519d4886179SRui Paulo if (!entry->size) 520d4886179SRui Paulo continue; 521d4886179SRui Paulo 522d4886179SRui Paulo /* Send the requested PHY DB section */ 52330a0fd92SAdrian Chadd err = iwm_send_phy_db_cmd(phy_db, 52430a0fd92SAdrian Chadd type, 52530a0fd92SAdrian Chadd entry->size, 52630a0fd92SAdrian Chadd entry->data); 527d4886179SRui Paulo if (err) { 52830a0fd92SAdrian Chadd device_printf(phy_db->sc->sc_dev, 52930a0fd92SAdrian Chadd "Can't SEND phy_db section %d (%d), err %d\n", 53030a0fd92SAdrian Chadd type, i, err); 531d4886179SRui Paulo return err; 532d4886179SRui Paulo } 533d4886179SRui Paulo 53430a0fd92SAdrian Chadd IWM_DPRINTF(phy_db->sc, IWM_DEBUG_CMD, 535d4886179SRui Paulo "Sent PHY_DB HCMD, type = %d num = %d\n", type, i); 536d4886179SRui Paulo } 537d4886179SRui Paulo 538d4886179SRui Paulo return 0; 539d4886179SRui Paulo } 540d4886179SRui Paulo 541d4886179SRui Paulo int 54230a0fd92SAdrian Chadd iwm_send_phy_db_data(struct iwm_phy_db *phy_db) 543d4886179SRui Paulo { 544d4886179SRui Paulo uint8_t *data = NULL; 545d4886179SRui Paulo uint16_t size = 0; 546d4886179SRui Paulo int err; 547d4886179SRui Paulo 54830a0fd92SAdrian Chadd IWM_DPRINTF(phy_db->sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, 549d4886179SRui Paulo "%s: Sending phy db data and configuration to runtime image\n", 550d4886179SRui Paulo __func__); 551d4886179SRui Paulo 552d4886179SRui Paulo /* Send PHY DB CFG section */ 55330a0fd92SAdrian Chadd err = iwm_phy_db_get_section_data(phy_db, IWM_PHY_DB_CFG, 55430a0fd92SAdrian Chadd &data, &size, 0); 555d4886179SRui Paulo if (err) { 55630a0fd92SAdrian Chadd device_printf(phy_db->sc->sc_dev, 557d4886179SRui Paulo "%s: Cannot get Phy DB cfg section, %d\n", 558d4886179SRui Paulo __func__, err); 559d4886179SRui Paulo return err; 560d4886179SRui Paulo } 561d4886179SRui Paulo 56230a0fd92SAdrian Chadd err = iwm_send_phy_db_cmd(phy_db, IWM_PHY_DB_CFG, size, data); 563d4886179SRui Paulo if (err) { 56430a0fd92SAdrian Chadd device_printf(phy_db->sc->sc_dev, 565d4886179SRui Paulo "%s: Cannot send HCMD of Phy DB cfg section, %d\n", 566d4886179SRui Paulo __func__, err); 567d4886179SRui Paulo return err; 568d4886179SRui Paulo } 569d4886179SRui Paulo 57030a0fd92SAdrian Chadd err = iwm_phy_db_get_section_data(phy_db, IWM_PHY_DB_CALIB_NCH, 571d4886179SRui Paulo &data, &size, 0); 572d4886179SRui Paulo if (err) { 57330a0fd92SAdrian Chadd device_printf(phy_db->sc->sc_dev, 574d4886179SRui Paulo "%s: Cannot get Phy DB non specific channel section, " 575d4886179SRui Paulo "%d\n", __func__, err); 576d4886179SRui Paulo return err; 577d4886179SRui Paulo } 578d4886179SRui Paulo 57930a0fd92SAdrian Chadd err = iwm_send_phy_db_cmd(phy_db, IWM_PHY_DB_CALIB_NCH, size, data); 580d4886179SRui Paulo if (err) { 58130a0fd92SAdrian Chadd device_printf(phy_db->sc->sc_dev, 582d4886179SRui Paulo "%s: Cannot send HCMD of Phy DB non specific channel " 583d4886179SRui Paulo "sect, %d\n", __func__, err); 584d4886179SRui Paulo return err; 585d4886179SRui Paulo } 586d4886179SRui Paulo 587d4886179SRui Paulo /* Send all the TXP channel specific data */ 58830a0fd92SAdrian Chadd err = iwm_phy_db_send_all_channel_groups(phy_db, 58930a0fd92SAdrian Chadd IWM_PHY_DB_CALIB_CHG_PAPD, phy_db->n_group_papd); 590d4886179SRui Paulo if (err) { 59130a0fd92SAdrian Chadd device_printf(phy_db->sc->sc_dev, 592d4886179SRui Paulo "%s: Cannot send channel specific PAPD groups, %d\n", 593d4886179SRui Paulo __func__, err); 594d4886179SRui Paulo return err; 595d4886179SRui Paulo } 596d4886179SRui Paulo 597d4886179SRui Paulo /* Send all the TXP channel specific data */ 59830a0fd92SAdrian Chadd err = iwm_phy_db_send_all_channel_groups(phy_db, 59930a0fd92SAdrian Chadd IWM_PHY_DB_CALIB_CHG_TXP, phy_db->n_group_txp); 600d4886179SRui Paulo if (err) { 60130a0fd92SAdrian Chadd device_printf(phy_db->sc->sc_dev, 602d4886179SRui Paulo "%s: Cannot send channel specific TX power groups, " 603d4886179SRui Paulo "%d\n", __func__, err); 604d4886179SRui Paulo return err; 605d4886179SRui Paulo } 606d4886179SRui Paulo 60730a0fd92SAdrian Chadd IWM_DPRINTF(phy_db->sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, 608d4886179SRui Paulo "%s: Finished sending phy db non channel data\n", 609d4886179SRui Paulo __func__); 610d4886179SRui Paulo return 0; 611d4886179SRui Paulo } 612