diff options
-rw-r--r-- | drivers/net/wireless/wl12xx/main.c | 79 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/tx.c | 43 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/tx.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl12xx.h | 4 |
4 files changed, 89 insertions, 39 deletions
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 1cd396306e7b..7dce24c0b331 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c | |||
@@ -1212,20 +1212,46 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
1212 | spin_unlock_irqrestore(&wl->wl_lock, flags); | 1212 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
1213 | } | 1213 | } |
1214 | 1214 | ||
1215 | #define TX_DUMMY_PACKET_SIZE 1400 | ||
1216 | int wl1271_tx_dummy_packet(struct wl1271 *wl) | 1215 | int wl1271_tx_dummy_packet(struct wl1271 *wl) |
1217 | { | 1216 | { |
1218 | struct sk_buff *skb = NULL; | 1217 | unsigned long flags; |
1218 | |||
1219 | spin_lock_irqsave(&wl->wl_lock, flags); | ||
1220 | set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags); | ||
1221 | wl->tx_queue_count++; | ||
1222 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
1223 | |||
1224 | /* The FW is low on RX memory blocks, so send the dummy packet asap */ | ||
1225 | if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) | ||
1226 | wl1271_tx_work_locked(wl); | ||
1227 | |||
1228 | /* | ||
1229 | * If the FW TX is busy, TX work will be scheduled by the threaded | ||
1230 | * interrupt handler function | ||
1231 | */ | ||
1232 | return 0; | ||
1233 | } | ||
1234 | |||
1235 | /* | ||
1236 | * The size of the dummy packet should be at least 1400 bytes. However, in | ||
1237 | * order to minimize the number of bus transactions, aligning it to 512 bytes | ||
1238 | * boundaries could be beneficial, performance wise | ||
1239 | */ | ||
1240 | #define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512)) | ||
1241 | |||
1242 | struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) | ||
1243 | { | ||
1244 | struct sk_buff *skb; | ||
1219 | struct ieee80211_hdr_3addr *hdr; | 1245 | struct ieee80211_hdr_3addr *hdr; |
1220 | int ret = 0; | 1246 | unsigned int dummy_packet_size; |
1247 | |||
1248 | dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE - | ||
1249 | sizeof(struct wl1271_tx_hw_descr) - sizeof(*hdr); | ||
1221 | 1250 | ||
1222 | skb = dev_alloc_skb( | 1251 | skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE); |
1223 | sizeof(struct wl1271_tx_hw_descr) + sizeof(*hdr) + | ||
1224 | TX_DUMMY_PACKET_SIZE); | ||
1225 | if (!skb) { | 1252 | if (!skb) { |
1226 | wl1271_warning("failed to allocate buffer for dummy packet"); | 1253 | wl1271_warning("Failed to allocate a dummy packet skb"); |
1227 | ret = -ENOMEM; | 1254 | return NULL; |
1228 | goto out; | ||
1229 | } | 1255 | } |
1230 | 1256 | ||
1231 | skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr)); | 1257 | skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr)); |
@@ -1233,29 +1259,22 @@ int wl1271_tx_dummy_packet(struct wl1271 *wl) | |||
1233 | hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr)); | 1259 | hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr)); |
1234 | memset(hdr, 0, sizeof(*hdr)); | 1260 | memset(hdr, 0, sizeof(*hdr)); |
1235 | hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | | 1261 | hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | |
1236 | IEEE80211_FCTL_TODS | | 1262 | IEEE80211_STYPE_NULLFUNC | |
1237 | IEEE80211_STYPE_NULLFUNC); | 1263 | IEEE80211_FCTL_TODS); |
1238 | |||
1239 | memcpy(hdr->addr1, wl->bssid, ETH_ALEN); | ||
1240 | memcpy(hdr->addr2, wl->mac_addr, ETH_ALEN); | ||
1241 | memcpy(hdr->addr3, wl->bssid, ETH_ALEN); | ||
1242 | 1264 | ||
1243 | skb_put(skb, TX_DUMMY_PACKET_SIZE); | 1265 | memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size); |
1244 | 1266 | ||
1245 | memset(skb->data, 0, TX_DUMMY_PACKET_SIZE); | ||
1246 | |||
1247 | skb->pkt_type = TX_PKT_TYPE_DUMMY_REQ; | ||
1248 | /* Dummy packets require the TID to be management */ | 1267 | /* Dummy packets require the TID to be management */ |
1249 | skb->priority = WL1271_TID_MGMT; | 1268 | skb->priority = WL1271_TID_MGMT; |
1250 | /* CONF_TX_AC_VO */ | ||
1251 | skb->queue_mapping = 0; | ||
1252 | 1269 | ||
1253 | wl1271_op_tx(wl->hw, skb); | 1270 | /* Initialize all fields that might be used */ |
1271 | skb->queue_mapping = 0; | ||
1272 | memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info)); | ||
1254 | 1273 | ||
1255 | out: | 1274 | return skb; |
1256 | return ret; | ||
1257 | } | 1275 | } |
1258 | 1276 | ||
1277 | |||
1259 | static struct notifier_block wl1271_dev_notifier = { | 1278 | static struct notifier_block wl1271_dev_notifier = { |
1260 | .notifier_call = wl1271_dev_notify, | 1279 | .notifier_call = wl1271_dev_notify, |
1261 | }; | 1280 | }; |
@@ -3653,11 +3672,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void) | |||
3653 | goto err_hw; | 3672 | goto err_hw; |
3654 | } | 3673 | } |
3655 | 3674 | ||
3675 | wl->dummy_packet = wl12xx_alloc_dummy_packet(wl); | ||
3676 | if (!wl->dummy_packet) { | ||
3677 | ret = -ENOMEM; | ||
3678 | goto err_aggr; | ||
3679 | } | ||
3680 | |||
3656 | /* Register platform device */ | 3681 | /* Register platform device */ |
3657 | ret = platform_device_register(wl->plat_dev); | 3682 | ret = platform_device_register(wl->plat_dev); |
3658 | if (ret) { | 3683 | if (ret) { |
3659 | wl1271_error("couldn't register platform device"); | 3684 | wl1271_error("couldn't register platform device"); |
3660 | goto err_aggr; | 3685 | goto err_dummy_packet; |
3661 | } | 3686 | } |
3662 | dev_set_drvdata(&wl->plat_dev->dev, wl); | 3687 | dev_set_drvdata(&wl->plat_dev->dev, wl); |
3663 | 3688 | ||
@@ -3683,6 +3708,9 @@ err_bt_coex_state: | |||
3683 | err_platform: | 3708 | err_platform: |
3684 | platform_device_unregister(wl->plat_dev); | 3709 | platform_device_unregister(wl->plat_dev); |
3685 | 3710 | ||
3711 | err_dummy_packet: | ||
3712 | dev_kfree_skb(wl->dummy_packet); | ||
3713 | |||
3686 | err_aggr: | 3714 | err_aggr: |
3687 | free_pages((unsigned long)wl->aggr_buf, order); | 3715 | free_pages((unsigned long)wl->aggr_buf, order); |
3688 | 3716 | ||
@@ -3702,6 +3730,7 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw); | |||
3702 | int wl1271_free_hw(struct wl1271 *wl) | 3730 | int wl1271_free_hw(struct wl1271 *wl) |
3703 | { | 3731 | { |
3704 | platform_device_unregister(wl->plat_dev); | 3732 | platform_device_unregister(wl->plat_dev); |
3733 | dev_kfree_skb(wl->dummy_packet); | ||
3705 | free_pages((unsigned long)wl->aggr_buf, | 3734 | free_pages((unsigned long)wl->aggr_buf, |
3706 | get_order(WL1271_AGGR_BUFFER_SIZE)); | 3735 | get_order(WL1271_AGGR_BUFFER_SIZE)); |
3707 | kfree(wl->plat_dev); | 3736 | kfree(wl->plat_dev); |
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index ba69ba7051fa..67245ad396ea 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c | |||
@@ -219,6 +219,11 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, | |||
219 | return ret; | 219 | return ret; |
220 | } | 220 | } |
221 | 221 | ||
222 | static bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb) | ||
223 | { | ||
224 | return wl->dummy_packet == skb; | ||
225 | } | ||
226 | |||
222 | static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, | 227 | static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, |
223 | u32 extra, struct ieee80211_tx_info *control, | 228 | u32 extra, struct ieee80211_tx_info *control, |
224 | u8 hlid) | 229 | u8 hlid) |
@@ -253,7 +258,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, | |||
253 | ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); | 258 | ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); |
254 | desc->tid = skb->priority; | 259 | desc->tid = skb->priority; |
255 | 260 | ||
256 | if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { | 261 | if (wl12xx_is_dummy_packet(wl, skb)) { |
257 | /* | 262 | /* |
258 | * FW expects the dummy packet to have an invalid session id - | 263 | * FW expects the dummy packet to have an invalid session id - |
259 | * any session id that is different than the one set in the join | 264 | * any session id that is different than the one set in the join |
@@ -396,6 +401,10 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, | |||
396 | memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len); | 401 | memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len); |
397 | memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len); | 402 | memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len); |
398 | 403 | ||
404 | /* Revert side effects in the dummy packet skb, so it can be reused */ | ||
405 | if (wl12xx_is_dummy_packet(wl, skb)) | ||
406 | skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); | ||
407 | |||
399 | return total_len; | 408 | return total_len; |
400 | } | 409 | } |
401 | 410 | ||
@@ -508,10 +517,23 @@ out: | |||
508 | 517 | ||
509 | static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) | 518 | static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) |
510 | { | 519 | { |
520 | unsigned long flags; | ||
521 | struct sk_buff *skb = NULL; | ||
522 | |||
511 | if (wl->bss_type == BSS_TYPE_AP_BSS) | 523 | if (wl->bss_type == BSS_TYPE_AP_BSS) |
512 | return wl1271_ap_skb_dequeue(wl); | 524 | skb = wl1271_ap_skb_dequeue(wl); |
525 | else | ||
526 | skb = wl1271_sta_skb_dequeue(wl); | ||
513 | 527 | ||
514 | return wl1271_sta_skb_dequeue(wl); | 528 | if (!skb && |
529 | test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) { | ||
530 | skb = wl->dummy_packet; | ||
531 | spin_lock_irqsave(&wl->wl_lock, flags); | ||
532 | wl->tx_queue_count--; | ||
533 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
534 | } | ||
535 | |||
536 | return skb; | ||
515 | } | 537 | } |
516 | 538 | ||
517 | static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) | 539 | static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) |
@@ -519,7 +541,9 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) | |||
519 | unsigned long flags; | 541 | unsigned long flags; |
520 | int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); | 542 | int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); |
521 | 543 | ||
522 | if (wl->bss_type == BSS_TYPE_AP_BSS) { | 544 | if (wl12xx_is_dummy_packet(wl, skb)) { |
545 | set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags); | ||
546 | } else if (wl->bss_type == BSS_TYPE_AP_BSS) { | ||
523 | u8 hlid = wl1271_tx_get_hlid(skb); | 547 | u8 hlid = wl1271_tx_get_hlid(skb); |
524 | skb_queue_head(&wl->links[hlid].tx_queue[q], skb); | 548 | skb_queue_head(&wl->links[hlid].tx_queue[q], skb); |
525 | 549 | ||
@@ -628,8 +652,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, | |||
628 | skb = wl->tx_frames[id]; | 652 | skb = wl->tx_frames[id]; |
629 | info = IEEE80211_SKB_CB(skb); | 653 | info = IEEE80211_SKB_CB(skb); |
630 | 654 | ||
631 | if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { | 655 | if (wl12xx_is_dummy_packet(wl, skb)) { |
632 | dev_kfree_skb(skb); | ||
633 | wl1271_free_tx_id(wl, id); | 656 | wl1271_free_tx_id(wl, id); |
634 | return; | 657 | return; |
635 | } | 658 | } |
@@ -764,9 +787,7 @@ void wl1271_tx_reset(struct wl1271 *wl) | |||
764 | wl1271_debug(DEBUG_TX, "freeing skb 0x%p", | 787 | wl1271_debug(DEBUG_TX, "freeing skb 0x%p", |
765 | skb); | 788 | skb); |
766 | 789 | ||
767 | if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { | 790 | if (!wl12xx_is_dummy_packet(wl, skb)) { |
768 | dev_kfree_skb(skb); | ||
769 | } else { | ||
770 | info = IEEE80211_SKB_CB(skb); | 791 | info = IEEE80211_SKB_CB(skb); |
771 | info->status.rates[0].idx = -1; | 792 | info->status.rates[0].idx = -1; |
772 | info->status.rates[0].count = 0; | 793 | info->status.rates[0].count = 0; |
@@ -792,9 +813,7 @@ void wl1271_tx_reset(struct wl1271 *wl) | |||
792 | wl1271_free_tx_id(wl, i); | 813 | wl1271_free_tx_id(wl, i); |
793 | wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); | 814 | wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); |
794 | 815 | ||
795 | if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { | 816 | if (!wl12xx_is_dummy_packet(wl, skb)) { |
796 | dev_kfree_skb(skb); | ||
797 | } else { | ||
798 | /* | 817 | /* |
799 | * Remove private headers before passing the skb to | 818 | * Remove private headers before passing the skb to |
800 | * mac80211 | 819 | * mac80211 |
diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index d6b05d980626..fc7835c4cf63 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h | |||
@@ -42,8 +42,6 @@ | |||
42 | #define TX_HW_ATTR_TX_CMPLT_REQ BIT(12) | 42 | #define TX_HW_ATTR_TX_CMPLT_REQ BIT(12) |
43 | #define TX_HW_ATTR_TX_DUMMY_REQ BIT(13) | 43 | #define TX_HW_ATTR_TX_DUMMY_REQ BIT(13) |
44 | 44 | ||
45 | #define TX_PKT_TYPE_DUMMY_REQ 5 | ||
46 | |||
47 | #define TX_HW_ATTR_OFST_SAVE_RETRIES 0 | 45 | #define TX_HW_ATTR_OFST_SAVE_RETRIES 0 |
48 | #define TX_HW_ATTR_OFST_HEADER_PAD 1 | 46 | #define TX_HW_ATTR_OFST_HEADER_PAD 1 |
49 | #define TX_HW_ATTR_OFST_SESSION_COUNTER 2 | 47 | #define TX_HW_ATTR_OFST_SESSION_COUNTER 2 |
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index c7c42b687f5e..1b430d2aec4e 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h | |||
@@ -356,6 +356,7 @@ enum wl12xx_flags { | |||
356 | WL1271_FLAG_FW_TX_BUSY, | 356 | WL1271_FLAG_FW_TX_BUSY, |
357 | WL1271_FLAG_AP_STARTED, | 357 | WL1271_FLAG_AP_STARTED, |
358 | WL1271_FLAG_IF_INITIALIZED, | 358 | WL1271_FLAG_IF_INITIALIZED, |
359 | WL1271_FLAG_DUMMY_PACKET_PENDING, | ||
359 | }; | 360 | }; |
360 | 361 | ||
361 | struct wl1271_link { | 362 | struct wl1271_link { |
@@ -461,6 +462,9 @@ struct wl1271 { | |||
461 | /* Intermediate buffer, used for packet aggregation */ | 462 | /* Intermediate buffer, used for packet aggregation */ |
462 | u8 *aggr_buf; | 463 | u8 *aggr_buf; |
463 | 464 | ||
465 | /* Reusable dummy packet template */ | ||
466 | struct sk_buff *dummy_packet; | ||
467 | |||
464 | /* Network stack work */ | 468 | /* Network stack work */ |
465 | struct work_struct netstack_work; | 469 | struct work_struct netstack_work; |
466 | 470 | ||