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) 2015 Intel Mobile Communications GmbH 9 * Copyright(c) 2016 - 2017 Intel Deutschland GmbH 10 * Copyright(c) 2019 - 2020 Intel Corporation 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of version 2 of the GNU General Public License as 14 * published by the Free Software Foundation. 15 * 16 * This program is distributed in the hope that it will be useful, but 17 * WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * General Public License for more details. 20 * 21 * The full GNU General Public License is included in this distribution 22 * in the file called COPYING. 23 * 24 * Contact Information: 25 * Intel Linux Wireless <linuxwifi@intel.com> 26 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 27 * 28 * BSD LICENSE 29 * 30 * Copyright(c) 2015 Intel Mobile Communications GmbH 31 * Copyright(c) 2016 - 2017 Intel Deutschland GmbH 32 * Copyright(c) 2019 - 2020 Intel Corporation 33 * All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 39 * * Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * * Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in 43 * the documentation and/or other materials provided with the 44 * distribution. 45 * * Neither the name Intel Corporation nor the names of its 46 * contributors may be used to endorse or promote products derived 47 * from this software without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 50 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 51 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 52 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 53 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 54 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 55 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 56 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 57 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 58 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 59 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 60 * 61 *****************************************************************************/ 62 #include <linux/kernel.h> 63 #include <linux/bsearch.h> 64 65 #include "fw/api/tx.h" 66 #include "iwl-trans.h" 67 #include "iwl-drv.h" 68 #include "iwl-fh.h" 69 70 struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, 71 struct device *dev, 72 const struct iwl_trans_ops *ops, 73 const struct iwl_cfg_trans_params *cfg_trans) 74 { 75 struct iwl_trans *trans; 76 int txcmd_size, txcmd_align; 77 #ifdef CONFIG_LOCKDEP 78 static struct lock_class_key __key; 79 #endif 80 81 trans = devm_kzalloc(dev, sizeof(*trans) + priv_size, GFP_KERNEL); 82 if (!trans) 83 return NULL; 84 85 trans->trans_cfg = cfg_trans; 86 if (!cfg_trans->gen2) { 87 txcmd_size = sizeof(struct iwl_tx_cmd); 88 txcmd_align = sizeof(void *); 89 } else if (cfg_trans->device_family < IWL_DEVICE_FAMILY_AX210) { 90 txcmd_size = sizeof(struct iwl_tx_cmd_gen2); 91 txcmd_align = 64; 92 } else { 93 txcmd_size = sizeof(struct iwl_tx_cmd_gen3); 94 txcmd_align = 128; 95 } 96 97 txcmd_size += sizeof(struct iwl_cmd_header); 98 txcmd_size += 36; /* biggest possible 802.11 header */ 99 100 /* Ensure device TX cmd cannot reach/cross a page boundary in gen2 */ 101 if (WARN_ON(cfg_trans->gen2 && txcmd_size >= txcmd_align)) 102 return ERR_PTR(-EINVAL); 103 104 #ifdef CONFIG_LOCKDEP 105 lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map", 106 &__key, 0); 107 #endif 108 109 trans->dev = dev; 110 trans->ops = ops; 111 trans->num_rx_queues = 1; 112 113 snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name), 114 "iwl_cmd_pool:%s", dev_name(trans->dev)); 115 trans->dev_cmd_pool = 116 kmem_cache_create(trans->dev_cmd_pool_name, 117 txcmd_size, txcmd_align, 118 SLAB_HWCACHE_ALIGN, NULL); 119 if (!trans->dev_cmd_pool) 120 return NULL; 121 122 WARN_ON(!ops->wait_txq_empty && !ops->wait_tx_queues_empty); 123 124 return trans; 125 } 126 127 void iwl_trans_free(struct iwl_trans *trans) 128 { 129 kmem_cache_destroy(trans->dev_cmd_pool); 130 } 131 132 int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) 133 { 134 int ret; 135 136 if (unlikely(!(cmd->flags & CMD_SEND_IN_RFKILL) && 137 test_bit(STATUS_RFKILL_OPMODE, &trans->status))) 138 return -ERFKILL; 139 140 if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) 141 return -EIO; 142 143 if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) { 144 IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); 145 return -EIO; 146 } 147 148 if (WARN_ON((cmd->flags & CMD_WANT_ASYNC_CALLBACK) && 149 !(cmd->flags & CMD_ASYNC))) 150 return -EINVAL; 151 152 if (!(cmd->flags & CMD_ASYNC)) 153 lock_map_acquire_read(&trans->sync_cmd_lockdep_map); 154 155 if (trans->wide_cmd_header && !iwl_cmd_groupid(cmd->id)) 156 cmd->id = DEF_ID(cmd->id); 157 158 ret = trans->ops->send_cmd(trans, cmd); 159 160 if (!(cmd->flags & CMD_ASYNC)) 161 lock_map_release(&trans->sync_cmd_lockdep_map); 162 163 if (WARN_ON((cmd->flags & CMD_WANT_SKB) && !ret && !cmd->resp_pkt)) 164 return -EIO; 165 166 return ret; 167 } 168 IWL_EXPORT_SYMBOL(iwl_trans_send_cmd); 169 170 /* Comparator for struct iwl_hcmd_names. 171 * Used in the binary search over a list of host commands. 172 * 173 * @key: command_id that we're looking for. 174 * @elt: struct iwl_hcmd_names candidate for match. 175 * 176 * @return 0 iff equal. 177 */ 178 static int iwl_hcmd_names_cmp(const void *key, const void *elt) 179 { 180 const struct iwl_hcmd_names *name = elt; 181 u8 cmd1 = *(u8 *)key; 182 u8 cmd2 = name->cmd_id; 183 184 return (cmd1 - cmd2); 185 } 186 187 const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id) 188 { 189 u8 grp, cmd; 190 struct iwl_hcmd_names *ret; 191 const struct iwl_hcmd_arr *arr; 192 size_t size = sizeof(struct iwl_hcmd_names); 193 194 grp = iwl_cmd_groupid(id); 195 cmd = iwl_cmd_opcode(id); 196 197 if (!trans->command_groups || grp >= trans->command_groups_size || 198 !trans->command_groups[grp].arr) 199 return "UNKNOWN"; 200 201 arr = &trans->command_groups[grp]; 202 ret = bsearch(&cmd, arr->arr, arr->size, size, iwl_hcmd_names_cmp); 203 if (!ret) 204 return "UNKNOWN"; 205 return ret->cmd_name; 206 } 207 IWL_EXPORT_SYMBOL(iwl_get_cmd_string); 208 209 int iwl_cmd_groups_verify_sorted(const struct iwl_trans_config *trans) 210 { 211 int i, j; 212 const struct iwl_hcmd_arr *arr; 213 214 for (i = 0; i < trans->command_groups_size; i++) { 215 arr = &trans->command_groups[i]; 216 if (!arr->arr) 217 continue; 218 for (j = 0; j < arr->size - 1; j++) 219 if (arr->arr[j].cmd_id > arr->arr[j + 1].cmd_id) 220 return -1; 221 } 222 return 0; 223 } 224 IWL_EXPORT_SYMBOL(iwl_cmd_groups_verify_sorted); 225