nexmon – Rev 1

Subversion Repositories:
Rev:
/* Copyright (c) 2014 Broadcom Corporation
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*******************************************************************************
 * Communicates with the dongle by using dcmd codes.
 * For certain dcmd codes, the dongle interprets string data from the host.
 ******************************************************************************/

#include <linux/types.h>
#include <linux/netdevice.h>

#include <brcmu_utils.h>
#include <brcmu_wifi.h>

#include "core.h"
#include "debug.h"
#include "proto.h"
#include "msgbuf.h"
#include "commonring.h"
#include "flowring.h"
#include "bus.h"
#include "tracepoint.h"


#define MSGBUF_IOCTL_RESP_TIMEOUT               2000

#define MSGBUF_TYPE_GEN_STATUS                  0x1
#define MSGBUF_TYPE_RING_STATUS                 0x2
#define MSGBUF_TYPE_FLOW_RING_CREATE            0x3
#define MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT      0x4
#define MSGBUF_TYPE_FLOW_RING_DELETE            0x5
#define MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT      0x6
#define MSGBUF_TYPE_FLOW_RING_FLUSH             0x7
#define MSGBUF_TYPE_FLOW_RING_FLUSH_CMPLT       0x8
#define MSGBUF_TYPE_IOCTLPTR_REQ                0x9
#define MSGBUF_TYPE_IOCTLPTR_REQ_ACK            0xA
#define MSGBUF_TYPE_IOCTLRESP_BUF_POST          0xB
#define MSGBUF_TYPE_IOCTL_CMPLT                 0xC
#define MSGBUF_TYPE_EVENT_BUF_POST              0xD
#define MSGBUF_TYPE_WL_EVENT                    0xE
#define MSGBUF_TYPE_TX_POST                     0xF
#define MSGBUF_TYPE_TX_STATUS                   0x10
#define MSGBUF_TYPE_RXBUF_POST                  0x11
#define MSGBUF_TYPE_RX_CMPLT                    0x12
#define MSGBUF_TYPE_LPBK_DMAXFER                0x13
#define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT          0x14

#define NR_TX_PKTIDS                            2048
#define NR_RX_PKTIDS                            1024

#define BRCMF_IOCTL_REQ_PKTID                   0xFFFE

#define BRCMF_MSGBUF_MAX_PKT_SIZE               2048
#define BRCMF_MSGBUF_RXBUFPOST_THRESHOLD        32
#define BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST      8
#define BRCMF_MSGBUF_MAX_EVENTBUF_POST          8

#define BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3      0x01
#define BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT       5

#define BRCMF_MSGBUF_TX_FLUSH_CNT1              32
#define BRCMF_MSGBUF_TX_FLUSH_CNT2              96

#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS        96
#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS      32
#define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS         48


struct msgbuf_common_hdr {
        u8                              msgtype;
        u8                              ifidx;
        u8                              flags;
        u8                              rsvd0;
        __le32                          request_id;
};

struct msgbuf_buf_addr {
        __le32                          low_addr;
        __le32                          high_addr;
};

struct msgbuf_ioctl_req_hdr {
        struct msgbuf_common_hdr        msg;
        __le32                          cmd;
        __le16                          trans_id;
        __le16                          input_buf_len;
        __le16                          output_buf_len;
        __le16                          rsvd0[3];
        struct msgbuf_buf_addr          req_buf_addr;
        __le32                          rsvd1[2];
};

struct msgbuf_tx_msghdr {
        struct msgbuf_common_hdr        msg;
        u8                              txhdr[ETH_HLEN];
        u8                              flags;
        u8                              seg_cnt;
        struct msgbuf_buf_addr          metadata_buf_addr;
        struct msgbuf_buf_addr          data_buf_addr;
        __le16                          metadata_buf_len;
        __le16                          data_len;
        __le32                          rsvd0;
};

struct msgbuf_rx_bufpost {
        struct msgbuf_common_hdr        msg;
        __le16                          metadata_buf_len;
        __le16                          data_buf_len;
        __le32                          rsvd0;
        struct msgbuf_buf_addr          metadata_buf_addr;
        struct msgbuf_buf_addr          data_buf_addr;
};

struct msgbuf_rx_ioctl_resp_or_event {
        struct msgbuf_common_hdr        msg;
        __le16                          host_buf_len;
        __le16                          rsvd0[3];
        struct msgbuf_buf_addr          host_buf_addr;
        __le32                          rsvd1[4];
};

struct msgbuf_completion_hdr {
        __le16                          status;
        __le16                          flow_ring_id;
};

struct msgbuf_rx_event {
        struct msgbuf_common_hdr        msg;
        struct msgbuf_completion_hdr    compl_hdr;
        __le16                          event_data_len;
        __le16                          seqnum;
        __le16                          rsvd0[4];
};

struct msgbuf_ioctl_resp_hdr {
        struct msgbuf_common_hdr        msg;
        struct msgbuf_completion_hdr    compl_hdr;
        __le16                          resp_len;
        __le16                          trans_id;
        __le32                          cmd;
        __le32                          rsvd0;
};

struct msgbuf_tx_status {
        struct msgbuf_common_hdr        msg;
        struct msgbuf_completion_hdr    compl_hdr;
        __le16                          metadata_len;
        __le16                          tx_status;
};

struct msgbuf_rx_complete {
        struct msgbuf_common_hdr        msg;
        struct msgbuf_completion_hdr    compl_hdr;
        __le16                          metadata_len;
        __le16                          data_len;
        __le16                          data_offset;
        __le16                          flags;
        __le32                          rx_status_0;
        __le32                          rx_status_1;
        __le32                          rsvd0;
};

struct msgbuf_tx_flowring_create_req {
        struct msgbuf_common_hdr        msg;
        u8                              da[ETH_ALEN];
        u8                              sa[ETH_ALEN];
        u8                              tid;
        u8                              if_flags;
        __le16                          flow_ring_id;
        u8                              tc;
        u8                              priority;
        __le16                          int_vector;
        __le16                          max_items;
        __le16                          len_item;
        struct msgbuf_buf_addr          flow_ring_addr;
};

struct msgbuf_tx_flowring_delete_req {
        struct msgbuf_common_hdr        msg;
        __le16                          flow_ring_id;
        __le16                          reason;
        __le32                          rsvd0[7];
};

struct msgbuf_flowring_create_resp {
        struct msgbuf_common_hdr        msg;
        struct msgbuf_completion_hdr    compl_hdr;
        __le32                          rsvd0[3];
};

