* @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver.
  * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue.
  * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware.
+ * @BRCMF_FWS_SKBSTATE_TIM: allocated for TIM update info.
  */
 enum brcmf_fws_skb_state {
        BRCMF_FWS_SKBSTATE_NEW,
        BRCMF_FWS_SKBSTATE_DELAYED,
-       BRCMF_FWS_SKBSTATE_SUPPRESSED
+       BRCMF_FWS_SKBSTATE_SUPPRESSED,
+       BRCMF_FWS_SKBSTATE_TIM
 };
 
 /**
 /**
  * enum brcmf_fws_fifo - fifo indices used by dongle firmware.
  *
+ * @BRCMF_FWS_FIFO_FIRST: first fifo, ie. background.
  * @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic.
  * @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic.
  * @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic.
  * @BRCMF_FWS_FIFO_COUNT: number of fifos.
  */
 enum brcmf_fws_fifo {
-       BRCMF_FWS_FIFO_AC_BK,
+       BRCMF_FWS_FIFO_FIRST,
+       BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST,
        BRCMF_FWS_FIFO_AC_BE,
        BRCMF_FWS_FIFO_AC_VI,
        BRCMF_FWS_FIFO_AC_VO,
        brcmf_fws_hanger_cleanup(fws, matchfn, ifidx);
 }
 
-static void brcmf_fws_tim_update(struct brcmf_fws_info *ctx,
-                                struct brcmf_fws_mac_descriptor *entry,
-                                int prec)
+static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
 {
-       if (entry->state == BRCMF_FWS_STATE_CLOSE) {
-               /* check delayedQ and suppressQ in one call using bitmap */
-               if (brcmu_pktq_mlen(&entry->psq, 3 << (prec * 2)) == 0)
-                       entry->traffic_pending_bmp =
-                               entry->traffic_pending_bmp & ~NBITVAL(prec);
-               else
-                       entry->traffic_pending_bmp =
-                               entry->traffic_pending_bmp | NBITVAL(prec);
+       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+       u8 *wlh;
+       u16 data_offset = 0;
+       u8 fillers;
+       __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
+
+       brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u (%u), pkttag=0x%08X, hslot=%d\n",
+                 entry->ea, entry->interface_id,
+                 brcmf_skb_if_flags_get_field(skb, INDEX),
+                 le32_to_cpu(pkttag), (le32_to_cpu(pkttag) >> 8) & 0xffff);
+       if (entry->send_tim_signal)
+               data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
+
+       /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
+       data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
+       fillers = round_up(data_offset, 4) - data_offset;
+       data_offset += fillers;
+
+       skb_push(skb, data_offset);
+       wlh = skb->data;
+
+       wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
+       wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
+       memcpy(&wlh[2], &pkttag, sizeof(pkttag));
+       wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2;
+
+       if (entry->send_tim_signal) {
+               entry->send_tim_signal = 0;
+               wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
+               wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
+               wlh[2] = entry->mac_handle;
+               wlh[3] = entry->traffic_pending_bmp;
+               brcmf_dbg(TRACE, "adding TIM info: %02X:%02X:%02X:%02X\n",
+                         wlh[0], wlh[1], wlh[2], wlh[3]);
+               wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
+               entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
        }
-       /* request a TIM update to firmware at the next piggyback opportunity */
+       if (fillers)
+               memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);
+
+       brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX),
+                           data_offset >> 2, skb);
+       return 0;
+}
+
+static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
+                                struct brcmf_fws_mac_descriptor *entry,
+                                int prec, bool send_immediately)
+{
+       struct sk_buff *skb;
+       struct brcmf_bus *bus;
+       struct brcmf_skbuff_cb *skcb;
+       s32 err;
+       u32 len;
+
+       /* check delayedQ and suppressQ in one call using bitmap */
+       if (brcmu_pktq_mlen(&entry->psq, 3 << (prec * 2)) == 0)
+               entry->traffic_pending_bmp &= ~NBITVAL(prec);
+       else
+               entry->traffic_pending_bmp |= NBITVAL(prec);
+
+       entry->send_tim_signal = false;
        if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp)
                entry->send_tim_signal = true;
