Merge branch 'for-next' into for-linus
[safe/jmp/linux-2.6] / drivers / net / wireless / rt2x00 / rt2x00queue.c
index 65435c9..5b6b789 100644 (file)
@@ -1,5 +1,6 @@
 /*
-       Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+       Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+       Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
        <http://rt2x00.serialmonkey.com>
 
        This program is free software; you can redistribute it and/or modify
@@ -103,7 +104,7 @@ void rt2x00queue_map_txskb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb)
         * 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);
+       skb_push(skb, rt2x00dev->ops->extra_tx_headroom);
 
        skbdesc->skb_dma =
            dma_map_single(rt2x00dev->dev, skb->data, skb->len, DMA_TO_DEVICE);
@@ -111,7 +112,7 @@ void rt2x00queue_map_txskb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb)
        /*
         * Restore data pointer to original location again.
         */
-       skb_pull(skb, rt2x00dev->hw->extra_tx_headroom);
+       skb_pull(skb, rt2x00dev->ops->extra_tx_headroom);
 
        skbdesc->flags |= SKBDESC_DMA_MAPPED_TX;
 }
@@ -133,7 +134,7 @@ void rt2x00queue_unmap_skb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb)
                 * 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,
+                                skb->len + rt2x00dev->ops->extra_tx_headroom,
                                 DMA_TO_DEVICE);
                skbdesc->flags &= ~SKBDESC_DMA_MAPPED_TX;
        }
@@ -148,33 +149,77 @@ void rt2x00queue_free_skb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb)
        dev_kfree_skb_any(skb);
 }
 
-void rt2x00queue_payload_align(struct sk_buff *skb,
-                              bool l2pad, unsigned int header_length)
+void rt2x00queue_align_frame(struct sk_buff *skb)
+{
+       unsigned int frame_length = skb->len;
+       unsigned int align = ALIGN_SIZE(skb, 0);
+
+       if (!align)
+               return;
+
+       skb_push(skb, align);
+       memmove(skb->data, skb->data + align, frame_length);
+       skb_trim(skb, frame_length);
+}
+
+void rt2x00queue_align_payload(struct sk_buff *skb, unsigned int header_length)
 {
-       struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
        unsigned int frame_length = skb->len;
        unsigned int align = ALIGN_SIZE(skb, header_length);
 
        if (!align)
                return;
 
-       if (l2pad) {
-               if (skbdesc->flags & SKBDESC_L2_PADDED) {
-                       /* Remove L2 padding */
-                       memmove(skb->data + align, skb->data, header_length);
-                       skb_pull(skb, align);
-                       skbdesc->flags &= ~SKBDESC_L2_PADDED;
-               } else {
-                       /* Add L2 padding */
-                       skb_push(skb, align);
-                       memmove(skb->data, skb->data + align, header_length);
-                       skbdesc->flags |= SKBDESC_L2_PADDED;
-               }
-       } else {
-               /* Generic payload alignment to 4-byte boundary */
-               skb_push(skb, align);
-               memmove(skb->data, skb->data + align, frame_length);
-       }
+       skb_push(skb, align);
+       memmove(skb->data, skb->data + align, frame_length);
+       skb_trim(skb, frame_length);
+}
+
+void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length)
+{
+       unsigned int payload_length = skb->len - header_length;
+       unsigned int header_align = ALIGN_SIZE(skb, 0);
+       unsigned int payload_align = ALIGN_SIZE(skb, header_length);
+       unsigned int l2pad = payload_length ? L2PAD_SIZE(header_length) : 0;
+
+       /*
+        * Adjust the header alignment if the payload needs to be moved more
+        * than the header.
+        */
+       if (payload_align > header_align)
+               header_align += 4;
+
+       /* There is nothing to do if no alignment is needed */
+       if (!header_align)
+               return;
+
+       /* Reserve the amount of space needed in front of the frame */
+       skb_push(skb, header_align);
+
+       /*
+        * Move the header.
+        */
+       memmove(skb->data, skb->data + header_align, header_length);
+
+       /* Move the payload, if present and if required */
+       if (payload_length && payload_align)
+               memmove(skb->data + header_length + l2pad,
+                       skb->data + header_length + l2pad + payload_align,
+                       payload_length);
+
+       /* Trim the skb to the correct size */
+       skb_trim(skb, header_length + l2pad + payload_length);
+}
+
+void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length)
+{
+       unsigned int l2pad = L2PAD_SIZE(header_length);
+
+       if (!l2pad)
+               return;
+
+       memmove(skb->data + l2pad, skb->data, header_length);
+       skb_pull(skb, l2pad);
 }
 
 static void rt2x00queue_create_tx_descriptor_seq(struct queue_entry *entry,
@@ -291,7 +336,9 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
         * Header and alignment information.
         */
        txdesc->header_length = ieee80211_get_hdrlen_from_skb(entry->skb);
-       txdesc->l2pad = ALIGN_SIZE(entry->skb, txdesc->header_length);
+       if (test_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags) &&
+           (entry->skb->len > txdesc->header_length))
+               txdesc->l2pad = L2PAD_SIZE(txdesc->header_length);
 
        /*
         * Check whether this frame is to be acked.
@@ -332,10 +379,13 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
 
        /*
         * Beacons and probe responses require the tsf timestamp
-        * to be inserted into the frame.
+        * to be inserted into the frame, except for a frame that has been injected
+        * through a monitor interface. This latter is needed for testing a
+        * monitor interface.
         */
