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