diff --git a/dma.c b/dma.c index f8ca17fa..6af6ea1f 100644 --- a/dma.c +++ b/dma.c @@ -15,6 +15,8 @@ #include "mt76.h" #include "dma.h" +#define DMA_DUMMY_TXWI ((void *) ~0) + static int mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q) { @@ -48,27 +50,45 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q) static int mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q, - u32 buf0, int len0, u32 buf1, int len1, u32 info) + struct mt76_queue_buf *buf, int nbufs, u32 info, + struct sk_buff *skb, void *txwi) { struct mt76_desc *desc; u32 ctrl; - int idx; + int i, idx = -1; - ctrl = MT76_SET(MT_DMA_CTL_SD_LEN0, len0) | - MT76_SET(MT_DMA_CTL_SD_LEN1, len1) | - (len1 ? MT_DMA_CTL_LAST_SEC1 : MT_DMA_CTL_LAST_SEC0); + if (txwi) + q->entry[q->head].txwi = DMA_DUMMY_TXWI; - idx = q->head; - q->head = (q->head + 1) % q->ndesc; + for (i = 0; i < nbufs; i += 2, buf += 2) { + u32 buf0 = buf[0].addr, buf1 = 0; - desc = &q->desc[idx]; + ctrl = MT76_SET(MT_DMA_CTL_SD_LEN0, buf[0].len); + if (i < nbufs - 1) { + buf1 = buf[1].addr; + ctrl |= MT76_SET(MT_DMA_CTL_SD_LEN1, buf[1].len); + } - ACCESS_ONCE(desc->buf0) = cpu_to_le32(buf0); - ACCESS_ONCE(desc->buf1) = cpu_to_le32(buf1); - ACCESS_ONCE(desc->info) = cpu_to_le32(info); - ACCESS_ONCE(desc->ctrl) = cpu_to_le32(ctrl); + if (i == nbufs - 1) + ctrl |= MT_DMA_CTL_LAST_SEC0; + else if (i == nbufs - 2) + ctrl |= MT_DMA_CTL_LAST_SEC1; - q->queued++; + idx = q->head; + q->head = (q->head + 1) % q->ndesc; + + desc = &q->desc[idx]; + + ACCESS_ONCE(desc->buf0) = cpu_to_le32(buf0); + ACCESS_ONCE(desc->buf1) = cpu_to_le32(buf1); + ACCESS_ONCE(desc->info) = cpu_to_le32(info); + ACCESS_ONCE(desc->ctrl) = cpu_to_le32(ctrl); + + q->queued++; + } + + q->entry[idx].txwi = txwi; + q->entry[idx].skb = skb; return idx; } @@ -78,15 +98,25 @@ mt76_dma_tx_cleanup_idx(struct mt76_dev *dev, struct mt76_queue *q, int idx, struct mt76_queue_entry *prev_e) { struct mt76_queue_entry *e = &q->entry[idx]; - struct sk_buff *skb = e->skb; - dma_addr_t skb_addr; + __le32 __ctrl = ACCESS_ONCE(q->desc[idx].ctrl); + u32 ctrl = le32_to_cpu(__ctrl); - if (e->txwi) - skb_addr = ACCESS_ONCE(q->desc[idx].buf1); - else - skb_addr = ACCESS_ONCE(q->desc[idx].buf0); + if (!e->txwi || !e->skb) { + __le32 addr = ACCESS_ONCE(q->desc[idx].buf0); + u32 len = MT76_GET(MT_DMA_CTL_SD_LEN0, ctrl); + dma_unmap_single(dev->dev, le32_to_cpu(addr), len, + DMA_TO_DEVICE); + } - dma_unmap_single(dev->dev, skb_addr, skb->len, DMA_TO_DEVICE); + if (!(ctrl & MT_DMA_CTL_LAST_SEC0)) { + __le32 addr = ACCESS_ONCE(q->desc[idx].buf1); + u32 len = MT76_GET(MT_DMA_CTL_SD_LEN1, ctrl); + dma_unmap_single(dev->dev, le32_to_cpu(addr), len, + DMA_TO_DEVICE); + } + + if (e->txwi == DMA_DUMMY_TXWI) + e->txwi = NULL; *prev_e = *e; memset(e, 0, sizeof(*e)); @@ -111,7 +141,9 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush, if (entry.schedule) q->swq_queued--; - done(dev, q, &entry); + if (entry.skb) + done(dev, q, &entry); + if (entry.txwi) mt76_put_txwi(dev, entry.txwi); @@ -196,6 +228,8 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q, bool napi) spin_lock_bh(&q->lock); while (q->queued < q->ndesc - 1) { + struct mt76_queue_buf qbuf; + buf = alloc(q->buf_size); if (!buf) break; @@ -206,8 +240,9 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q, bool napi) break; } - idx = mt76_dma_add_buf(dev, q, addr + offset, len - offset, 0, 0, 0); - q->entry[idx].buf = buf; + qbuf.addr = addr + offset; + qbuf.len = len - offset; + idx = mt76_dma_add_buf(dev, q, &qbuf, 1, 0, buf, NULL); frames++; } diff --git a/mt76.h b/mt76.h index 795d287e..e97b7ea6 100644 --- a/mt76.h +++ b/mt76.h @@ -53,12 +53,17 @@ enum mt76_rxq_id { __MT_RXQ_MAX }; +struct mt76_queue_buf { + dma_addr_t addr; + int len; +}; + struct mt76_queue_entry { - struct sk_buff *skb; union { void *buf; - struct mt76_txwi_cache *txwi; + struct sk_buff *skb; }; + struct mt76_txwi_cache *txwi; bool schedule; }; @@ -96,7 +101,8 @@ struct mt76_queue_ops { int (*alloc)(struct mt76_dev *dev, struct mt76_queue *q); int (*add_buf)(struct mt76_dev *dev, struct mt76_queue *q, - u32 buf0, int len0, u32 buf1, int len1, u32 info); + struct mt76_queue_buf *buf, int nbufs, u32 info, + struct sk_buff *skb, void *txwi); void *(*dequeue)(struct mt76_dev *dev, struct mt76_queue *q, bool flush, int *len, u32 *info, bool *more); diff --git a/mt7603_dma.c b/mt7603_dma.c index ed148176..600ec62e 100644 --- a/mt7603_dma.c +++ b/mt7603_dma.c @@ -20,6 +20,7 @@ mt7603_tx_queue_mcu(struct mt7603_dev *dev, enum mt76_txq_id qid, struct sk_buff *skb) { struct mt76_queue *q = &dev->mt76.q_tx[qid]; + struct mt76_queue_buf buf; dma_addr_t addr; int idx; @@ -27,9 +28,10 @@ mt7603_tx_queue_mcu(struct mt7603_dev *dev, enum mt76_txq_id qid, if (dma_mapping_error(dev->mt76.dev, addr)) return -ENOMEM; + buf.addr = addr; + buf.len = skb->len; spin_lock_bh(&q->lock); - idx = mt76_queue_add_buf(dev, q, addr, skb->len, 0, 0, 0); - q->entry[idx].skb = skb; + idx = mt76_queue_add_buf(dev, q, &buf, 1, 0, skb, NULL); mt76_queue_kick(dev, q); spin_unlock_bh(&q->lock); diff --git a/mt76x2_dma.c b/mt76x2_dma.c index d29464b3..303781a1 100644 --- a/mt76x2_dma.c +++ b/mt76x2_dma.c @@ -19,6 +19,7 @@ mt76x2_tx_queue_mcu(struct mt76x2_dev *dev, enum mt76_txq_id qid, struct sk_buff *skb, int cmd, int seq) { struct mt76_queue *q = &dev->mt76.q_tx[qid]; + struct mt76_queue_buf buf; dma_addr_t addr; u32 tx_info; int idx; @@ -33,9 +34,10 @@ mt76x2_tx_queue_mcu(struct mt76x2_dev *dev, enum mt76_txq_id qid, if (dma_mapping_error(dev->mt76.dev, addr)) return -ENOMEM; + buf.addr = addr; + buf.len = skb->len; spin_lock_bh(&q->lock); - idx = mt76_queue_add_buf(dev, q, addr, skb->len, 0, 0, tx_info); - q->entry[idx].skb = skb; + idx = mt76_queue_add_buf(dev, q, &buf, 1, tx_info, skb, NULL); mt76_queue_kick(dev, q); spin_unlock_bh(&q->lock); diff --git a/mt76x2_tx.c b/mt76x2_tx.c index e98fa172..925a8b25 100644 --- a/mt76x2_tx.c +++ b/mt76x2_tx.c @@ -64,23 +64,20 @@ int mt76x2_tx_prepare_skb(struct mt76_dev *mdev, void *txwi, { struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int len, qsel = MT_QSEL_EDCA; + int qsel = MT_QSEL_EDCA; int ret; mt76x2_mac_write_txwi(dev, txwi, skb, wcid, sta); ret = mt76_insert_hdr_pad(skb); - if (ret) + if (ret < 0) return ret; if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) qsel = 0; - len = skb->len + sizeof(struct mt76x2_txwi); - len += mt76x2_mac_skb_tx_overhead(dev, skb); - *tx_info = MT76_SET(MT_TXD_INFO_LEN, len) | - MT76_SET(MT_TXD_INFO_QSEL, qsel) | - MT_TXD_INFO_80211; + *tx_info = MT76_SET(MT_TXD_INFO_QSEL, qsel) | + MT_TXD_INFO_80211; if (!wcid || wcid->hw_key_idx == 0xff) *tx_info |= MT_TXD_INFO_WIV; diff --git a/tx.c b/tx.c index 884f355f..27fe339b 100644 --- a/tx.c +++ b/tx.c @@ -92,9 +92,12 @@ int mt76_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q, struct ieee80211_sta *sta) { struct mt76_txwi_cache *t; + struct mt76_queue_buf buf[32]; + struct sk_buff *iter; dma_addr_t addr; + int len; u32 tx_info = 0; - int ret; + int n, ret; t = mt76_get_txwi(dev); if (!t) { @@ -110,21 +113,40 @@ int mt76_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q, if (ret < 0) goto free_txwi; - addr = dma_map_single(dev->dev, skb->data, skb->len, DMA_TO_DEVICE); + len = skb->len - skb->data_len; + addr = dma_map_single(dev->dev, skb->data, len, DMA_TO_DEVICE); if (dma_mapping_error(dev->dev, addr)) { ret = -ENOMEM; goto free_txwi; } - ret = dev->queue_ops->add_buf(dev, q, t->dma_addr, dev->drv->txwi_size, - addr, skb->len, tx_info); - if (ret < 0) - return ret; + n = 0; + buf[n].addr = t->dma_addr; + buf[n++].len = dev->drv->txwi_size; + buf[n].addr = addr; + buf[n++].len = len; - q->entry[ret].skb = skb; - q->entry[ret].txwi = t; + skb_walk_frags(skb, iter) { + if (n == ARRAY_SIZE(buf)) + goto unmap; - return ret; + addr = dma_map_single(dev->dev, iter->data, iter->len, DMA_TO_DEVICE); + if (dma_mapping_error(dev->dev, addr)) + goto unmap; + + buf[n].addr = addr; + buf[n++].len = iter->len; + } + + if (q->queued + (n + 1) / 2 >= q->ndesc) + goto unmap; + + return dev->queue_ops->add_buf(dev, q, buf, n, tx_info, skb, t); + +unmap: + ret = -ENOMEM; + for (n--; n > 0; n--) + dma_unmap_single(dev->dev, buf[n].addr, buf[n].len, DMA_TO_DEVICE); free_txwi: mt76_put_txwi(dev, t); diff --git a/util.c b/util.c index 16d299b9..7bf2e966 100644 --- a/util.c +++ b/util.c @@ -39,7 +39,7 @@ int mt76_insert_hdr_pad(struct sk_buff *skb) skb->data[len] = 0; skb->data[len + 1] = 0; - return 0; + return 2; } EXPORT_SYMBOL_GPL(mt76_insert_hdr_pad);