-       if (ieee80211_is_beacon(hdr->frame_control) ||
-           ieee80211_is_probe_resp(hdr->frame_control))
+       if ((ieee80211_is_beacon(hdr->frame_control) ||
+           ieee80211_is_probe_resp(hdr->frame_control)) &&
+           (!(tx_info->flags & IEEE80211_TX_CTL_INJECTED)))
                __set_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags);
 
        /*
@@ -399,7 +449,8 @@ static void rt2x00queue_write_tx_descriptor(struct queue_entry *entry,
                rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, queue->qid);
 }
 
-int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb)
+int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
+                              bool local)
 {
        struct ieee80211_tx_info *tx_info;
        struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX);
@@ -440,10 +491,13 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb)
        skbdesc->tx_rate_idx = rate_idx;
        skbdesc->tx_rate_flags = rate_flags;
 
+       if (local)
+               skbdesc->flags |= SKBDESC_NOT_MAC80211;
+
        /*
         * 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.
+        * the frame so we can provide it to the driver separately.
         */
        if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) &&
            !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) {
@@ -453,9 +507,18 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb)
                        rt2x00crypto_tx_remove_iv(skb, &txdesc);
        }
 
+       /*
+        * When DMA allocation is required we should guarentee to the
+        * driver that the DMA is aligned to a 4-byte boundary.
+        * However some drivers require L2 padding to pad the payload
+        * rather then the header. This could be a requirement for
+        * PCI and USB devices, while header alignment only is valid
+        * for PCI devices.
+        */
        if (test_bit(DRIVER_REQUIRE_L2PAD, &queue->rt2x00dev->flags))
-               rt2x00queue_payload_align(entry->skb, true,
-                                         txdesc.header_length);
+               rt2x00queue_insert_l2pad(entry->skb, txdesc.header_length);
+       else if (test_bit(DRIVER_REQUIRE_DMA, &queue->rt2x00dev->flags))
+               rt2x00queue_align_frame(entry->skb);
 
        /*
         * It could be possible that the queue was corrupted and this
@@ -491,14 +554,25 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
        if (unlikely(!intf->beacon))
                return -ENOBUFS;
 
+       mutex_lock(&intf->beacon_skb_mutex);
+
+       /*
+        * Clean up the beacon skb.
+        */
+       rt2x00queue_free_skb(rt2x00dev, intf->beacon->skb);
+       intf->beacon->skb = NULL;
+
        if (!enable_beacon) {
                rt2x00dev->ops->lib->kill_tx_queue(rt2x00dev, QID_BEACON);
+               mutex_unlock(&intf->beacon_skb_mutex);
                return 0;
        }
 
        intf->beacon->skb = ieee80211_beacon_get(rt2x00dev->hw, vif);
-       if (!intf->beacon->skb)
+       if (!intf->beacon->skb) {
+               mutex_unlock(&intf->beacon_skb_mutex);
                return -ENOMEM;
+       }
 
        /*
         * Copy all TX descriptor information into txdesc,
@@ -536,6 +610,8 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
        rt2x00dev->ops->lib->write_beacon(intf->beacon);
        rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, QID_BEACON);
 
+       mutex_unlock(&intf->beacon_skb_mutex);
+
        return 0;
 }