| Zhu Yi | bb9f869 | 2009-05-21 21:20:45 +0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Intel Wireless Multicomm 3200 WiFi driver | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | 
|  | 5 | * | 
|  | 6 | * Redistribution and use in source and binary forms, with or without | 
|  | 7 | * modification, are permitted provided that the following conditions | 
|  | 8 | * are met: | 
|  | 9 | * | 
|  | 10 | *   * Redistributions of source code must retain the above copyright | 
|  | 11 | *     notice, this list of conditions and the following disclaimer. | 
|  | 12 | *   * Redistributions in binary form must reproduce the above copyright | 
|  | 13 | *     notice, this list of conditions and the following disclaimer in | 
|  | 14 | *     the documentation and/or other materials provided with the | 
|  | 15 | *     distribution. | 
|  | 16 | *   * Neither the name of Intel Corporation nor the names of its | 
|  | 17 | *     contributors may be used to endorse or promote products derived | 
|  | 18 | *     from this software without specific prior written permission. | 
|  | 19 | * | 
|  | 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
|  | 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
|  | 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
|  | 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
|  | 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
|  | 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
|  | 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | 31 | * | 
|  | 32 | * | 
|  | 33 | * Intel Corporation <ilw@linux.intel.com> | 
|  | 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | 
|  | 35 | * Zhu Yi <yi.zhu@intel.com> | 
|  | 36 | * | 
|  | 37 | */ | 
|  | 38 |  | 
|  | 39 | /* | 
|  | 40 | * Hardware Abstraction Layer for iwm. | 
|  | 41 | * | 
|  | 42 | * This file mostly defines an abstraction API for | 
|  | 43 | * sending various commands to the target. | 
|  | 44 | * | 
|  | 45 | * We have 2 types of commands: wifi and non-wifi ones. | 
|  | 46 | * | 
|  | 47 | * - wifi commands: | 
|  | 48 | *   They are used for sending LMAC and UMAC commands, | 
|  | 49 | *   and thus are the most commonly used ones. | 
|  | 50 | *   There are 2 different wifi command types, the regular | 
|  | 51 | *   one and the LMAC one. The former is used to send | 
|  | 52 | *   UMAC commands (see UMAC_CMD_OPCODE_* from umac.h) | 
|  | 53 | *   while the latter is used for sending commands to the | 
|  | 54 | *   LMAC. If you look at LMAC commands you'll se that they | 
|  | 55 | *   are actually regular iwlwifi target commands encapsulated | 
|  | 56 | *   into a special UMAC command called UMAC passthrough. | 
|  | 57 | *   This is due to the fact the the host talks exclusively | 
|  | 58 | *   to the UMAC and so there needs to be a special UMAC | 
|  | 59 | *   command for talking to the LMAC. | 
|  | 60 | *   This is how a wifi command is layed out: | 
|  | 61 | *    ------------------------ | 
|  | 62 | *   | iwm_udma_out_wifi_hdr  | | 
|  | 63 | *    ------------------------ | 
|  | 64 | *   | SW meta_data (32 bits) | | 
|  | 65 | *    ------------------------ | 
|  | 66 | *   | iwm_dev_cmd_hdr        | | 
|  | 67 | *    ------------------------ | 
|  | 68 | *   | payload                | | 
|  | 69 | *   | ....                   | | 
|  | 70 | * | 
|  | 71 | * - non-wifi, or general commands: | 
|  | 72 | *   Those commands are handled by the device's bootrom, | 
|  | 73 | *   and are typically sent when the UMAC and the LMAC | 
|  | 74 | *   are not yet available. | 
|  | 75 | *    *   This is how a non-wifi command is layed out: | 
|  | 76 | *    --------------------------- | 
|  | 77 | *   | iwm_udma_out_nonwifi_hdr  | | 
|  | 78 | *    --------------------------- | 
|  | 79 | *   | payload                   | | 
|  | 80 | *   | ....                      | | 
|  | 81 |  | 
|  | 82 | * | 
|  | 83 | * All the commands start with a UDMA header, which is | 
|  | 84 | * basically a 32 bits field. The 4 LSB there define | 
|  | 85 | * an opcode that allows the target to differentiate | 
|  | 86 | * between wifi (opcode is 0xf) and non-wifi commands | 
|  | 87 | * (opcode is [0..0xe]). | 
|  | 88 | * | 
|  | 89 | * When a command (wifi or non-wifi) is supposed to receive | 
|  | 90 | * an answer, we queue the command buffer. When we do receive | 
|  | 91 | * a command response from the UMAC, we go through the list | 
|  | 92 | * of pending command, and pass both the command and the answer | 
|  | 93 | * to the rx handler. Each command is sent with a unique | 
|  | 94 | * sequence id, and the answer is sent with the same one. This | 
|  | 95 | * is how we're supposed to match an answer with its command. | 
|  | 96 | * See rx.c:iwm_rx_handle_[non]wifi() and iwm_get_pending_[non]wifi() | 
|  | 97 | * for the implementation details. | 
|  | 98 | */ | 
|  | 99 | #include <linux/kernel.h> | 
|  | 100 | #include <linux/netdevice.h> | 
|  | 101 |  | 
|  | 102 | #include "iwm.h" | 
|  | 103 | #include "bus.h" | 
|  | 104 | #include "hal.h" | 
|  | 105 | #include "umac.h" | 
|  | 106 | #include "debug.h" | 
|  | 107 |  | 
| Zhu Yi | 971ad01 | 2009-07-20 11:47:47 +0800 | [diff] [blame] | 108 | static int iwm_nonwifi_cmd_init(struct iwm_priv *iwm, | 
|  | 109 | struct iwm_nonwifi_cmd *cmd, | 
|  | 110 | struct iwm_udma_nonwifi_cmd *udma_cmd) | 
| Zhu Yi | bb9f869 | 2009-05-21 21:20:45 +0800 | [diff] [blame] | 111 | { | 
|  | 112 | INIT_LIST_HEAD(&cmd->pending); | 
|  | 113 |  | 
|  | 114 | spin_lock(&iwm->cmd_lock); | 
|  | 115 |  | 
|  | 116 | cmd->resp_received = 0; | 
|  | 117 |  | 
|  | 118 | cmd->seq_num = iwm->nonwifi_seq_num; | 
|  | 119 | udma_cmd->seq_num = cpu_to_le16(cmd->seq_num); | 
|  | 120 |  | 
| Zhu Yi | 971ad01 | 2009-07-20 11:47:47 +0800 | [diff] [blame] | 121 | iwm->nonwifi_seq_num++; | 
| Zhu Yi | bb9f869 | 2009-05-21 21:20:45 +0800 | [diff] [blame] | 122 | iwm->nonwifi_seq_num %= UMAC_NONWIFI_SEQ_NUM_MAX; | 
|  | 123 |  | 
|  | 124 | if (udma_cmd->resp) | 
|  | 125 | list_add_tail(&cmd->pending, &iwm->nonwifi_pending_cmd); | 
|  | 126 |  | 
|  | 127 | spin_unlock(&iwm->cmd_lock); | 
|  | 128 |  | 
|  | 129 | cmd->buf.start = cmd->buf.payload; | 
|  | 130 | cmd->buf.len = 0; | 
|  | 131 |  | 
|  | 132 | memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd)); | 
| Zhu Yi | 971ad01 | 2009-07-20 11:47:47 +0800 | [diff] [blame] | 133 |  | 
|  | 134 | return cmd->seq_num; | 
| Zhu Yi | bb9f869 | 2009-05-21 21:20:45 +0800 | [diff] [blame] | 135 | } | 
|  | 136 |  | 
|  | 137 | u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm) | 
|  | 138 | { | 
|  | 139 | u16 seq_num = iwm->wifi_seq_num; | 
|  | 140 |  | 
|  | 141 | iwm->wifi_seq_num++; | 
|  | 142 | iwm->wifi_seq_num %= UMAC_WIFI_SEQ_NUM_MAX; | 
|  | 143 |  | 
|  | 144 | return seq_num; | 
|  | 145 | } | 
|  | 146 |  | 
|  | 147 | static void iwm_wifi_cmd_init(struct iwm_priv *iwm, | 
|  | 148 | struct iwm_wifi_cmd *cmd, | 
|  | 149 | struct iwm_udma_wifi_cmd *udma_cmd, | 
|  | 150 | struct iwm_umac_cmd *umac_cmd, | 
|  | 151 | struct iwm_lmac_cmd *lmac_cmd, | 
|  | 152 | u16 payload_size) | 
|  | 153 | { | 
|  | 154 | INIT_LIST_HEAD(&cmd->pending); | 
|  | 155 |  | 
|  | 156 | spin_lock(&iwm->cmd_lock); | 
|  | 157 |  | 
|  | 158 | cmd->seq_num = iwm_alloc_wifi_cmd_seq(iwm); | 
|  | 159 | umac_cmd->seq_num = cpu_to_le16(cmd->seq_num); | 
|  | 160 |  | 
|  | 161 | if (umac_cmd->resp) | 
|  | 162 | list_add_tail(&cmd->pending, &iwm->wifi_pending_cmd); | 
|  | 163 |  | 
|  | 164 | spin_unlock(&iwm->cmd_lock); | 
|  | 165 |  | 
|  | 166 | cmd->buf.start = cmd->buf.payload; | 
|  | 167 | cmd->buf.len = 0; | 
|  | 168 |  | 
|  | 169 | if (lmac_cmd) { | 
|  | 170 | cmd->buf.start -= sizeof(struct iwm_lmac_hdr); | 
|  | 171 |  | 
|  | 172 | lmac_cmd->seq_num = cpu_to_le16(cmd->seq_num); | 
|  | 173 | lmac_cmd->count = cpu_to_le16(payload_size); | 
|  | 174 |  | 
|  | 175 | memcpy(&cmd->lmac_cmd, lmac_cmd, sizeof(*lmac_cmd)); | 
|  | 176 |  | 
|  | 177 | umac_cmd->count = cpu_to_le16(sizeof(struct iwm_lmac_hdr)); | 
|  | 178 | } else | 
|  | 179 | umac_cmd->count = 0; | 
|  | 180 |  | 
|  | 181 | umac_cmd->count = cpu_to_le16(payload_size + | 
|  | 182 | le16_to_cpu(umac_cmd->count)); | 
|  | 183 | udma_cmd->count = cpu_to_le16(sizeof(struct iwm_umac_fw_cmd_hdr) + | 
|  | 184 | le16_to_cpu(umac_cmd->count)); | 
|  | 185 |  | 
|  | 186 | memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd)); | 
|  | 187 | memcpy(&cmd->umac_cmd, umac_cmd, sizeof(*umac_cmd)); | 
|  | 188 | } | 
|  | 189 |  | 
|  | 190 | void iwm_cmd_flush(struct iwm_priv *iwm) | 
|  | 191 | { | 
|  | 192 | struct iwm_wifi_cmd *wcmd, *wnext; | 
|  | 193 | struct iwm_nonwifi_cmd *nwcmd, *nwnext; | 
|  | 194 |  | 
|  | 195 | list_for_each_entry_safe(wcmd, wnext, &iwm->wifi_pending_cmd, pending) { | 
|  | 196 | list_del(&wcmd->pending); | 
|  | 197 | kfree(wcmd); | 
|  | 198 | } | 
|  | 199 |  | 
|  | 200 | list_for_each_entry_safe(nwcmd, nwnext, &iwm->nonwifi_pending_cmd, | 
|  | 201 | pending) { | 
|  | 202 | list_del(&nwcmd->pending); | 
|  | 203 | kfree(nwcmd); | 
|  | 204 | } | 
|  | 205 | } | 
|  | 206 |  | 
|  | 207 | struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm, u16 seq_num) | 
|  | 208 | { | 
|  | 209 | struct iwm_wifi_cmd *cmd, *next; | 
|  | 210 |  | 
|  | 211 | list_for_each_entry_safe(cmd, next, &iwm->wifi_pending_cmd, pending) | 
|  | 212 | if (cmd->seq_num == seq_num) { | 
|  | 213 | list_del(&cmd->pending); | 
|  | 214 | return cmd; | 
|  | 215 | } | 
|  | 216 |  | 
|  | 217 | return NULL; | 
|  | 218 | } | 
|  | 219 |  | 
|  | 220 | struct iwm_nonwifi_cmd * | 
|  | 221 | iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm, u8 seq_num, u8 cmd_opcode) | 
|  | 222 | { | 
|  | 223 | struct iwm_nonwifi_cmd *cmd, *next; | 
|  | 224 |  | 
|  | 225 | list_for_each_entry_safe(cmd, next, &iwm->nonwifi_pending_cmd, pending) | 
|  | 226 | if ((cmd->seq_num == seq_num) && | 
|  | 227 | (cmd->udma_cmd.opcode == cmd_opcode) && | 
|  | 228 | (cmd->resp_received)) { | 
|  | 229 | list_del(&cmd->pending); | 
|  | 230 | return cmd; | 
|  | 231 | } | 
|  | 232 |  | 
|  | 233 | return NULL; | 
|  | 234 | } | 
|  | 235 |  | 
|  | 236 | static void iwm_build_udma_nonwifi_hdr(struct iwm_priv *iwm, | 
|  | 237 | struct iwm_udma_out_nonwifi_hdr *hdr, | 
|  | 238 | struct iwm_udma_nonwifi_cmd *cmd) | 
|  | 239 | { | 
|  | 240 | memset(hdr, 0, sizeof(*hdr)); | 
|  | 241 |  | 
|  | 242 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, cmd->opcode); | 
|  | 243 | SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_RESP, cmd->resp); | 
|  | 244 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, 1); | 
|  | 245 | SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW, | 
|  | 246 | cmd->handle_by_hw); | 
|  | 247 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE); | 
|  | 248 | SET_VAL32(hdr->cmd, UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM, | 
|  | 249 | le16_to_cpu(cmd->seq_num)); | 
|  | 250 |  | 
|  | 251 | hdr->addr = cmd->addr; | 
|  | 252 | hdr->op1_sz = cmd->op1_sz; | 
|  | 253 | hdr->op2 = cmd->op2; | 
|  | 254 | } | 
|  | 255 |  | 
|  | 256 | static int iwm_send_udma_nonwifi_cmd(struct iwm_priv *iwm, | 
|  | 257 | struct iwm_nonwifi_cmd *cmd) | 
|  | 258 | { | 
|  | 259 | struct iwm_udma_out_nonwifi_hdr *udma_hdr; | 
|  | 260 | struct iwm_nonwifi_cmd_buff *buf; | 
|  | 261 | struct iwm_udma_nonwifi_cmd *udma_cmd = &cmd->udma_cmd; | 
|  | 262 |  | 
|  | 263 | buf = &cmd->buf; | 
|  | 264 |  | 
|  | 265 | buf->start -= sizeof(struct iwm_umac_nonwifi_out_hdr); | 
|  | 266 | buf->len += sizeof(struct iwm_umac_nonwifi_out_hdr); | 
|  | 267 |  | 
|  | 268 | udma_hdr = (struct iwm_udma_out_nonwifi_hdr *)(buf->start); | 
|  | 269 |  | 
|  | 270 | iwm_build_udma_nonwifi_hdr(iwm, udma_hdr, udma_cmd); | 
|  | 271 |  | 
|  | 272 | IWM_DBG_CMD(iwm, DBG, | 
|  | 273 | "Send UDMA nonwifi cmd: opcode = 0x%x, resp = 0x%x, " | 
|  | 274 | "hw = 0x%x, seqnum = %d, addr = 0x%x, op1_sz = 0x%x, " | 
|  | 275 | "op2 = 0x%x\n", udma_cmd->opcode, udma_cmd->resp, | 
|  | 276 | udma_cmd->handle_by_hw, cmd->seq_num, udma_cmd->addr, | 
|  | 277 | udma_cmd->op1_sz, udma_cmd->op2); | 
|  | 278 |  | 
|  | 279 | return iwm_bus_send_chunk(iwm, buf->start, buf->len); | 
|  | 280 | } | 
|  | 281 |  | 
|  | 282 | void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop) | 
|  | 283 | { | 
|  | 284 | struct iwm_udma_out_wifi_hdr *hdr = (struct iwm_udma_out_wifi_hdr *)buf; | 
|  | 285 |  | 
|  | 286 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, eop); | 
|  | 287 | } | 
|  | 288 |  | 
|  | 289 | void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm, | 
|  | 290 | struct iwm_udma_out_wifi_hdr *hdr, | 
|  | 291 | struct iwm_udma_wifi_cmd *cmd) | 
|  | 292 | { | 
|  | 293 | memset(hdr, 0, sizeof(*hdr)); | 
|  | 294 |  | 
|  | 295 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, UMAC_HDI_OUT_OPCODE_WIFI); | 
|  | 296 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, cmd->eop); | 
|  | 297 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE); | 
|  | 298 |  | 
|  | 299 | SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_BYTE_COUNT, | 
|  | 300 | le16_to_cpu(cmd->count)); | 
|  | 301 | SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_CREDIT_GRP, cmd->credit_group); | 
|  | 302 | SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_RATID, cmd->ra_tid); | 
|  | 303 | SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_LMAC_OFFSET, cmd->lmac_offset); | 
|  | 304 | } | 
|  | 305 |  | 
|  | 306 | void iwm_build_umac_hdr(struct iwm_priv *iwm, | 
|  | 307 | struct iwm_umac_fw_cmd_hdr *hdr, | 
|  | 308 | struct iwm_umac_cmd *cmd) | 
|  | 309 | { | 
|  | 310 | memset(hdr, 0, sizeof(*hdr)); | 
|  | 311 |  | 
|  | 312 | SET_VAL32(hdr->meta_data, UMAC_FW_CMD_BYTE_COUNT, | 
|  | 313 | le16_to_cpu(cmd->count)); | 
|  | 314 | SET_VAL32(hdr->meta_data, UMAC_FW_CMD_TX_STA_COLOR, cmd->color); | 
|  | 315 | SET_VAL8(hdr->cmd.flags, UMAC_DEV_CMD_FLAGS_RESP_REQ, cmd->resp); | 
|  | 316 |  | 
|  | 317 | hdr->cmd.cmd = cmd->id; | 
|  | 318 | hdr->cmd.seq_num = cmd->seq_num; | 
|  | 319 | } | 
|  | 320 |  | 
|  | 321 | static int iwm_send_udma_wifi_cmd(struct iwm_priv *iwm, | 
|  | 322 | struct iwm_wifi_cmd *cmd) | 
|  | 323 | { | 
|  | 324 | struct iwm_umac_wifi_out_hdr *umac_hdr; | 
|  | 325 | struct iwm_wifi_cmd_buff *buf; | 
|  | 326 | struct iwm_udma_wifi_cmd *udma_cmd = &cmd->udma_cmd; | 
|  | 327 | struct iwm_umac_cmd *umac_cmd = &cmd->umac_cmd; | 
|  | 328 | int ret; | 
|  | 329 |  | 
|  | 330 | buf = &cmd->buf; | 
|  | 331 |  | 
|  | 332 | buf->start -= sizeof(struct iwm_umac_wifi_out_hdr); | 
|  | 333 | buf->len += sizeof(struct iwm_umac_wifi_out_hdr); | 
|  | 334 |  | 
|  | 335 | umac_hdr = (struct iwm_umac_wifi_out_hdr *)(buf->start); | 
|  | 336 |  | 
|  | 337 | iwm_build_udma_wifi_hdr(iwm, &umac_hdr->hw_hdr, udma_cmd); | 
|  | 338 | iwm_build_umac_hdr(iwm, &umac_hdr->sw_hdr, umac_cmd); | 
|  | 339 |  | 
|  | 340 | IWM_DBG_CMD(iwm, DBG, | 
|  | 341 | "Send UDMA wifi cmd: opcode = 0x%x, UMAC opcode = 0x%x, " | 
|  | 342 | "eop = 0x%x, count = 0x%x, credit_group = 0x%x, " | 
|  | 343 | "ra_tid = 0x%x, lmac_offset = 0x%x, seqnum = %d\n", | 
|  | 344 | UMAC_HDI_OUT_OPCODE_WIFI, umac_cmd->id, | 
|  | 345 | udma_cmd->eop, udma_cmd->count, udma_cmd->credit_group, | 
|  | 346 | udma_cmd->ra_tid, udma_cmd->lmac_offset, cmd->seq_num); | 
|  | 347 |  | 
|  | 348 | if (umac_cmd->id == UMAC_CMD_OPCODE_WIFI_PASS_THROUGH) | 
|  | 349 | IWM_DBG_CMD(iwm, DBG, "\tLMAC opcode: 0x%x\n", | 
|  | 350 | cmd->lmac_cmd.id); | 
|  | 351 |  | 
|  | 352 | ret = iwm_tx_credit_alloc(iwm, udma_cmd->credit_group, buf->len); | 
|  | 353 |  | 
|  | 354 | /* We keep sending UMAC reset regardless of the command credits. | 
|  | 355 | * The UMAC is supposed to be reset anyway and the Tx credits are | 
|  | 356 | * reinitialized afterwards. If we are lucky, the reset could | 
|  | 357 | * still be done even though we have run out of credits for the | 
|  | 358 | * command pool at this moment.*/ | 
|  | 359 | if (ret && (umac_cmd->id != UMAC_CMD_OPCODE_RESET)) { | 
|  | 360 | IWM_DBG_TX(iwm, DBG, "Failed to alloc tx credit for cmd %d\n", | 
|  | 361 | umac_cmd->id); | 
|  | 362 | return ret; | 
|  | 363 | } | 
|  | 364 |  | 
|  | 365 | return iwm_bus_send_chunk(iwm, buf->start, buf->len); | 
|  | 366 | } | 
|  | 367 |  | 
|  | 368 | /* target_cmd a.k.a udma_nonwifi_cmd can be sent when UMAC is not available */ | 
|  | 369 | int iwm_hal_send_target_cmd(struct iwm_priv *iwm, | 
|  | 370 | struct iwm_udma_nonwifi_cmd *udma_cmd, | 
|  | 371 | const void *payload) | 
|  | 372 | { | 
|  | 373 | struct iwm_nonwifi_cmd *cmd; | 
| Zhu Yi | 971ad01 | 2009-07-20 11:47:47 +0800 | [diff] [blame] | 374 | int ret, seq_num; | 
| Zhu Yi | bb9f869 | 2009-05-21 21:20:45 +0800 | [diff] [blame] | 375 |  | 
|  | 376 | cmd = kzalloc(sizeof(struct iwm_nonwifi_cmd), GFP_KERNEL); | 
|  | 377 | if (!cmd) { | 
|  | 378 | IWM_ERR(iwm, "Couldn't alloc memory for hal cmd\n"); | 
|  | 379 | return -ENOMEM; | 
|  | 380 | } | 
|  | 381 |  | 
| Zhu Yi | 971ad01 | 2009-07-20 11:47:47 +0800 | [diff] [blame] | 382 | seq_num = iwm_nonwifi_cmd_init(iwm, cmd, udma_cmd); | 
| Zhu Yi | bb9f869 | 2009-05-21 21:20:45 +0800 | [diff] [blame] | 383 |  | 
|  | 384 | if (cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE || | 
|  | 385 | cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT) { | 
|  | 386 | cmd->buf.len = le32_to_cpu(cmd->udma_cmd.op1_sz); | 
|  | 387 | memcpy(&cmd->buf.payload, payload, cmd->buf.len); | 
|  | 388 | } | 
|  | 389 |  | 
|  | 390 | ret = iwm_send_udma_nonwifi_cmd(iwm, cmd); | 
|  | 391 |  | 
|  | 392 | if (!udma_cmd->resp) | 
|  | 393 | kfree(cmd); | 
|  | 394 |  | 
|  | 395 | if (ret < 0) | 
|  | 396 | return ret; | 
|  | 397 |  | 
| Zhu Yi | 971ad01 | 2009-07-20 11:47:47 +0800 | [diff] [blame] | 398 | return seq_num; | 
| Zhu Yi | bb9f869 | 2009-05-21 21:20:45 +0800 | [diff] [blame] | 399 | } | 
|  | 400 |  | 
|  | 401 | static void iwm_build_lmac_hdr(struct iwm_priv *iwm, struct iwm_lmac_hdr *hdr, | 
|  | 402 | struct iwm_lmac_cmd *cmd) | 
|  | 403 | { | 
|  | 404 | memset(hdr, 0, sizeof(*hdr)); | 
|  | 405 |  | 
|  | 406 | hdr->id = cmd->id; | 
|  | 407 | hdr->flags = 0; /* Is this ever used? */ | 
|  | 408 | hdr->seq_num = cmd->seq_num; | 
|  | 409 | } | 
|  | 410 |  | 
|  | 411 | /* | 
|  | 412 | * iwm_hal_send_host_cmd(): sends commands to the UMAC or the LMAC. | 
|  | 413 | * Sending command to the LMAC is equivalent to sending a | 
|  | 414 | * regular UMAC command with the LMAC passtrough or the LMAC | 
|  | 415 | * wrapper UMAC command IDs. | 
|  | 416 | */ | 
|  | 417 | int iwm_hal_send_host_cmd(struct iwm_priv *iwm, | 
|  | 418 | struct iwm_udma_wifi_cmd *udma_cmd, | 
|  | 419 | struct iwm_umac_cmd *umac_cmd, | 
|  | 420 | struct iwm_lmac_cmd *lmac_cmd, | 
|  | 421 | const void *payload, u16 payload_size) | 
|  | 422 | { | 
|  | 423 | struct iwm_wifi_cmd *cmd; | 
|  | 424 | struct iwm_lmac_hdr *hdr; | 
|  | 425 | int lmac_hdr_len = 0; | 
|  | 426 | int ret; | 
|  | 427 |  | 
|  | 428 | cmd = kzalloc(sizeof(struct iwm_wifi_cmd), GFP_KERNEL); | 
|  | 429 | if (!cmd) { | 
|  | 430 | IWM_ERR(iwm, "Couldn't alloc memory for wifi hal cmd\n"); | 
|  | 431 | return -ENOMEM; | 
|  | 432 | } | 
|  | 433 |  | 
|  | 434 | iwm_wifi_cmd_init(iwm, cmd, udma_cmd, umac_cmd, lmac_cmd, payload_size); | 
|  | 435 |  | 
|  | 436 | if (lmac_cmd) { | 
|  | 437 | hdr = (struct iwm_lmac_hdr *)(cmd->buf.start); | 
|  | 438 |  | 
|  | 439 | iwm_build_lmac_hdr(iwm, hdr, &cmd->lmac_cmd); | 
|  | 440 | lmac_hdr_len = sizeof(struct iwm_lmac_hdr); | 
|  | 441 | } | 
|  | 442 |  | 
|  | 443 | memcpy(cmd->buf.payload, payload, payload_size); | 
|  | 444 | cmd->buf.len = le16_to_cpu(umac_cmd->count); | 
|  | 445 |  | 
|  | 446 | ret = iwm_send_udma_wifi_cmd(iwm, cmd); | 
|  | 447 |  | 
|  | 448 | /* We free the cmd if we're not expecting any response */ | 
|  | 449 | if (!umac_cmd->resp) | 
|  | 450 | kfree(cmd); | 
|  | 451 | return ret; | 
|  | 452 | } | 
|  | 453 |  | 
|  | 454 | /* | 
|  | 455 | * iwm_hal_send_umac_cmd(): This is a special case for | 
|  | 456 | * iwm_hal_send_host_cmd() to send direct UMAC cmd (without | 
|  | 457 | * LMAC involved). | 
|  | 458 | */ | 
|  | 459 | int iwm_hal_send_umac_cmd(struct iwm_priv *iwm, | 
|  | 460 | struct iwm_udma_wifi_cmd *udma_cmd, | 
|  | 461 | struct iwm_umac_cmd *umac_cmd, | 
|  | 462 | const void *payload, u16 payload_size) | 
|  | 463 | { | 
|  | 464 | return iwm_hal_send_host_cmd(iwm, udma_cmd, umac_cmd, NULL, | 
|  | 465 | payload, payload_size); | 
|  | 466 | } |