linux/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2015 Intel Mobile Communications GmbH
   4 * Copyright (C) 2016-2017 Intel Deutschland GmbH
   5 * Copyright (C) 2019-2020 Intel Corporation
   6 */
   7#include <linux/kernel.h>
   8#include <linux/bsearch.h>
   9
  10#include "fw/api/tx.h"
  11#include "iwl-trans.h"
  12#include "iwl-drv.h"
  13#include "iwl-fh.h"
  14#include "queue/tx.h"
  15#include <linux/dmapool.h>
  16
  17struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
  18                                  struct device *dev,
  19                                  const struct iwl_trans_ops *ops,
  20                                  const struct iwl_cfg_trans_params *cfg_trans)
  21{
  22        struct iwl_trans *trans;
  23        int txcmd_size, txcmd_align;
  24#ifdef CONFIG_LOCKDEP
  25        static struct lock_class_key __key;
  26#endif
  27
  28        trans = devm_kzalloc(dev, sizeof(*trans) + priv_size, GFP_KERNEL);
  29        if (!trans)
  30                return NULL;
  31
  32        trans->trans_cfg = cfg_trans;
  33        if (!cfg_trans->gen2) {
  34                txcmd_size = sizeof(struct iwl_tx_cmd);
  35                txcmd_align = sizeof(void *);
  36        } else if (cfg_trans->device_family < IWL_DEVICE_FAMILY_AX210) {
  37                txcmd_size = sizeof(struct iwl_tx_cmd_gen2);
  38                txcmd_align = 64;
  39        } else {
  40                txcmd_size = sizeof(struct iwl_tx_cmd_gen3);
  41                txcmd_align = 128;
  42        }
  43
  44        txcmd_size += sizeof(struct iwl_cmd_header);
  45        txcmd_size += 36; /* biggest possible 802.11 header */
  46
  47        /* Ensure device TX cmd cannot reach/cross a page boundary in gen2 */
  48        if (WARN_ON(cfg_trans->gen2 && txcmd_size >= txcmd_align))
  49                return ERR_PTR(-EINVAL);
  50
  51#ifdef CONFIG_LOCKDEP
  52        lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map",
  53                         &__key, 0);
  54#endif
  55
  56        trans->dev = dev;
  57        trans->ops = ops;
  58        trans->num_rx_queues = 1;
  59
  60        if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
  61                trans->txqs.bc_tbl_size = sizeof(struct iwl_gen3_bc_tbl);
  62        else
  63                trans->txqs.bc_tbl_size = sizeof(struct iwlagn_scd_bc_tbl);
  64        /*
  65         * For gen2 devices, we use a single allocation for each byte-count
  66         * table, but they're pretty small (1k) so use a DMA pool that we
  67         * allocate here.
  68         */
  69        if (trans->trans_cfg->gen2) {
  70                trans->txqs.bc_pool = dmam_pool_create("iwlwifi:bc", dev,
  71                                                       trans->txqs.bc_tbl_size,
  72                                                       256, 0);
  73                if (!trans->txqs.bc_pool)
  74                        return NULL;
  75        }
  76
  77        if (trans->trans_cfg->use_tfh) {
  78                trans->txqs.tfd.addr_size = 64;
  79                trans->txqs.tfd.max_tbs = IWL_TFH_NUM_TBS;
  80                trans->txqs.tfd.size = sizeof(struct iwl_tfh_tfd);
  81        } else {
  82                trans->txqs.tfd.addr_size = 36;
  83                trans->txqs.tfd.max_tbs = IWL_NUM_OF_TBS;
  84                trans->txqs.tfd.size = sizeof(struct iwl_tfd);
  85        }
  86        trans->max_skb_frags = IWL_TRANS_MAX_FRAGS(trans);
  87
  88        snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name),
  89                 "iwl_cmd_pool:%s", dev_name(trans->dev));
  90        trans->dev_cmd_pool =
  91                kmem_cache_create(trans->dev_cmd_pool_name,
  92                                  txcmd_size, txcmd_align,
  93                                  SLAB_HWCACHE_ALIGN, NULL);
  94        if (!trans->dev_cmd_pool)
  95                return NULL;
  96
  97        WARN_ON(!ops->wait_txq_empty && !ops->wait_tx_queues_empty);
  98
  99        trans->txqs.tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page);
 100        if (!trans->txqs.tso_hdr_page) {
 101                kmem_cache_destroy(trans->dev_cmd_pool);
 102                return NULL;
 103        }
 104
 105        return trans;
 106}
 107
 108void iwl_trans_free(struct iwl_trans *trans)
 109{
 110        int i;
 111
 112        for_each_possible_cpu(i) {
 113                struct iwl_tso_hdr_page *p =
 114                        per_cpu_ptr(trans->txqs.tso_hdr_page, i);
 115
 116                if (p->page)
 117                        __free_page(p->page);
 118        }
 119
 120        free_percpu(trans->txqs.tso_hdr_page);
 121
 122        kmem_cache_destroy(trans->dev_cmd_pool);
 123}
 124
 125int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
 126{
 127        int ret;
 128
 129        if (unlikely(!(cmd->flags & CMD_SEND_IN_RFKILL) &&
 130                     test_bit(STATUS_RFKILL_OPMODE, &trans->status)))
 131                return -ERFKILL;
 132
 133        if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
 134                return -EIO;
 135
 136        if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) {
 137                IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
 138                return -EIO;
 139        }
 140
 141        if (WARN_ON((cmd->flags & CMD_WANT_ASYNC_CALLBACK) &&
 142                    !(cmd->flags & CMD_ASYNC)))
 143                return -EINVAL;
 144
 145        if (!(cmd->flags & CMD_ASYNC))
 146                lock_map_acquire_read(&trans->sync_cmd_lockdep_map);
 147
 148        if (trans->wide_cmd_header && !iwl_cmd_groupid(cmd->id))
 149                cmd->id = DEF_ID(cmd->id);
 150
 151        ret = trans->ops->send_cmd(trans, cmd);
 152
 153        if (!(cmd->flags & CMD_ASYNC))
 154                lock_map_release(&trans->sync_cmd_lockdep_map);
 155
 156        if (WARN_ON((cmd->flags & CMD_WANT_SKB) && !ret && !cmd->resp_pkt))
 157                return -EIO;
 158
 159        return ret;
 160}
 161IWL_EXPORT_SYMBOL(iwl_trans_send_cmd);
 162
 163/* Comparator for struct iwl_hcmd_names.
 164 * Used in the binary search over a list of host commands.
 165 *
 166 * @key: command_id that we're looking for.
 167 * @elt: struct iwl_hcmd_names candidate for match.
 168 *
 169 * @return 0 iff equal.
 170 */
 171static int iwl_hcmd_names_cmp(const void *key, const void *elt)
 172{
 173        const struct iwl_hcmd_names *name = elt;
 174        u8 cmd1 = *(u8 *)key;
 175        u8 cmd2 = name->cmd_id;
 176
 177        return (cmd1 - cmd2);
 178}
 179
 180const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id)
 181{
 182        u8 grp, cmd;
 183        struct iwl_hcmd_names *ret;
 184        const struct iwl_hcmd_arr *arr;
 185        size_t size = sizeof(struct iwl_hcmd_names);
 186
 187        grp = iwl_cmd_groupid(id);
 188        cmd = iwl_cmd_opcode(id);
 189
 190        if (!trans->command_groups || grp >= trans->command_groups_size ||
 191            !trans->command_groups[grp].arr)
 192                return "UNKNOWN";
 193
 194        arr = &trans->command_groups[grp];
 195        ret = bsearch(&cmd, arr->arr, arr->size, size, iwl_hcmd_names_cmp);
 196        if (!ret)
 197                return "UNKNOWN";
 198        return ret->cmd_name;
 199}
 200IWL_EXPORT_SYMBOL(iwl_get_cmd_string);
 201
 202int iwl_cmd_groups_verify_sorted(const struct iwl_trans_config *trans)
 203{
 204        int i, j;
 205        const struct iwl_hcmd_arr *arr;
 206
 207        for (i = 0; i < trans->command_groups_size; i++) {
 208                arr = &trans->command_groups[i];
 209                if (!arr->arr)
 210                        continue;
 211                for (j = 0; j < arr->size - 1; j++)
 212                        if (arr->arr[j].cmd_id > arr->arr[j + 1].cmd_id)
 213                                return -1;
 214        }
 215        return 0;
 216}
 217IWL_EXPORT_SYMBOL(iwl_cmd_groups_verify_sorted);
 218