struct msgbuf_flowring_delete_resp {
        struct msgbuf_common_hdr        msg;
        struct msgbuf_completion_hdr    compl_hdr;
        __le32                          rsvd0[3];
};

struct msgbuf_flowring_flush_resp {
        struct msgbuf_common_hdr        msg;
        struct msgbuf_completion_hdr    compl_hdr;
        __le32                          rsvd0[3];
};

struct brcmf_msgbuf_work_item {
        struct list_head queue;
        u32 flowid;
        int ifidx;
        u8 sa[ETH_ALEN];
        u8 da[ETH_ALEN];
};

struct brcmf_msgbuf {
        struct brcmf_pub *drvr;

        struct brcmf_commonring **commonrings;
        struct brcmf_commonring **flowrings;
        dma_addr_t *flowring_dma_handle;
        u16 nrof_flowrings;

        u16 rx_dataoffset;
        u32 max_rxbufpost;
        u16 rx_metadata_offset;
        u32 rxbufpost;

        u32 max_ioctlrespbuf;
        u32 cur_ioctlrespbuf;
        u32 max_eventbuf;
        u32 cur_eventbuf;

        void *ioctbuf;
        dma_addr_t ioctbuf_handle;
        u32 ioctbuf_phys_hi;
        u32 ioctbuf_phys_lo;
        int ioctl_resp_status;
        u32 ioctl_resp_ret_len;
        u32 ioctl_resp_pktid;

        u16 data_seq_no;
        u16 ioctl_seq_no;
        u32 reqid;
        wait_queue_head_t ioctl_resp_wait;
        bool ctl_completed;

        struct brcmf_msgbuf_pktids *tx_pktids;
        struct brcmf_msgbuf_pktids *rx_pktids;
        struct brcmf_flowring *flow;

        struct workqueue_struct *txflow_wq;
        struct work_struct txflow_work;
        unsigned long *flow_map;
        unsigned long *txstatus_done_map;

        struct work_struct flowring_work;
        spinlock_t flowring_work_lock;
        struct list_head work_queue;
};

struct brcmf_msgbuf_pktid {
        atomic_t  allocated;
        u16 data_offset;
        struct sk_buff *skb;
        dma_addr_t physaddr;
};

struct brcmf_msgbuf_pktids {
        u32 array_size;
        u32 last_allocated_idx;
        enum dma_data_direction direction;
        struct brcmf_msgbuf_pktid *array;
};

static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf);


static struct brcmf_msgbuf_pktids *
brcmf_msgbuf_init_pktids(u32 nr_array_entries,
                         enum dma_data_direction direction)
{
        struct brcmf_msgbuf_pktid *array;
        struct brcmf_msgbuf_pktids *pktids;

        array = kcalloc(nr_array_entries, sizeof(*array), GFP_KERNEL);
        if (!array)
                return NULL;

        pktids = kzalloc(sizeof(*pktids), GFP_KERNEL);
        if (!pktids) {
                kfree(array);
                return NULL;
        }
        pktids->array = array;
        pktids->array_size = nr_array_entries;

        return pktids;
}


static int
brcmf_msgbuf_alloc_pktid(struct device *dev,
                         struct brcmf_msgbuf_pktids *pktids,
                         struct sk_buff *skb, u16 data_offset,
                         dma_addr_t *physaddr, u32 *idx)
{
        struct brcmf_msgbuf_pktid *array;
        u32 count;

        array = pktids->array;

        *physaddr = dma_map_single(dev, skb->data + data_offset,
                                   skb->len - data_offset, pktids->direction);

        if (dma_mapping_error(dev, *physaddr)) {
                brcmf_err("dma_map_single failed !!\n");
                return -ENOMEM;
        }

        *idx = pktids->last_allocated_idx;

        count = 0;
        do {
                (*idx)++;
                if (*idx == pktids->array_size)
                        *idx = 0;
                if (array[*idx].allocated.counter == 0)
                        if (atomic_cmpxchg(&array[*idx].allocated, 0, 1) == 0)
                                break;
                count++;
        } while (count < pktids->array_size);

        if (count == pktids->array_size)
                return -ENOMEM;

        array[*idx].data_offset = data_offset;
        array[*idx].physaddr = *physaddr;
        array[*idx].skb = skb;

        pktids->last_allocated_idx = *idx;

        return 0;
}


static struct sk_buff *
brcmf_msgbuf_get_pktid(struct device *dev, struct brcmf_msgbuf_pktids *pktids,
                       u32 idx)
{
        struct brcmf_msgbuf_pktid *pktid;
        struct sk_buff *skb;

        if (idx >= pktids->array_size) {
                brcmf_err("Invalid packet id %d (max %d)\n", idx,
                          pktids->array_size);
                return NULL;
        }
        if (pktids->array[idx].allocated.counter) {
                pktid = &pktids->array[idx];
                dma_unmap_single(dev, pktid->physaddr,
                                 pktid->skb->len - pktid->data_offset,
                                 pktids->direction);
                skb = pktid->skb;
                pktid->allocated.counter = 0;
                return skb;
        } else {
                brcmf_err("Invalid packet id %d (not in use)\n", idx);
        }

        return NULL;
}


static void
brcmf_msgbuf_release_array(struct device *dev,
                           struct brcmf_msgbuf_pktids *pktids)
{
        struct brcmf_msgbuf_pktid *array;
        struct brcmf_msgbuf_pktid *pktid;
        u32 count;

        array = pktids->array;
        count = 0;
        do {
                if (array[count].allocated.counter) {
                        pktid = &array[count];
                        dma_unmap_single(dev, pktid->physaddr,
                                         pktid->skb->len - pktid->data_offset,
                                         pktids->direction);
                        brcmu_pkt_buf_free_skb(pktid->skb);
                }
                count++;
        } while (count < pktids->array_size);

        kfree(array);
        kfree(pktids);
}


static void brcmf_msgbuf_release_pktids(struct brcmf_msgbuf *msgbuf)
{
        if (msgbuf->rx_pktids)
                brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev,
                                           msgbuf->rx_pktids);
        if (msgbuf->tx_pktids)
                brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev,
                                           msgbuf->tx_pktids);
}


