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) spin_unlock_irqrestore(&wl->wl_lock, flags); } -#define TX_DUMMY_PACKET_SIZE 1400 int wl1271_tx_dummy_packet(struct wl1271 *wl) { - struct sk_buff *skb = NULL; + unsigned long flags; + + spin_lock_irqsave(&wl->wl_lock, flags); + set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags); + wl->tx_queue_count++; + spin_unlock_irqrestore(&wl->wl_lock, flags); + + /* The FW is low on RX memory blocks, so send the dummy packet asap */ + if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) + wl1271_tx_work_locked(wl); + + /* + * If the FW TX is busy, TX work will be scheduled by the threaded + * interrupt handler function + */ + return 0; +} + +/* + * The size of the dummy packet should be at least 1400 bytes. However, in + * order to minimize the number of bus transactions, aligning it to 512 bytes + * boundaries could be beneficial, performance wise + */ +#define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512)) + +struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) +{ + struct sk_buff *skb; struct ieee80211_hdr_3addr *hdr; - int ret = 0; + unsigned int dummy_packet_size; + + dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE - + sizeof(struct wl1271_tx_hw_descr) - sizeof(*hdr); - skb = dev_alloc_skb( - sizeof(struct wl1271_tx_hw_descr) + sizeof(*hdr) + - TX_DUMMY_PACKET_SIZE); + skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE); if (!skb) { - wl1271_warning("failed to allocate buffer for dummy packet"); - ret = -ENOMEM; - goto out; + wl1271_warning("Failed to allocate a dummy packet skb"); + return NULL; } skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr)); @@ -1233,29 +1259,22 @@ int wl1271_tx_dummy_packet(struct wl1271 *wl) hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr)); memset(hdr, 0, sizeof(*hdr)); hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_FCTL_TODS | - IEEE80211_STYPE_NULLFUNC); - - memcpy(hdr->addr1, wl->bssid, ETH_ALEN); - memcpy(hdr->addr2, wl->mac_addr, ETH_ALEN); - memcpy(hdr->addr3, wl->bssid, ETH_ALEN); + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_TODS); - skb_put(skb, TX_DUMMY_PACKET_SIZE); + memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size); - memset(skb->data, 0, TX_DUMMY_PACKET_SIZE); - - skb->pkt_type = TX_PKT_TYPE_DUMMY_REQ; /* Dummy packets require the TID to be management */ skb->priority = WL1271_TID_MGMT; - /* CONF_TX_AC_VO */ - skb->queue_mapping = 0; - wl1271_op_tx(wl->hw, skb); + /* Initialize all fields that might be used */ + skb->queue_mapping = 0; + memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info)); -out: - return ret; + return skb; } + static struct notifier_block wl1271_dev_notifier = { .notifier_call = wl1271_dev_notify, }; @@ -3653,11 +3672,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void) goto err_hw; } + wl->dummy_packet = wl12xx_alloc_dummy_packet(wl); + if (!wl->dummy_packet) { + ret = -ENOMEM; + goto err_aggr; + } + /* Register platform device */ ret = platform_device_register(wl->plat_dev); if (ret) { wl1271_error("couldn't register platform device"); - goto err_aggr; + goto err_dummy_packet; } dev_set_drvdata(&wl->plat_dev->dev, wl); @@ -3683,6 +3708,9 @@ err_bt_coex_state: err_platform: platform_device_unregister(wl->plat_dev); +err_dummy_packet: + dev_kfree_skb(wl->dummy_packet); + err_aggr: free_pages((unsigned long)wl->aggr_buf, order); @@ -3702,6 +3730,7 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw); int wl1271_free_hw(struct wl1271 *wl) { platform_device_unregister(wl->plat_dev); + dev_kfree_skb(wl->dummy_packet); free_pages((unsigned long)wl->aggr_buf, get_order(WL1271_AGGR_BUFFER_SIZE)); 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, return ret; } +static bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb) +{ + return wl->dummy_packet == skb; +} + static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, u32 extra, struct ieee80211_tx_info *control, u8 hlid) @@ -253,7 +258,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); desc->tid = skb->priority; - if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { + if (wl12xx_is_dummy_packet(wl, skb)) { /* * FW expects the dummy packet to have an invalid session id - * 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, memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len); memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len); + /* Revert side effects in the dummy packet skb, so it can be reused */ + if (wl12xx_is_dummy_packet(wl, skb)) + skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); + return total_len; } @@ -508,10 +517,23 @@ out: static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) { + unsigned long flags; + struct sk_buff *skb = NULL; + if (wl->bss_type == BSS_TYPE_AP_BSS) - return wl1271_ap_skb_dequeue(wl); + skb = wl1271_ap_skb_dequeue(wl); + else + skb = wl1271_sta_skb_dequeue(wl); - return wl1271_sta_skb_dequeue(wl); + if (!skb && + test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) { + skb = wl->dummy_packet; + spin_lock_irqsave(&wl->wl_lock, flags); + wl->tx_queue_count--; + spin_unlock_irqrestore(&wl->wl_lock, flags); + } + + return skb; } 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) unsigned long flags; int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); - if (wl->bss_type == BSS_TYPE_AP_BSS) { + if (wl12xx_is_dummy_packet(wl, skb)) { + set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags); + } else if (wl->bss_type == BSS_TYPE_AP_BSS) { u8 hlid = wl1271_tx_get_hlid(skb); skb_queue_head(&wl->links[hlid].tx_queue[q], skb); @@ -628,8 +652,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, skb = wl->tx_frames[id]; info = IEEE80211_SKB_CB(skb); - if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { - dev_kfree_skb(skb); + if (wl12xx_is_dummy_packet(wl, skb)) { wl1271_free_tx_id(wl, id); return; } @@ -764,9 +787,7 @@ void wl1271_tx_reset(struct wl1271 *wl) wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); - if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { - dev_kfree_skb(skb); - } else { + if (!wl12xx_is_dummy_packet(wl, skb)) { info = IEEE80211_SKB_CB(skb); info->status.rates[0].idx = -1; info->status.rates[0].count = 0; @@ -792,9 +813,7 @@ void wl1271_tx_reset(struct wl1271 *wl) wl1271_free_tx_id(wl, i); wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); - if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { - dev_kfree_skb(skb); - } else { + if (!wl12xx_is_dummy_packet(wl, skb)) { /* * Remove private headers before passing the skb to * 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 @@ #define TX_HW_ATTR_TX_CMPLT_REQ BIT(12) #define TX_HW_ATTR_TX_DUMMY_REQ BIT(13) -#define TX_PKT_TYPE_DUMMY_REQ 5 - #define TX_HW_ATTR_OFST_SAVE_RETRIES 0 #define TX_HW_ATTR_OFST_HEADER_PAD 1 #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 { WL1271_FLAG_FW_TX_BUSY, WL1271_FLAG_AP_STARTED, WL1271_FLAG_IF_INITIALIZED, + WL1271_FLAG_DUMMY_PACKET_PENDING, }; struct wl1271_link { @@ -461,6 +462,9 @@ struct wl1271 { /* Intermediate buffer, used for packet aggregation */ u8 *aggr_buf; + /* Reusable dummy packet template */ + struct sk_buff *dummy_packet; + /* Network stack work */ struct work_struct netstack_work; |