| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 1 | /****************************************************************************** | 
|  | 2 | * | 
|  | 3 | * Copyright(c) 2003 - 2008 Intel Corporation. All rights reserved. | 
|  | 4 | * | 
|  | 5 | * Portions of this file are derived from the ipw3945 project, as well | 
|  | 6 | * as portions of the ieee80211 subsystem header files. | 
|  | 7 | * | 
|  | 8 | * This program is free software; you can redistribute it and/or modify it | 
|  | 9 | * under the terms of version 2 of the GNU General Public License as | 
|  | 10 | * published by the Free Software Foundation. | 
|  | 11 | * | 
|  | 12 | * This program is distributed in the hope that it will be useful, but WITHOUT | 
|  | 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|  | 14 | * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
|  | 15 | * more details. | 
|  | 16 | * | 
|  | 17 | * You should have received a copy of the GNU General Public License along with | 
|  | 18 | * this program; if not, write to the Free Software Foundation, Inc., | 
|  | 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA | 
|  | 20 | * | 
|  | 21 | * The full GNU General Public License is included in this distribution in the | 
|  | 22 | * file called LICENSE. | 
|  | 23 | * | 
|  | 24 | * Contact Information: | 
|  | 25 | * James P. Ketrenos <ipw2100-admin@linux.intel.com> | 
|  | 26 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | 
|  | 27 | * | 
|  | 28 | *****************************************************************************/ | 
|  | 29 |  | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 30 | #include <linux/etherdevice.h> | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 31 | #include <net/mac80211.h> | 
|  | 32 | #include "iwl-eeprom.h" | 
|  | 33 | #include "iwl-dev.h" | 
|  | 34 | #include "iwl-core.h" | 
|  | 35 | #include "iwl-sta.h" | 
|  | 36 | #include "iwl-io.h" | 
|  | 37 | #include "iwl-helpers.h" | 
|  | 38 |  | 
| Tomas Winkler | 30e553e | 2008-05-29 16:35:16 +0800 | [diff] [blame] | 39 | static const u16 default_tid_to_tx_fifo[] = { | 
|  | 40 | IWL_TX_FIFO_AC1, | 
|  | 41 | IWL_TX_FIFO_AC0, | 
|  | 42 | IWL_TX_FIFO_AC0, | 
|  | 43 | IWL_TX_FIFO_AC1, | 
|  | 44 | IWL_TX_FIFO_AC2, | 
|  | 45 | IWL_TX_FIFO_AC2, | 
|  | 46 | IWL_TX_FIFO_AC3, | 
|  | 47 | IWL_TX_FIFO_AC3, | 
|  | 48 | IWL_TX_FIFO_NONE, | 
|  | 49 | IWL_TX_FIFO_NONE, | 
|  | 50 | IWL_TX_FIFO_NONE, | 
|  | 51 | IWL_TX_FIFO_NONE, | 
|  | 52 | IWL_TX_FIFO_NONE, | 
|  | 53 | IWL_TX_FIFO_NONE, | 
|  | 54 | IWL_TX_FIFO_NONE, | 
|  | 55 | IWL_TX_FIFO_NONE, | 
|  | 56 | IWL_TX_FIFO_AC3 | 
|  | 57 | }; | 
|  | 58 |  | 
| Tomas Winkler | 30e553e | 2008-05-29 16:35:16 +0800 | [diff] [blame] | 59 |  | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 60 | /** | 
|  | 61 | * iwl_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] | 
|  | 62 | * | 
|  | 63 | * Does NOT advance any TFD circular buffer read/write indexes | 
|  | 64 | * Does NOT free the TFD itself (which is within circular buffer) | 
|  | 65 | */ | 
| Ron Rindjunsky | 1646690 | 2008-05-05 10:22:50 +0800 | [diff] [blame] | 66 | int iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 67 | { | 
|  | 68 | struct iwl_tfd_frame *bd_tmp = (struct iwl_tfd_frame *)&txq->bd[0]; | 
|  | 69 | struct iwl_tfd_frame *bd = &bd_tmp[txq->q.read_ptr]; | 
|  | 70 | struct pci_dev *dev = priv->pci_dev; | 
|  | 71 | int i; | 
|  | 72 | int counter = 0; | 
|  | 73 | int index, is_odd; | 
|  | 74 |  | 
|  | 75 | /* Host command buffers stay mapped in memory, nothing to clean */ | 
|  | 76 | if (txq->q.id == IWL_CMD_QUEUE_NUM) | 
|  | 77 | return 0; | 
|  | 78 |  | 
|  | 79 | /* Sanity check on number of chunks */ | 
|  | 80 | counter = IWL_GET_BITS(*bd, num_tbs); | 
|  | 81 | if (counter > MAX_NUM_OF_TBS) { | 
|  | 82 | IWL_ERROR("Too many chunks: %i\n", counter); | 
|  | 83 | /* @todo issue fatal error, it is quite serious situation */ | 
|  | 84 | return 0; | 
|  | 85 | } | 
|  | 86 |  | 
|  | 87 | /* Unmap chunks, if any. | 
|  | 88 | * TFD info for odd chunks is different format than for even chunks. */ | 
|  | 89 | for (i = 0; i < counter; i++) { | 
|  | 90 | index = i / 2; | 
|  | 91 | is_odd = i & 0x1; | 
|  | 92 |  | 
|  | 93 | if (is_odd) | 
|  | 94 | pci_unmap_single( | 
|  | 95 | dev, | 
|  | 96 | IWL_GET_BITS(bd->pa[index], tb2_addr_lo16) | | 
|  | 97 | (IWL_GET_BITS(bd->pa[index], | 
|  | 98 | tb2_addr_hi20) << 16), | 
|  | 99 | IWL_GET_BITS(bd->pa[index], tb2_len), | 
|  | 100 | PCI_DMA_TODEVICE); | 
|  | 101 |  | 
|  | 102 | else if (i > 0) | 
|  | 103 | pci_unmap_single(dev, | 
|  | 104 | le32_to_cpu(bd->pa[index].tb1_addr), | 
|  | 105 | IWL_GET_BITS(bd->pa[index], tb1_len), | 
|  | 106 | PCI_DMA_TODEVICE); | 
|  | 107 |  | 
|  | 108 | /* Free SKB, if any, for this chunk */ | 
|  | 109 | if (txq->txb[txq->q.read_ptr].skb[i]) { | 
|  | 110 | struct sk_buff *skb = txq->txb[txq->q.read_ptr].skb[i]; | 
|  | 111 |  | 
|  | 112 | dev_kfree_skb(skb); | 
|  | 113 | txq->txb[txq->q.read_ptr].skb[i] = NULL; | 
|  | 114 | } | 
|  | 115 | } | 
|  | 116 | return 0; | 
|  | 117 | } | 
|  | 118 | EXPORT_SYMBOL(iwl_hw_txq_free_tfd); | 
|  | 119 |  | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 120 |  | 
|  | 121 | int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr, | 
|  | 122 | dma_addr_t addr, u16 len) | 
|  | 123 | { | 
|  | 124 | int index, is_odd; | 
|  | 125 | struct iwl_tfd_frame *tfd = ptr; | 
|  | 126 | u32 num_tbs = IWL_GET_BITS(*tfd, num_tbs); | 
|  | 127 |  | 
|  | 128 | /* Each TFD can point to a maximum 20 Tx buffers */ | 
|  | 129 | if ((num_tbs >= MAX_NUM_OF_TBS) || (num_tbs < 0)) { | 
|  | 130 | IWL_ERROR("Error can not send more than %d chunks\n", | 
|  | 131 | MAX_NUM_OF_TBS); | 
|  | 132 | return -EINVAL; | 
|  | 133 | } | 
|  | 134 |  | 
|  | 135 | index = num_tbs / 2; | 
|  | 136 | is_odd = num_tbs & 0x1; | 
|  | 137 |  | 
|  | 138 | if (!is_odd) { | 
|  | 139 | tfd->pa[index].tb1_addr = cpu_to_le32(addr); | 
|  | 140 | IWL_SET_BITS(tfd->pa[index], tb1_addr_hi, | 
|  | 141 | iwl_get_dma_hi_address(addr)); | 
|  | 142 | IWL_SET_BITS(tfd->pa[index], tb1_len, len); | 
|  | 143 | } else { | 
|  | 144 | IWL_SET_BITS(tfd->pa[index], tb2_addr_lo16, | 
|  | 145 | (u32) (addr & 0xffff)); | 
|  | 146 | IWL_SET_BITS(tfd->pa[index], tb2_addr_hi20, addr >> 16); | 
|  | 147 | IWL_SET_BITS(tfd->pa[index], tb2_len, len); | 
|  | 148 | } | 
|  | 149 |  | 
|  | 150 | IWL_SET_BITS(*tfd, num_tbs, num_tbs + 1); | 
|  | 151 |  | 
|  | 152 | return 0; | 
|  | 153 | } | 
|  | 154 | EXPORT_SYMBOL(iwl_hw_txq_attach_buf_to_tfd); | 
|  | 155 |  | 
|  | 156 | /** | 
|  | 157 | * iwl_txq_update_write_ptr - Send new write index to hardware | 
|  | 158 | */ | 
|  | 159 | int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq) | 
|  | 160 | { | 
|  | 161 | u32 reg = 0; | 
|  | 162 | int ret = 0; | 
|  | 163 | int txq_id = txq->q.id; | 
|  | 164 |  | 
|  | 165 | if (txq->need_update == 0) | 
|  | 166 | return ret; | 
|  | 167 |  | 
|  | 168 | /* if we're trying to save power */ | 
|  | 169 | if (test_bit(STATUS_POWER_PMI, &priv->status)) { | 
|  | 170 | /* wake up nic if it's powered down ... | 
|  | 171 | * uCode will wake up, and interrupt us again, so next | 
|  | 172 | * time we'll skip this part. */ | 
|  | 173 | reg = iwl_read32(priv, CSR_UCODE_DRV_GP1); | 
|  | 174 |  | 
|  | 175 | if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { | 
|  | 176 | IWL_DEBUG_INFO("Requesting wakeup, GP1 = 0x%x\n", reg); | 
|  | 177 | iwl_set_bit(priv, CSR_GP_CNTRL, | 
|  | 178 | CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); | 
|  | 179 | return ret; | 
|  | 180 | } | 
|  | 181 |  | 
|  | 182 | /* restore this queue's parameters in nic hardware. */ | 
|  | 183 | ret = iwl_grab_nic_access(priv); | 
|  | 184 | if (ret) | 
|  | 185 | return ret; | 
|  | 186 | iwl_write_direct32(priv, HBUS_TARG_WRPTR, | 
|  | 187 | txq->q.write_ptr | (txq_id << 8)); | 
|  | 188 | iwl_release_nic_access(priv); | 
|  | 189 |  | 
|  | 190 | /* else not in power-save mode, uCode will never sleep when we're | 
|  | 191 | * trying to tx (during RFKILL, we're not trying to tx). */ | 
|  | 192 | } else | 
|  | 193 | iwl_write32(priv, HBUS_TARG_WRPTR, | 
|  | 194 | txq->q.write_ptr | (txq_id << 8)); | 
|  | 195 |  | 
|  | 196 | txq->need_update = 0; | 
|  | 197 |  | 
|  | 198 | return ret; | 
|  | 199 | } | 
|  | 200 | EXPORT_SYMBOL(iwl_txq_update_write_ptr); | 
|  | 201 |  | 
|  | 202 |  | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 203 | /** | 
|  | 204 | * iwl_tx_queue_free - Deallocate DMA queue. | 
|  | 205 | * @txq: Transmit queue to deallocate. | 
|  | 206 | * | 
|  | 207 | * Empty queue by removing and destroying all BD's. | 
|  | 208 | * Free all buffers. | 
|  | 209 | * 0-fill, but do not free "txq" descriptor structure. | 
|  | 210 | */ | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 211 | static void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id) | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 212 | { | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 213 | struct iwl_tx_queue *txq = &priv->txq[txq_id]; | 
| Tomas Winkler | 443cfd4 | 2008-05-15 13:53:57 +0800 | [diff] [blame] | 214 | struct iwl_queue *q = &txq->q; | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 215 | struct pci_dev *dev = priv->pci_dev; | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 216 | int i, slots_num, len; | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 217 |  | 
|  | 218 | if (q->n_bd == 0) | 
|  | 219 | return; | 
|  | 220 |  | 
|  | 221 | /* first, empty all BD's */ | 
|  | 222 | for (; q->write_ptr != q->read_ptr; | 
|  | 223 | q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) | 
|  | 224 | iwl_hw_txq_free_tfd(priv, txq); | 
|  | 225 |  | 
|  | 226 | len = sizeof(struct iwl_cmd) * q->n_window; | 
|  | 227 | if (q->id == IWL_CMD_QUEUE_NUM) | 
|  | 228 | len += IWL_MAX_SCAN_SIZE; | 
|  | 229 |  | 
|  | 230 | /* De-alloc array of command/tx buffers */ | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 231 | slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ? | 
|  | 232 | TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; | 
|  | 233 | for (i = 0; i < slots_num; i++) | 
|  | 234 | kfree(txq->cmd[i]); | 
|  | 235 | if (txq_id == IWL_CMD_QUEUE_NUM) | 
|  | 236 | kfree(txq->cmd[slots_num]); | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 237 |  | 
|  | 238 | /* De-alloc circular buffer of TFDs */ | 
|  | 239 | if (txq->q.n_bd) | 
|  | 240 | pci_free_consistent(dev, sizeof(struct iwl_tfd_frame) * | 
|  | 241 | txq->q.n_bd, txq->bd, txq->q.dma_addr); | 
|  | 242 |  | 
|  | 243 | /* De-alloc array of per-TFD driver data */ | 
|  | 244 | kfree(txq->txb); | 
|  | 245 | txq->txb = NULL; | 
|  | 246 |  | 
|  | 247 | /* 0-fill queue descriptor structure */ | 
|  | 248 | memset(txq, 0, sizeof(*txq)); | 
|  | 249 | } | 
|  | 250 |  | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 251 | /*************** DMA-QUEUE-GENERAL-FUNCTIONS  ***** | 
|  | 252 | * DMA services | 
|  | 253 | * | 
|  | 254 | * Theory of operation | 
|  | 255 | * | 
|  | 256 | * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer | 
|  | 257 | * of buffer descriptors, each of which points to one or more data buffers for | 
|  | 258 | * the device to read from or fill.  Driver and device exchange status of each | 
|  | 259 | * queue via "read" and "write" pointers.  Driver keeps minimum of 2 empty | 
|  | 260 | * entries in each circular buffer, to protect against confusing empty and full | 
|  | 261 | * queue states. | 
|  | 262 | * | 
|  | 263 | * The device reads or writes the data in the queues via the device's several | 
|  | 264 | * DMA/FIFO channels.  Each queue is mapped to a single DMA channel. | 
|  | 265 | * | 
|  | 266 | * For Tx queue, there are low mark and high mark limits. If, after queuing | 
|  | 267 | * the packet for Tx, free space become < low mark, Tx queue stopped. When | 
|  | 268 | * reclaiming packets (on 'tx done IRQ), if free space become > high mark, | 
|  | 269 | * Tx queue resumed. | 
|  | 270 | * | 
|  | 271 | * See more detailed info in iwl-4965-hw.h. | 
|  | 272 | ***************************************************/ | 
|  | 273 |  | 
|  | 274 | int iwl_queue_space(const struct iwl_queue *q) | 
|  | 275 | { | 
|  | 276 | int s = q->read_ptr - q->write_ptr; | 
|  | 277 |  | 
|  | 278 | if (q->read_ptr > q->write_ptr) | 
|  | 279 | s -= q->n_bd; | 
|  | 280 |  | 
|  | 281 | if (s <= 0) | 
|  | 282 | s += q->n_window; | 
|  | 283 | /* keep some reserve to not confuse empty and full situations */ | 
|  | 284 | s -= 2; | 
|  | 285 | if (s < 0) | 
|  | 286 | s = 0; | 
|  | 287 | return s; | 
|  | 288 | } | 
|  | 289 | EXPORT_SYMBOL(iwl_queue_space); | 
|  | 290 |  | 
|  | 291 |  | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 292 | /** | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 293 | * iwl_queue_init - Initialize queue's high/low-water and read/write indexes | 
|  | 294 | */ | 
| Tomas Winkler | 443cfd4 | 2008-05-15 13:53:57 +0800 | [diff] [blame] | 295 | static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q, | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 296 | int count, int slots_num, u32 id) | 
|  | 297 | { | 
|  | 298 | q->n_bd = count; | 
|  | 299 | q->n_window = slots_num; | 
|  | 300 | q->id = id; | 
|  | 301 |  | 
|  | 302 | /* count must be power-of-two size, otherwise iwl_queue_inc_wrap | 
|  | 303 | * and iwl_queue_dec_wrap are broken. */ | 
|  | 304 | BUG_ON(!is_power_of_2(count)); | 
|  | 305 |  | 
|  | 306 | /* slots_num must be power-of-two size, otherwise | 
|  | 307 | * get_cmd_index is broken. */ | 
|  | 308 | BUG_ON(!is_power_of_2(slots_num)); | 
|  | 309 |  | 
|  | 310 | q->low_mark = q->n_window / 4; | 
|  | 311 | if (q->low_mark < 4) | 
|  | 312 | q->low_mark = 4; | 
|  | 313 |  | 
|  | 314 | q->high_mark = q->n_window / 8; | 
|  | 315 | if (q->high_mark < 2) | 
|  | 316 | q->high_mark = 2; | 
|  | 317 |  | 
|  | 318 | q->write_ptr = q->read_ptr = 0; | 
|  | 319 |  | 
|  | 320 | return 0; | 
|  | 321 | } | 
|  | 322 |  | 
|  | 323 | /** | 
|  | 324 | * iwl_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue | 
|  | 325 | */ | 
|  | 326 | static int iwl_tx_queue_alloc(struct iwl_priv *priv, | 
| Ron Rindjunsky | 1646690 | 2008-05-05 10:22:50 +0800 | [diff] [blame] | 327 | struct iwl_tx_queue *txq, u32 id) | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 328 | { | 
|  | 329 | struct pci_dev *dev = priv->pci_dev; | 
|  | 330 |  | 
|  | 331 | /* Driver private data, only for Tx (not command) queues, | 
|  | 332 | * not shared with device. */ | 
|  | 333 | if (id != IWL_CMD_QUEUE_NUM) { | 
|  | 334 | txq->txb = kmalloc(sizeof(txq->txb[0]) * | 
|  | 335 | TFD_QUEUE_SIZE_MAX, GFP_KERNEL); | 
|  | 336 | if (!txq->txb) { | 
|  | 337 | IWL_ERROR("kmalloc for auxiliary BD " | 
|  | 338 | "structures failed\n"); | 
|  | 339 | goto error; | 
|  | 340 | } | 
|  | 341 | } else | 
|  | 342 | txq->txb = NULL; | 
|  | 343 |  | 
|  | 344 | /* Circular buffer of transmit frame descriptors (TFDs), | 
|  | 345 | * shared with device */ | 
|  | 346 | txq->bd = pci_alloc_consistent(dev, | 
|  | 347 | sizeof(txq->bd[0]) * TFD_QUEUE_SIZE_MAX, | 
|  | 348 | &txq->q.dma_addr); | 
|  | 349 |  | 
|  | 350 | if (!txq->bd) { | 
|  | 351 | IWL_ERROR("pci_alloc_consistent(%zd) failed\n", | 
|  | 352 | sizeof(txq->bd[0]) * TFD_QUEUE_SIZE_MAX); | 
|  | 353 | goto error; | 
|  | 354 | } | 
|  | 355 | txq->q.id = id; | 
|  | 356 |  | 
|  | 357 | return 0; | 
|  | 358 |  | 
|  | 359 | error: | 
|  | 360 | kfree(txq->txb); | 
|  | 361 | txq->txb = NULL; | 
|  | 362 |  | 
|  | 363 | return -ENOMEM; | 
|  | 364 | } | 
|  | 365 |  | 
|  | 366 | /* | 
|  | 367 | * Tell nic where to find circular buffer of Tx Frame Descriptors for | 
|  | 368 | * given Tx queue, and enable the DMA channel used for that queue. | 
|  | 369 | * | 
|  | 370 | * 4965 supports up to 16 Tx queues in DRAM, mapped to up to 8 Tx DMA | 
|  | 371 | * channels supported in hardware. | 
|  | 372 | */ | 
|  | 373 | static int iwl_hw_tx_queue_init(struct iwl_priv *priv, | 
| Ron Rindjunsky | 1646690 | 2008-05-05 10:22:50 +0800 | [diff] [blame] | 374 | struct iwl_tx_queue *txq) | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 375 | { | 
|  | 376 | int rc; | 
|  | 377 | unsigned long flags; | 
|  | 378 | int txq_id = txq->q.id; | 
|  | 379 |  | 
|  | 380 | spin_lock_irqsave(&priv->lock, flags); | 
|  | 381 | rc = iwl_grab_nic_access(priv); | 
|  | 382 | if (rc) { | 
|  | 383 | spin_unlock_irqrestore(&priv->lock, flags); | 
|  | 384 | return rc; | 
|  | 385 | } | 
|  | 386 |  | 
|  | 387 | /* Circular buffer (TFD queue in DRAM) physical base address */ | 
|  | 388 | iwl_write_direct32(priv, FH_MEM_CBBC_QUEUE(txq_id), | 
|  | 389 | txq->q.dma_addr >> 8); | 
|  | 390 |  | 
|  | 391 | /* Enable DMA channel, using same id as for TFD queue */ | 
|  | 392 | iwl_write_direct32( | 
|  | 393 | priv, FH_TCSR_CHNL_TX_CONFIG_REG(txq_id), | 
|  | 394 | FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | | 
|  | 395 | FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL); | 
|  | 396 | iwl_release_nic_access(priv); | 
|  | 397 | spin_unlock_irqrestore(&priv->lock, flags); | 
|  | 398 |  | 
|  | 399 | return 0; | 
|  | 400 | } | 
|  | 401 |  | 
|  | 402 | /** | 
|  | 403 | * iwl_tx_queue_init - Allocate and initialize one tx/cmd queue | 
|  | 404 | */ | 
| Tomas Winkler | 73b7d74 | 2008-09-03 11:18:48 +0800 | [diff] [blame] | 405 | static int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 406 | int slots_num, u32 txq_id) | 
|  | 407 | { | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 408 | int i, len; | 
| Tomas Winkler | 73b7d74 | 2008-09-03 11:18:48 +0800 | [diff] [blame] | 409 | int ret; | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 410 |  | 
|  | 411 | /* | 
|  | 412 | * Alloc buffer array for commands (Tx or other types of commands). | 
|  | 413 | * For the command queue (#4), allocate command space + one big | 
|  | 414 | * command for scan, since scan command is very huge; the system will | 
|  | 415 | * not have two scans at the same time, so only one is needed. | 
|  | 416 | * For normal Tx queues (all other queues), no super-size command | 
|  | 417 | * space is needed. | 
|  | 418 | */ | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 419 | len = sizeof(struct iwl_cmd); | 
|  | 420 | for (i = 0; i <= slots_num; i++) { | 
|  | 421 | if (i == slots_num) { | 
|  | 422 | if (txq_id == IWL_CMD_QUEUE_NUM) | 
|  | 423 | len += IWL_MAX_SCAN_SIZE; | 
|  | 424 | else | 
|  | 425 | continue; | 
|  | 426 | } | 
|  | 427 |  | 
| John W. Linville | 4989885 | 2008-09-02 15:07:18 -0400 | [diff] [blame] | 428 | txq->cmd[i] = kmalloc(len, GFP_KERNEL); | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 429 | if (!txq->cmd[i]) | 
| Tomas Winkler | 73b7d74 | 2008-09-03 11:18:48 +0800 | [diff] [blame] | 430 | goto err; | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 431 | } | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 432 |  | 
|  | 433 | /* Alloc driver data array and TFD circular buffer */ | 
| Tomas Winkler | 73b7d74 | 2008-09-03 11:18:48 +0800 | [diff] [blame] | 434 | ret = iwl_tx_queue_alloc(priv, txq, txq_id); | 
|  | 435 | if (ret) | 
|  | 436 | goto err; | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 437 |  | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 438 | txq->need_update = 0; | 
|  | 439 |  | 
|  | 440 | /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise | 
|  | 441 | * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */ | 
|  | 442 | BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); | 
|  | 443 |  | 
|  | 444 | /* Initialize queue's high/low-water marks, and head/tail indexes */ | 
|  | 445 | iwl_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, slots_num, txq_id); | 
|  | 446 |  | 
|  | 447 | /* Tell device where to find queue */ | 
|  | 448 | iwl_hw_tx_queue_init(priv, txq); | 
|  | 449 |  | 
|  | 450 | return 0; | 
| Tomas Winkler | 73b7d74 | 2008-09-03 11:18:48 +0800 | [diff] [blame] | 451 | err: | 
|  | 452 | for (i = 0; i < slots_num; i++) { | 
|  | 453 | kfree(txq->cmd[i]); | 
|  | 454 | txq->cmd[i] = NULL; | 
|  | 455 | } | 
|  | 456 |  | 
|  | 457 | if (txq_id == IWL_CMD_QUEUE_NUM) { | 
|  | 458 | kfree(txq->cmd[slots_num]); | 
|  | 459 | txq->cmd[slots_num] = NULL; | 
|  | 460 | } | 
|  | 461 | return -ENOMEM; | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 462 | } | 
| Tomas Winkler | da1bc45 | 2008-05-29 16:35:00 +0800 | [diff] [blame] | 463 | /** | 
|  | 464 | * iwl_hw_txq_ctx_free - Free TXQ Context | 
|  | 465 | * | 
|  | 466 | * Destroy all TX DMA queues and structures | 
|  | 467 | */ | 
|  | 468 | void iwl_hw_txq_ctx_free(struct iwl_priv *priv) | 
|  | 469 | { | 
|  | 470 | int txq_id; | 
|  | 471 |  | 
|  | 472 | /* Tx queues */ | 
|  | 473 | for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 474 | iwl_tx_queue_free(priv, txq_id); | 
| Tomas Winkler | da1bc45 | 2008-05-29 16:35:00 +0800 | [diff] [blame] | 475 |  | 
|  | 476 | /* Keep-warm buffer */ | 
|  | 477 | iwl_kw_free(priv); | 
|  | 478 | } | 
|  | 479 | EXPORT_SYMBOL(iwl_hw_txq_ctx_free); | 
|  | 480 |  | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 481 |  | 
|  | 482 | /** | 
|  | 483 | * iwl_txq_ctx_reset - Reset TX queue context | 
|  | 484 | * Destroys all DMA structures and initialise them again | 
|  | 485 | * | 
|  | 486 | * @param priv | 
|  | 487 | * @return error code | 
|  | 488 | */ | 
|  | 489 | int iwl_txq_ctx_reset(struct iwl_priv *priv) | 
|  | 490 | { | 
|  | 491 | int ret = 0; | 
|  | 492 | int txq_id, slots_num; | 
| Tomas Winkler | da1bc45 | 2008-05-29 16:35:00 +0800 | [diff] [blame] | 493 | unsigned long flags; | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 494 |  | 
|  | 495 | iwl_kw_free(priv); | 
|  | 496 |  | 
|  | 497 | /* Free all tx/cmd queues and keep-warm buffer */ | 
|  | 498 | iwl_hw_txq_ctx_free(priv); | 
|  | 499 |  | 
|  | 500 | /* Alloc keep-warm buffer */ | 
|  | 501 | ret = iwl_kw_alloc(priv); | 
|  | 502 | if (ret) { | 
| Jiri Slaby | 6f14792 | 2008-08-11 23:49:41 +0200 | [diff] [blame] | 503 | IWL_ERROR("Keep Warm allocation failed\n"); | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 504 | goto error_kw; | 
|  | 505 | } | 
| Tomas Winkler | da1bc45 | 2008-05-29 16:35:00 +0800 | [diff] [blame] | 506 | spin_lock_irqsave(&priv->lock, flags); | 
|  | 507 | ret = iwl_grab_nic_access(priv); | 
|  | 508 | if (unlikely(ret)) { | 
|  | 509 | spin_unlock_irqrestore(&priv->lock, flags); | 
|  | 510 | goto error_reset; | 
|  | 511 | } | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 512 |  | 
|  | 513 | /* Turn off all Tx DMA fifos */ | 
| Tomas Winkler | da1bc45 | 2008-05-29 16:35:00 +0800 | [diff] [blame] | 514 | priv->cfg->ops->lib->txq_set_sched(priv, 0); | 
|  | 515 |  | 
|  | 516 | iwl_release_nic_access(priv); | 
|  | 517 | spin_unlock_irqrestore(&priv->lock, flags); | 
|  | 518 |  | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 519 |  | 
|  | 520 | /* Tell nic where to find the keep-warm buffer */ | 
|  | 521 | ret = iwl_kw_init(priv); | 
|  | 522 | if (ret) { | 
|  | 523 | IWL_ERROR("kw_init failed\n"); | 
|  | 524 | goto error_reset; | 
|  | 525 | } | 
|  | 526 |  | 
| Tomas Winkler | da1bc45 | 2008-05-29 16:35:00 +0800 | [diff] [blame] | 527 | /* Alloc and init all Tx queues, including the command queue (#4) */ | 
| Ron Rindjunsky | 1053d35 | 2008-05-05 10:22:43 +0800 | [diff] [blame] | 528 | for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) { | 
|  | 529 | slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ? | 
|  | 530 | TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; | 
|  | 531 | ret = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num, | 
|  | 532 | txq_id); | 
|  | 533 | if (ret) { | 
|  | 534 | IWL_ERROR("Tx %d queue init failed\n", txq_id); | 
|  | 535 | goto error; | 
|  | 536 | } | 
|  | 537 | } | 
|  | 538 |  | 
|  | 539 | return ret; | 
|  | 540 |  | 
|  | 541 | error: | 
|  | 542 | iwl_hw_txq_ctx_free(priv); | 
|  | 543 | error_reset: | 
|  | 544 | iwl_kw_free(priv); | 
|  | 545 | error_kw: | 
|  | 546 | return ret; | 
|  | 547 | } | 
| Tomas Winkler | da1bc45 | 2008-05-29 16:35:00 +0800 | [diff] [blame] | 548 | /** | 
|  | 549 | * iwl_txq_ctx_stop - Stop all Tx DMA channels, free Tx queue memory | 
|  | 550 | */ | 
|  | 551 | void iwl_txq_ctx_stop(struct iwl_priv *priv) | 
|  | 552 | { | 
|  | 553 |  | 
|  | 554 | int txq_id; | 
|  | 555 | unsigned long flags; | 
|  | 556 |  | 
|  | 557 |  | 
|  | 558 | /* Turn off all Tx DMA fifos */ | 
|  | 559 | spin_lock_irqsave(&priv->lock, flags); | 
|  | 560 | if (iwl_grab_nic_access(priv)) { | 
|  | 561 | spin_unlock_irqrestore(&priv->lock, flags); | 
|  | 562 | return; | 
|  | 563 | } | 
|  | 564 |  | 
|  | 565 | priv->cfg->ops->lib->txq_set_sched(priv, 0); | 
|  | 566 |  | 
|  | 567 | /* Stop each Tx DMA channel, and wait for it to be idle */ | 
|  | 568 | for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) { | 
|  | 569 | iwl_write_direct32(priv, | 
|  | 570 | FH_TCSR_CHNL_TX_CONFIG_REG(txq_id), 0x0); | 
|  | 571 | iwl_poll_direct_bit(priv, FH_TSSR_TX_STATUS_REG, | 
|  | 572 | FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE | 
|  | 573 | (txq_id), 200); | 
|  | 574 | } | 
|  | 575 | iwl_release_nic_access(priv); | 
|  | 576 | spin_unlock_irqrestore(&priv->lock, flags); | 
|  | 577 |  | 
|  | 578 | /* Deallocate memory for all Tx queues */ | 
|  | 579 | iwl_hw_txq_ctx_free(priv); | 
|  | 580 | } | 
|  | 581 | EXPORT_SYMBOL(iwl_txq_ctx_stop); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 582 |  | 
|  | 583 | /* | 
|  | 584 | * handle build REPLY_TX command notification. | 
|  | 585 | */ | 
|  | 586 | static void iwl_tx_cmd_build_basic(struct iwl_priv *priv, | 
|  | 587 | struct iwl_tx_cmd *tx_cmd, | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 588 | struct ieee80211_tx_info *info, | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 589 | struct ieee80211_hdr *hdr, | 
|  | 590 | int is_unicast, u8 std_id) | 
|  | 591 | { | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 592 | __le16 fc = hdr->frame_control; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 593 | __le32 tx_flags = tx_cmd->tx_flags; | 
|  | 594 |  | 
|  | 595 | tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 596 | if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 597 | tx_flags |= TX_CMD_FLG_ACK_MSK; | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 598 | if (ieee80211_is_mgmt(fc)) | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 599 | tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 600 | if (ieee80211_is_probe_resp(fc) && | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 601 | !(le16_to_cpu(hdr->seq_ctrl) & 0xf)) | 
|  | 602 | tx_flags |= TX_CMD_FLG_TSF_MSK; | 
|  | 603 | } else { | 
|  | 604 | tx_flags &= (~TX_CMD_FLG_ACK_MSK); | 
|  | 605 | tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; | 
|  | 606 | } | 
|  | 607 |  | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 608 | if (ieee80211_is_back_req(fc)) | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 609 | tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; | 
|  | 610 |  | 
|  | 611 |  | 
|  | 612 | tx_cmd->sta_id = std_id; | 
| Harvey Harrison | 8b7b1e0 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 613 | if (ieee80211_has_morefrags(fc)) | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 614 | tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; | 
|  | 615 |  | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 616 | if (ieee80211_is_data_qos(fc)) { | 
|  | 617 | u8 *qc = ieee80211_get_qos_ctl(hdr); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 618 | tx_cmd->tid_tspec = qc[0] & 0xf; | 
|  | 619 | tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; | 
|  | 620 | } else { | 
|  | 621 | tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; | 
|  | 622 | } | 
|  | 623 |  | 
| Emmanuel Grumbach | a326a5d | 2008-07-11 11:53:31 +0800 | [diff] [blame] | 624 | priv->cfg->ops->utils->rts_tx_cmd_flag(info, &tx_flags); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 625 |  | 
|  | 626 | if ((tx_flags & TX_CMD_FLG_RTS_MSK) || (tx_flags & TX_CMD_FLG_CTS_MSK)) | 
|  | 627 | tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; | 
|  | 628 |  | 
|  | 629 | tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 630 | if (ieee80211_is_mgmt(fc)) { | 
|  | 631 | if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 632 | tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); | 
|  | 633 | else | 
|  | 634 | tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); | 
|  | 635 | } else { | 
|  | 636 | tx_cmd->timeout.pm_frame_timeout = 0; | 
|  | 637 | } | 
|  | 638 |  | 
|  | 639 | tx_cmd->driver_txop = 0; | 
|  | 640 | tx_cmd->tx_flags = tx_flags; | 
|  | 641 | tx_cmd->next_frame_len = 0; | 
|  | 642 | } | 
|  | 643 |  | 
|  | 644 | #define RTS_HCCA_RETRY_LIMIT		3 | 
|  | 645 | #define RTS_DFAULT_RETRY_LIMIT		60 | 
|  | 646 |  | 
|  | 647 | static void iwl_tx_cmd_build_rate(struct iwl_priv *priv, | 
|  | 648 | struct iwl_tx_cmd *tx_cmd, | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 649 | struct ieee80211_tx_info *info, | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 650 | __le16 fc, int sta_id, | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 651 | int is_hcca) | 
|  | 652 | { | 
|  | 653 | u8 rts_retry_limit = 0; | 
|  | 654 | u8 data_retry_limit = 0; | 
|  | 655 | u8 rate_plcp; | 
|  | 656 | u16 rate_flags = 0; | 
| Johannes Berg | 2e92e6f | 2008-05-15 12:55:27 +0200 | [diff] [blame] | 657 | int rate_idx; | 
|  | 658 |  | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 659 | rate_idx = min(ieee80211_get_tx_rate(priv->hw, info)->hw_value & 0xffff, | 
| Johannes Berg | 2e92e6f | 2008-05-15 12:55:27 +0200 | [diff] [blame] | 660 | IWL_RATE_COUNT - 1); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 661 |  | 
|  | 662 | rate_plcp = iwl_rates[rate_idx].plcp; | 
|  | 663 |  | 
|  | 664 | rts_retry_limit = (is_hcca) ? | 
|  | 665 | RTS_HCCA_RETRY_LIMIT : RTS_DFAULT_RETRY_LIMIT; | 
|  | 666 |  | 
|  | 667 | if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) | 
|  | 668 | rate_flags |= RATE_MCS_CCK_MSK; | 
|  | 669 |  | 
|  | 670 |  | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 671 | if (ieee80211_is_probe_resp(fc)) { | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 672 | data_retry_limit = 3; | 
|  | 673 | if (data_retry_limit < rts_retry_limit) | 
|  | 674 | rts_retry_limit = data_retry_limit; | 
|  | 675 | } else | 
|  | 676 | data_retry_limit = IWL_DEFAULT_TX_RETRY; | 
|  | 677 |  | 
|  | 678 | if (priv->data_retry_limit != -1) | 
|  | 679 | data_retry_limit = priv->data_retry_limit; | 
|  | 680 |  | 
|  | 681 |  | 
|  | 682 | if (ieee80211_is_data(fc)) { | 
|  | 683 | tx_cmd->initial_rate_index = 0; | 
|  | 684 | tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; | 
|  | 685 | } else { | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 686 | switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { | 
|  | 687 | case cpu_to_le16(IEEE80211_STYPE_AUTH): | 
|  | 688 | case cpu_to_le16(IEEE80211_STYPE_DEAUTH): | 
|  | 689 | case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): | 
|  | 690 | case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 691 | if (tx_cmd->tx_flags & TX_CMD_FLG_RTS_MSK) { | 
|  | 692 | tx_cmd->tx_flags &= ~TX_CMD_FLG_RTS_MSK; | 
|  | 693 | tx_cmd->tx_flags |= TX_CMD_FLG_CTS_MSK; | 
|  | 694 | } | 
|  | 695 | break; | 
|  | 696 | default: | 
|  | 697 | break; | 
|  | 698 | } | 
|  | 699 |  | 
|  | 700 | /* Alternate between antenna A and B for successive frames */ | 
|  | 701 | if (priv->use_ant_b_for_management_frame) { | 
|  | 702 | priv->use_ant_b_for_management_frame = 0; | 
|  | 703 | rate_flags |= RATE_MCS_ANT_B_MSK; | 
|  | 704 | } else { | 
|  | 705 | priv->use_ant_b_for_management_frame = 1; | 
|  | 706 | rate_flags |= RATE_MCS_ANT_A_MSK; | 
|  | 707 | } | 
|  | 708 | } | 
|  | 709 |  | 
|  | 710 | tx_cmd->rts_retry_limit = rts_retry_limit; | 
|  | 711 | tx_cmd->data_retry_limit = data_retry_limit; | 
| Tomas Winkler | e7d326a | 2008-06-12 09:47:11 +0800 | [diff] [blame] | 712 | tx_cmd->rate_n_flags = iwl_hw_set_rate_n_flags(rate_plcp, rate_flags); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 713 | } | 
|  | 714 |  | 
|  | 715 | static void iwl_tx_cmd_build_hwcrypto(struct iwl_priv *priv, | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 716 | struct ieee80211_tx_info *info, | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 717 | struct iwl_tx_cmd *tx_cmd, | 
|  | 718 | struct sk_buff *skb_frag, | 
|  | 719 | int sta_id) | 
|  | 720 | { | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 721 | struct ieee80211_key_conf *keyconf = info->control.hw_key; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 722 |  | 
| Emmanuel Grumbach | ccc038a | 2008-05-15 13:54:09 +0800 | [diff] [blame] | 723 | switch (keyconf->alg) { | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 724 | case ALG_CCMP: | 
|  | 725 | tx_cmd->sec_ctl = TX_CMD_SEC_CCM; | 
| Emmanuel Grumbach | ccc038a | 2008-05-15 13:54:09 +0800 | [diff] [blame] | 726 | memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 727 | if (info->flags & IEEE80211_TX_CTL_AMPDU) | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 728 | tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK; | 
|  | 729 | IWL_DEBUG_TX("tx_cmd with aes hwcrypto\n"); | 
|  | 730 | break; | 
|  | 731 |  | 
|  | 732 | case ALG_TKIP: | 
|  | 733 | tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; | 
| Emmanuel Grumbach | ccc038a | 2008-05-15 13:54:09 +0800 | [diff] [blame] | 734 | ieee80211_get_tkip_key(keyconf, skb_frag, | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 735 | IEEE80211_TKIP_P2_KEY, tx_cmd->key); | 
|  | 736 | IWL_DEBUG_TX("tx_cmd with tkip hwcrypto\n"); | 
|  | 737 | break; | 
|  | 738 |  | 
|  | 739 | case ALG_WEP: | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 740 | tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP | | 
| Emmanuel Grumbach | ccc038a | 2008-05-15 13:54:09 +0800 | [diff] [blame] | 741 | (keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT); | 
|  | 742 |  | 
|  | 743 | if (keyconf->keylen == WEP_KEY_LEN_128) | 
|  | 744 | tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; | 
|  | 745 |  | 
|  | 746 | memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 747 |  | 
|  | 748 | IWL_DEBUG_TX("Configuring packet for WEP encryption " | 
| Emmanuel Grumbach | ccc038a | 2008-05-15 13:54:09 +0800 | [diff] [blame] | 749 | "with key %d\n", keyconf->keyidx); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 750 | break; | 
|  | 751 |  | 
|  | 752 | default: | 
| Emmanuel Grumbach | ccc038a | 2008-05-15 13:54:09 +0800 | [diff] [blame] | 753 | printk(KERN_ERR "Unknown encode alg %d\n", keyconf->alg); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 754 | break; | 
|  | 755 | } | 
|  | 756 | } | 
|  | 757 |  | 
|  | 758 | static void iwl_update_tx_stats(struct iwl_priv *priv, u16 fc, u16 len) | 
|  | 759 | { | 
|  | 760 | /* 0 - mgmt, 1 - cnt, 2 - data */ | 
|  | 761 | int idx = (fc & IEEE80211_FCTL_FTYPE) >> 2; | 
|  | 762 | priv->tx_stats[idx].cnt++; | 
|  | 763 | priv->tx_stats[idx].bytes += len; | 
|  | 764 | } | 
|  | 765 |  | 
|  | 766 | /* | 
|  | 767 | * start REPLY_TX command process | 
|  | 768 | */ | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 769 | int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 770 | { | 
|  | 771 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 772 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 773 | struct iwl_tfd_frame *tfd; | 
| Tomas Winkler | f367422 | 2008-08-04 16:00:44 +0800 | [diff] [blame] | 774 | struct iwl_tx_queue *txq; | 
|  | 775 | struct iwl_queue *q; | 
|  | 776 | struct iwl_cmd *out_cmd; | 
|  | 777 | struct iwl_tx_cmd *tx_cmd; | 
|  | 778 | int swq_id, txq_id; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 779 | dma_addr_t phys_addr; | 
|  | 780 | dma_addr_t txcmd_phys; | 
|  | 781 | dma_addr_t scratch_phys; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 782 | u16 len, idx, len_org; | 
|  | 783 | u16 seq_number = 0; | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 784 | __le16 fc; | 
| Tomas Winkler | f367422 | 2008-08-04 16:00:44 +0800 | [diff] [blame] | 785 | u8 hdr_len, unicast; | 
|  | 786 | u8 sta_id; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 787 | u8 wait_write_ptr = 0; | 
|  | 788 | u8 tid = 0; | 
|  | 789 | u8 *qc = NULL; | 
|  | 790 | unsigned long flags; | 
|  | 791 | int ret; | 
|  | 792 |  | 
|  | 793 | spin_lock_irqsave(&priv->lock, flags); | 
|  | 794 | if (iwl_is_rfkill(priv)) { | 
|  | 795 | IWL_DEBUG_DROP("Dropping - RF KILL\n"); | 
|  | 796 | goto drop_unlock; | 
|  | 797 | } | 
|  | 798 |  | 
|  | 799 | if (!priv->vif) { | 
|  | 800 | IWL_DEBUG_DROP("Dropping - !priv->vif\n"); | 
|  | 801 | goto drop_unlock; | 
|  | 802 | } | 
|  | 803 |  | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 804 | if ((ieee80211_get_tx_rate(priv->hw, info)->hw_value & 0xFF) == | 
| Johannes Berg | 2e92e6f | 2008-05-15 12:55:27 +0200 | [diff] [blame] | 805 | IWL_INVALID_RATE) { | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 806 | IWL_ERROR("ERROR: No TX rate available.\n"); | 
|  | 807 | goto drop_unlock; | 
|  | 808 | } | 
|  | 809 |  | 
|  | 810 | unicast = !is_multicast_ether_addr(hdr->addr1); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 811 |  | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 812 | fc = hdr->frame_control; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 813 |  | 
|  | 814 | #ifdef CONFIG_IWLWIFI_DEBUG | 
|  | 815 | if (ieee80211_is_auth(fc)) | 
|  | 816 | IWL_DEBUG_TX("Sending AUTH frame\n"); | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 817 | else if (ieee80211_is_assoc_req(fc)) | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 818 | IWL_DEBUG_TX("Sending ASSOC frame\n"); | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 819 | else if (ieee80211_is_reassoc_req(fc)) | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 820 | IWL_DEBUG_TX("Sending REASSOC frame\n"); | 
|  | 821 | #endif | 
|  | 822 |  | 
|  | 823 | /* drop all data frame if we are not associated */ | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 824 | if (ieee80211_is_data(fc) && | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 825 | (!iwl_is_associated(priv) || | 
|  | 826 | ((priv->iw_mode == IEEE80211_IF_TYPE_STA) && !priv->assoc_id) || | 
|  | 827 | !priv->assoc_station_added)) { | 
|  | 828 | IWL_DEBUG_DROP("Dropping - !iwl_is_associated\n"); | 
|  | 829 | goto drop_unlock; | 
|  | 830 | } | 
|  | 831 |  | 
|  | 832 | spin_unlock_irqrestore(&priv->lock, flags); | 
|  | 833 |  | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 834 | hdr_len = ieee80211_get_hdrlen(le16_to_cpu(fc)); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 835 |  | 
|  | 836 | /* Find (or create) index into station table for destination station */ | 
|  | 837 | sta_id = iwl_get_sta_id(priv, hdr); | 
|  | 838 | if (sta_id == IWL_INVALID_STATION) { | 
|  | 839 | DECLARE_MAC_BUF(mac); | 
|  | 840 |  | 
|  | 841 | IWL_DEBUG_DROP("Dropping - INVALID STATION: %s\n", | 
|  | 842 | print_mac(mac, hdr->addr1)); | 
|  | 843 | goto drop; | 
|  | 844 | } | 
|  | 845 |  | 
|  | 846 | IWL_DEBUG_TX("station Id %d\n", sta_id); | 
|  | 847 |  | 
| Tomas Winkler | f367422 | 2008-08-04 16:00:44 +0800 | [diff] [blame] | 848 | swq_id = skb_get_queue_mapping(skb); | 
|  | 849 | txq_id = swq_id; | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 850 | if (ieee80211_is_data_qos(fc)) { | 
|  | 851 | qc = ieee80211_get_qos_ctl(hdr); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 852 | tid = qc[0] & 0xf; | 
| Tomas Winkler | f367422 | 2008-08-04 16:00:44 +0800 | [diff] [blame] | 853 | seq_number = priv->stations[sta_id].tid[tid].seq_number; | 
|  | 854 | seq_number &= IEEE80211_SCTL_SEQ; | 
|  | 855 | hdr->seq_ctrl = hdr->seq_ctrl & | 
|  | 856 | __constant_cpu_to_le16(IEEE80211_SCTL_FRAG); | 
|  | 857 | hdr->seq_ctrl |= cpu_to_le16(seq_number); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 858 | seq_number += 0x10; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 859 | /* aggregation is on for this <sta,tid> */ | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 860 | if (info->flags & IEEE80211_TX_CTL_AMPDU) | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 861 | txq_id = priv->stations[sta_id].tid[tid].agg.txq_id; | 
|  | 862 | priv->stations[sta_id].tid[tid].tfds_in_queue++; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 863 | } | 
|  | 864 |  | 
|  | 865 | /* Descriptor for chosen Tx queue */ | 
|  | 866 | txq = &priv->txq[txq_id]; | 
|  | 867 | q = &txq->q; | 
|  | 868 |  | 
|  | 869 | spin_lock_irqsave(&priv->lock, flags); | 
|  | 870 |  | 
|  | 871 | /* Set up first empty TFD within this queue's circular TFD buffer */ | 
|  | 872 | tfd = &txq->bd[q->write_ptr]; | 
|  | 873 | memset(tfd, 0, sizeof(*tfd)); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 874 | idx = get_cmd_index(q, q->write_ptr, 0); | 
|  | 875 |  | 
|  | 876 | /* Set up driver data for this TFD */ | 
|  | 877 | memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl_tx_info)); | 
|  | 878 | txq->txb[q->write_ptr].skb[0] = skb; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 879 |  | 
|  | 880 | /* Set up first empty entry in queue's array of Tx/cmd buffers */ | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 881 | out_cmd = txq->cmd[idx]; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 882 | tx_cmd = &out_cmd->cmd.tx; | 
|  | 883 | memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); | 
|  | 884 | memset(tx_cmd, 0, sizeof(struct iwl_tx_cmd)); | 
|  | 885 |  | 
|  | 886 | /* | 
|  | 887 | * Set up the Tx-command (not MAC!) header. | 
|  | 888 | * Store the chosen Tx queue and TFD index within the sequence field; | 
|  | 889 | * after Tx, uCode's Tx response will return this value so driver can | 
|  | 890 | * locate the frame within the tx queue and do post-tx processing. | 
|  | 891 | */ | 
|  | 892 | out_cmd->hdr.cmd = REPLY_TX; | 
|  | 893 | out_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | | 
|  | 894 | INDEX_TO_SEQ(q->write_ptr))); | 
|  | 895 |  | 
|  | 896 | /* Copy MAC header from skb into command buffer */ | 
|  | 897 | memcpy(tx_cmd->hdr, hdr, hdr_len); | 
|  | 898 |  | 
|  | 899 | /* | 
|  | 900 | * Use the first empty entry in this queue's command buffer array | 
|  | 901 | * to contain the Tx command and MAC header concatenated together | 
|  | 902 | * (payload data will be in another buffer). | 
|  | 903 | * Size of this varies, due to varying MAC header length. | 
|  | 904 | * If end is not dword aligned, we'll have 2 extra bytes at the end | 
|  | 905 | * of the MAC header (device reads on dword boundaries). | 
|  | 906 | * We'll tell device about this padding later. | 
|  | 907 | */ | 
|  | 908 | len = sizeof(struct iwl_tx_cmd) + | 
|  | 909 | sizeof(struct iwl_cmd_header) + hdr_len; | 
|  | 910 |  | 
|  | 911 | len_org = len; | 
|  | 912 | len = (len + 3) & ~3; | 
|  | 913 |  | 
|  | 914 | if (len_org != len) | 
|  | 915 | len_org = 1; | 
|  | 916 | else | 
|  | 917 | len_org = 0; | 
|  | 918 |  | 
|  | 919 | /* Physical address of this Tx command's header (not MAC header!), | 
|  | 920 | * within command buffer array. */ | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 921 | txcmd_phys = pci_map_single(priv->pci_dev, out_cmd, | 
|  | 922 | sizeof(struct iwl_cmd), PCI_DMA_TODEVICE); | 
|  | 923 | txcmd_phys += offsetof(struct iwl_cmd, hdr); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 924 |  | 
|  | 925 | /* Add buffer containing Tx command and MAC(!) header to TFD's | 
|  | 926 | * first entry */ | 
|  | 927 | iwl_hw_txq_attach_buf_to_tfd(priv, tfd, txcmd_phys, len); | 
|  | 928 |  | 
| Johannes Berg | d0f0980 | 2008-07-29 11:32:07 +0200 | [diff] [blame] | 929 | if (info->control.hw_key) | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 930 | iwl_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb, sta_id); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 931 |  | 
|  | 932 | /* Set up TFD's 2nd entry to point directly to remainder of skb, | 
|  | 933 | * if any (802.11 null frames have no payload). */ | 
|  | 934 | len = skb->len - hdr_len; | 
|  | 935 | if (len) { | 
|  | 936 | phys_addr = pci_map_single(priv->pci_dev, skb->data + hdr_len, | 
|  | 937 | len, PCI_DMA_TODEVICE); | 
|  | 938 | iwl_hw_txq_attach_buf_to_tfd(priv, tfd, phys_addr, len); | 
|  | 939 | } | 
|  | 940 |  | 
|  | 941 | /* Tell NIC about any 2-byte padding after MAC header */ | 
|  | 942 | if (len_org) | 
|  | 943 | tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; | 
|  | 944 |  | 
|  | 945 | /* Total # bytes to be transmitted */ | 
|  | 946 | len = (u16)skb->len; | 
|  | 947 | tx_cmd->len = cpu_to_le16(len); | 
|  | 948 | /* TODO need this for burst mode later on */ | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 949 | iwl_tx_cmd_build_basic(priv, tx_cmd, info, hdr, unicast, sta_id); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 950 |  | 
|  | 951 | /* set is_hcca to 0; it probably will never be implemented */ | 
| Johannes Berg | e039fa4 | 2008-05-15 12:55:29 +0200 | [diff] [blame] | 952 | iwl_tx_cmd_build_rate(priv, tx_cmd, info, fc, sta_id, 0); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 953 |  | 
| Harvey Harrison | fd7c8a4 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 954 | iwl_update_tx_stats(priv, le16_to_cpu(fc), len); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 955 |  | 
|  | 956 | scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + | 
|  | 957 | offsetof(struct iwl_tx_cmd, scratch); | 
|  | 958 | tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); | 
|  | 959 | tx_cmd->dram_msb_ptr = iwl_get_dma_hi_address(scratch_phys); | 
|  | 960 |  | 
| Harvey Harrison | 8b7b1e0 | 2008-06-11 14:21:56 -0700 | [diff] [blame] | 961 | if (!ieee80211_has_morefrags(hdr->frame_control)) { | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 962 | txq->need_update = 1; | 
|  | 963 | if (qc) | 
|  | 964 | priv->stations[sta_id].tid[tid].seq_number = seq_number; | 
|  | 965 | } else { | 
|  | 966 | wait_write_ptr = 1; | 
|  | 967 | txq->need_update = 0; | 
|  | 968 | } | 
|  | 969 |  | 
|  | 970 | iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd, sizeof(*tx_cmd)); | 
|  | 971 |  | 
|  | 972 | iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len); | 
|  | 973 |  | 
|  | 974 | /* Set up entry for this TFD in Tx byte-count array */ | 
|  | 975 | priv->cfg->ops->lib->txq_update_byte_cnt_tbl(priv, txq, len); | 
|  | 976 |  | 
|  | 977 | /* Tell device the write index *just past* this latest filled TFD */ | 
|  | 978 | q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); | 
|  | 979 | ret = iwl_txq_update_write_ptr(priv, txq); | 
|  | 980 | spin_unlock_irqrestore(&priv->lock, flags); | 
|  | 981 |  | 
|  | 982 | if (ret) | 
|  | 983 | return ret; | 
|  | 984 |  | 
| Tomas Winkler | 143b09e | 2008-07-24 21:33:42 +0300 | [diff] [blame] | 985 | if ((iwl_queue_space(q) < q->high_mark) && priv->mac80211_registered) { | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 986 | if (wait_write_ptr) { | 
|  | 987 | spin_lock_irqsave(&priv->lock, flags); | 
|  | 988 | txq->need_update = 1; | 
|  | 989 | iwl_txq_update_write_ptr(priv, txq); | 
|  | 990 | spin_unlock_irqrestore(&priv->lock, flags); | 
| Tomas Winkler | 143b09e | 2008-07-24 21:33:42 +0300 | [diff] [blame] | 991 | } else { | 
| Tomas Winkler | f367422 | 2008-08-04 16:00:44 +0800 | [diff] [blame] | 992 | ieee80211_stop_queue(priv->hw, swq_id); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 993 | } | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 994 | } | 
|  | 995 |  | 
|  | 996 | return 0; | 
|  | 997 |  | 
|  | 998 | drop_unlock: | 
|  | 999 | spin_unlock_irqrestore(&priv->lock, flags); | 
|  | 1000 | drop: | 
|  | 1001 | return -1; | 
|  | 1002 | } | 
|  | 1003 | EXPORT_SYMBOL(iwl_tx_skb); | 
|  | 1004 |  | 
|  | 1005 | /*************** HOST COMMAND QUEUE FUNCTIONS   *****/ | 
|  | 1006 |  | 
|  | 1007 | /** | 
|  | 1008 | * iwl_enqueue_hcmd - enqueue a uCode command | 
|  | 1009 | * @priv: device private data point | 
|  | 1010 | * @cmd: a point to the ucode command structure | 
|  | 1011 | * | 
|  | 1012 | * The function returns < 0 values to indicate the operation is | 
|  | 1013 | * failed. On success, it turns the index (> 0) of command in the | 
|  | 1014 | * command queue. | 
|  | 1015 | */ | 
|  | 1016 | int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) | 
|  | 1017 | { | 
|  | 1018 | struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM]; | 
|  | 1019 | struct iwl_queue *q = &txq->q; | 
|  | 1020 | struct iwl_tfd_frame *tfd; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 1021 | struct iwl_cmd *out_cmd; | 
| Tomas Winkler | f367422 | 2008-08-04 16:00:44 +0800 | [diff] [blame] | 1022 | dma_addr_t phys_addr; | 
|  | 1023 | unsigned long flags; | 
|  | 1024 | int len, ret; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 1025 | u32 idx; | 
|  | 1026 | u16 fix_size; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 1027 |  | 
|  | 1028 | cmd->len = priv->cfg->ops->utils->get_hcmd_size(cmd->id, cmd->len); | 
|  | 1029 | fix_size = (u16)(cmd->len + sizeof(out_cmd->hdr)); | 
|  | 1030 |  | 
|  | 1031 | /* If any of the command structures end up being larger than | 
|  | 1032 | * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then | 
|  | 1033 | * we will need to increase the size of the TFD entries */ | 
|  | 1034 | BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) && | 
|  | 1035 | !(cmd->meta.flags & CMD_SIZE_HUGE)); | 
|  | 1036 |  | 
|  | 1037 | if (iwl_is_rfkill(priv)) { | 
|  | 1038 | IWL_DEBUG_INFO("Not sending command - RF KILL"); | 
|  | 1039 | return -EIO; | 
|  | 1040 | } | 
|  | 1041 |  | 
|  | 1042 | if (iwl_queue_space(q) < ((cmd->meta.flags & CMD_ASYNC) ? 2 : 1)) { | 
|  | 1043 | IWL_ERROR("No space for Tx\n"); | 
|  | 1044 | return -ENOSPC; | 
|  | 1045 | } | 
|  | 1046 |  | 
|  | 1047 | spin_lock_irqsave(&priv->hcmd_lock, flags); | 
|  | 1048 |  | 
|  | 1049 | tfd = &txq->bd[q->write_ptr]; | 
|  | 1050 | memset(tfd, 0, sizeof(*tfd)); | 
|  | 1051 |  | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 1052 |  | 
|  | 1053 | idx = get_cmd_index(q, q->write_ptr, cmd->meta.flags & CMD_SIZE_HUGE); | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 1054 | out_cmd = txq->cmd[idx]; | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 1055 |  | 
|  | 1056 | out_cmd->hdr.cmd = cmd->id; | 
|  | 1057 | memcpy(&out_cmd->meta, &cmd->meta, sizeof(cmd->meta)); | 
|  | 1058 | memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len); | 
|  | 1059 |  | 
|  | 1060 | /* At this point, the out_cmd now has all of the incoming cmd | 
|  | 1061 | * information */ | 
|  | 1062 |  | 
|  | 1063 | out_cmd->hdr.flags = 0; | 
|  | 1064 | out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(IWL_CMD_QUEUE_NUM) | | 
|  | 1065 | INDEX_TO_SEQ(q->write_ptr)); | 
|  | 1066 | if (out_cmd->meta.flags & CMD_SIZE_HUGE) | 
|  | 1067 | out_cmd->hdr.sequence |= cpu_to_le16(SEQ_HUGE_FRAME); | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 1068 | len = (idx == TFD_CMD_SLOTS) ? | 
|  | 1069 | IWL_MAX_SCAN_SIZE : sizeof(struct iwl_cmd); | 
|  | 1070 | phys_addr = pci_map_single(priv->pci_dev, out_cmd, len, | 
|  | 1071 | PCI_DMA_TODEVICE); | 
|  | 1072 | phys_addr += offsetof(struct iwl_cmd, hdr); | 
| Tomas Winkler | fd4abac | 2008-05-15 13:54:07 +0800 | [diff] [blame] | 1073 | iwl_hw_txq_attach_buf_to_tfd(priv, tfd, phys_addr, fix_size); | 
|  | 1074 |  | 
|  | 1075 | IWL_DEBUG_HC("Sending command %s (#%x), seq: 0x%04X, " | 
|  | 1076 | "%d bytes at %d[%d]:%d\n", | 
|  | 1077 | get_cmd_string(out_cmd->hdr.cmd), | 
|  | 1078 | out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), | 
|  | 1079 | fix_size, q->write_ptr, idx, IWL_CMD_QUEUE_NUM); | 
|  | 1080 |  | 
|  | 1081 | txq->need_update = 1; | 
|  | 1082 |  | 
|  | 1083 | /* Set up entry in queue's byte count circular buffer */ | 
|  | 1084 | priv->cfg->ops->lib->txq_update_byte_cnt_tbl(priv, txq, 0); | 
|  | 1085 |  | 
|  | 1086 | /* Increment and update queue's write index */ | 
|  | 1087 | q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); | 
|  | 1088 | ret = iwl_txq_update_write_ptr(priv, txq); | 
|  | 1089 |  | 
|  | 1090 | spin_unlock_irqrestore(&priv->hcmd_lock, flags); | 
|  | 1091 | return ret ? ret : idx; | 
|  | 1092 | } | 
|  | 1093 |  | 
| Tomas Winkler | 17b8892 | 2008-05-29 16:35:12 +0800 | [diff] [blame] | 1094 | int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) | 
|  | 1095 | { | 
|  | 1096 | struct iwl_tx_queue *txq = &priv->txq[txq_id]; | 
|  | 1097 | struct iwl_queue *q = &txq->q; | 
|  | 1098 | struct iwl_tx_info *tx_info; | 
|  | 1099 | int nfreed = 0; | 
|  | 1100 |  | 
|  | 1101 | if ((index >= q->n_bd) || (iwl_queue_used(q, index) == 0)) { | 
|  | 1102 | IWL_ERROR("Read index for DMA queue txq id (%d), index %d, " | 
|  | 1103 | "is out of range [0-%d] %d %d.\n", txq_id, | 
|  | 1104 | index, q->n_bd, q->write_ptr, q->read_ptr); | 
|  | 1105 | return 0; | 
|  | 1106 | } | 
|  | 1107 |  | 
|  | 1108 | for (index = iwl_queue_inc_wrap(index, q->n_bd); q->read_ptr != index; | 
|  | 1109 | q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { | 
|  | 1110 |  | 
|  | 1111 | tx_info = &txq->txb[txq->q.read_ptr]; | 
|  | 1112 | ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb[0]); | 
|  | 1113 | tx_info->skb[0] = NULL; | 
| Tomas Winkler | 17b8892 | 2008-05-29 16:35:12 +0800 | [diff] [blame] | 1114 |  | 
| Tomas Winkler | 972cf44 | 2008-05-29 16:35:13 +0800 | [diff] [blame] | 1115 | if (priv->cfg->ops->lib->txq_inval_byte_cnt_tbl) | 
|  | 1116 | priv->cfg->ops->lib->txq_inval_byte_cnt_tbl(priv, txq); | 
|  | 1117 |  | 
|  | 1118 | iwl_hw_txq_free_tfd(priv, txq); | 
| Tomas Winkler | 17b8892 | 2008-05-29 16:35:12 +0800 | [diff] [blame] | 1119 | nfreed++; | 
|  | 1120 | } | 
|  | 1121 | return nfreed; | 
|  | 1122 | } | 
|  | 1123 | EXPORT_SYMBOL(iwl_tx_queue_reclaim); | 
|  | 1124 |  | 
|  | 1125 |  | 
|  | 1126 | /** | 
|  | 1127 | * iwl_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd | 
|  | 1128 | * | 
|  | 1129 | * When FW advances 'R' index, all entries between old and new 'R' index | 
|  | 1130 | * need to be reclaimed. As result, some free space forms.  If there is | 
|  | 1131 | * enough free space (> low mark), wake the stack that feeds us. | 
|  | 1132 | */ | 
|  | 1133 | static void iwl_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) | 
|  | 1134 | { | 
|  | 1135 | struct iwl_tx_queue *txq = &priv->txq[txq_id]; | 
|  | 1136 | struct iwl_queue *q = &txq->q; | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 1137 | struct iwl_tfd_frame *bd = &txq->bd[index]; | 
|  | 1138 | dma_addr_t dma_addr; | 
|  | 1139 | int is_odd, buf_len; | 
| Tomas Winkler | 17b8892 | 2008-05-29 16:35:12 +0800 | [diff] [blame] | 1140 | int nfreed = 0; | 
|  | 1141 |  | 
|  | 1142 | if ((index >= q->n_bd) || (iwl_queue_used(q, index) == 0)) { | 
|  | 1143 | IWL_ERROR("Read index for DMA queue txq id (%d), index %d, " | 
|  | 1144 | "is out of range [0-%d] %d %d.\n", txq_id, | 
|  | 1145 | index, q->n_bd, q->write_ptr, q->read_ptr); | 
|  | 1146 | return; | 
|  | 1147 | } | 
|  | 1148 |  | 
|  | 1149 | for (index = iwl_queue_inc_wrap(index, q->n_bd); q->read_ptr != index; | 
|  | 1150 | q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { | 
|  | 1151 |  | 
|  | 1152 | if (nfreed > 1) { | 
|  | 1153 | IWL_ERROR("HCMD skipped: index (%d) %d %d\n", index, | 
|  | 1154 | q->write_ptr, q->read_ptr); | 
|  | 1155 | queue_work(priv->workqueue, &priv->restart); | 
|  | 1156 | } | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 1157 | is_odd = (index/2) & 0x1; | 
|  | 1158 | if (is_odd) { | 
|  | 1159 | dma_addr = IWL_GET_BITS(bd->pa[index], tb2_addr_lo16) | | 
|  | 1160 | (IWL_GET_BITS(bd->pa[index], | 
|  | 1161 | tb2_addr_hi20) << 16); | 
|  | 1162 | buf_len = IWL_GET_BITS(bd->pa[index], tb2_len); | 
|  | 1163 | } else { | 
|  | 1164 | dma_addr = le32_to_cpu(bd->pa[index].tb1_addr); | 
|  | 1165 | buf_len = IWL_GET_BITS(bd->pa[index], tb1_len); | 
|  | 1166 | } | 
|  | 1167 |  | 
|  | 1168 | pci_unmap_single(priv->pci_dev, dma_addr, buf_len, | 
|  | 1169 | PCI_DMA_TODEVICE); | 
| Tomas Winkler | 17b8892 | 2008-05-29 16:35:12 +0800 | [diff] [blame] | 1170 | nfreed++; | 
|  | 1171 | } | 
|  | 1172 | } | 
|  | 1173 |  | 
|  | 1174 | /** | 
|  | 1175 | * iwl_tx_cmd_complete - Pull unused buffers off the queue and reclaim them | 
|  | 1176 | * @rxb: Rx buffer to reclaim | 
|  | 1177 | * | 
|  | 1178 | * If an Rx buffer has an async callback associated with it the callback | 
|  | 1179 | * will be executed.  The attached skb (if present) will only be freed | 
|  | 1180 | * if the callback returns 1 | 
|  | 1181 | */ | 
|  | 1182 | void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) | 
|  | 1183 | { | 
|  | 1184 | struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; | 
|  | 1185 | u16 sequence = le16_to_cpu(pkt->hdr.sequence); | 
|  | 1186 | int txq_id = SEQ_TO_QUEUE(sequence); | 
|  | 1187 | int index = SEQ_TO_INDEX(sequence); | 
|  | 1188 | int huge = sequence & SEQ_HUGE_FRAME; | 
|  | 1189 | int cmd_index; | 
|  | 1190 | struct iwl_cmd *cmd; | 
|  | 1191 |  | 
|  | 1192 | /* If a Tx command is being handled and it isn't in the actual | 
|  | 1193 | * command queue then there a command routing bug has been introduced | 
|  | 1194 | * in the queue management code. */ | 
|  | 1195 | if (txq_id != IWL_CMD_QUEUE_NUM) | 
|  | 1196 | IWL_ERROR("Error wrong command queue %d command id 0x%X\n", | 
|  | 1197 | txq_id, pkt->hdr.cmd); | 
|  | 1198 | BUG_ON(txq_id != IWL_CMD_QUEUE_NUM); | 
|  | 1199 |  | 
|  | 1200 | cmd_index = get_cmd_index(&priv->txq[IWL_CMD_QUEUE_NUM].q, index, huge); | 
| Gregory Greenman | da99c4b | 2008-08-04 16:00:40 +0800 | [diff] [blame] | 1201 | cmd = priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_index]; | 
| Tomas Winkler | 17b8892 | 2008-05-29 16:35:12 +0800 | [diff] [blame] | 1202 |  | 
|  | 1203 | /* Input error checking is done when commands are added to queue. */ | 
|  | 1204 | if (cmd->meta.flags & CMD_WANT_SKB) { | 
|  | 1205 | cmd->meta.source->u.skb = rxb->skb; | 
|  | 1206 | rxb->skb = NULL; | 
|  | 1207 | } else if (cmd->meta.u.callback && | 
|  | 1208 | !cmd->meta.u.callback(priv, cmd, rxb->skb)) | 
|  | 1209 | rxb->skb = NULL; | 
|  | 1210 |  | 
|  | 1211 | iwl_hcmd_queue_reclaim(priv, txq_id, index); | 
|  | 1212 |  | 
|  | 1213 | if (!(cmd->meta.flags & CMD_ASYNC)) { | 
|  | 1214 | clear_bit(STATUS_HCMD_ACTIVE, &priv->status); | 
|  | 1215 | wake_up_interruptible(&priv->wait_command_queue); | 
|  | 1216 | } | 
|  | 1217 | } | 
|  | 1218 | EXPORT_SYMBOL(iwl_tx_cmd_complete); | 
|  | 1219 |  | 
| Tomas Winkler | 30e553e | 2008-05-29 16:35:16 +0800 | [diff] [blame] | 1220 | /* | 
|  | 1221 | * Find first available (lowest unused) Tx Queue, mark it "active". | 
|  | 1222 | * Called only when finding queue for aggregation. | 
|  | 1223 | * Should never return anything < 7, because they should already | 
|  | 1224 | * be in use as EDCA AC (0-3), Command (4), HCCA (5, 6). | 
|  | 1225 | */ | 
|  | 1226 | static int iwl_txq_ctx_activate_free(struct iwl_priv *priv) | 
|  | 1227 | { | 
|  | 1228 | int txq_id; | 
|  | 1229 |  | 
|  | 1230 | for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) | 
|  | 1231 | if (!test_and_set_bit(txq_id, &priv->txq_ctx_active_msk)) | 
|  | 1232 | return txq_id; | 
|  | 1233 | return -1; | 
|  | 1234 | } | 
|  | 1235 |  | 
|  | 1236 | int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn) | 
|  | 1237 | { | 
|  | 1238 | int sta_id; | 
|  | 1239 | int tx_fifo; | 
|  | 1240 | int txq_id; | 
|  | 1241 | int ret; | 
|  | 1242 | unsigned long flags; | 
|  | 1243 | struct iwl_tid_data *tid_data; | 
|  | 1244 | DECLARE_MAC_BUF(mac); | 
|  | 1245 |  | 
|  | 1246 | if (likely(tid < ARRAY_SIZE(default_tid_to_tx_fifo))) | 
|  | 1247 | tx_fifo = default_tid_to_tx_fifo[tid]; | 
|  | 1248 | else | 
|  | 1249 | return -EINVAL; | 
|  | 1250 |  | 
|  | 1251 | IWL_WARNING("%s on ra = %s tid = %d\n", | 
|  | 1252 | __func__, print_mac(mac, ra), tid); | 
|  | 1253 |  | 
|  | 1254 | sta_id = iwl_find_station(priv, ra); | 
|  | 1255 | if (sta_id == IWL_INVALID_STATION) | 
|  | 1256 | return -ENXIO; | 
|  | 1257 |  | 
|  | 1258 | if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_OFF) { | 
|  | 1259 | IWL_ERROR("Start AGG when state is not IWL_AGG_OFF !\n"); | 
|  | 1260 | return -ENXIO; | 
|  | 1261 | } | 
|  | 1262 |  | 
|  | 1263 | txq_id = iwl_txq_ctx_activate_free(priv); | 
|  | 1264 | if (txq_id == -1) | 
|  | 1265 | return -ENXIO; | 
|  | 1266 |  | 
|  | 1267 | spin_lock_irqsave(&priv->sta_lock, flags); | 
|  | 1268 | tid_data = &priv->stations[sta_id].tid[tid]; | 
|  | 1269 | *ssn = SEQ_TO_SN(tid_data->seq_number); | 
|  | 1270 | tid_data->agg.txq_id = txq_id; | 
|  | 1271 | spin_unlock_irqrestore(&priv->sta_lock, flags); | 
|  | 1272 |  | 
|  | 1273 | ret = priv->cfg->ops->lib->txq_agg_enable(priv, txq_id, tx_fifo, | 
|  | 1274 | sta_id, tid, *ssn); | 
|  | 1275 | if (ret) | 
|  | 1276 | return ret; | 
|  | 1277 |  | 
|  | 1278 | if (tid_data->tfds_in_queue == 0) { | 
|  | 1279 | printk(KERN_ERR "HW queue is empty\n"); | 
|  | 1280 | tid_data->agg.state = IWL_AGG_ON; | 
|  | 1281 | ieee80211_start_tx_ba_cb_irqsafe(priv->hw, ra, tid); | 
|  | 1282 | } else { | 
|  | 1283 | IWL_DEBUG_HT("HW queue is NOT empty: %d packets in HW queue\n", | 
|  | 1284 | tid_data->tfds_in_queue); | 
|  | 1285 | tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; | 
|  | 1286 | } | 
|  | 1287 | return ret; | 
|  | 1288 | } | 
|  | 1289 | EXPORT_SYMBOL(iwl_tx_agg_start); | 
|  | 1290 |  | 
|  | 1291 | int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid) | 
|  | 1292 | { | 
|  | 1293 | int tx_fifo_id, txq_id, sta_id, ssn = -1; | 
|  | 1294 | struct iwl_tid_data *tid_data; | 
|  | 1295 | int ret, write_ptr, read_ptr; | 
|  | 1296 | unsigned long flags; | 
|  | 1297 | DECLARE_MAC_BUF(mac); | 
|  | 1298 |  | 
|  | 1299 | if (!ra) { | 
|  | 1300 | IWL_ERROR("ra = NULL\n"); | 
|  | 1301 | return -EINVAL; | 
|  | 1302 | } | 
|  | 1303 |  | 
|  | 1304 | if (likely(tid < ARRAY_SIZE(default_tid_to_tx_fifo))) | 
|  | 1305 | tx_fifo_id = default_tid_to_tx_fifo[tid]; | 
|  | 1306 | else | 
|  | 1307 | return -EINVAL; | 
|  | 1308 |  | 
|  | 1309 | sta_id = iwl_find_station(priv, ra); | 
|  | 1310 |  | 
|  | 1311 | if (sta_id == IWL_INVALID_STATION) | 
|  | 1312 | return -ENXIO; | 
|  | 1313 |  | 
|  | 1314 | if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_ON) | 
|  | 1315 | IWL_WARNING("Stopping AGG while state not IWL_AGG_ON\n"); | 
|  | 1316 |  | 
|  | 1317 | tid_data = &priv->stations[sta_id].tid[tid]; | 
|  | 1318 | ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4; | 
|  | 1319 | txq_id = tid_data->agg.txq_id; | 
|  | 1320 | write_ptr = priv->txq[txq_id].q.write_ptr; | 
|  | 1321 | read_ptr = priv->txq[txq_id].q.read_ptr; | 
|  | 1322 |  | 
|  | 1323 | /* The queue is not empty */ | 
|  | 1324 | if (write_ptr != read_ptr) { | 
|  | 1325 | IWL_DEBUG_HT("Stopping a non empty AGG HW QUEUE\n"); | 
|  | 1326 | priv->stations[sta_id].tid[tid].agg.state = | 
|  | 1327 | IWL_EMPTYING_HW_QUEUE_DELBA; | 
|  | 1328 | return 0; | 
|  | 1329 | } | 
|  | 1330 |  | 
|  | 1331 | IWL_DEBUG_HT("HW queue is empty\n"); | 
|  | 1332 | priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF; | 
|  | 1333 |  | 
|  | 1334 | spin_lock_irqsave(&priv->lock, flags); | 
|  | 1335 | ret = priv->cfg->ops->lib->txq_agg_disable(priv, txq_id, ssn, | 
|  | 1336 | tx_fifo_id); | 
|  | 1337 | spin_unlock_irqrestore(&priv->lock, flags); | 
|  | 1338 |  | 
|  | 1339 | if (ret) | 
|  | 1340 | return ret; | 
|  | 1341 |  | 
|  | 1342 | ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, ra, tid); | 
|  | 1343 |  | 
|  | 1344 | return 0; | 
|  | 1345 | } | 
|  | 1346 | EXPORT_SYMBOL(iwl_tx_agg_stop); | 
|  | 1347 |  | 
|  | 1348 | int iwl_txq_check_empty(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id) | 
|  | 1349 | { | 
|  | 1350 | struct iwl_queue *q = &priv->txq[txq_id].q; | 
|  | 1351 | u8 *addr = priv->stations[sta_id].sta.sta.addr; | 
|  | 1352 | struct iwl_tid_data *tid_data = &priv->stations[sta_id].tid[tid]; | 
|  | 1353 |  | 
|  | 1354 | switch (priv->stations[sta_id].tid[tid].agg.state) { | 
|  | 1355 | case IWL_EMPTYING_HW_QUEUE_DELBA: | 
|  | 1356 | /* We are reclaiming the last packet of the */ | 
|  | 1357 | /* aggregated HW queue */ | 
|  | 1358 | if (txq_id  == tid_data->agg.txq_id && | 
|  | 1359 | q->read_ptr == q->write_ptr) { | 
|  | 1360 | u16 ssn = SEQ_TO_SN(tid_data->seq_number); | 
|  | 1361 | int tx_fifo = default_tid_to_tx_fifo[tid]; | 
|  | 1362 | IWL_DEBUG_HT("HW queue empty: continue DELBA flow\n"); | 
|  | 1363 | priv->cfg->ops->lib->txq_agg_disable(priv, txq_id, | 
|  | 1364 | ssn, tx_fifo); | 
|  | 1365 | tid_data->agg.state = IWL_AGG_OFF; | 
|  | 1366 | ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, addr, tid); | 
|  | 1367 | } | 
|  | 1368 | break; | 
|  | 1369 | case IWL_EMPTYING_HW_QUEUE_ADDBA: | 
|  | 1370 | /* We are reclaiming the last packet of the queue */ | 
|  | 1371 | if (tid_data->tfds_in_queue == 0) { | 
|  | 1372 | IWL_DEBUG_HT("HW queue empty: continue ADDBA flow\n"); | 
|  | 1373 | tid_data->agg.state = IWL_AGG_ON; | 
|  | 1374 | ieee80211_start_tx_ba_cb_irqsafe(priv->hw, addr, tid); | 
|  | 1375 | } | 
|  | 1376 | break; | 
|  | 1377 | } | 
|  | 1378 | return 0; | 
|  | 1379 | } | 
|  | 1380 | EXPORT_SYMBOL(iwl_txq_check_empty); | 
| Tomas Winkler | 30e553e | 2008-05-29 16:35:16 +0800 | [diff] [blame] | 1381 |  | 
| Emmanuel Grumbach | 653fa4a | 2008-06-30 17:23:11 +0800 | [diff] [blame] | 1382 | /** | 
|  | 1383 | * iwl_tx_status_reply_compressed_ba - Update tx status from block-ack | 
|  | 1384 | * | 
|  | 1385 | * Go through block-ack's bitmap of ACK'd frames, update driver's record of | 
|  | 1386 | * ACK vs. not.  This gets sent to mac80211, then to rate scaling algo. | 
|  | 1387 | */ | 
|  | 1388 | static int iwl_tx_status_reply_compressed_ba(struct iwl_priv *priv, | 
|  | 1389 | struct iwl_ht_agg *agg, | 
|  | 1390 | struct iwl_compressed_ba_resp *ba_resp) | 
|  | 1391 |  | 
|  | 1392 | { | 
|  | 1393 | int i, sh, ack; | 
|  | 1394 | u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl); | 
|  | 1395 | u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); | 
|  | 1396 | u64 bitmap; | 
|  | 1397 | int successes = 0; | 
|  | 1398 | struct ieee80211_tx_info *info; | 
|  | 1399 |  | 
|  | 1400 | if (unlikely(!agg->wait_for_ba))  { | 
|  | 1401 | IWL_ERROR("Received BA when not expected\n"); | 
|  | 1402 | return -EINVAL; | 
|  | 1403 | } | 
|  | 1404 |  | 
|  | 1405 | /* Mark that the expected block-ack response arrived */ | 
|  | 1406 | agg->wait_for_ba = 0; | 
|  | 1407 | IWL_DEBUG_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->seq_ctl); | 
|  | 1408 |  | 
|  | 1409 | /* Calculate shift to align block-ack bits with our Tx window bits */ | 
|  | 1410 | sh = agg->start_idx - SEQ_TO_INDEX(seq_ctl>>4); | 
|  | 1411 | if (sh < 0) /* tbw something is wrong with indices */ | 
|  | 1412 | sh += 0x100; | 
|  | 1413 |  | 
|  | 1414 | /* don't use 64-bit values for now */ | 
|  | 1415 | bitmap = le64_to_cpu(ba_resp->bitmap) >> sh; | 
|  | 1416 |  | 
|  | 1417 | if (agg->frame_count > (64 - sh)) { | 
|  | 1418 | IWL_DEBUG_TX_REPLY("more frames than bitmap size"); | 
|  | 1419 | return -1; | 
|  | 1420 | } | 
|  | 1421 |  | 
|  | 1422 | /* check for success or failure according to the | 
|  | 1423 | * transmitted bitmap and block-ack bitmap */ | 
|  | 1424 | bitmap &= agg->bitmap; | 
|  | 1425 |  | 
|  | 1426 | /* For each frame attempted in aggregation, | 
|  | 1427 | * update driver's record of tx frame's status. */ | 
|  | 1428 | for (i = 0; i < agg->frame_count ; i++) { | 
| Emmanuel Grumbach | 4aa41f1 | 2008-07-18 13:53:09 +0800 | [diff] [blame] | 1429 | ack = bitmap & (1ULL << i); | 
| Emmanuel Grumbach | 653fa4a | 2008-06-30 17:23:11 +0800 | [diff] [blame] | 1430 | successes += !!ack; | 
|  | 1431 | IWL_DEBUG_TX_REPLY("%s ON i=%d idx=%d raw=%d\n", | 
|  | 1432 | ack? "ACK":"NACK", i, (agg->start_idx + i) & 0xff, | 
|  | 1433 | agg->start_idx + i); | 
|  | 1434 | } | 
|  | 1435 |  | 
|  | 1436 | info = IEEE80211_SKB_CB(priv->txq[scd_flow].txb[agg->start_idx].skb[0]); | 
|  | 1437 | memset(&info->status, 0, sizeof(info->status)); | 
|  | 1438 | info->flags = IEEE80211_TX_STAT_ACK; | 
|  | 1439 | info->flags |= IEEE80211_TX_STAT_AMPDU; | 
|  | 1440 | info->status.ampdu_ack_map = successes; | 
|  | 1441 | info->status.ampdu_ack_len = agg->frame_count; | 
|  | 1442 | iwl_hwrate_to_tx_control(priv, agg->rate_n_flags, info); | 
|  | 1443 |  | 
|  | 1444 | IWL_DEBUG_TX_REPLY("Bitmap %llx\n", (unsigned long long)bitmap); | 
|  | 1445 |  | 
|  | 1446 | return 0; | 
|  | 1447 | } | 
|  | 1448 |  | 
|  | 1449 | /** | 
|  | 1450 | * iwl_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA | 
|  | 1451 | * | 
|  | 1452 | * Handles block-acknowledge notification from device, which reports success | 
|  | 1453 | * of frames sent via aggregation. | 
|  | 1454 | */ | 
|  | 1455 | void iwl_rx_reply_compressed_ba(struct iwl_priv *priv, | 
|  | 1456 | struct iwl_rx_mem_buffer *rxb) | 
|  | 1457 | { | 
|  | 1458 | struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; | 
|  | 1459 | struct iwl_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba; | 
|  | 1460 | int index; | 
|  | 1461 | struct iwl_tx_queue *txq = NULL; | 
|  | 1462 | struct iwl_ht_agg *agg; | 
|  | 1463 | DECLARE_MAC_BUF(mac); | 
|  | 1464 |  | 
|  | 1465 | /* "flow" corresponds to Tx queue */ | 
|  | 1466 | u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); | 
|  | 1467 |  | 
|  | 1468 | /* "ssn" is start of block-ack Tx window, corresponds to index | 
|  | 1469 | * (in Tx queue's circular buffer) of first TFD/frame in window */ | 
|  | 1470 | u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); | 
|  | 1471 |  | 
|  | 1472 | if (scd_flow >= priv->hw_params.max_txq_num) { | 
| Jiri Slaby | 6f14792 | 2008-08-11 23:49:41 +0200 | [diff] [blame] | 1473 | IWL_ERROR("BUG_ON scd_flow is bigger than number of queues\n"); | 
| Emmanuel Grumbach | 653fa4a | 2008-06-30 17:23:11 +0800 | [diff] [blame] | 1474 | return; | 
|  | 1475 | } | 
|  | 1476 |  | 
|  | 1477 | txq = &priv->txq[scd_flow]; | 
|  | 1478 | agg = &priv->stations[ba_resp->sta_id].tid[ba_resp->tid].agg; | 
|  | 1479 |  | 
|  | 1480 | /* Find index just before block-ack window */ | 
|  | 1481 | index = iwl_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd); | 
|  | 1482 |  | 
|  | 1483 | /* TODO: Need to get this copy more safely - now good for debug */ | 
|  | 1484 |  | 
|  | 1485 | IWL_DEBUG_TX_REPLY("REPLY_COMPRESSED_BA [%d]Received from %s, " | 
|  | 1486 | "sta_id = %d\n", | 
|  | 1487 | agg->wait_for_ba, | 
|  | 1488 | print_mac(mac, (u8 *) &ba_resp->sta_addr_lo32), | 
|  | 1489 | ba_resp->sta_id); | 
|  | 1490 | IWL_DEBUG_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = " | 
|  | 1491 | "%d, scd_ssn = %d\n", | 
|  | 1492 | ba_resp->tid, | 
|  | 1493 | ba_resp->seq_ctl, | 
|  | 1494 | (unsigned long long)le64_to_cpu(ba_resp->bitmap), | 
|  | 1495 | ba_resp->scd_flow, | 
|  | 1496 | ba_resp->scd_ssn); | 
|  | 1497 | IWL_DEBUG_TX_REPLY("DAT start_idx = %d, bitmap = 0x%llx \n", | 
|  | 1498 | agg->start_idx, | 
|  | 1499 | (unsigned long long)agg->bitmap); | 
|  | 1500 |  | 
|  | 1501 | /* Update driver's record of ACK vs. not for each frame in window */ | 
|  | 1502 | iwl_tx_status_reply_compressed_ba(priv, agg, ba_resp); | 
|  | 1503 |  | 
|  | 1504 | /* Release all TFDs before the SSN, i.e. all TFDs in front of | 
|  | 1505 | * block-ack window (we assume that they've been successfully | 
|  | 1506 | * transmitted ... if not, it's too late anyway). */ | 
|  | 1507 | if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) { | 
|  | 1508 | /* calculate mac80211 ampdu sw queue to wake */ | 
|  | 1509 | int ampdu_q = | 
|  | 1510 | scd_flow - priv->hw_params.first_ampdu_q + priv->hw->queues; | 
|  | 1511 | int freed = iwl_tx_queue_reclaim(priv, scd_flow, index); | 
|  | 1512 | priv->stations[ba_resp->sta_id]. | 
|  | 1513 | tid[ba_resp->tid].tfds_in_queue -= freed; | 
|  | 1514 | if (iwl_queue_space(&txq->q) > txq->q.low_mark && | 
|  | 1515 | priv->mac80211_registered && | 
|  | 1516 | agg->state != IWL_EMPTYING_HW_QUEUE_DELBA) | 
|  | 1517 | ieee80211_wake_queue(priv->hw, ampdu_q); | 
|  | 1518 |  | 
|  | 1519 | iwl_txq_check_empty(priv, ba_resp->sta_id, | 
|  | 1520 | ba_resp->tid, scd_flow); | 
|  | 1521 | } | 
|  | 1522 | } | 
|  | 1523 | EXPORT_SYMBOL(iwl_rx_reply_compressed_ba); | 
|  | 1524 |  | 
| Helmut Schaa | 994d31f | 2008-07-02 12:17:06 +0200 | [diff] [blame] | 1525 | #ifdef CONFIG_IWLWIFI_DEBUG | 
| Tomas Winkler | a332f8d | 2008-05-29 16:35:08 +0800 | [diff] [blame] | 1526 | #define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x | 
|  | 1527 |  | 
|  | 1528 | const char *iwl_get_tx_fail_reason(u32 status) | 
|  | 1529 | { | 
|  | 1530 | switch (status & TX_STATUS_MSK) { | 
|  | 1531 | case TX_STATUS_SUCCESS: | 
|  | 1532 | return "SUCCESS"; | 
|  | 1533 | TX_STATUS_ENTRY(SHORT_LIMIT); | 
|  | 1534 | TX_STATUS_ENTRY(LONG_LIMIT); | 
|  | 1535 | TX_STATUS_ENTRY(FIFO_UNDERRUN); | 
|  | 1536 | TX_STATUS_ENTRY(MGMNT_ABORT); | 
|  | 1537 | TX_STATUS_ENTRY(NEXT_FRAG); | 
|  | 1538 | TX_STATUS_ENTRY(LIFE_EXPIRE); | 
|  | 1539 | TX_STATUS_ENTRY(DEST_PS); | 
|  | 1540 | TX_STATUS_ENTRY(ABORTED); | 
|  | 1541 | TX_STATUS_ENTRY(BT_RETRY); | 
|  | 1542 | TX_STATUS_ENTRY(STA_INVALID); | 
|  | 1543 | TX_STATUS_ENTRY(FRAG_DROPPED); | 
|  | 1544 | TX_STATUS_ENTRY(TID_DISABLE); | 
|  | 1545 | TX_STATUS_ENTRY(FRAME_FLUSHED); | 
|  | 1546 | TX_STATUS_ENTRY(INSUFFICIENT_CF_POLL); | 
|  | 1547 | TX_STATUS_ENTRY(TX_LOCKED); | 
|  | 1548 | TX_STATUS_ENTRY(NO_BEACON_ON_RADAR); | 
|  | 1549 | } | 
|  | 1550 |  | 
|  | 1551 | return "UNKNOWN"; | 
|  | 1552 | } | 
|  | 1553 | EXPORT_SYMBOL(iwl_get_tx_fail_reason); | 
|  | 1554 | #endif /* CONFIG_IWLWIFI_DEBUG */ |