static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx,
                                 uint cmd, void *buf, uint len)
{
        struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
        struct brcmf_commonring *commonring;
        struct msgbuf_ioctl_req_hdr *request;
        u16 buf_len;
        void *ret_ptr;
        int err;

        commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
        brcmf_commonring_lock(commonring);
        ret_ptr = brcmf_commonring_reserve_for_write(commonring);
        if (!ret_ptr) {
                brcmf_err("Failed to reserve space in commonring\n");
                brcmf_commonring_unlock(commonring);
                return -ENOMEM;
        }

        msgbuf->reqid++;

        request = (struct msgbuf_ioctl_req_hdr *)ret_ptr;
        request->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ;
        request->msg.ifidx = (u8)ifidx;
        request->msg.flags = 0;
        request->msg.request_id = cpu_to_le32(BRCMF_IOCTL_REQ_PKTID);
        request->cmd = cpu_to_le32(cmd);
        request->output_buf_len = cpu_to_le16(len);
        request->trans_id = cpu_to_le16(msgbuf->reqid);

        buf_len = min_t(u16, len, BRCMF_TX_IOCTL_MAX_MSG_SIZE);
        request->input_buf_len = cpu_to_le16(buf_len);
        request->req_buf_addr.high_addr = cpu_to_le32(msgbuf->ioctbuf_phys_hi);
        request->req_buf_addr.low_addr = cpu_to_le32(msgbuf->ioctbuf_phys_lo);
        if (buf)
                memcpy(msgbuf->ioctbuf, buf, buf_len);
        else
                memset(msgbuf->ioctbuf, 0, buf_len);

        err = brcmf_commonring_write_complete(commonring);
        brcmf_commonring_unlock(commonring);

        return err;
}


static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf)
{
        return wait_event_timeout(msgbuf->ioctl_resp_wait,
                                  msgbuf->ctl_completed,
                                  msecs_to_jiffies(MSGBUF_IOCTL_RESP_TIMEOUT));
}


static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf)
{
        msgbuf->ctl_completed = true;
        if (waitqueue_active(&msgbuf->ioctl_resp_wait))
                wake_up(&msgbuf->ioctl_resp_wait);
}


static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx,
                                   uint cmd, void *buf, uint len)
{
        struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
        struct sk_buff *skb = NULL;
        int timeout;
        int err;

        brcmf_dbg(MSGBUF, "ifidx=%d, cmd=%d, len=%d\n", ifidx, cmd, len);
        msgbuf->ctl_completed = false;
        err = brcmf_msgbuf_tx_ioctl(drvr, ifidx, cmd, buf, len);
        if (err)
                return err;

        timeout = brcmf_msgbuf_ioctl_resp_wait(msgbuf);
        if (!timeout) {
                brcmf_err("Timeout on response for query command\n");
                return -EIO;
        }

        skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
                                     msgbuf->rx_pktids,
                                     msgbuf->ioctl_resp_pktid);
        if (msgbuf->ioctl_resp_ret_len != 0) {
                if (!skb)
                        return -EBADF;

                memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ?
                                       len : msgbuf->ioctl_resp_ret_len);
        }
        brcmu_pkt_buf_free_skb(skb);

        return msgbuf->ioctl_resp_status;
}


static int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx,
                                 uint cmd, void *buf, uint len)
{
        return brcmf_msgbuf_query_dcmd(drvr, ifidx, cmd, buf, len);
}


static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws,
                                struct sk_buff *skb, struct brcmf_if **ifp)
{
        return -ENODEV;
}


static void
brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid)
{
        u32 dma_sz;
        void *dma_buf;

        brcmf_dbg(MSGBUF, "Removing flowring %d\n", flowid);

        dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE;
        dma_buf = msgbuf->flowrings[flowid]->buf_addr;
        dma_free_coherent(msgbuf->drvr->bus_if->dev, dma_sz, dma_buf,
                          msgbuf->flowring_dma_handle[flowid]);

        brcmf_flowring_delete(msgbuf->flow, flowid);
}


static struct brcmf_msgbuf_work_item *
brcmf_msgbuf_dequeue_work(struct brcmf_msgbuf *msgbuf)
{
        struct brcmf_msgbuf_work_item *work = NULL;
        ulong flags;

        spin_lock_irqsave(&msgbuf->flowring_work_lock, flags);
        if (!list_empty(&msgbuf->work_queue)) {
                work = list_first_entry(&msgbuf->work_queue,
                                        struct brcmf_msgbuf_work_item, queue);
                list_del(&work->queue);
        }
        spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags);

        return work;
}


static u32
brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf,
                                    struct brcmf_msgbuf_work_item *work)
{
        struct msgbuf_tx_flowring_create_req *create;
        struct brcmf_commonring *commonring;
        void *ret_ptr;
        u32 flowid;
        void *dma_buf;
        u32 dma_sz;
        u64 address;
        int err;

        flowid = work->flowid;
        dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE;
        dma_buf = dma_alloc_coherent(msgbuf->drvr->bus_if->dev, dma_sz,
                                     &msgbuf->flowring_dma_handle[flowid],
                                     GFP_KERNEL);
        if (!dma_buf) {
                brcmf_err("dma_alloc_coherent failed\n");
                brcmf_flowring_delete(msgbuf->flow, flowid);
                return BRCMF_FLOWRING_INVALID_ID;
        }

        brcmf_commonring_config(msgbuf->flowrings[flowid],
                                BRCMF_H2D_TXFLOWRING_MAX_ITEM,
                                BRCMF_H2D_TXFLOWRING_ITEMSIZE, dma_buf);

        commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
        brcmf_commonring_lock(commonring);
        ret_ptr = brcmf_commonring_reserve_for_write(commonring);
        if (!ret_ptr) {
                brcmf_err("Failed to reserve space in commonring\n");
                brcmf_commonring_unlock(commonring);
                brcmf_msgbuf_remove_flowring(msgbuf, flowid);
                return BRCMF_FLOWRING_INVALID_ID;
        }

