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));
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,
};
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);
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);
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);
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)
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
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;
}
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)
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);
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;
}
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;
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