diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-agn.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 7e0baf6deedf..4a15e42ad00a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c | |||
@@ -471,6 +471,126 @@ static int iwl_send_beacon_cmd(struct iwl_priv *priv) | |||
471 | return rc; | 471 | return rc; |
472 | } | 472 | } |
473 | 473 | ||
474 | static inline dma_addr_t iwl_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) | ||
475 | { | ||
476 | struct iwl_tfd_tb *tb = &tfd->tbs[idx]; | ||
477 | |||
478 | dma_addr_t addr = get_unaligned_le32(&tb->lo); | ||
479 | if (sizeof(dma_addr_t) > sizeof(u32)) | ||
480 | addr |= | ||
481 | ((dma_addr_t)(le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 16; | ||
482 | |||
483 | return addr; | ||
484 | } | ||
485 | |||
486 | static inline u16 iwl_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) | ||
487 | { | ||
488 | struct iwl_tfd_tb *tb = &tfd->tbs[idx]; | ||
489 | |||
490 | return le16_to_cpu(tb->hi_n_len) >> 4; | ||
491 | } | ||
492 | |||
493 | static inline void iwl_tfd_set_tb(struct iwl_tfd *tfd, u8 idx, | ||
494 | dma_addr_t addr, u16 len) | ||
495 | { | ||
496 | struct iwl_tfd_tb *tb = &tfd->tbs[idx]; | ||
497 | u16 hi_n_len = len << 4; | ||
498 | |||
499 | put_unaligned_le32(addr, &tb->lo); | ||
500 | if (sizeof(dma_addr_t) > sizeof(u32)) | ||
501 | hi_n_len |= ((addr >> 16) >> 16) & 0xF; | ||
502 | |||
503 | tb->hi_n_len = cpu_to_le16(hi_n_len); | ||
504 | |||
505 | tfd->num_tbs = idx + 1; | ||
506 | } | ||
507 | |||
508 | static inline u8 iwl_tfd_get_num_tbs(struct iwl_tfd *tfd) | ||
509 | { | ||
510 | return tfd->num_tbs & 0x1f; | ||
511 | } | ||
512 | |||
513 | /** | ||
514 | * iwl_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] | ||
515 | * @priv - driver private data | ||
516 | * @txq - tx queue | ||
517 | * | ||
518 | * Does NOT advance any TFD circular buffer read/write indexes | ||
519 | * Does NOT free the TFD itself (which is within circular buffer) | ||
520 | */ | ||
521 | void iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) | ||
522 | { | ||
523 | struct iwl_tfd *tfd_tmp = (struct iwl_tfd *)&txq->tfds[0]; | ||
524 | struct iwl_tfd *tfd; | ||
525 | struct pci_dev *dev = priv->pci_dev; | ||
526 | int index = txq->q.read_ptr; | ||
527 | int i; | ||
528 | int num_tbs; | ||
529 | |||
530 | tfd = &tfd_tmp[index]; | ||
531 | |||
532 | /* Sanity check on number of chunks */ | ||
533 | num_tbs = iwl_tfd_get_num_tbs(tfd); | ||
534 | |||
535 | if (num_tbs >= IWL_NUM_OF_TBS) { | ||
536 | IWL_ERR(priv, "Too many chunks: %i\n", num_tbs); | ||
537 | /* @todo issue fatal error, it is quite serious situation */ | ||
538 | return; | ||
539 | } | ||
540 | |||
541 | /* Unmap tx_cmd */ | ||
542 | if (num_tbs) | ||
543 | pci_unmap_single(dev, | ||
544 | pci_unmap_addr(&txq->cmd[index]->meta, mapping), | ||
545 | pci_unmap_len(&txq->cmd[index]->meta, len), | ||
546 | PCI_DMA_TODEVICE); | ||
547 | |||
548 | /* Unmap chunks, if any. */ | ||
549 | for (i = 1; i < num_tbs; i++) { | ||
550 | pci_unmap_single(dev, iwl_tfd_tb_get_addr(tfd, i), | ||
551 | iwl_tfd_tb_get_len(tfd, i), PCI_DMA_TODEVICE); | ||
552 | |||
553 | if (txq->txb) { | ||
554 | dev_kfree_skb(txq->txb[txq->q.read_ptr].skb[i - 1]); | ||
555 | txq->txb[txq->q.read_ptr].skb[i - 1] = NULL; | ||
556 | } | ||
557 | } | ||
558 | } | ||
559 | |||
560 | int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, | ||
561 | struct iwl_tx_queue *txq, | ||
562 | dma_addr_t addr, u16 len, | ||
563 | u8 reset, u8 pad) | ||
564 | { | ||
565 | struct iwl_queue *q; | ||
566 | struct iwl_tfd *tfd; | ||
567 | u32 num_tbs; | ||
568 | |||
569 | q = &txq->q; | ||
570 | tfd = &txq->tfds[q->write_ptr]; | ||
571 | |||
572 | if (reset) | ||
573 | memset(tfd, 0, sizeof(*tfd)); | ||
574 | |||
575 | num_tbs = iwl_tfd_get_num_tbs(tfd); | ||
576 | |||
577 | /* Each TFD can point to a maximum 20 Tx buffers */ | ||
578 | if (num_tbs >= IWL_NUM_OF_TBS) { | ||
579 | IWL_ERR(priv, "Error can not send more than %d chunks\n", | ||
580 | IWL_NUM_OF_TBS); | ||
581 | return -EINVAL; | ||
582 | } | ||
583 | |||
584 | BUG_ON(addr & ~DMA_BIT_MASK(36)); | ||
585 | if (unlikely(addr & ~IWL_TX_DMA_MASK)) | ||
586 | IWL_ERR(priv, "Unaligned address = %llx\n", | ||
587 | (unsigned long long)addr); | ||
588 | |||
589 | iwl_tfd_set_tb(tfd, num_tbs, addr, len); | ||
590 | |||
591 | return 0; | ||
592 | } | ||
593 | |||
474 | /****************************************************************************** | 594 | /****************************************************************************** |
475 | * | 595 | * |
476 | * Misc. internal state and helper functions | 596 | * Misc. internal state and helper functions |