        create = (struct msgbuf_tx_flowring_create_req *)ret_ptr;
        create->msg.msgtype = MSGBUF_TYPE_FLOW_RING_CREATE;
        create->msg.ifidx = work->ifidx;
        create->msg.request_id = 0;
        create->tid = brcmf_flowring_tid(msgbuf->flow, flowid);
        create->flow_ring_id = cpu_to_le16(flowid +
                                           BRCMF_NROF_H2D_COMMON_MSGRINGS);
        memcpy(create->sa, work->sa, ETH_ALEN);
        memcpy(create->da, work->da, ETH_ALEN);
        address = (u64)msgbuf->flowring_dma_handle[flowid];
        create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32);
        create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff);
        create->max_items = cpu_to_le16(BRCMF_H2D_TXFLOWRING_MAX_ITEM);
        create->len_item = cpu_to_le16(BRCMF_H2D_TXFLOWRING_ITEMSIZE);

        brcmf_dbg(MSGBUF, "Send Flow Create Req flow ID %d for peer %pM prio %d ifindex %d\n",
                  flowid, work->da, create->tid, work->ifidx);

        err = brcmf_commonring_write_complete(commonring);
        brcmf_commonring_unlock(commonring);
        if (err) {
                brcmf_err("Failed to write commonring\n");
                brcmf_msgbuf_remove_flowring(msgbuf, flowid);
                return BRCMF_FLOWRING_INVALID_ID;
        }

        return flowid;
}


static void brcmf_msgbuf_flowring_worker(struct work_struct *work)
{
        struct brcmf_msgbuf *msgbuf;
        struct brcmf_msgbuf_work_item *create;

        msgbuf = container_of(work, struct brcmf_msgbuf, flowring_work);

        while ((create = brcmf_msgbuf_dequeue_work(msgbuf))) {
                brcmf_msgbuf_flowring_create_worker(msgbuf, create);
                kfree(create);
        }
}


static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx,
                                        struct sk_buff *skb)
{
        struct brcmf_msgbuf_work_item *create;
        struct ethhdr *eh = (struct ethhdr *)(skb->data);
        u32 flowid;
        ulong flags;

        create = kzalloc(sizeof(*create), GFP_ATOMIC);
        if (create == NULL)
                return BRCMF_FLOWRING_INVALID_ID;

        flowid = brcmf_flowring_create(msgbuf->flow, eh->h_dest,
                                       skb->priority, ifidx);
        if (flowid == BRCMF_FLOWRING_INVALID_ID) {
                kfree(create);
                return flowid;
        }

        create->flowid = flowid;
        create->ifidx = ifidx;
        memcpy(create->sa, eh->h_source, ETH_ALEN);
        memcpy(create->da, eh->h_dest, ETH_ALEN);

        spin_lock_irqsave(&msgbuf->flowring_work_lock, flags);
        list_add_tail(&create->queue, &msgbuf->work_queue);
        spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags);
        schedule_work(&msgbuf->flowring_work);

        return flowid;
}


static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid)
{
        struct brcmf_flowring *flow = msgbuf->flow;
        struct brcmf_commonring *commonring;
        void *ret_ptr;
        u32 count;
        struct sk_buff *skb;
        dma_addr_t physaddr;
        u32 pktid;
        struct msgbuf_tx_msghdr *tx_msghdr;
        u64 address;

        commonring = msgbuf->flowrings[flowid];
        if (!brcmf_commonring_write_available(commonring))
                return;

        brcmf_commonring_lock(commonring);

        count = BRCMF_MSGBUF_TX_FLUSH_CNT2 - BRCMF_MSGBUF_TX_FLUSH_CNT1;
        while (brcmf_flowring_qlen(flow, flowid)) {
                skb = brcmf_flowring_dequeue(flow, flowid);
                if (skb == NULL) {
                        brcmf_err("No SKB, but qlen %d\n",
                                  brcmf_flowring_qlen(flow, flowid));
                        break;
                }
                skb_orphan(skb);
                if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
                                             msgbuf->tx_pktids, skb, ETH_HLEN,
                                             &physaddr, &pktid)) {
                        brcmf_flowring_reinsert(flow, flowid, skb);
                        brcmf_err("No PKTID available !!\n");
                        break;
                }
                ret_ptr = brcmf_commonring_reserve_for_write(commonring);
                if (!ret_ptr) {
                        brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
                                               msgbuf->tx_pktids, pktid);
                        brcmf_flowring_reinsert(flow, flowid, skb);
                        break;
                }
                count++;

                tx_msghdr = (struct msgbuf_tx_msghdr *)ret_ptr;

                tx_msghdr->msg.msgtype = MSGBUF_TYPE_TX_POST;
                tx_msghdr->msg.request_id = cpu_to_le32(pktid);
                tx_msghdr->msg.ifidx = brcmf_flowring_ifidx_get(flow, flowid);
                tx_msghdr->flags = BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3;
                tx_msghdr->flags |= (skb->priority & 0x07) <<
                                    BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT;
                tx_msghdr->seg_cnt = 1;
                memcpy(tx_msghdr->txhdr, skb->data, ETH_HLEN);
                tx_msghdr->data_len = cpu_to_le16(skb->len - ETH_HLEN);
                address = (u64)physaddr;
                tx_msghdr->data_buf_addr.high_addr = cpu_to_le32(address >> 32);
                tx_msghdr->data_buf_addr.low_addr =
                        cpu_to_le32(address & 0xffffffff);
                tx_msghdr->metadata_buf_len = 0;
                tx_msghdr->metadata_buf_addr.high_addr = 0;
                tx_msghdr->metadata_buf_addr.low_addr = 0;
                atomic_inc(&commonring->outstanding_tx);
                if (count >= BRCMF_MSGBUF_TX_FLUSH_CNT2) {
                        brcmf_commonring_write_complete(commonring);
                        count = 0;
                }
        }
        if (count)
                brcmf_commonring_write_complete(commonring);
        brcmf_commonring_unlock(commonring);
}


static void brcmf_msgbuf_txflow_worker(struct work_struct *worker)
{
        struct brcmf_msgbuf *msgbuf;
        u32 flowid;

        msgbuf = container_of(worker, struct brcmf_msgbuf, txflow_work);
        for_each_set_bit(flowid, msgbuf->flow_map, msgbuf->nrof_flowrings) {
                clear_bit(flowid, msgbuf->flow_map);
                brcmf_msgbuf_txflow(msgbuf, flowid);
        }
}


static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid,
                                        bool force)
{
        struct brcmf_commonring *commonring;

        set_bit(flowid, msgbuf->flow_map);
        commonring = msgbuf->flowrings[flowid];
        if ((force) || (atomic_read(&commonring->outstanding_tx) <
                        BRCMF_MSGBUF_DELAY_TXWORKER_THRS))
                queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work);

        return 0;
}


