Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[safe/jmp/linux-2.6] / drivers / net / wireless / rt2x00 / rt2x00queue.c
index 06af823..9915a09 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,35 +149,89 @@ 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;
-               }
+       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)
+{
+       struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
+       unsigned int frame_length = skb->len;
+       unsigned int header_align = ALIGN_SIZE(skb, 0);
+       unsigned int payload_align = ALIGN_SIZE(skb, header_length);
+       unsigned int l2pad = 4 - (payload_align - header_align);
+
+       if (header_align == payload_align) {
+               /*
+                * Both header and payload must be moved the same
+                * amount of bytes to align them properly. This means
+                * we don't use the L2 padding but just move the entire
+                * frame.
+                */
+               rt2x00queue_align_frame(skb);
+       } else if (!payload_align) {
+               /*
+                * Simple L2 padding, only the header needs to be moved,
+                * the payload is already properly aligned.
+                */
+               skb_push(skb, header_align);
+               memmove(skb->data, skb->data + header_align, frame_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);
+               /*
+                *
+                * Complicated L2 padding, both header and payload need
+                * to be moved. By default we only move to the start
+                * of the buffer, so our header alignment needs to be
+                * increased if there is not enough room for the header
+                * to be moved.
+                */
+               if (payload_align > header_align)
+                       header_align += 4;
+
+               skb_push(skb, header_align);
+               memmove(skb->data, skb->data + header_align, header_length);
+               memmove(skb->data + header_length + l2pad,
+                       skb->data + header_length + l2pad + payload_align,
+                       frame_length - header_length);
+               skbdesc->flags |= SKBDESC_L2_PADDED;
        }
 }
 
+void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length)
+{
+       struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
+       unsigned int l2pad = 4 - (header_length & 3);
+
+       if (!l2pad || (skbdesc->flags & SKBDESC_L2_PADDED))
+               return;
+
+       memmove(skb->data + l2pad, skb->data, header_length);
+       skb_pull(skb, l2pad);
+}
+
 static void rt2x00queue_create_tx_descriptor_seq(struct queue_entry *entry,
                                                 struct txentry_desc *txdesc)
 {
@@ -399,7 +454,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,6 +496,9 @@ 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
@@ -456,18 +515,15 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb)
        /*
         * When DMA allocation is required we should guarentee to the
         * driver that the DMA is aligned to a 4-byte boundary.
-        * Aligning the header to this boundary can be done by calling
-        * rt2x00queue_payload_align with the header length of 0.
         * 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_payload_align(entry->skb, false, 0);
+               rt2x00queue_align_frame(entry->skb);
 
        /*
         * It could be possible that the queue was corrupted and this