From 2bb057d07a0bc17475a7bf897fc41667ab08b73f Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 4 Aug 2008 16:37:44 +0200 Subject: rt2x00: Implement HW encryption Various rt2x00 devices support hardware encryption. Most of them require the IV/EIV to be generated by mac80211, but require it to be provided seperately instead of within the frame itself. This means that rt2x00lib should extract the data from the frame and place it in the frame descriptor. During RX the IV/EIV is provided in the descriptor by the hardware which means that it should be inserted into the frame by rt2x00lib. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00queue.c | 81 ++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00queue.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 898cdd7f57d9..c0f97c53e5ce 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -33,10 +33,11 @@ struct sk_buff *rt2x00queue_alloc_rxskb(struct rt2x00_dev *rt2x00dev, struct queue_entry *entry) { - unsigned int frame_size; - unsigned int reserved_size; struct sk_buff *skb; struct skb_frame_desc *skbdesc; + unsigned int frame_size; + unsigned int head_size = 0; + unsigned int tail_size = 0; /* * The frame size includes descriptor size, because the @@ -49,16 +50,32 @@ struct sk_buff *rt2x00queue_alloc_rxskb(struct rt2x00_dev *rt2x00dev, * this means we need at least 3 bytes for moving the frame * into the correct offset. */ - reserved_size = 4; + head_size = 4; + + /* + * For IV/EIV/ICV assembly we must make sure there is + * at least 8 bytes bytes available in headroom for IV/EIV + * and 4 bytes for ICV data as tailroon. + */ +#ifdef CONFIG_RT2X00_LIB_CRYPTO + if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) { + head_size += 8; + tail_size += 4; + } +#endif /* CONFIG_RT2X00_LIB_CRYPTO */ /* * Allocate skbuffer. */ - skb = dev_alloc_skb(frame_size + reserved_size); + skb = dev_alloc_skb(frame_size + head_size + tail_size); if (!skb) return NULL; - skb_reserve(skb, reserved_size); + /* + * Make sure we not have a frame with the requested bytes + * available in the head and tail. + */ + skb_reserve(skb, head_size); skb_put(skb, frame_size); /* @@ -140,7 +157,7 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, txdesc->cw_max = entry->queue->cw_max; txdesc->aifs = entry->queue->aifs; - /* Data length should be extended with 4 bytes for CRC */ + /* Data length + CRC + IV/EIV/ICV/MMIC (when using encryption) */ data_length = entry->skb->len + 4; /* @@ -149,6 +166,35 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) __set_bit(ENTRY_TXD_ACK, &txdesc->flags); +#ifdef CONFIG_RT2X00_LIB_CRYPTO + if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags) && + !entry->skb->do_not_encrypt) { + struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; + + __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags); + + txdesc->cipher = rt2x00crypto_key_to_cipher(hw_key); + + if (hw_key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + __set_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags); + + txdesc->key_idx = hw_key->hw_key_idx; + txdesc->iv_offset = ieee80211_get_hdrlen_from_skb(entry->skb); + + /* + * Extend frame length to include all encryption overhead + * that will be added by the hardware. + */ + data_length += rt2x00crypto_tx_overhead(tx_info); + + if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) + __set_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags); + + if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) + __set_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags); + } +#endif /* CONFIG_RT2X00_LIB_CRYPTO */ + /* * Check if this is a RTS/CTS frame */ @@ -305,6 +351,7 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX); struct txentry_desc txdesc; struct skb_frame_desc *skbdesc; + unsigned int iv_len = IEEE80211_SKB_CB(skb)->control.iv_len; if (unlikely(rt2x00queue_full(queue))) return -EINVAL; @@ -326,15 +373,33 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) rt2x00queue_create_tx_descriptor(entry, &txdesc); /* - * skb->cb array is now ours and we are free to use it. + * All information is retreived from the skb->cb array, + * now we should claim ownership of the driver part of that + * array. */ skbdesc = get_skb_frame_desc(entry->skb); memset(skbdesc, 0, sizeof(*skbdesc)); skbdesc->entry = entry; + /* + * When hardware encryption is supported, and this frame + * is to be encrypted, we should strip the IV/EIV data from + * the frame so we can provide it to the driver seperately. + */ + if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) && + !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) + rt2x00crypto_tx_remove_iv(skb, iv_len); + + /* + * It could be possible that the queue was corrupted and this + * call failed. Just drop the frame, we cannot rollback and pass + * the frame to mac80211 because the skb->cb has now been tainted. + */ if (unlikely(queue->rt2x00dev->ops->lib->write_tx_data(entry))) { __clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); - return -EIO; + dev_kfree_skb_any(entry->skb); + entry->skb = NULL; + return 0; } if (test_bit(DRIVER_REQUIRE_DMA, &queue->rt2x00dev->flags)) -- cgit v1.2.3 From 0262ab0df64a67d4c0ed7577a29b7d866819cc68 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Fri, 29 Aug 2008 21:04:26 +0200 Subject: rt2x00: Fix race conditions in flag handling Some of the flags should be accessed atomically to prevent race conditions. The flags that are most important are those that can change often and indicate the actual state of the device, queue or queue entry. The big flag rename was done to move all state flags to the same naming type as the other rt2x00dev flags and made sure all places where the flags were used were changed. ;) Thanks to Stephen for most of the queue flags updates, which fixes some of the most obvious consequences of the race conditions. Among those the notorious: rt2x00queue_write_tx_frame: Error - Arrived at non-free entry in the non-full queue 0. rt2x00queue_write_tx_frame: Error - Arrived at non-free entry in the non-full queue 0. rt2x00queue_write_tx_frame: Error - Arrived at non-free entry in the non-full queue 0. Signed-off-by: Stephen Blackheath Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00queue.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00queue.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index c0f97c53e5ce..d10a8012f387 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -356,7 +356,7 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) if (unlikely(rt2x00queue_full(queue))) return -EINVAL; - if (__test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) { + if (test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) { ERROR(queue->rt2x00dev, "Arrived at non-free entry in the non-full queue %d.\n" "Please file bug report to %s.\n", @@ -396,7 +396,7 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) * the frame to mac80211 because the skb->cb has now been tainted. */ if (unlikely(queue->rt2x00dev->ops->lib->write_tx_data(entry))) { - __clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); + clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); dev_kfree_skb_any(entry->skb); entry->skb = NULL; return 0; @@ -405,7 +405,7 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) if (test_bit(DRIVER_REQUIRE_DMA, &queue->rt2x00dev->flags)) rt2x00queue_map_txskb(queue->rt2x00dev, skb); - __set_bit(ENTRY_DATA_PENDING, &entry->flags); + set_bit(ENTRY_DATA_PENDING, &entry->flags); rt2x00queue_index_inc(queue, Q_INDEX); rt2x00queue_write_tx_descriptor(entry, &txdesc); -- cgit v1.2.3 From 3ee54a07d34fd9b5c34bb1488113fb32be58e38f Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Fri, 29 Aug 2008 21:04:50 +0200 Subject: rt2x00: Map extra_tx_headroom to DMA If a driver requests additional headroom it should be mapped to DMA as well because it will be send to the hardware as well (as form of extra descriptor). Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00queue.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00queue.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index d10a8012f387..2822684c505e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -100,8 +100,21 @@ void rt2x00queue_map_txskb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); - skbdesc->skb_dma = dma_map_single(rt2x00dev->dev, skb->data, skb->len, - DMA_TO_DEVICE); + /* + * If device has requested headroom, we should make sure that + * is also mapped to the DMA so it can be used for transfering + * additional descriptor information to the hardware. + */ + skb_push(skb, rt2x00dev->hw->extra_tx_headroom); + + skbdesc->skb_dma = + dma_map_single(rt2x00dev->dev, skb->data, skb->len, DMA_TO_DEVICE); + + /* + * Restore data pointer to original location again. + */ + skb_pull(skb, rt2x00dev->hw->extra_tx_headroom); + skbdesc->flags |= SKBDESC_DMA_MAPPED_TX; } EXPORT_SYMBOL_GPL(rt2x00queue_map_txskb); @@ -117,7 +130,12 @@ void rt2x00queue_unmap_skb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb) } if (skbdesc->flags & SKBDESC_DMA_MAPPED_TX) { - dma_unmap_single(rt2x00dev->dev, skbdesc->skb_dma, skb->len, + /* + * Add headroom to the skb length, it has been removed + * by the driver, but it was actually mapped to DMA. + */ + dma_unmap_single(rt2x00dev->dev, skbdesc->skb_dma, + skb->len + rt2x00dev->hw->extra_tx_headroom, DMA_TO_DEVICE); skbdesc->flags &= ~SKBDESC_DMA_MAPPED_TX; } -- cgit v1.2.3 From 2af0a570b45ec315f364ea2c8a6d072cfcaa9d32 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Fri, 29 Aug 2008 21:05:45 +0200 Subject: rt2x00: Initialize txop during conf_tx() callback The txop parameter is supported by rt61pci and rt73usb, and thus should be written to the register instead of using the fixed value set during initialization. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00queue.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/rt2x00/rt2x00queue.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 2822684c505e..a5e965068c83 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -736,6 +736,7 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, queue->rt2x00dev = rt2x00dev; queue->qid = qid; + queue->txop = 0; queue->aifs = 2; queue->cw_min = 5; queue->cw_max = 10; -- cgit v1.2.3 From 25d834e16294c8dfd923dae6bdb8a055391a99a5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 12 Sep 2008 22:52:47 +0200 Subject: mac80211: fix virtual interfaces vs. injection Currently, virtual interface pointers passed to drivers might be from monitor interfaces and as such completely uninitialised because we do not tell the driver about monitor interfaces when those are created. Instead of passing them, we should therefore indicate to the driver that there is no information; do that by passing a NULL value and adjust drivers to cope with it. As a result, some mac80211 API functions also need to cope with a NULL vif pointer so drivers can still call them unconditionally. Also, when injecting frames we really don't want to pass NULL all the time, if we know we are the source address of a frame and have a local interface for that address, we can to use that interface. This also helps with processing the frame correctly for that interface which will help the 802.11w implementation. It's not entirely correct for VLANs or WDS interfaces because there the MAC address isn't unique, but it's already a lot better than what we do now. Finally, when injecting without a matching local interface, don't assign sequence numbers at all. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00queue.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00queue.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index a5e965068c83..b7f4fe8fba6e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -155,7 +155,6 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb); - struct rt2x00_intf *intf = vif_to_intf(tx_info->control.vif); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)entry->skb->data; struct ieee80211_rate *rate = ieee80211_get_tx_rate(rt2x00dev->hw, tx_info); @@ -278,16 +277,22 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, * sequence counter given by mac80211. */ if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - spin_lock_irqsave(&intf->seqlock, irqflags); + if (likely(tx_info->control.vif)) { + struct rt2x00_intf *intf; - if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)) - intf->seqno += 0x10; - hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - hdr->seq_ctrl |= cpu_to_le16(intf->seqno); + intf = vif_to_intf(tx_info->control.vif); - spin_unlock_irqrestore(&intf->seqlock, irqflags); + spin_lock_irqsave(&intf->seqlock, irqflags); - __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); + if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)) + intf->seqno += 0x10; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(intf->seqno); + + spin_unlock_irqrestore(&intf->seqlock, irqflags); + + __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); + } } /* -- cgit v1.2.3 From 76708dee382a69b2f9d0e50f413f99fefb2dc509 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 5 Oct 2008 18:02:48 +0200 Subject: mac80211: free up 2 bytes in skb->cb Free up 2 bytes in skb->cb to be used for multi-rate retry later. Move iv_len and icv_len initialization into key alloc. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00queue.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00queue.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index b7f4fe8fba6e..1676ac484790 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -374,7 +374,7 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX); struct txentry_desc txdesc; struct skb_frame_desc *skbdesc; - unsigned int iv_len = IEEE80211_SKB_CB(skb)->control.iv_len; + unsigned int iv_len; if (unlikely(rt2x00queue_full(queue))) return -EINVAL; @@ -410,8 +410,11 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) * the frame so we can provide it to the driver seperately. */ if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) && - !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) + !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags) && + (IEEE80211_SKB_CB(skb)->control.hw_key != NULL)) { + iv_len = IEEE80211_SKB_CB(skb)->control.hw_key->iv_len; rt2x00crypto_tx_remove_iv(skb, iv_len); + } /* * It could be possible that the queue was corrupted and this -- cgit v1.2.3