static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx,
                               u8 offset, struct sk_buff *skb)
{
        struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
        struct brcmf_flowring *flow = msgbuf->flow;
        struct ethhdr *eh = (struct ethhdr *)(skb->data);
        u32 flowid;
        u32 queue_count;
        bool force;

        flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx);
        if (flowid == BRCMF_FLOWRING_INVALID_ID) {
                flowid = brcmf_msgbuf_flowring_create(msgbuf, ifidx, skb);
                if (flowid == BRCMF_FLOWRING_INVALID_ID)
                        return -ENOMEM;
        }
        queue_count = brcmf_flowring_enqueue(flow, flowid, skb);
        force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0);
        brcmf_msgbuf_schedule_txdata(msgbuf, flowid, force);

        return 0;
}


static void
brcmf_msgbuf_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
                                 enum proto_addr_mode addr_mode)
{
        struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;

        brcmf_flowring_configure_addr_mode(msgbuf->flow, ifidx, addr_mode);
}


static void
brcmf_msgbuf_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
{
        struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;

        brcmf_flowring_delete_peer(msgbuf->flow, ifidx, peer);
}


static void
brcmf_msgbuf_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
{
        struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;

        brcmf_flowring_add_tdls_peer(msgbuf->flow, ifidx, peer);
}


static void
brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf)
{
        struct msgbuf_ioctl_resp_hdr *ioctl_resp;

        ioctl_resp = (struct msgbuf_ioctl_resp_hdr *)buf;

        msgbuf->ioctl_resp_status =
                        (s16)le16_to_cpu(ioctl_resp->compl_hdr.status);
        msgbuf->ioctl_resp_ret_len = le16_to_cpu(ioctl_resp->resp_len);
        msgbuf->ioctl_resp_pktid = le32_to_cpu(ioctl_resp->msg.request_id);

        brcmf_msgbuf_ioctl_resp_wake(msgbuf);

        if (msgbuf->cur_ioctlrespbuf)
                msgbuf->cur_ioctlrespbuf--;
        brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf);
}


static void
brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf)
{
        struct brcmf_commonring *commonring;
        struct msgbuf_tx_status *tx_status;
        u32 idx;
        struct sk_buff *skb;
        u16 flowid;

        tx_status = (struct msgbuf_tx_status *)buf;
        idx = le32_to_cpu(tx_status->msg.request_id);
        flowid = le16_to_cpu(tx_status->compl_hdr.flow_ring_id);
        flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS;
        skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
                                     msgbuf->tx_pktids, idx);
        if (!skb)
                return;

        set_bit(flowid, msgbuf->txstatus_done_map);
        commonring = msgbuf->flowrings[flowid];
        atomic_dec(&commonring->outstanding_tx);

        brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx),
                         skb, true);
}


static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count)
{
        struct brcmf_commonring *commonring;
        void *ret_ptr;
        struct sk_buff *skb;
        u16 alloced;
        u32 pktlen;
        dma_addr_t physaddr;
        struct msgbuf_rx_bufpost *rx_bufpost;
        u64 address;
        u32 pktid;
        u32 i;

        commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT];
        ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring,
                                                              count,
                                                              &alloced);
        if (!ret_ptr) {
                brcmf_dbg(MSGBUF, "Failed to reserve space in commonring\n");
                return 0;
        }

        for (i = 0; i < alloced; i++) {
                rx_bufpost = (struct msgbuf_rx_bufpost *)ret_ptr;
                memset(rx_bufpost, 0, sizeof(*rx_bufpost));

                skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE);

                if (skb == NULL) {
                        brcmf_err("Failed to alloc SKB\n");
                        brcmf_commonring_write_cancel(commonring, alloced - i);
                        break;
                }

                pktlen = skb->len;
                if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
                                             msgbuf->rx_pktids, skb, 0,
                                             &physaddr, &pktid)) {
                        dev_kfree_skb_any(skb);
                        brcmf_err("No PKTID available !!\n");
                        brcmf_commonring_write_cancel(commonring, alloced - i);
                        break;
                }

                if (msgbuf->rx_metadata_offset) {
                        address = (u64)physaddr;
                        rx_bufpost->metadata_buf_len =
                                cpu_to_le16(msgbuf->rx_metadata_offset);
                        rx_bufpost->metadata_buf_addr.high_addr =
                                cpu_to_le32(address >> 32);
                        rx_bufpost->metadata_buf_addr.low_addr =
                                cpu_to_le32(address & 0xffffffff);

                        skb_pull(skb, msgbuf->rx_metadata_offset);
                        pktlen = skb->len;
                        physaddr += msgbuf->rx_metadata_offset;
                }
                rx_bufpost->msg.msgtype = MSGBUF_TYPE_RXBUF_POST;
                rx_bufpost->msg.request_id = cpu_to_le32(pktid);

                address = (u64)physaddr;
                rx_bufpost->data_buf_len = cpu_to_le16((u16)pktlen);
                rx_bufpost->data_buf_addr.high_addr =
                        cpu_to_le32(address >> 32);
                rx_bufpost->data_buf_addr.low_addr =
                        cpu_to_le32(address & 0xffffffff);

                ret_ptr += brcmf_commonring_len_item(commonring);
        }

        if (i)
                brcmf_commonring_write_complete(commonring);

        return i;
}


static void
brcmf_msgbuf_rxbuf_data_fill(struct brcmf_msgbuf *msgbuf)
{
        u32 fillbufs;
        u32 retcount;

        fillbufs = msgbuf->max_rxbufpost - msgbuf->rxbufpost;

        while (fillbufs) {
                retcount = brcmf_msgbuf_rxbuf_data_post(msgbuf, fillbufs);
                if (!retcount)
                        break;
                msgbuf->rxbufpost += retcount;
                fillbufs -= retcount;
        }
}


static void
brcmf_msgbuf_update_rxbufpost_count(struct brcmf_msgbuf *msgbuf, u16 rxcnt)
{
        msgbuf->rxbufpost -= rxcnt;
        if (msgbuf->rxbufpost <= (msgbuf->max_rxbufpost -
                                  BRCMF_MSGBUF_RXBUFPOST_THRESHOLD))
                brcmf_msgbuf_rxbuf_data_fill(msgbuf);
}


