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/contrib/dev/iwlwifi/iwl-phy-db.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 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
    2 /*
    3  * Copyright (C) 2005-2014, 2020-2021 Intel Corporation
    4  * Copyright (C) 2016 Intel Deutschland GmbH
    5  */
    6 #include <linux/slab.h>
    7 #include <linux/string.h>
    8 #include <linux/export.h>
    9 
   10 #include "iwl-drv.h"
   11 #include "iwl-phy-db.h"
   12 #include "iwl-debug.h"
   13 #include "iwl-op-mode.h"
   14 #include "iwl-trans.h"
   15 
   16 struct iwl_phy_db_entry {
   17         u16     size;
   18         u8      *data;
   19 };
   20 
   21 /**
   22  * struct iwl_phy_db - stores phy configuration and calibration data.
   23  *
   24  * @cfg: phy configuration.
   25  * @calib_nch: non channel specific calibration data.
   26  * @n_group_papd: number of entries in papd channel group.
   27  * @calib_ch_group_papd: calibration data related to papd channel group.
   28  * @n_group_txp: number of entries in tx power channel group.
   29  * @calib_ch_group_txp: calibration data related to tx power chanel group.
   30  * @trans: transport layer
   31  */
   32 struct iwl_phy_db {
   33         struct iwl_phy_db_entry cfg;
   34         struct iwl_phy_db_entry calib_nch;
   35         int n_group_papd;
   36         struct iwl_phy_db_entry *calib_ch_group_papd;
   37         int n_group_txp;
   38         struct iwl_phy_db_entry *calib_ch_group_txp;
   39 
   40         struct iwl_trans *trans;
   41 };
   42 
   43 enum iwl_phy_db_section_type {
   44         IWL_PHY_DB_CFG = 1,
   45         IWL_PHY_DB_CALIB_NCH,
   46         IWL_PHY_DB_UNUSED,
   47         IWL_PHY_DB_CALIB_CHG_PAPD,
   48         IWL_PHY_DB_CALIB_CHG_TXP,
   49         IWL_PHY_DB_MAX
   50 };
   51 
   52 #define PHY_DB_CMD 0x6c
   53 
   54 /* for parsing of tx power channel group data that comes from the firmware*/
   55 struct iwl_phy_db_chg_txp {
   56         __le32 space;
   57         __le16 max_channel_idx;
   58 } __packed;
   59 
   60 struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans)
   61 {
   62         struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),
   63                                             GFP_KERNEL);
   64 
   65         if (!phy_db)
   66                 return phy_db;
   67 
   68         phy_db->trans = trans;
   69 
   70         phy_db->n_group_txp = -1;
   71         phy_db->n_group_papd = -1;
   72 
   73         /* TODO: add default values of the phy db. */
   74         return phy_db;
   75 }
   76 IWL_EXPORT_SYMBOL(iwl_phy_db_init);
   77 
   78 /*
   79  * get phy db section: returns a pointer to a phy db section specified by
   80  * type and channel group id.
   81  */
   82 static struct iwl_phy_db_entry *
   83 iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
   84                        enum iwl_phy_db_section_type type,
   85                        u16 chg_id)
   86 {
   87         if (!phy_db || type >= IWL_PHY_DB_MAX)
   88                 return NULL;
   89 
   90         switch (type) {
   91         case IWL_PHY_DB_CFG:
   92                 return &phy_db->cfg;
   93         case IWL_PHY_DB_CALIB_NCH:
   94                 return &phy_db->calib_nch;
   95         case IWL_PHY_DB_CALIB_CHG_PAPD:
   96                 if (chg_id >= phy_db->n_group_papd)
   97                         return NULL;
   98                 return &phy_db->calib_ch_group_papd[chg_id];
   99         case IWL_PHY_DB_CALIB_CHG_TXP:
  100                 if (chg_id >= phy_db->n_group_txp)
  101                         return NULL;
  102                 return &phy_db->calib_ch_group_txp[chg_id];
  103         default:
  104                 return NULL;
  105         }
  106         return NULL;
  107 }
  108 
  109 static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,
  110                                     enum iwl_phy_db_section_type type,
  111                                     u16 chg_id)
  112 {
  113         struct iwl_phy_db_entry *entry =
  114                                 iwl_phy_db_get_section(phy_db, type, chg_id);
  115         if (!entry)
  116                 return;
  117 
  118         kfree(entry->data);
  119         entry->data = NULL;
  120         entry->size = 0;
  121 }
  122 
  123 void iwl_phy_db_free(struct iwl_phy_db *phy_db)
  124 {
  125         int i;
  126 
  127         if (!phy_db)
  128                 return;
  129 
  130         iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
  131         iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
  132 
  133         for (i = 0; i < phy_db->n_group_papd; i++)
  134                 iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
  135         kfree(phy_db->calib_ch_group_papd);
  136 
  137         for (i = 0; i < phy_db->n_group_txp; i++)
  138                 iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);
  139         kfree(phy_db->calib_ch_group_txp);
  140 
  141         kfree(phy_db);
  142 }
  143 IWL_EXPORT_SYMBOL(iwl_phy_db_free);
  144 
  145 int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,
  146                            struct iwl_rx_packet *pkt)
  147 {
  148         unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
  149         struct iwl_calib_res_notif_phy_db *phy_db_notif =
  150                         (struct iwl_calib_res_notif_phy_db *)pkt->data;
  151         enum iwl_phy_db_section_type type;
  152         u16 size;
  153         struct iwl_phy_db_entry *entry;
  154         u16 chg_id = 0;
  155 
  156         if (pkt_len < sizeof(*phy_db_notif))
  157                 return -EINVAL;
  158 
  159         type = le16_to_cpu(phy_db_notif->type);
  160         size = le16_to_cpu(phy_db_notif->length);
  161 
  162         if (pkt_len < sizeof(*phy_db_notif) + size)
  163                 return -EINVAL;
  164 
  165         if (!phy_db)
  166                 return -EINVAL;
  167 
  168         if (type == IWL_PHY_DB_CALIB_CHG_PAPD) {
  169                 chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
  170                 if (phy_db && !phy_db->calib_ch_group_papd) {
  171                         /*
  172                          * Firmware sends the largest index first, so we can use
  173                          * it to know how much we should allocate.
  174                          */
  175                         phy_db->calib_ch_group_papd = kcalloc(chg_id + 1,
  176                                                               sizeof(struct iwl_phy_db_entry),
  177                                                               GFP_ATOMIC);
  178                         if (!phy_db->calib_ch_group_papd)
  179                                 return -ENOMEM;
  180                         phy_db->n_group_papd = chg_id + 1;
  181                 }
  182         } else if (type == IWL_PHY_DB_CALIB_CHG_TXP) {
  183                 chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
  184                 if (phy_db && !phy_db->calib_ch_group_txp) {
  185                         /*
  186                          * Firmware sends the largest index first, so we can use
  187                          * it to know how much we should allocate.
  188                          */
  189                         phy_db->calib_ch_group_txp = kcalloc(chg_id + 1,
  190                                                              sizeof(struct iwl_phy_db_entry),
  191                                                              GFP_ATOMIC);
  192                         if (!phy_db->calib_ch_group_txp)
  193                                 return -ENOMEM;
  194                         phy_db->n_group_txp = chg_id + 1;
  195                 }
  196         }
  197 
  198         entry = iwl_phy_db_get_section(phy_db, type, chg_id);
  199         if (!entry)
  200                 return -EINVAL;
  201 
  202         kfree(entry->data);
  203         entry->data = kmemdup(phy_db_notif->data, size, GFP_ATOMIC);
  204         if (!entry->data) {
  205                 entry->size = 0;
  206                 return -ENOMEM;
  207         }
  208 
  209         entry->size = size;
  210 
  211         IWL_DEBUG_INFO(phy_db->trans,
  212                        "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
  213                        __func__, __LINE__, type, size);
  214 
  215         return 0;
  216 }
  217 IWL_EXPORT_SYMBOL(iwl_phy_db_set_section);
  218 
  219 static int is_valid_channel(u16 ch_id)
  220 {
  221         if (ch_id <= 14 ||
  222             (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
  223             (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
  224             (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
  225                 return 1;
  226         return 0;
  227 }
  228 
  229 static u8 ch_id_to_ch_index(u16 ch_id)
  230 {
  231         if (WARN_ON(!is_valid_channel(ch_id)))
  232                 return 0xff;
  233 
  234         if (ch_id <= 14)
  235                 return ch_id - 1;
  236         if (ch_id <= 64)
  237                 return (ch_id + 20) / 4;
  238         if (ch_id <= 140)
  239                 return (ch_id - 12) / 4;
  240         return (ch_id - 13) / 4;
  241 }
  242 
  243 
  244 static u16 channel_id_to_papd(u16 ch_id)
  245 {
  246         if (WARN_ON(!is_valid_channel(ch_id)))
  247                 return 0xff;
  248 
  249         if (1 <= ch_id && ch_id <= 14)
  250                 return 0;
  251         if (36 <= ch_id && ch_id <= 64)
  252                 return 1;
  253         if (100 <= ch_id && ch_id <= 140)
  254                 return 2;
  255         return 3;
  256 }
  257 
  258 static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)
  259 {
  260         struct iwl_phy_db_chg_txp *txp_chg;
  261         int i;
  262         u8 ch_index = ch_id_to_ch_index(ch_id);
  263         if (ch_index == 0xff)
  264                 return 0xff;
  265 
  266         for (i = 0; i < phy_db->n_group_txp; i++) {
  267                 txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
  268                 if (!txp_chg)
  269                         return 0xff;
  270                 /*
  271                  * Looking for the first channel group that its max channel is
  272                  * higher then wanted channel.
  273                  */
  274                 if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)
  275                         return i;
  276         }
  277         return 0xff;
  278 }
  279 static
  280 int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
  281                                 u32 type, u8 **data, u16 *size, u16 ch_id)
  282 {
  283         struct iwl_phy_db_entry *entry;
  284         u16 ch_group_id = 0;
  285 
  286         if (!phy_db)
  287                 return -EINVAL;
  288 
  289         /* find wanted channel group */
  290         if (type == IWL_PHY_DB_CALIB_CHG_PAPD)
  291                 ch_group_id = channel_id_to_papd(ch_id);
  292         else if (type == IWL_PHY_DB_CALIB_CHG_TXP)
  293                 ch_group_id = channel_id_to_txp(phy_db, ch_id);
  294 
  295         entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);
  296         if (!entry)
  297                 return -EINVAL;
  298 
  299         *data = entry->data;
  300         *size = entry->size;
  301 
  302         IWL_DEBUG_INFO(phy_db->trans,
  303                        "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
  304                        __func__, __LINE__, type, *size);
  305 
  306         return 0;
  307 }
  308 
  309 static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,
  310                                u16 length, void *data)
  311 {
  312         struct iwl_phy_db_cmd phy_db_cmd;
  313         struct iwl_host_cmd cmd = {
  314                 .id = PHY_DB_CMD,
  315         };
  316 
  317         IWL_DEBUG_INFO(phy_db->trans,
  318                        "Sending PHY-DB hcmd of type %d, of length %d\n",
  319                        type, length);
  320 
  321         /* Set phy db cmd variables */
  322         phy_db_cmd.type = cpu_to_le16(type);
  323         phy_db_cmd.length = cpu_to_le16(length);
  324 
  325         /* Set hcmd variables */
  326         cmd.data[0] = &phy_db_cmd;
  327         cmd.len[0] = sizeof(struct iwl_phy_db_cmd);
  328         cmd.data[1] = data;
  329         cmd.len[1] = length;
  330         cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
  331 
  332         return iwl_trans_send_cmd(phy_db->trans, &cmd);
  333 }
  334 
  335 static int iwl_phy_db_send_all_channel_groups(
  336                                         struct iwl_phy_db *phy_db,
  337                                         enum iwl_phy_db_section_type type,
  338                                         u8 max_ch_groups)
  339 {
  340         u16 i;
  341         int err;
  342         struct iwl_phy_db_entry *entry;
  343 
  344         /* Send all the  channel specific groups to operational fw */
  345         for (i = 0; i < max_ch_groups; i++) {
  346                 entry = iwl_phy_db_get_section(phy_db,
  347                                                type,
  348                                                i);
  349                 if (!entry)
  350                         return -EINVAL;
  351 
  352                 if (!entry->size)
  353                         continue;
  354 
  355                 /* Send the requested PHY DB section */
  356                 err = iwl_send_phy_db_cmd(phy_db,
  357                                           type,
  358                                           entry->size,
  359                                           entry->data);
  360                 if (err) {
  361                         IWL_ERR(phy_db->trans,
  362                                 "Can't SEND phy_db section %d (%d), err %d\n",
  363                                 type, i, err);
  364                         return err;
  365                 }
  366 
  367                 IWL_DEBUG_INFO(phy_db->trans,
  368                                "Sent PHY_DB HCMD, type = %d num = %d\n",
  369                                type, i);
  370         }
  371 
  372         return 0;
  373 }
  374 
  375 int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
  376 {
  377         u8 *data = NULL;
  378         u16 size = 0;
  379         int err;
  380 
  381         IWL_DEBUG_INFO(phy_db->trans,
  382                        "Sending phy db data and configuration to runtime image\n");
  383 
  384         /* Send PHY DB CFG section */
  385         err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG,
  386                                           &data, &size, 0);
  387         if (err) {
  388                 IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n");
  389                 return err;
  390         }
  391 
  392         err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data);
  393         if (err) {
  394                 IWL_ERR(phy_db->trans,
  395                         "Cannot send HCMD of  Phy DB cfg section\n");
  396                 return err;
  397         }
  398 
  399         err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH,
  400                                           &data, &size, 0);
  401         if (err) {
  402                 IWL_ERR(phy_db->trans,
  403                         "Cannot get Phy DB non specific channel section\n");
  404                 return err;
  405         }
  406 
  407         err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data);
  408         if (err) {
  409                 IWL_ERR(phy_db->trans,
  410                         "Cannot send HCMD of Phy DB non specific channel section\n");
  411                 return err;
  412         }
  413 
  414         /* Send all the TXP channel specific data */
  415         err = iwl_phy_db_send_all_channel_groups(phy_db,
  416                                                  IWL_PHY_DB_CALIB_CHG_PAPD,
  417                                                  phy_db->n_group_papd);
  418         if (err) {
  419                 IWL_ERR(phy_db->trans,
  420                         "Cannot send channel specific PAPD groups\n");
  421                 return err;
  422         }
  423 
  424         /* Send all the TXP channel specific data */
  425         err = iwl_phy_db_send_all_channel_groups(phy_db,
  426                                                  IWL_PHY_DB_CALIB_CHG_TXP,
  427                                                  phy_db->n_group_txp);
  428         if (err) {
  429                 IWL_ERR(phy_db->trans,
  430                         "Cannot send channel specific TX power groups\n");
  431                 return err;
  432         }
  433 
  434         IWL_DEBUG_INFO(phy_db->trans,
  435                        "Finished sending phy db non channel data\n");
  436         return 0;
  437 }
  438 IWL_EXPORT_SYMBOL(iwl_send_phy_db_data);

Cache object: 7ecbca44da864077453fbea14b1fd881


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