+       if (send_immediately && entry->send_tim_signal &&
+           entry->state == BRCMF_FWS_STATE_CLOSE) {
+               /* create a dummy packet and sent that. The traffic          */
+               /* bitmap info will automatically be attached to that packet */
+               len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 +
+                     BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 +
+                     4 + fws->drvr->hdrlen;
+               skb = brcmu_pkt_buf_get_skb(len);
+               if (skb == NULL)
+                       return false;
+               skb_pull(skb, len);
+               skcb = brcmf_skbcb(skb);
+               skcb->mac = entry;
+               skcb->state = BRCMF_FWS_SKBSTATE_TIM;
+               bus = fws->drvr->bus_if;
+               err = brcmf_fws_hdrpush(fws, skb);
+               if (err == 0)
+                       err = brcmf_bus_txdata(bus, skb);
+               if (err)
+                       brcmu_pkt_buf_free_skb(skb);
+               return true;
+       }
+       return false;
 }
 
 static void
 {
        struct brcmf_fws_mac_descriptor *entry;
        u8 mac_handle;
-       int i;
 
        mac_handle = data[0];
        entry = &fws->desc.nodes[mac_handle & 0x1F];
                fws->stats.mac_ps_update_failed++;
                return -ESRCH;
        }
-
-       brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
-                 entry->name);
-       /* a state update should wipe old credits? */
+       /* a state update should wipe old credits */
        entry->requested_credit = 0;
+       entry->requested_packet = 0;
        if (type == BRCMF_FWS_TYPE_MAC_OPEN) {
                entry->state = BRCMF_FWS_STATE_OPEN;
                return BRCMF_FWS_RET_OK_SCHEDULE;
        } else {
                entry->state = BRCMF_FWS_STATE_CLOSE;
-               for (i = BRCMF_FWS_FIFO_AC_BE; i < NL80211_NUM_ACS; i++)
-                       brcmf_fws_tim_update(fws, entry, i);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true);
        }
        return BRCMF_FWS_RET_OK_NOSCHEDULE;
 }
         * A packet has been pushed so update traffic
         * availability bitmap, if applicable
         */
-       brcmf_fws_tim_update(fws, entry, fifo);
+       brcmf_fws_tim_update(fws, entry, fifo, true);
        brcmf_fws_flow_control_check(fws, &entry->psq,
                                     brcmf_skb_if_flags_get_field(p, INDEX));
        return 0;
                 * A packet has been picked up, update traffic
                 * availability bitmap, if applicable
                 */
-               brcmf_fws_tim_update(fws, entry, fifo);
+               brcmf_fws_tim_update(fws, entry, fifo, false);
 
                /*
                 * decrement total enqueued fifo packets and
        return 0;
 }
 
-static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
-{
-       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
-       u8 *wlh;
-       u16 data_offset = 0;
-       u8 fillers;
-       __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
-
-       if (entry->send_tim_signal)
-               data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
-
-       /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
-       data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
-       fillers = round_up(data_offset, 4) - data_offset;
-       data_offset += fillers;
-
-       skb_push(skb, data_offset);
-       wlh = skb->data;
-
-       wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
-       wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
-       memcpy(&wlh[2], &pkttag, sizeof(pkttag));
-       wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2;
-
-       if (entry->send_tim_signal) {
-               entry->send_tim_signal = 0;
-               wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
-               wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
-               wlh[2] = entry->mac_handle;
-               wlh[3] = entry->traffic_pending_bmp;
-               wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
-               entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
-       }
-       if (fillers)
-               memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);
-
-       brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX),
-                           data_offset >> 2, skb);
-       return 0;
-}
-
 static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
                                   struct sk_buff *p)
 {
        ulong flags;
        int fifo;
 
+       if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
+               brcmu_pkt_buf_free_skb(skb);
+               return;
+       }
        brcmf_fws_lock(fws->drvr, flags);
        brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED,
                                   brcmf_skb_htod_tag_get_field(skb, HSLOT), 0);