static u32
brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf,
                             u32 count)
{
        struct brcmf_commonring *commonring;
        void *ret_ptr;
        struct sk_buff *skb;
        u16 alloced;
        u32 pktlen;
        dma_addr_t physaddr;
        struct msgbuf_rx_ioctl_resp_or_event *rx_bufpost;
        u64 address;
        u32 pktid;
        u32 i;

        commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
        brcmf_commonring_lock(commonring);
        ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring,
                                                              count,
                                                              &alloced);
        if (!ret_ptr) {
                brcmf_err("Failed to reserve space in commonring\n");
                brcmf_commonring_unlock(commonring);
                return 0;
        }

        for (i = 0; i < alloced; i++) {
                rx_bufpost = (struct msgbuf_rx_ioctl_resp_or_event *)ret_ptr;
                memset(rx_bufpost, 0, sizeof(*rx_bufpost));

                skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE);

                if (skb == NULL) {
                        brcmf_err("Failed to alloc SKB\n");
                        brcmf_commonring_write_cancel(commonring, alloced - i);
                        break;
                }

                pktlen = skb->len;
                if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
                                             msgbuf->rx_pktids, skb, 0,
                                             &physaddr, &pktid)) {
                        dev_kfree_skb_any(skb);
                        brcmf_err("No PKTID available !!\n");
                        brcmf_commonring_write_cancel(commonring, alloced - i);
                        break;
                }
                if (event_buf)
                        rx_bufpost->msg.msgtype = MSGBUF_TYPE_EVENT_BUF_POST;
                else
                        rx_bufpost->msg.msgtype =
                                MSGBUF_TYPE_IOCTLRESP_BUF_POST;
                rx_bufpost->msg.request_id = cpu_to_le32(pktid);

                address = (u64)physaddr;
                rx_bufpost->host_buf_len = cpu_to_le16((u16)pktlen);
                rx_bufpost->host_buf_addr.high_addr =
                        cpu_to_le32(address >> 32);
                rx_bufpost->host_buf_addr.low_addr =
                        cpu_to_le32(address & 0xffffffff);

                ret_ptr += brcmf_commonring_len_item(commonring);
        }

        if (i)
                brcmf_commonring_write_complete(commonring);

        brcmf_commonring_unlock(commonring);

        return i;
}


static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf)
{
        u32 count;

        count = msgbuf->max_ioctlrespbuf - msgbuf->cur_ioctlrespbuf;
        count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, false, count);
        msgbuf->cur_ioctlrespbuf += count;
}


static void brcmf_msgbuf_rxbuf_event_post(struct brcmf_msgbuf *msgbuf)
{
        u32 count;

        count = msgbuf->max_eventbuf - msgbuf->cur_eventbuf;
        count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, true, count);
        msgbuf->cur_eventbuf += count;
}


static void
brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb,
                    u8 ifidx)
{
        struct brcmf_if *ifp;

        ifp = brcmf_get_ifp(msgbuf->drvr, ifidx);
        if (!ifp || !ifp->ndev) {
                brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
                brcmu_pkt_buf_free_skb(skb);
                return;
        }
        brcmf_netif_rx(ifp, skb);
}


static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf)
{
        struct msgbuf_rx_event *event;
        u32 idx;
        u16 buflen;
        struct sk_buff *skb;

        event = (struct msgbuf_rx_event *)buf;
        idx = le32_to_cpu(event->msg.request_id);
        buflen = le16_to_cpu(event->event_data_len);

        if (msgbuf->cur_eventbuf)
                msgbuf->cur_eventbuf--;
        brcmf_msgbuf_rxbuf_event_post(msgbuf);

        skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
                                     msgbuf->rx_pktids, idx);
        if (!skb)
                return;

        if (msgbuf->rx_dataoffset)
                skb_pull(skb, msgbuf->rx_dataoffset);

        skb_trim(skb, buflen);

        brcmf_msgbuf_rx_skb(msgbuf, skb, event->msg.ifidx);
}


static void
brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf)
{
        struct msgbuf_rx_complete *rx_complete;
        struct sk_buff *skb;
        u16 data_offset;
        u16 buflen;
        u32 idx;

        brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1);

        rx_complete = (struct msgbuf_rx_complete *)buf;
        data_offset = le16_to_cpu(rx_complete->data_offset);
        buflen = le16_to_cpu(rx_complete->data_len);
        idx = le32_to_cpu(rx_complete->msg.request_id);

        skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
                                     msgbuf->rx_pktids, idx);
        if (!skb)
                return;

        if (data_offset)
                skb_pull(skb, data_offset);
        else if (msgbuf->rx_dataoffset)
                skb_pull(skb, msgbuf->rx_dataoffset);

        skb_trim(skb, buflen);

        brcmf_msgbuf_rx_skb(msgbuf, skb, rx_complete->msg.ifidx);
}


static void
brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf,
                                               void *buf)
{
        struct msgbuf_flowring_create_resp *flowring_create_resp;
        u16 status;
        u16 flowid;

        flowring_create_resp = (struct msgbuf_flowring_create_resp *)buf;

        flowid = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id);
        flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS;
        status =  le16_to_cpu(flowring_create_resp->compl_hdr.status);

        if (status) {
                brcmf_err("Flowring creation failed, code %d\n", status);
                brcmf_msgbuf_remove_flowring(msgbuf, flowid);
                return;
        }
        brcmf_dbg(MSGBUF, "Flowring %d Create response status %d\n", flowid,
                  status);

        brcmf_flowring_open(msgbuf->flow, flowid);

        brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
}


static void
brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf,
                                               void *buf)
{
        struct msgbuf_flowring_delete_resp *flowring_delete_resp;
        u16 status;
        u16 flowid;

        flowring_delete_resp = (struct msgbuf_flowring_delete_resp *)buf;

        flowid = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id);
        flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS;
        status =  le16_to_cpu(flowring_delete_resp->compl_hdr.status);

        if (status) {
                brcmf_err("Flowring deletion failed, code %d\n", status);
                brcmf_flowring_delete(msgbuf->flow, flowid);
                return;
        }
        brcmf_dbg(MSGBUF, "Flowring %d Delete response status %d\n", flowid,
                  status);

        brcmf_msgbuf_remove_flowring(msgbuf, flowid);
}


