diff options
author | Ido Yariv <ido@wizery.com> | 2010-09-30 07:28:27 -0400 |
---|---|---|
committer | Luciano Coelho <luciano.coelho@nokia.com> | 2010-10-05 09:27:29 -0400 |
commit | 1f37cbc9363462c99794699442da39f36be0aaf7 (patch) | |
tree | 3f7bf42c5fa78b08010071da8f1977234e255c64 | |
parent | 5c57a901dc96fc81d0041282269b43542f170d2a (diff) |
wl1271: Support firmware RX packet aggregation
Instead of retrieving one packet at a time from the firmware, try to
retrieve all available packets at once.
This optimization decreases the number of transactions, which saves
CPU cycles and increases network throughput.
Signed-off-by: Ido Yariv <ido@wizery.com>
Tested-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_main.c | 15 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_rx.c | 63 |
3 files changed, 66 insertions, 17 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 779b130fdb3..8a4cd763e5a 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h | |||
@@ -130,6 +130,8 @@ enum { | |||
130 | 130 | ||
131 | #define ACX_TX_DESCRIPTORS 32 | 131 | #define ACX_TX_DESCRIPTORS 32 |
132 | 132 | ||
133 | #define WL1271_AGGR_BUFFER_SIZE (4 * PAGE_SIZE) | ||
134 | |||
133 | enum wl1271_state { | 135 | enum wl1271_state { |
134 | WL1271_STATE_OFF, | 136 | WL1271_STATE_OFF, |
135 | WL1271_STATE_ON, | 137 | WL1271_STATE_ON, |
@@ -408,6 +410,9 @@ struct wl1271 { | |||
408 | /* Rx memory pool address */ | 410 | /* Rx memory pool address */ |
409 | struct wl1271_rx_mem_pool_addr rx_mem_pool_addr; | 411 | struct wl1271_rx_mem_pool_addr rx_mem_pool_addr; |
410 | 412 | ||
413 | /* Intermediate buffer, used for packet aggregation */ | ||
414 | u8 *aggr_buf; | ||
415 | |||
411 | /* The target interrupt mask */ | 416 | /* The target interrupt mask */ |
412 | struct work_struct irq_work; | 417 | struct work_struct irq_work; |
413 | 418 | ||
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index cb18f22bbc5..8071da10dbc 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c | |||
@@ -2459,6 +2459,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) | |||
2459 | struct platform_device *plat_dev = NULL; | 2459 | struct platform_device *plat_dev = NULL; |
2460 | struct wl1271 *wl; | 2460 | struct wl1271 *wl; |
2461 | int i, ret; | 2461 | int i, ret; |
2462 | unsigned int order; | ||
2462 | 2463 | ||
2463 | hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); | 2464 | hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); |
2464 | if (!hw) { | 2465 | if (!hw) { |
@@ -2517,11 +2518,18 @@ struct ieee80211_hw *wl1271_alloc_hw(void) | |||
2517 | 2518 | ||
2518 | wl1271_debugfs_init(wl); | 2519 | wl1271_debugfs_init(wl); |
2519 | 2520 | ||
2521 | order = get_order(WL1271_AGGR_BUFFER_SIZE); | ||
2522 | wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order); | ||
2523 | if (!wl->aggr_buf) { | ||
2524 | ret = -ENOMEM; | ||
2525 | goto err_hw; | ||
2526 | } | ||
2527 | |||
2520 | /* Register platform device */ | 2528 | /* Register platform device */ |
2521 | ret = platform_device_register(wl->plat_dev); | 2529 | ret = platform_device_register(wl->plat_dev); |
2522 | if (ret) { | 2530 | if (ret) { |
2523 | wl1271_error("couldn't register platform device"); | 2531 | wl1271_error("couldn't register platform device"); |
2524 | goto err_hw; | 2532 | goto err_aggr; |
2525 | } | 2533 | } |
2526 | dev_set_drvdata(&wl->plat_dev->dev, wl); | 2534 | dev_set_drvdata(&wl->plat_dev->dev, wl); |
2527 | 2535 | ||
@@ -2547,6 +2555,9 @@ err_bt_coex_state: | |||
2547 | err_platform: | 2555 | err_platform: |
2548 | platform_device_unregister(wl->plat_dev); | 2556 | platform_device_unregister(wl->plat_dev); |
2549 | 2557 | ||
2558 | err_aggr: | ||
2559 | free_pages((unsigned long)wl->aggr_buf, order); | ||
2560 | |||
2550 | err_hw: | 2561 | err_hw: |
2551 | wl1271_debugfs_exit(wl); | 2562 | wl1271_debugfs_exit(wl); |
2552 | kfree(plat_dev); | 2563 | kfree(plat_dev); |
@@ -2563,6 +2574,8 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw); | |||
2563 | int wl1271_free_hw(struct wl1271 *wl) | 2574 | int wl1271_free_hw(struct wl1271 *wl) |
2564 | { | 2575 | { |
2565 | platform_device_unregister(wl->plat_dev); | 2576 | platform_device_unregister(wl->plat_dev); |
2577 | free_pages((unsigned long)wl->aggr_buf, | ||
2578 | get_order(WL1271_AGGR_BUFFER_SIZE)); | ||
2566 | kfree(wl->plat_dev); | 2579 | kfree(wl->plat_dev); |
2567 | 2580 | ||
2568 | wl1271_debugfs_exit(wl); | 2581 | wl1271_debugfs_exit(wl); |
diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c index 94da5dd7723..bea133b6e48 100644 --- a/drivers/net/wireless/wl12xx/wl1271_rx.c +++ b/drivers/net/wireless/wl12xx/wl1271_rx.c | |||
@@ -74,7 +74,7 @@ static void wl1271_rx_status(struct wl1271 *wl, | |||
74 | } | 74 | } |
75 | } | 75 | } |
76 | 76 | ||
77 | static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) | 77 | static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) |
78 | { | 78 | { |
79 | struct wl1271_rx_descriptor *desc; | 79 | struct wl1271_rx_descriptor *desc; |
80 | struct sk_buff *skb; | 80 | struct sk_buff *skb; |
@@ -87,16 +87,16 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) | |||
87 | * workaround this by not retrieving them at all. | 87 | * workaround this by not retrieving them at all. |
88 | */ | 88 | */ |
89 | if (unlikely(wl->state == WL1271_STATE_PLT)) | 89 | if (unlikely(wl->state == WL1271_STATE_PLT)) |
90 | return; | 90 | return -EINVAL; |
91 | 91 | ||
92 | skb = __dev_alloc_skb(length, GFP_KERNEL); | 92 | skb = __dev_alloc_skb(length, GFP_KERNEL); |
93 | if (!skb) { | 93 | if (!skb) { |
94 | wl1271_error("Couldn't allocate RX frame"); | 94 | wl1271_error("Couldn't allocate RX frame"); |
95 | return; | 95 | return -ENOMEM; |
96 | } | 96 | } |
97 | 97 | ||
98 | buf = skb_put(skb, length); | 98 | buf = skb_put(skb, length); |
99 | wl1271_read(wl, WL1271_SLV_MEM_DATA, buf, length, true); | 99 | memcpy(buf, data, length); |
100 | 100 | ||
101 | /* the data read starts with the descriptor */ | 101 | /* the data read starts with the descriptor */ |
102 | desc = (struct wl1271_rx_descriptor *) buf; | 102 | desc = (struct wl1271_rx_descriptor *) buf; |
@@ -116,6 +116,8 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) | |||
116 | skb_trim(skb, skb->len - desc->pad_len); | 116 | skb_trim(skb, skb->len - desc->pad_len); |
117 | 117 | ||
118 | ieee80211_rx_ni(wl->hw, skb); | 118 | ieee80211_rx_ni(wl->hw, skb); |
119 | |||
120 | return 0; | ||
119 | } | 121 | } |
120 | 122 | ||
121 | void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) | 123 | void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) |
@@ -124,31 +126,60 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) | |||
124 | u32 buf_size; | 126 | u32 buf_size; |
125 | u32 fw_rx_counter = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK; | 127 | u32 fw_rx_counter = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK; |
126 | u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; | 128 | u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; |
129 | u32 rx_counter; | ||
127 | u32 mem_block; | 130 | u32 mem_block; |
131 | u32 pkt_length; | ||
132 | u32 pkt_offset; | ||
128 | 133 | ||
129 | while (drv_rx_counter != fw_rx_counter) { | 134 | while (drv_rx_counter != fw_rx_counter) { |
130 | mem_block = wl1271_rx_get_mem_block(status, drv_rx_counter); | 135 | buf_size = 0; |
131 | buf_size = wl1271_rx_get_buf_size(status, drv_rx_counter); | 136 | rx_counter = drv_rx_counter; |
137 | while (rx_counter != fw_rx_counter) { | ||
138 | pkt_length = wl1271_rx_get_buf_size(status, rx_counter); | ||
139 | if (buf_size + pkt_length > WL1271_AGGR_BUFFER_SIZE) | ||
140 | break; | ||
141 | buf_size += pkt_length; | ||
142 | rx_counter++; | ||
143 | rx_counter &= NUM_RX_PKT_DESC_MOD_MASK; | ||
144 | } | ||
132 | 145 | ||
133 | if (buf_size == 0) { | 146 | if (buf_size == 0) { |
134 | wl1271_warning("received empty data"); | 147 | wl1271_warning("received empty data"); |
135 | break; | 148 | break; |
136 | } | 149 | } |
137 | 150 | ||
151 | /* | ||
152 | * Choose the block we want to read | ||
153 | * For aggregated packets, only the first memory block should | ||
154 | * be retrieved. The FW takes care of the rest. | ||
155 | */ | ||
156 | mem_block = wl1271_rx_get_mem_block(status, drv_rx_counter); | ||
138 | wl->rx_mem_pool_addr.addr = (mem_block << 8) + | 157 | wl->rx_mem_pool_addr.addr = (mem_block << 8) + |
139 | le32_to_cpu(wl_mem_map->packet_memory_pool_start); | 158 | le32_to_cpu(wl_mem_map->packet_memory_pool_start); |
140 | wl->rx_mem_pool_addr.addr_extra = | 159 | wl->rx_mem_pool_addr.addr_extra = |
141 | wl->rx_mem_pool_addr.addr + 4; | 160 | wl->rx_mem_pool_addr.addr + 4; |
142 | |||
143 | /* Choose the block we want to read */ | ||
144 | wl1271_write(wl, WL1271_SLV_REG_DATA, &wl->rx_mem_pool_addr, | 161 | wl1271_write(wl, WL1271_SLV_REG_DATA, &wl->rx_mem_pool_addr, |
145 | sizeof(wl->rx_mem_pool_addr), false); | 162 | sizeof(wl->rx_mem_pool_addr), false); |
146 | 163 | ||
147 | wl1271_rx_handle_data(wl, buf_size); | 164 | /* Read all available packets at once */ |
148 | 165 | wl1271_read(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf, | |
149 | wl->rx_counter++; | 166 | buf_size, true); |
150 | drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; | 167 | |
168 | /* Split data into separate packets */ | ||
169 | pkt_offset = 0; | ||
170 | while (pkt_offset < buf_size) { | ||
171 | pkt_length = wl1271_rx_get_buf_size(status, | ||
172 | drv_rx_counter); | ||
173 | if (wl1271_rx_handle_data(wl, | ||
174 | wl->aggr_buf + pkt_offset, | ||
175 | pkt_length) < 0) | ||
176 | break; | ||
177 | wl->rx_counter++; | ||
178 | drv_rx_counter++; | ||
179 | drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK; | ||
180 | pkt_offset += pkt_length; | ||
181 | } | ||
151 | } | 182 | } |
152 | 183 | wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, | |
153 | wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); | 184 | cpu_to_le32(wl->rx_counter)); |
154 | } | 185 | } |