root/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. iwl_phy_db_init
  2. iwl_phy_db_get_section
  3. iwl_phy_db_free_section
  4. iwl_phy_db_free
  5. iwl_phy_db_set_section
  6. is_valid_channel
  7. ch_id_to_ch_index
  8. channel_id_to_papd
  9. channel_id_to_txp
  10. iwl_phy_db_get_section_data
  11. iwl_send_phy_db_cmd
  12. iwl_phy_db_send_all_channel_groups
  13. iwl_send_phy_db_data

   1 /******************************************************************************
   2  *
   3  * This file is provided under a dual BSD/GPLv2 license.  When using or
   4  * redistributing this file, you may do so under either license.
   5  *
   6  * GPL LICENSE SUMMARY
   7  *
   8  * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
   9  * Copyright(c) 2016 Intel Deutschland GmbH
  10  *
  11  * This program is free software; you can redistribute it and/or modify
  12  * it under the terms of version 2 of the GNU General Public License as
  13  * published by the Free Software Foundation.
  14  *
  15  * This program is distributed in the hope that it will be useful, but
  16  * WITHOUT ANY WARRANTY; without even the implied warranty of
  17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18  * General Public License for more details.
  19  *
  20  * The full GNU General Public License is included in this distribution
  21  * in the file called COPYING.
  22  *
  23  * Contact Information:
  24  *  Intel Linux Wireless <linuxwifi@intel.com>
  25  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  26  *
  27  * BSD LICENSE
  28  *
  29  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  30  * All rights reserved.
  31  *
  32  * Redistribution and use in source and binary forms, with or without
  33  * modification, are permitted provided that the following conditions
  34  * are met:
  35  *
  36  *  * Redistributions of source code must retain the above copyright
  37  *    notice, this list of conditions and the following disclaimer.
  38  *  * Redistributions in binary form must reproduce the above copyright
  39  *    notice, this list of conditions and the following disclaimer in
  40  *    the documentation and/or other materials provided with the
  41  *    distribution.
  42  *  * Neither the name Intel Corporation nor the names of its
  43  *    contributors may be used to endorse or promote products derived
  44  *    from this software without specific prior written permission.
  45  *
  46  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  47  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  48  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  49  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  50  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  51  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  52  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  53  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  54  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  55  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  56  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  57  *
  58  *****************************************************************************/
  59 
  60 #include <linux/slab.h>
  61 #include <linux/string.h>
  62 #include <linux/export.h>
  63 
  64 #include "iwl-drv.h"
  65 #include "iwl-phy-db.h"
  66 #include "iwl-debug.h"
  67 #include "iwl-op-mode.h"
  68 #include "iwl-trans.h"
  69 
  70 #define CHANNEL_NUM_SIZE        4       /* num of channels in calib_ch size */
  71 
  72 struct iwl_phy_db_entry {
  73         u16     size;
  74         u8      *data;
  75 };
  76 
  77 /**
  78  * struct iwl_phy_db - stores phy configuration and calibration data.
  79  *
  80  * @cfg: phy configuration.
  81  * @calib_nch: non channel specific calibration data.
  82  * @calib_ch: channel specific calibration data.
  83  * @n_group_papd: number of entries in papd channel group.
  84  * @calib_ch_group_papd: calibration data related to papd channel group.
  85  * @n_group_txp: number of entries in tx power channel group.
  86  * @calib_ch_group_txp: calibration data related to tx power chanel group.
  87  */
  88 struct iwl_phy_db {
  89         struct iwl_phy_db_entry cfg;
  90         struct iwl_phy_db_entry calib_nch;
  91         int n_group_papd;
  92         struct iwl_phy_db_entry *calib_ch_group_papd;
  93         int n_group_txp;
  94         struct iwl_phy_db_entry *calib_ch_group_txp;
  95 
  96         struct iwl_trans *trans;
  97 };
  98 
  99 enum iwl_phy_db_section_type {
 100         IWL_PHY_DB_CFG = 1,
 101         IWL_PHY_DB_CALIB_NCH,
 102         IWL_PHY_DB_UNUSED,
 103         IWL_PHY_DB_CALIB_CHG_PAPD,
 104         IWL_PHY_DB_CALIB_CHG_TXP,
 105         IWL_PHY_DB_MAX
 106 };
 107 
 108 #define PHY_DB_CMD 0x6c
 109 
 110 /* for parsing of tx power channel group data that comes from the firmware*/
 111 struct iwl_phy_db_chg_txp {
 112         __le32 space;
 113         __le16 max_channel_idx;
 114 } __packed;
 115 
 116 struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans)
 117 {
 118         struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),
 119                                             GFP_KERNEL);
 120 
 121         if (!phy_db)
 122                 return phy_db;
 123 
 124         phy_db->trans = trans;
 125 
 126         phy_db->n_group_txp = -1;
 127         phy_db->n_group_papd = -1;
 128 
 129         /* TODO: add default values of the phy db. */
 130         return phy_db;
 131 }
 132 IWL_EXPORT_SYMBOL(iwl_phy_db_init);
 133 
 134 /*
 135  * get phy db section: returns a pointer to a phy db section specified by
 136  * type and channel group id.
 137  */
 138 static struct iwl_phy_db_entry *
 139 iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
 140                        enum iwl_phy_db_section_type type,
 141                        u16 chg_id)
 142 {
 143         if (!phy_db || type >= IWL_PHY_DB_MAX)
 144                 return NULL;
 145 
 146         switch (type) {
 147         case IWL_PHY_DB_CFG:
 148                 return &phy_db->cfg;
 149         case IWL_PHY_DB_CALIB_NCH:
 150                 return &phy_db->calib_nch;
 151         case IWL_PHY_DB_CALIB_CHG_PAPD:
 152                 if (chg_id >= phy_db->n_group_papd)
 153                         return NULL;
 154                 return &phy_db->calib_ch_group_papd[chg_id];
 155         case IWL_PHY_DB_CALIB_CHG_TXP:
 156                 if (chg_id >= phy_db->n_group_txp)
 157                         return NULL;
 158                 return &phy_db->calib_ch_group_txp[chg_id];
 159         default:
 160                 return NULL;
 161         }
 162         return NULL;
 163 }
 164 
 165 static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,
 166                                     enum iwl_phy_db_section_type type,
 167                                     u16 chg_id)
 168 {
 169         struct iwl_phy_db_entry *entry =
 170                                 iwl_phy_db_get_section(phy_db, type, chg_id);
 171         if (!entry)
 172                 return;
 173 
 174         kfree(entry->data);
 175         entry->data = NULL;
 176         entry->size = 0;
 177 }
 178 
 179 void iwl_phy_db_free(struct iwl_phy_db *phy_db)
 180 {
 181         int i;
 182 
 183         if (!phy_db)
 184                 return;
 185 
 186         iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
 187         iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
 188 
 189         for (i = 0; i < phy_db->n_group_papd; i++)
 190                 iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
 191         kfree(phy_db->calib_ch_group_papd);
 192 
 193         for (i = 0; i < phy_db->n_group_txp; i++)
 194                 iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);
 195         kfree(phy_db->calib_ch_group_txp);
 196 
 197         kfree(phy_db);
 198 }
 199 IWL_EXPORT_SYMBOL(iwl_phy_db_free);
 200 
 201 int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,
 202                            struct iwl_rx_packet *pkt)
 203 {
 204         struct iwl_calib_res_notif_phy_db *phy_db_notif =
 205                         (struct iwl_calib_res_notif_phy_db *)pkt->data;
 206         enum iwl_phy_db_section_type type = le16_to_cpu(phy_db_notif->type);
 207         u16 size  = le16_to_cpu(phy_db_notif->length);
 208         struct iwl_phy_db_entry *entry;
 209         u16 chg_id = 0;
 210 
 211         if (!phy_db)
 212                 return -EINVAL;
 213 
 214         if (type == IWL_PHY_DB_CALIB_CHG_PAPD) {
 215                 chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
 216                 if (phy_db && !phy_db->calib_ch_group_papd) {
 217                         /*
 218                          * Firmware sends the largest index first, so we can use
 219                          * it to know how much we should allocate.
 220                          */
 221                         phy_db->calib_ch_group_papd = kcalloc(chg_id + 1,
 222                                                               sizeof(struct iwl_phy_db_entry),
 223                                                               GFP_ATOMIC);
 224                         if (!phy_db->calib_ch_group_papd)
 225                                 return -ENOMEM;
 226                         phy_db->n_group_papd = chg_id + 1;
 227                 }
 228         } else if (type == IWL_PHY_DB_CALIB_CHG_TXP) {
 229                 chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
 230                 if (phy_db && !phy_db->calib_ch_group_txp) {
 231                         /*
 232                          * Firmware sends the largest index first, so we can use
 233                          * it to know how much we should allocate.
 234                          */
 235                         phy_db->calib_ch_group_txp = kcalloc(chg_id + 1,
 236                                                              sizeof(struct iwl_phy_db_entry),
 237                                                              GFP_ATOMIC);
 238                         if (!phy_db->calib_ch_group_txp)
 239                                 return -ENOMEM;
 240                         phy_db->n_group_txp = chg_id + 1;
 241                 }
 242         }
 243 
 244         entry = iwl_phy_db_get_section(phy_db, type, chg_id);
 245         if (!entry)
 246                 return -EINVAL;
 247 
 248         kfree(entry->data);
 249         entry->data = kmemdup(phy_db_notif->data, size, GFP_ATOMIC);
 250         if (!entry->data) {
 251                 entry->size = 0;
 252                 return -ENOMEM;
 253         }
 254 
 255         entry->size = size;
 256 
 257         IWL_DEBUG_INFO(phy_db->trans,
 258                        "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
 259                        __func__, __LINE__, type, size);
 260 
 261         return 0;
 262 }
 263 IWL_EXPORT_SYMBOL(iwl_phy_db_set_section);
 264 
 265 static int is_valid_channel(u16 ch_id)
 266 {
 267         if (ch_id <= 14 ||
 268             (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
 269             (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
 270             (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
 271                 return 1;
 272         return 0;
 273 }
 274 
 275 static u8 ch_id_to_ch_index(u16 ch_id)
 276 {
 277         if (WARN_ON(!is_valid_channel(ch_id)))
 278                 return 0xff;
 279 
 280         if (ch_id <= 14)
 281                 return ch_id - 1;
 282         if (ch_id <= 64)
 283                 return (ch_id + 20) / 4;
 284         if (ch_id <= 140)
 285                 return (ch_id - 12) / 4;
 286         return (ch_id - 13) / 4;
 287 }
 288 
 289 
 290 static u16 channel_id_to_papd(u16 ch_id)
 291 {
 292         if (WARN_ON(!is_valid_channel(ch_id)))
 293                 return 0xff;
 294 
 295         if (1 <= ch_id && ch_id <= 14)
 296                 return 0;
 297         if (36 <= ch_id && ch_id <= 64)
 298                 return 1;
 299         if (100 <= ch_id && ch_id <= 140)
 300                 return 2;
 301         return 3;
 302 }
 303 
 304 static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)
 305 {
 306         struct iwl_phy_db_chg_txp *txp_chg;
 307         int i;
 308         u8 ch_index = ch_id_to_ch_index(ch_id);
 309         if (ch_index == 0xff)
 310                 return 0xff;
 311 
 312         for (i = 0; i < phy_db->n_group_txp; i++) {
 313                 txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
 314                 if (!txp_chg)
 315                         return 0xff;
 316                 /*
 317                  * Looking for the first channel group that its max channel is
 318                  * higher then wanted channel.
 319                  */
 320                 if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)
 321                         return i;
 322         }
 323         return 0xff;
 324 }
 325 static
 326 int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
 327                                 u32 type, u8 **data, u16 *size, u16 ch_id)
 328 {
 329         struct iwl_phy_db_entry *entry;
 330         u16 ch_group_id = 0;
 331 
 332         if (!phy_db)
 333                 return -EINVAL;
 334 
 335         /* find wanted channel group */
 336         if (type == IWL_PHY_DB_CALIB_CHG_PAPD)
 337                 ch_group_id = channel_id_to_papd(ch_id);
 338         else if (type == IWL_PHY_DB_CALIB_CHG_TXP)
 339                 ch_group_id = channel_id_to_txp(phy_db, ch_id);
 340 
 341         entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);
 342         if (!entry)
 343                 return -EINVAL;
 344 
 345         *data = entry->data;
 346         *size = entry->size;
 347 
 348         IWL_DEBUG_INFO(phy_db->trans,
 349                        "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
 350                        __func__, __LINE__, type, *size);
 351 
 352         return 0;
 353 }
 354 
 355 static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,
 356                                u16 length, void *data)
 357 {
 358         struct iwl_phy_db_cmd phy_db_cmd;
 359         struct iwl_host_cmd cmd = {
 360                 .id = PHY_DB_CMD,
 361         };
 362 
 363         IWL_DEBUG_INFO(phy_db->trans,
 364                        "Sending PHY-DB hcmd of type %d, of length %d\n",
 365                        type, length);
 366 
 367         /* Set phy db cmd variables */
 368         phy_db_cmd.type = cpu_to_le16(type);
 369         phy_db_cmd.length = cpu_to_le16(length);
 370 
 371         /* Set hcmd variables */
 372         cmd.data[0] = &phy_db_cmd;
 373         cmd.len[0] = sizeof(struct iwl_phy_db_cmd);
 374         cmd.data[1] = data;
 375         cmd.len[1] = length;
 376         cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
 377 
 378         return iwl_trans_send_cmd(phy_db->trans, &cmd);
 379 }
 380 
 381 static int iwl_phy_db_send_all_channel_groups(
 382                                         struct iwl_phy_db *phy_db,
 383                                         enum iwl_phy_db_section_type type,
 384                                         u8 max_ch_groups)
 385 {
 386         u16 i;
 387         int err;
 388         struct iwl_phy_db_entry *entry;
 389 
 390         /* Send all the  channel specific groups to operational fw */
 391         for (i = 0; i < max_ch_groups; i++) {
 392                 entry = iwl_phy_db_get_section(phy_db,
 393                                                type,
 394                                                i);
 395                 if (!entry)
 396                         return -EINVAL;
 397 
 398                 if (!entry->size)
 399                         continue;
 400 
 401                 /* Send the requested PHY DB section */
 402                 err = iwl_send_phy_db_cmd(phy_db,
 403                                           type,
 404                                           entry->size,
 405                                           entry->data);
 406                 if (err) {
 407                         IWL_ERR(phy_db->trans,
 408                                 "Can't SEND phy_db section %d (%d), err %d\n",
 409                                 type, i, err);
 410                         return err;
 411                 }
 412 
 413                 IWL_DEBUG_INFO(phy_db->trans,
 414                                "Sent PHY_DB HCMD, type = %d num = %d\n",
 415                                type, i);
 416         }
 417 
 418         return 0;
 419 }
 420 
 421 int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
 422 {
 423         u8 *data = NULL;
 424         u16 size = 0;
 425         int err;
 426 
 427         IWL_DEBUG_INFO(phy_db->trans,
 428                        "Sending phy db data and configuration to runtime image\n");
 429 
 430         /* Send PHY DB CFG section */
 431         err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG,
 432                                           &data, &size, 0);
 433         if (err) {
 434                 IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n");
 435                 return err;
 436         }
 437 
 438         err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data);
 439         if (err) {
 440                 IWL_ERR(phy_db->trans,
 441                         "Cannot send HCMD of  Phy DB cfg section\n");
 442                 return err;
 443         }
 444 
 445         err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH,
 446                                           &data, &size, 0);
 447         if (err) {
 448                 IWL_ERR(phy_db->trans,
 449                         "Cannot get Phy DB non specific channel section\n");
 450                 return err;
 451         }
 452 
 453         err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data);
 454         if (err) {
 455                 IWL_ERR(phy_db->trans,
 456                         "Cannot send HCMD of Phy DB non specific channel section\n");
 457                 return err;
 458         }
 459 
 460         /* Send all the TXP channel specific data */
 461         err = iwl_phy_db_send_all_channel_groups(phy_db,
 462                                                  IWL_PHY_DB_CALIB_CHG_PAPD,
 463                                                  phy_db->n_group_papd);
 464         if (err) {
 465                 IWL_ERR(phy_db->trans,
 466                         "Cannot send channel specific PAPD groups\n");
 467                 return err;
 468         }
 469 
 470         /* Send all the TXP channel specific data */
 471         err = iwl_phy_db_send_all_channel_groups(phy_db,
 472                                                  IWL_PHY_DB_CALIB_CHG_TXP,
 473                                                  phy_db->n_group_txp);
 474         if (err) {
 475                 IWL_ERR(phy_db->trans,
 476                         "Cannot send channel specific TX power groups\n");
 477                 return err;
 478         }
 479 
 480         IWL_DEBUG_INFO(phy_db->trans,
 481                        "Finished sending phy db non channel data\n");
 482         return 0;
 483 }
 484 IWL_EXPORT_SYMBOL(iwl_send_phy_db_data);

/* [<][>][^][v][top][bottom][index][help] */