static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf)
{
        struct msgbuf_common_hdr *msg;

        msg = (struct msgbuf_common_hdr *)buf;
        switch (msg->msgtype) {
        case MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT:
                brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT\n");
                brcmf_msgbuf_process_flow_ring_create_response(msgbuf, buf);
                break;
        case MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT:
                brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT\n");
                brcmf_msgbuf_process_flow_ring_delete_response(msgbuf, buf);
                break;
        case MSGBUF_TYPE_IOCTLPTR_REQ_ACK:
                brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTLPTR_REQ_ACK\n");
                break;
        case MSGBUF_TYPE_IOCTL_CMPLT:
                brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTL_CMPLT\n");
                brcmf_msgbuf_process_ioctl_complete(msgbuf, buf);
                break;
        case MSGBUF_TYPE_WL_EVENT:
                brcmf_dbg(MSGBUF, "MSGBUF_TYPE_WL_EVENT\n");
                brcmf_msgbuf_process_event(msgbuf, buf);
                break;
        case MSGBUF_TYPE_TX_STATUS:
                brcmf_dbg(MSGBUF, "MSGBUF_TYPE_TX_STATUS\n");
                brcmf_msgbuf_process_txstatus(msgbuf, buf);
                break;
        case MSGBUF_TYPE_RX_CMPLT:
                brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n");
                brcmf_msgbuf_process_rx_complete(msgbuf, buf);
                break;
        default:
                brcmf_err("Unsupported msgtype %d\n", msg->msgtype);
                break;
        }
}


static void brcmf_msgbuf_process_rx(struct brcmf_msgbuf *msgbuf,
                                    struct brcmf_commonring *commonring)
{
        void *buf;
        u16 count;
        u16 processed;

again:
        buf = brcmf_commonring_get_read_ptr(commonring, &count);
        if (buf == NULL)
                return;

        processed = 0;
        while (count) {
                brcmf_msgbuf_process_msgtype(msgbuf,
                                             buf + msgbuf->rx_dataoffset);
                buf += brcmf_commonring_len_item(commonring);
                processed++;
                if (processed == BRCMF_MSGBUF_UPDATE_RX_PTR_THRS) {
                        brcmf_commonring_read_complete(commonring, processed);
                        processed = 0;
                }
                count--;
        }
        if (processed)
                brcmf_commonring_read_complete(commonring, processed);

        if (commonring->r_ptr == 0)
                goto again;
}


int brcmf_proto_msgbuf_rx_trigger(struct device *dev)
{
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_pub *drvr = bus_if->drvr;
        struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
        struct brcmf_commonring *commonring;
        void *buf;
        u32 flowid;
        int qlen;

        buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
        brcmf_msgbuf_process_rx(msgbuf, buf);
        buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE];
        brcmf_msgbuf_process_rx(msgbuf, buf);
        buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE];
        brcmf_msgbuf_process_rx(msgbuf, buf);

        for_each_set_bit(flowid, msgbuf->txstatus_done_map,
                         msgbuf->nrof_flowrings) {
                clear_bit(flowid, msgbuf->txstatus_done_map);
                commonring = msgbuf->flowrings[flowid];
                qlen = brcmf_flowring_qlen(msgbuf->flow, flowid);
                if ((qlen > BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) ||
                    ((qlen) && (atomic_read(&commonring->outstanding_tx) <
                                BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS)))
                        brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
        }

        return 0;
}


void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid)
{
        struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
        struct msgbuf_tx_flowring_delete_req *delete;
        struct brcmf_commonring *commonring;
        void *ret_ptr;
        u8 ifidx;
        int err;

        commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
        brcmf_commonring_lock(commonring);
        ret_ptr = brcmf_commonring_reserve_for_write(commonring);
        if (!ret_ptr) {
                brcmf_err("FW unaware, flowring will be removed !!\n");
                brcmf_commonring_unlock(commonring);
                brcmf_msgbuf_remove_flowring(msgbuf, flowid);
                return;
        }

        delete = (struct msgbuf_tx_flowring_delete_req *)ret_ptr;

        ifidx = brcmf_flowring_ifidx_get(msgbuf->flow, flowid);

        delete->msg.msgtype = MSGBUF_TYPE_FLOW_RING_DELETE;
        delete->msg.ifidx = ifidx;
        delete->msg.request_id = 0;

        delete->flow_ring_id = cpu_to_le16(flowid +
                                           BRCMF_NROF_H2D_COMMON_MSGRINGS);
        delete->reason = 0;

        brcmf_dbg(MSGBUF, "Send Flow Delete Req flow ID %d, ifindex %d\n",
                  flowid, ifidx);

        err = brcmf_commonring_write_complete(commonring);
        brcmf_commonring_unlock(commonring);
        if (err) {
                brcmf_err("Failed to submit RING_DELETE, flowring will be removed\n");
                brcmf_msgbuf_remove_flowring(msgbuf, flowid);
        }
}

#ifdef DEBUG
static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
{
        struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
        struct brcmf_pub *drvr = bus_if->drvr;
        struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
        struct brcmf_commonring *commonring;
        u16 i;
        struct brcmf_flowring_ring *ring;
        struct brcmf_flowring_hash *hash;

        commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
        seq_printf(seq, "h2d_ctl_submit: rp %4u, wp %4u, depth %4u\n",
                   commonring->r_ptr, commonring->w_ptr, commonring->depth);
        commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT];
        seq_printf(seq, "h2d_rx_submit:  rp %4u, wp %4u, depth %4u\n",
                   commonring->r_ptr, commonring->w_ptr, commonring->depth);
        commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE];
        seq_printf(seq, "d2h_ctl_cmplt:  rp %4u, wp %4u, depth %4u\n",
                   commonring->r_ptr, commonring->w_ptr, commonring->depth);
        commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE];
        seq_printf(seq, "d2h_tx_cmplt:   rp %4u, wp %4u, depth %4u\n",
                   commonring->r_ptr, commonring->w_ptr, commonring->depth);
        commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
        seq_printf(seq, "d2h_rx_cmplt:   rp %4u, wp %4u, depth %4u\n",
                   commonring->r_ptr, commonring->w_ptr, commonring->depth);

        seq_printf(seq, "\nh2d_flowrings: depth %u\n",
                   BRCMF_H2D_TXFLOWRING_MAX_ITEM);
        seq_puts(seq, "Active flowrings:\n");
        hash = msgbuf->flow->hash;
        for (i = 0; i < msgbuf->flow->nrofrings; i++) {
                if (!msgbuf->flow->rings[i])
                        continue;
                ring = msgbuf->flow->rings[i];
                if (ring->status != RING_OPEN)
                        continue;
                commonring = msgbuf->flowrings[i];
                hash = &msgbuf->flow->hash[ring->hash_id];
                seq_printf(seq, "id %3u: rp %4u, wp %4u, qlen %4u, blocked %u\n"
                                "        ifidx %u, fifo %u, da %pM\n",
                                i, commonring->r_ptr, commonring->w_ptr,
                                skb_queue_len(&ring->skblist), ring->blocked,
                                hash->ifidx, hash->fifo, hash->mac);
        }

        return 0;
}
#else
static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
{
        return 0;
}
#endif

