0001
0002
0003
0004
0005
0006
0007 #include <linux/kernel.h>
0008 #include <linux/bsearch.h>
0009
0010 #include "fw/api/tx.h"
0011 #include "iwl-trans.h"
0012 #include "iwl-drv.h"
0013 #include "iwl-fh.h"
0014 #include "queue/tx.h"
0015 #include <linux/dmapool.h>
0016 #include "fw/api/commands.h"
0017
0018 struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
0019 struct device *dev,
0020 const struct iwl_trans_ops *ops,
0021 const struct iwl_cfg_trans_params *cfg_trans)
0022 {
0023 struct iwl_trans *trans;
0024 #ifdef CONFIG_LOCKDEP
0025 static struct lock_class_key __key;
0026 #endif
0027
0028 trans = devm_kzalloc(dev, sizeof(*trans) + priv_size, GFP_KERNEL);
0029 if (!trans)
0030 return NULL;
0031
0032 trans->trans_cfg = cfg_trans;
0033
0034 #ifdef CONFIG_LOCKDEP
0035 lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map",
0036 &__key, 0);
0037 #endif
0038
0039 trans->dev = dev;
0040 trans->ops = ops;
0041 trans->num_rx_queues = 1;
0042
0043 WARN_ON(!ops->wait_txq_empty && !ops->wait_tx_queues_empty);
0044
0045 if (trans->trans_cfg->use_tfh) {
0046 trans->txqs.tfd.addr_size = 64;
0047 trans->txqs.tfd.max_tbs = IWL_TFH_NUM_TBS;
0048 trans->txqs.tfd.size = sizeof(struct iwl_tfh_tfd);
0049 } else {
0050 trans->txqs.tfd.addr_size = 36;
0051 trans->txqs.tfd.max_tbs = IWL_NUM_OF_TBS;
0052 trans->txqs.tfd.size = sizeof(struct iwl_tfd);
0053 }
0054 trans->max_skb_frags = IWL_TRANS_MAX_FRAGS(trans);
0055
0056 return trans;
0057 }
0058
0059 int iwl_trans_init(struct iwl_trans *trans)
0060 {
0061 int txcmd_size, txcmd_align;
0062
0063 if (!trans->trans_cfg->gen2) {
0064 txcmd_size = sizeof(struct iwl_tx_cmd);
0065 txcmd_align = sizeof(void *);
0066 } else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
0067 txcmd_size = sizeof(struct iwl_tx_cmd_gen2);
0068 txcmd_align = 64;
0069 } else {
0070 txcmd_size = sizeof(struct iwl_tx_cmd_gen3);
0071 txcmd_align = 128;
0072 }
0073
0074 txcmd_size += sizeof(struct iwl_cmd_header);
0075 txcmd_size += 36;
0076
0077
0078 if (WARN_ON(trans->trans_cfg->gen2 && txcmd_size >= txcmd_align))
0079 return -EINVAL;
0080
0081 if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
0082 trans->txqs.bc_tbl_size =
0083 sizeof(struct iwl_gen3_bc_tbl_entry) * TFD_QUEUE_BC_SIZE_GEN3_BZ;
0084 else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
0085 trans->txqs.bc_tbl_size =
0086 sizeof(struct iwl_gen3_bc_tbl_entry) * TFD_QUEUE_BC_SIZE_GEN3_AX210;
0087 else
0088 trans->txqs.bc_tbl_size = sizeof(struct iwlagn_scd_bc_tbl);
0089
0090
0091
0092
0093
0094 if (trans->trans_cfg->gen2) {
0095 trans->txqs.bc_pool = dmam_pool_create("iwlwifi:bc", trans->dev,
0096 trans->txqs.bc_tbl_size,
0097 256, 0);
0098 if (!trans->txqs.bc_pool)
0099 return -ENOMEM;
0100 }
0101
0102
0103 WARN_ON(trans->txqs.tfd.addr_size !=
0104 (trans->trans_cfg->use_tfh ? 64 : 36));
0105
0106 snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name),
0107 "iwl_cmd_pool:%s", dev_name(trans->dev));
0108 trans->dev_cmd_pool =
0109 kmem_cache_create(trans->dev_cmd_pool_name,
0110 txcmd_size, txcmd_align,
0111 SLAB_HWCACHE_ALIGN, NULL);
0112 if (!trans->dev_cmd_pool)
0113 return -ENOMEM;
0114
0115 trans->txqs.tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page);
0116 if (!trans->txqs.tso_hdr_page) {
0117 kmem_cache_destroy(trans->dev_cmd_pool);
0118 return -ENOMEM;
0119 }
0120
0121
0122 init_waitqueue_head(&trans->wait_command_queue);
0123
0124 return 0;
0125 }
0126
0127 void iwl_trans_free(struct iwl_trans *trans)
0128 {
0129 int i;
0130
0131 if (trans->txqs.tso_hdr_page) {
0132 for_each_possible_cpu(i) {
0133 struct iwl_tso_hdr_page *p =
0134 per_cpu_ptr(trans->txqs.tso_hdr_page, i);
0135
0136 if (p && p->page)
0137 __free_page(p->page);
0138 }
0139
0140 free_percpu(trans->txqs.tso_hdr_page);
0141 }
0142
0143 kmem_cache_destroy(trans->dev_cmd_pool);
0144 }
0145
0146 int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
0147 {
0148 int ret;
0149
0150 if (unlikely(!(cmd->flags & CMD_SEND_IN_RFKILL) &&
0151 test_bit(STATUS_RFKILL_OPMODE, &trans->status)))
0152 return -ERFKILL;
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163 if (unlikely(trans->system_pm_mode == IWL_PLAT_PM_MODE_D3 &&
0164 !(cmd->flags & CMD_SEND_IN_D3)))
0165 return -EHOSTDOWN;
0166
0167 if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
0168 return -EIO;
0169
0170 if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) {
0171 IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
0172 return -EIO;
0173 }
0174
0175 if (WARN_ON((cmd->flags & CMD_WANT_ASYNC_CALLBACK) &&
0176 !(cmd->flags & CMD_ASYNC)))
0177 return -EINVAL;
0178
0179 if (!(cmd->flags & CMD_ASYNC))
0180 lock_map_acquire_read(&trans->sync_cmd_lockdep_map);
0181
0182 if (trans->wide_cmd_header && !iwl_cmd_groupid(cmd->id)) {
0183 if (cmd->id != REPLY_ERROR)
0184 cmd->id = DEF_ID(cmd->id);
0185 }
0186
0187 ret = iwl_trans_txq_send_hcmd(trans, cmd);
0188
0189 if (!(cmd->flags & CMD_ASYNC))
0190 lock_map_release(&trans->sync_cmd_lockdep_map);
0191
0192 if (WARN_ON((cmd->flags & CMD_WANT_SKB) && !ret && !cmd->resp_pkt))
0193 return -EIO;
0194
0195 return ret;
0196 }
0197 IWL_EXPORT_SYMBOL(iwl_trans_send_cmd);
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207 static int iwl_hcmd_names_cmp(const void *key, const void *elt)
0208 {
0209 const struct iwl_hcmd_names *name = elt;
0210 const u8 *cmd1 = key;
0211 u8 cmd2 = name->cmd_id;
0212
0213 return (*cmd1 - cmd2);
0214 }
0215
0216 const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id)
0217 {
0218 u8 grp, cmd;
0219 struct iwl_hcmd_names *ret;
0220 const struct iwl_hcmd_arr *arr;
0221 size_t size = sizeof(struct iwl_hcmd_names);
0222
0223 grp = iwl_cmd_groupid(id);
0224 cmd = iwl_cmd_opcode(id);
0225
0226 if (!trans->command_groups || grp >= trans->command_groups_size ||
0227 !trans->command_groups[grp].arr)
0228 return "UNKNOWN";
0229
0230 arr = &trans->command_groups[grp];
0231 ret = bsearch(&cmd, arr->arr, arr->size, size, iwl_hcmd_names_cmp);
0232 if (!ret)
0233 return "UNKNOWN";
0234 return ret->cmd_name;
0235 }
0236 IWL_EXPORT_SYMBOL(iwl_get_cmd_string);
0237
0238 int iwl_cmd_groups_verify_sorted(const struct iwl_trans_config *trans)
0239 {
0240 int i, j;
0241 const struct iwl_hcmd_arr *arr;
0242
0243 for (i = 0; i < trans->command_groups_size; i++) {
0244 arr = &trans->command_groups[i];
0245 if (!arr->arr)
0246 continue;
0247 for (j = 0; j < arr->size - 1; j++)
0248 if (arr->arr[j].cmd_id > arr->arr[j + 1].cmd_id)
0249 return -1;
0250 }
0251 return 0;
0252 }
0253 IWL_EXPORT_SYMBOL(iwl_cmd_groups_verify_sorted);