aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/rt2x00/rt2x00queue.c
diff options
context:
space:
mode:
authorIvo van Doorn <ivdoorn@gmail.com>2009-08-29 20:30:45 +0200
committerJohn W. Linville <linville@tuxdriver.com>2009-08-31 14:42:14 -0400
commitdaee6c092aa49ea090612738253ef0d11d120344 (patch)
tree97ab49a8a465b2e0fa470a6e9c5f0264e5db94d3 /drivers/net/wireless/rt2x00/rt2x00queue.c
parent2e27cff871dec9371e41022aaaebb3452ec069c0 (diff)
rt2x00: Reorganize padding & L2 padding
The old function rt2x00queue_payload_align() handled both adding and removing L2 padding and some basic frame alignment. The entire function was being abused because it had multiple functions and the header length argument was somtimes used to align the header instead of the payload. Additionally there was a bug when inserting L2 padding that only the payload was aligned but not the header. This happens when the header wasn't aligned properly by mac80211, but rt2x00lib only moves the payload. A secondary problem was that when removing L2 padding during TXdone or RX the skb wasn't resized to the proper size. Split the function into seperate functions each handling its task as it should. Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2x00queue.c')
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c99
1 files changed, 75 insertions, 24 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 06af823efd8..577029efe32 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -148,35 +148,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)
{
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
unsigned int frame_length = skb->len;
- unsigned int align = ALIGN_SIZE(skb, header_length);
+ unsigned int align = ALIGN_SIZE(skb, 0);
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_align_payload(struct sk_buff *skb, unsigned int header_lengt)
+{
+ unsigned int frame_length = skb->len;
+ unsigned int align = ALIGN_SIZE(skb, header_lengt);
+
+ if (!align)
+ return;
+
+ 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 + header_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)
{
@@ -456,18 +510,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