int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
{
        struct brcmf_bus_msgbuf *if_msgbuf;
        struct brcmf_msgbuf *msgbuf;
        u64 address;
        u32 count;

        if_msgbuf = drvr->bus_if->msgbuf;
        msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL);
        if (!msgbuf)
                goto fail;

        msgbuf->txflow_wq = create_singlethread_workqueue("msgbuf_txflow");
        if (msgbuf->txflow_wq == NULL) {
                brcmf_err("workqueue creation failed\n");
                goto fail;
        }
        INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker);
        count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings);
        count = count * sizeof(unsigned long);
        msgbuf->flow_map = kzalloc(count, GFP_KERNEL);
        if (!msgbuf->flow_map)
                goto fail;

        msgbuf->txstatus_done_map = kzalloc(count, GFP_KERNEL);
        if (!msgbuf->txstatus_done_map)
                goto fail;

        msgbuf->drvr = drvr;
        msgbuf->ioctbuf = dma_alloc_coherent(drvr->bus_if->dev,
                                             BRCMF_TX_IOCTL_MAX_MSG_SIZE,
                                             &msgbuf->ioctbuf_handle,
                                             GFP_KERNEL);
        if (!msgbuf->ioctbuf)
                goto fail;
        address = (u64)msgbuf->ioctbuf_handle;
        msgbuf->ioctbuf_phys_hi = address >> 32;
        msgbuf->ioctbuf_phys_lo = address & 0xffffffff;

        drvr->proto->hdrpull = brcmf_msgbuf_hdrpull;
        drvr->proto->query_dcmd = brcmf_msgbuf_query_dcmd;
        drvr->proto->set_dcmd = brcmf_msgbuf_set_dcmd;
        drvr->proto->txdata = brcmf_msgbuf_txdata;
        drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode;
        drvr->proto->delete_peer = brcmf_msgbuf_delete_peer;
        drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer;
        drvr->proto->pd = msgbuf;

        init_waitqueue_head(&msgbuf->ioctl_resp_wait);

        msgbuf->commonrings =
                (struct brcmf_commonring **)if_msgbuf->commonrings;
        msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings;
        msgbuf->nrof_flowrings = if_msgbuf->nrof_flowrings;
        msgbuf->flowring_dma_handle = kzalloc(msgbuf->nrof_flowrings *
                sizeof(*msgbuf->flowring_dma_handle), GFP_KERNEL);
        if (!msgbuf->flowring_dma_handle)
                goto fail;

        msgbuf->rx_dataoffset = if_msgbuf->rx_dataoffset;
        msgbuf->max_rxbufpost = if_msgbuf->max_rxbufpost;

        msgbuf->max_ioctlrespbuf = BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST;
        msgbuf->max_eventbuf = BRCMF_MSGBUF_MAX_EVENTBUF_POST;

        msgbuf->tx_pktids = brcmf_msgbuf_init_pktids(NR_TX_PKTIDS,
                                                     DMA_TO_DEVICE);
        if (!msgbuf->tx_pktids)
                goto fail;
        msgbuf->rx_pktids = brcmf_msgbuf_init_pktids(NR_RX_PKTIDS,
                                                     DMA_FROM_DEVICE);
        if (!msgbuf->rx_pktids)
                goto fail;

        msgbuf->flow = brcmf_flowring_attach(drvr->bus_if->dev,
                                             if_msgbuf->nrof_flowrings);
        if (!msgbuf->flow)
                goto fail;


        brcmf_dbg(MSGBUF, "Feeding buffers, rx data %d, rx event %d, rx ioctl resp %d\n",
                  msgbuf->max_rxbufpost, msgbuf->max_eventbuf,
                  msgbuf->max_ioctlrespbuf);
        count = 0;
        do {
                brcmf_msgbuf_rxbuf_data_fill(msgbuf);
                if (msgbuf->max_rxbufpost != msgbuf->rxbufpost)
                        msleep(10);
                else
                        break;
                count++;
        } while (count < 10);
        brcmf_msgbuf_rxbuf_event_post(msgbuf);
        brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf);

        INIT_WORK(&msgbuf->flowring_work, brcmf_msgbuf_flowring_worker);
        spin_lock_init(&msgbuf->flowring_work_lock);
        INIT_LIST_HEAD(&msgbuf->work_queue);

        brcmf_debugfs_add_entry(drvr, "msgbuf_stats", brcmf_msgbuf_stats_read);

        return 0;

fail:
        if (msgbuf) {
                kfree(msgbuf->flow_map);
                kfree(msgbuf->txstatus_done_map);
                brcmf_msgbuf_release_pktids(msgbuf);
                kfree(msgbuf->flowring_dma_handle);
                if (msgbuf->ioctbuf)
                        dma_free_coherent(drvr->bus_if->dev,
                                          BRCMF_TX_IOCTL_MAX_MSG_SIZE,
                                          msgbuf->ioctbuf,
                                          msgbuf->ioctbuf_handle);
                kfree(msgbuf);
        }
        return -ENOMEM;
}


void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr)
{
        struct brcmf_msgbuf *msgbuf;
        struct brcmf_msgbuf_work_item *work;

        brcmf_dbg(TRACE, "Enter\n");
        if (drvr->proto->pd) {
                msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
                cancel_work_sync(&msgbuf->flowring_work);
                while (!list_empty(&msgbuf->work_queue)) {
                        work = list_first_entry(&msgbuf->work_queue,
                                                struct brcmf_msgbuf_work_item,
                                                queue);
                        list_del(&work->queue);
                        kfree(work);
                }
                kfree(msgbuf->flow_map);
                kfree(msgbuf->txstatus_done_map);
                if (msgbuf->txflow_wq)
                        destroy_workqueue(msgbuf->txflow_wq);

                brcmf_flowring_detach(msgbuf->flow);
                dma_free_coherent(drvr->bus_if->dev,
                                  BRCMF_TX_IOCTL_MAX_MSG_SIZE,
                                  msgbuf->ioctbuf, msgbuf->ioctbuf_handle);
                brcmf_msgbuf_release_pktids(msgbuf);
                kfree(msgbuf->flowring_dma_handle);
                kfree(msgbuf);
                drvr->proto->pd = NULL;
        }
}