162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2014 Broadcom Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/*******************************************************************************
762306a36Sopenharmony_ci * Communicates with the dongle by using dcmd codes.
862306a36Sopenharmony_ci * For certain dcmd codes, the dongle interprets string data from the host.
962306a36Sopenharmony_ci ******************************************************************************/
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/netdevice.h>
1362306a36Sopenharmony_ci#include <linux/etherdevice.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <brcmu_utils.h>
1662306a36Sopenharmony_ci#include <brcmu_wifi.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "core.h"
1962306a36Sopenharmony_ci#include "debug.h"
2062306a36Sopenharmony_ci#include "proto.h"
2162306a36Sopenharmony_ci#include "msgbuf.h"
2262306a36Sopenharmony_ci#include "commonring.h"
2362306a36Sopenharmony_ci#include "flowring.h"
2462306a36Sopenharmony_ci#include "bus.h"
2562306a36Sopenharmony_ci#include "tracepoint.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define MSGBUF_IOCTL_RESP_TIMEOUT		msecs_to_jiffies(2000)
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define MSGBUF_TYPE_GEN_STATUS			0x1
3162306a36Sopenharmony_ci#define MSGBUF_TYPE_RING_STATUS			0x2
3262306a36Sopenharmony_ci#define MSGBUF_TYPE_FLOW_RING_CREATE		0x3
3362306a36Sopenharmony_ci#define MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT	0x4
3462306a36Sopenharmony_ci#define MSGBUF_TYPE_FLOW_RING_DELETE		0x5
3562306a36Sopenharmony_ci#define MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT	0x6
3662306a36Sopenharmony_ci#define MSGBUF_TYPE_FLOW_RING_FLUSH		0x7
3762306a36Sopenharmony_ci#define MSGBUF_TYPE_FLOW_RING_FLUSH_CMPLT	0x8
3862306a36Sopenharmony_ci#define MSGBUF_TYPE_IOCTLPTR_REQ		0x9
3962306a36Sopenharmony_ci#define MSGBUF_TYPE_IOCTLPTR_REQ_ACK		0xA
4062306a36Sopenharmony_ci#define MSGBUF_TYPE_IOCTLRESP_BUF_POST		0xB
4162306a36Sopenharmony_ci#define MSGBUF_TYPE_IOCTL_CMPLT			0xC
4262306a36Sopenharmony_ci#define MSGBUF_TYPE_EVENT_BUF_POST		0xD
4362306a36Sopenharmony_ci#define MSGBUF_TYPE_WL_EVENT			0xE
4462306a36Sopenharmony_ci#define MSGBUF_TYPE_TX_POST			0xF
4562306a36Sopenharmony_ci#define MSGBUF_TYPE_TX_STATUS			0x10
4662306a36Sopenharmony_ci#define MSGBUF_TYPE_RXBUF_POST			0x11
4762306a36Sopenharmony_ci#define MSGBUF_TYPE_RX_CMPLT			0x12
4862306a36Sopenharmony_ci#define MSGBUF_TYPE_LPBK_DMAXFER		0x13
4962306a36Sopenharmony_ci#define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT		0x14
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define NR_TX_PKTIDS				2048
5262306a36Sopenharmony_ci#define NR_RX_PKTIDS				1024
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define BRCMF_IOCTL_REQ_PKTID			0xFFFE
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define BRCMF_MSGBUF_MAX_PKT_SIZE		2048
5762306a36Sopenharmony_ci#define BRCMF_MSGBUF_MAX_CTL_PKT_SIZE           8192
5862306a36Sopenharmony_ci#define BRCMF_MSGBUF_RXBUFPOST_THRESHOLD	32
5962306a36Sopenharmony_ci#define BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST	8
6062306a36Sopenharmony_ci#define BRCMF_MSGBUF_MAX_EVENTBUF_POST		8
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3	0x01
6362306a36Sopenharmony_ci#define BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_11	0x02
6462306a36Sopenharmony_ci#define BRCMF_MSGBUF_PKT_FLAGS_FRAME_MASK	0x07
6562306a36Sopenharmony_ci#define BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT	5
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define BRCMF_MSGBUF_TX_FLUSH_CNT1		32
6862306a36Sopenharmony_ci#define BRCMF_MSGBUF_TX_FLUSH_CNT2		96
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS	96
7162306a36Sopenharmony_ci#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS	32
7262306a36Sopenharmony_ci#define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS		48
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define BRCMF_MAX_TXSTATUS_WAIT_RETRIES		10
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistruct msgbuf_common_hdr {
7762306a36Sopenharmony_ci	u8				msgtype;
7862306a36Sopenharmony_ci	u8				ifidx;
7962306a36Sopenharmony_ci	u8				flags;
8062306a36Sopenharmony_ci	u8				rsvd0;
8162306a36Sopenharmony_ci	__le32				request_id;
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistruct msgbuf_ioctl_req_hdr {
8562306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
8662306a36Sopenharmony_ci	__le32				cmd;
8762306a36Sopenharmony_ci	__le16				trans_id;
8862306a36Sopenharmony_ci	__le16				input_buf_len;
8962306a36Sopenharmony_ci	__le16				output_buf_len;
9062306a36Sopenharmony_ci	__le16				rsvd0[3];
9162306a36Sopenharmony_ci	struct msgbuf_buf_addr		req_buf_addr;
9262306a36Sopenharmony_ci	__le32				rsvd1[2];
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistruct msgbuf_tx_msghdr {
9662306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
9762306a36Sopenharmony_ci	u8				txhdr[ETH_HLEN];
9862306a36Sopenharmony_ci	u8				flags;
9962306a36Sopenharmony_ci	u8				seg_cnt;
10062306a36Sopenharmony_ci	struct msgbuf_buf_addr		metadata_buf_addr;
10162306a36Sopenharmony_ci	struct msgbuf_buf_addr		data_buf_addr;
10262306a36Sopenharmony_ci	__le16				metadata_buf_len;
10362306a36Sopenharmony_ci	__le16				data_len;
10462306a36Sopenharmony_ci	__le32				rsvd0;
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistruct msgbuf_rx_bufpost {
10862306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
10962306a36Sopenharmony_ci	__le16				metadata_buf_len;
11062306a36Sopenharmony_ci	__le16				data_buf_len;
11162306a36Sopenharmony_ci	__le32				rsvd0;
11262306a36Sopenharmony_ci	struct msgbuf_buf_addr		metadata_buf_addr;
11362306a36Sopenharmony_ci	struct msgbuf_buf_addr		data_buf_addr;
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistruct msgbuf_rx_ioctl_resp_or_event {
11762306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
11862306a36Sopenharmony_ci	__le16				host_buf_len;
11962306a36Sopenharmony_ci	__le16				rsvd0[3];
12062306a36Sopenharmony_ci	struct msgbuf_buf_addr		host_buf_addr;
12162306a36Sopenharmony_ci	__le32				rsvd1[4];
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistruct msgbuf_completion_hdr {
12562306a36Sopenharmony_ci	__le16				status;
12662306a36Sopenharmony_ci	__le16				flow_ring_id;
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/* Data struct for the MSGBUF_TYPE_GEN_STATUS */
13062306a36Sopenharmony_cistruct msgbuf_gen_status {
13162306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
13262306a36Sopenharmony_ci	struct msgbuf_completion_hdr	compl_hdr;
13362306a36Sopenharmony_ci	__le16				write_idx;
13462306a36Sopenharmony_ci	__le32				rsvd0[3];
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/* Data struct for the MSGBUF_TYPE_RING_STATUS */
13862306a36Sopenharmony_cistruct msgbuf_ring_status {
13962306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
14062306a36Sopenharmony_ci	struct msgbuf_completion_hdr	compl_hdr;
14162306a36Sopenharmony_ci	__le16				write_idx;
14262306a36Sopenharmony_ci	__le16				rsvd0[5];
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistruct msgbuf_rx_event {
14662306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
14762306a36Sopenharmony_ci	struct msgbuf_completion_hdr	compl_hdr;
14862306a36Sopenharmony_ci	__le16				event_data_len;
14962306a36Sopenharmony_ci	__le16				seqnum;
15062306a36Sopenharmony_ci	__le16				rsvd0[4];
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistruct msgbuf_ioctl_resp_hdr {
15462306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
15562306a36Sopenharmony_ci	struct msgbuf_completion_hdr	compl_hdr;
15662306a36Sopenharmony_ci	__le16				resp_len;
15762306a36Sopenharmony_ci	__le16				trans_id;
15862306a36Sopenharmony_ci	__le32				cmd;
15962306a36Sopenharmony_ci	__le32				rsvd0;
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistruct msgbuf_tx_status {
16362306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
16462306a36Sopenharmony_ci	struct msgbuf_completion_hdr	compl_hdr;
16562306a36Sopenharmony_ci	__le16				metadata_len;
16662306a36Sopenharmony_ci	__le16				tx_status;
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistruct msgbuf_rx_complete {
17062306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
17162306a36Sopenharmony_ci	struct msgbuf_completion_hdr	compl_hdr;
17262306a36Sopenharmony_ci	__le16				metadata_len;
17362306a36Sopenharmony_ci	__le16				data_len;
17462306a36Sopenharmony_ci	__le16				data_offset;
17562306a36Sopenharmony_ci	__le16				flags;
17662306a36Sopenharmony_ci	__le32				rx_status_0;
17762306a36Sopenharmony_ci	__le32				rx_status_1;
17862306a36Sopenharmony_ci	__le32				rsvd0;
17962306a36Sopenharmony_ci};
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistruct msgbuf_tx_flowring_create_req {
18262306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
18362306a36Sopenharmony_ci	u8				da[ETH_ALEN];
18462306a36Sopenharmony_ci	u8				sa[ETH_ALEN];
18562306a36Sopenharmony_ci	u8				tid;
18662306a36Sopenharmony_ci	u8				if_flags;
18762306a36Sopenharmony_ci	__le16				flow_ring_id;
18862306a36Sopenharmony_ci	u8				tc;
18962306a36Sopenharmony_ci	u8				priority;
19062306a36Sopenharmony_ci	__le16				int_vector;
19162306a36Sopenharmony_ci	__le16				max_items;
19262306a36Sopenharmony_ci	__le16				len_item;
19362306a36Sopenharmony_ci	struct msgbuf_buf_addr		flow_ring_addr;
19462306a36Sopenharmony_ci};
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistruct msgbuf_tx_flowring_delete_req {
19762306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
19862306a36Sopenharmony_ci	__le16				flow_ring_id;
19962306a36Sopenharmony_ci	__le16				reason;
20062306a36Sopenharmony_ci	__le32				rsvd0[7];
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistruct msgbuf_flowring_create_resp {
20462306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
20562306a36Sopenharmony_ci	struct msgbuf_completion_hdr	compl_hdr;
20662306a36Sopenharmony_ci	__le32				rsvd0[3];
20762306a36Sopenharmony_ci};
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistruct msgbuf_flowring_delete_resp {
21062306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
21162306a36Sopenharmony_ci	struct msgbuf_completion_hdr	compl_hdr;
21262306a36Sopenharmony_ci	__le32				rsvd0[3];
21362306a36Sopenharmony_ci};
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistruct msgbuf_flowring_flush_resp {
21662306a36Sopenharmony_ci	struct msgbuf_common_hdr	msg;
21762306a36Sopenharmony_ci	struct msgbuf_completion_hdr	compl_hdr;
21862306a36Sopenharmony_ci	__le32				rsvd0[3];
21962306a36Sopenharmony_ci};
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistruct brcmf_msgbuf_work_item {
22262306a36Sopenharmony_ci	struct list_head queue;
22362306a36Sopenharmony_ci	u32 flowid;
22462306a36Sopenharmony_ci	int ifidx;
22562306a36Sopenharmony_ci	u8 sa[ETH_ALEN];
22662306a36Sopenharmony_ci	u8 da[ETH_ALEN];
22762306a36Sopenharmony_ci};
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistruct brcmf_msgbuf {
23062306a36Sopenharmony_ci	struct brcmf_pub *drvr;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	struct brcmf_commonring **commonrings;
23362306a36Sopenharmony_ci	struct brcmf_commonring **flowrings;
23462306a36Sopenharmony_ci	dma_addr_t *flowring_dma_handle;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	u16 max_flowrings;
23762306a36Sopenharmony_ci	u16 max_submissionrings;
23862306a36Sopenharmony_ci	u16 max_completionrings;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	u16 rx_dataoffset;
24162306a36Sopenharmony_ci	u32 max_rxbufpost;
24262306a36Sopenharmony_ci	u16 rx_metadata_offset;
24362306a36Sopenharmony_ci	u32 rxbufpost;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	u32 max_ioctlrespbuf;
24662306a36Sopenharmony_ci	u32 cur_ioctlrespbuf;
24762306a36Sopenharmony_ci	u32 max_eventbuf;
24862306a36Sopenharmony_ci	u32 cur_eventbuf;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	void *ioctbuf;
25162306a36Sopenharmony_ci	dma_addr_t ioctbuf_handle;
25262306a36Sopenharmony_ci	u32 ioctbuf_phys_hi;
25362306a36Sopenharmony_ci	u32 ioctbuf_phys_lo;
25462306a36Sopenharmony_ci	int ioctl_resp_status;
25562306a36Sopenharmony_ci	u32 ioctl_resp_ret_len;
25662306a36Sopenharmony_ci	u32 ioctl_resp_pktid;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	u16 data_seq_no;
25962306a36Sopenharmony_ci	u16 ioctl_seq_no;
26062306a36Sopenharmony_ci	u32 reqid;
26162306a36Sopenharmony_ci	wait_queue_head_t ioctl_resp_wait;
26262306a36Sopenharmony_ci	bool ctl_completed;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	struct brcmf_msgbuf_pktids *tx_pktids;
26562306a36Sopenharmony_ci	struct brcmf_msgbuf_pktids *rx_pktids;
26662306a36Sopenharmony_ci	struct brcmf_flowring *flow;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	struct workqueue_struct *txflow_wq;
26962306a36Sopenharmony_ci	struct work_struct txflow_work;
27062306a36Sopenharmony_ci	unsigned long *flow_map;
27162306a36Sopenharmony_ci	unsigned long *txstatus_done_map;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	struct work_struct flowring_work;
27462306a36Sopenharmony_ci	spinlock_t flowring_work_lock;
27562306a36Sopenharmony_ci	struct list_head work_queue;
27662306a36Sopenharmony_ci};
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistruct brcmf_msgbuf_pktid {
27962306a36Sopenharmony_ci	atomic_t  allocated;
28062306a36Sopenharmony_ci	u16 data_offset;
28162306a36Sopenharmony_ci	struct sk_buff *skb;
28262306a36Sopenharmony_ci	dma_addr_t physaddr;
28362306a36Sopenharmony_ci};
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistruct brcmf_msgbuf_pktids {
28662306a36Sopenharmony_ci	u32 array_size;
28762306a36Sopenharmony_ci	u32 last_allocated_idx;
28862306a36Sopenharmony_ci	enum dma_data_direction direction;
28962306a36Sopenharmony_ci	struct brcmf_msgbuf_pktid *array;
29062306a36Sopenharmony_ci};
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic struct brcmf_msgbuf_pktids *
29662306a36Sopenharmony_cibrcmf_msgbuf_init_pktids(u32 nr_array_entries,
29762306a36Sopenharmony_ci			 enum dma_data_direction direction)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct brcmf_msgbuf_pktid *array;
30062306a36Sopenharmony_ci	struct brcmf_msgbuf_pktids *pktids;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	array = kcalloc(nr_array_entries, sizeof(*array), GFP_KERNEL);
30362306a36Sopenharmony_ci	if (!array)
30462306a36Sopenharmony_ci		return NULL;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	pktids = kzalloc(sizeof(*pktids), GFP_KERNEL);
30762306a36Sopenharmony_ci	if (!pktids) {
30862306a36Sopenharmony_ci		kfree(array);
30962306a36Sopenharmony_ci		return NULL;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci	pktids->array = array;
31262306a36Sopenharmony_ci	pktids->array_size = nr_array_entries;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return pktids;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int
31962306a36Sopenharmony_cibrcmf_msgbuf_alloc_pktid(struct device *dev,
32062306a36Sopenharmony_ci			 struct brcmf_msgbuf_pktids *pktids,
32162306a36Sopenharmony_ci			 struct sk_buff *skb, u16 data_offset,
32262306a36Sopenharmony_ci			 dma_addr_t *physaddr, u32 *idx)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct brcmf_msgbuf_pktid *array;
32562306a36Sopenharmony_ci	u32 count;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	array = pktids->array;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	*physaddr = dma_map_single(dev, skb->data + data_offset,
33062306a36Sopenharmony_ci				   skb->len - data_offset, pktids->direction);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (dma_mapping_error(dev, *physaddr)) {
33362306a36Sopenharmony_ci		brcmf_err("dma_map_single failed !!\n");
33462306a36Sopenharmony_ci		return -ENOMEM;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	*idx = pktids->last_allocated_idx;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	count = 0;
34062306a36Sopenharmony_ci	do {
34162306a36Sopenharmony_ci		(*idx)++;
34262306a36Sopenharmony_ci		if (*idx == pktids->array_size)
34362306a36Sopenharmony_ci			*idx = 0;
34462306a36Sopenharmony_ci		if (array[*idx].allocated.counter == 0)
34562306a36Sopenharmony_ci			if (atomic_cmpxchg(&array[*idx].allocated, 0, 1) == 0)
34662306a36Sopenharmony_ci				break;
34762306a36Sopenharmony_ci		count++;
34862306a36Sopenharmony_ci	} while (count < pktids->array_size);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (count == pktids->array_size) {
35162306a36Sopenharmony_ci		dma_unmap_single(dev, *physaddr, skb->len - data_offset,
35262306a36Sopenharmony_ci				 pktids->direction);
35362306a36Sopenharmony_ci		return -ENOMEM;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	array[*idx].data_offset = data_offset;
35762306a36Sopenharmony_ci	array[*idx].physaddr = *physaddr;
35862306a36Sopenharmony_ci	array[*idx].skb = skb;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	pktids->last_allocated_idx = *idx;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	return 0;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic struct sk_buff *
36762306a36Sopenharmony_cibrcmf_msgbuf_get_pktid(struct device *dev, struct brcmf_msgbuf_pktids *pktids,
36862306a36Sopenharmony_ci		       u32 idx)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	struct brcmf_msgbuf_pktid *pktid;
37162306a36Sopenharmony_ci	struct sk_buff *skb;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (idx >= pktids->array_size) {
37462306a36Sopenharmony_ci		brcmf_err("Invalid packet id %d (max %d)\n", idx,
37562306a36Sopenharmony_ci			  pktids->array_size);
37662306a36Sopenharmony_ci		return NULL;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci	if (pktids->array[idx].allocated.counter) {
37962306a36Sopenharmony_ci		pktid = &pktids->array[idx];
38062306a36Sopenharmony_ci		dma_unmap_single(dev, pktid->physaddr,
38162306a36Sopenharmony_ci				 pktid->skb->len - pktid->data_offset,
38262306a36Sopenharmony_ci				 pktids->direction);
38362306a36Sopenharmony_ci		skb = pktid->skb;
38462306a36Sopenharmony_ci		pktid->allocated.counter = 0;
38562306a36Sopenharmony_ci		return skb;
38662306a36Sopenharmony_ci	} else {
38762306a36Sopenharmony_ci		brcmf_err("Invalid packet id %d (not in use)\n", idx);
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return NULL;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic void
39562306a36Sopenharmony_cibrcmf_msgbuf_release_array(struct device *dev,
39662306a36Sopenharmony_ci			   struct brcmf_msgbuf_pktids *pktids)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct brcmf_msgbuf_pktid *array;
39962306a36Sopenharmony_ci	struct brcmf_msgbuf_pktid *pktid;
40062306a36Sopenharmony_ci	u32 count;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	array = pktids->array;
40362306a36Sopenharmony_ci	count = 0;
40462306a36Sopenharmony_ci	do {
40562306a36Sopenharmony_ci		if (array[count].allocated.counter) {
40662306a36Sopenharmony_ci			pktid = &array[count];
40762306a36Sopenharmony_ci			dma_unmap_single(dev, pktid->physaddr,
40862306a36Sopenharmony_ci					 pktid->skb->len - pktid->data_offset,
40962306a36Sopenharmony_ci					 pktids->direction);
41062306a36Sopenharmony_ci			brcmu_pkt_buf_free_skb(pktid->skb);
41162306a36Sopenharmony_ci		}
41262306a36Sopenharmony_ci		count++;
41362306a36Sopenharmony_ci	} while (count < pktids->array_size);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	kfree(array);
41662306a36Sopenharmony_ci	kfree(pktids);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic void brcmf_msgbuf_release_pktids(struct brcmf_msgbuf *msgbuf)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	if (msgbuf->rx_pktids)
42362306a36Sopenharmony_ci		brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev,
42462306a36Sopenharmony_ci					   msgbuf->rx_pktids);
42562306a36Sopenharmony_ci	if (msgbuf->tx_pktids)
42662306a36Sopenharmony_ci		brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev,
42762306a36Sopenharmony_ci					   msgbuf->tx_pktids);
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx,
43262306a36Sopenharmony_ci				 uint cmd, void *buf, uint len)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
43562306a36Sopenharmony_ci	struct brcmf_commonring *commonring;
43662306a36Sopenharmony_ci	struct msgbuf_ioctl_req_hdr *request;
43762306a36Sopenharmony_ci	u16 buf_len;
43862306a36Sopenharmony_ci	void *ret_ptr;
43962306a36Sopenharmony_ci	int err;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
44262306a36Sopenharmony_ci	brcmf_commonring_lock(commonring);
44362306a36Sopenharmony_ci	ret_ptr = brcmf_commonring_reserve_for_write(commonring);
44462306a36Sopenharmony_ci	if (!ret_ptr) {
44562306a36Sopenharmony_ci		bphy_err(drvr, "Failed to reserve space in commonring\n");
44662306a36Sopenharmony_ci		brcmf_commonring_unlock(commonring);
44762306a36Sopenharmony_ci		return -ENOMEM;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	msgbuf->reqid++;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	request = (struct msgbuf_ioctl_req_hdr *)ret_ptr;
45362306a36Sopenharmony_ci	request->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ;
45462306a36Sopenharmony_ci	request->msg.ifidx = (u8)ifidx;
45562306a36Sopenharmony_ci	request->msg.flags = 0;
45662306a36Sopenharmony_ci	request->msg.request_id = cpu_to_le32(BRCMF_IOCTL_REQ_PKTID);
45762306a36Sopenharmony_ci	request->cmd = cpu_to_le32(cmd);
45862306a36Sopenharmony_ci	request->output_buf_len = cpu_to_le16(len);
45962306a36Sopenharmony_ci	request->trans_id = cpu_to_le16(msgbuf->reqid);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	buf_len = min_t(u16, len, BRCMF_TX_IOCTL_MAX_MSG_SIZE);
46262306a36Sopenharmony_ci	request->input_buf_len = cpu_to_le16(buf_len);
46362306a36Sopenharmony_ci	request->req_buf_addr.high_addr = cpu_to_le32(msgbuf->ioctbuf_phys_hi);
46462306a36Sopenharmony_ci	request->req_buf_addr.low_addr = cpu_to_le32(msgbuf->ioctbuf_phys_lo);
46562306a36Sopenharmony_ci	if (buf)
46662306a36Sopenharmony_ci		memcpy(msgbuf->ioctbuf, buf, buf_len);
46762306a36Sopenharmony_ci	else
46862306a36Sopenharmony_ci		memset(msgbuf->ioctbuf, 0, buf_len);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	err = brcmf_commonring_write_complete(commonring);
47162306a36Sopenharmony_ci	brcmf_commonring_unlock(commonring);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return err;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	return wait_event_timeout(msgbuf->ioctl_resp_wait,
48062306a36Sopenharmony_ci				  msgbuf->ctl_completed,
48162306a36Sopenharmony_ci				  MSGBUF_IOCTL_RESP_TIMEOUT);
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	msgbuf->ctl_completed = true;
48862306a36Sopenharmony_ci	wake_up(&msgbuf->ioctl_resp_wait);
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx,
49362306a36Sopenharmony_ci				   uint cmd, void *buf, uint len, int *fwerr)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
49662306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
49762306a36Sopenharmony_ci	int timeout;
49862306a36Sopenharmony_ci	int err;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	brcmf_dbg(MSGBUF, "ifidx=%d, cmd=%d, len=%d\n", ifidx, cmd, len);
50162306a36Sopenharmony_ci	*fwerr = 0;
50262306a36Sopenharmony_ci	msgbuf->ctl_completed = false;
50362306a36Sopenharmony_ci	err = brcmf_msgbuf_tx_ioctl(drvr, ifidx, cmd, buf, len);
50462306a36Sopenharmony_ci	if (err)
50562306a36Sopenharmony_ci		return err;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	timeout = brcmf_msgbuf_ioctl_resp_wait(msgbuf);
50862306a36Sopenharmony_ci	if (!timeout) {
50962306a36Sopenharmony_ci		bphy_err(drvr, "Timeout on response for query command\n");
51062306a36Sopenharmony_ci		return -EIO;
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
51462306a36Sopenharmony_ci				     msgbuf->rx_pktids,
51562306a36Sopenharmony_ci				     msgbuf->ioctl_resp_pktid);
51662306a36Sopenharmony_ci	if (msgbuf->ioctl_resp_ret_len != 0) {
51762306a36Sopenharmony_ci		if (!skb)
51862306a36Sopenharmony_ci			return -EBADF;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ?
52162306a36Sopenharmony_ci				       len : msgbuf->ioctl_resp_ret_len);
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci	brcmu_pkt_buf_free_skb(skb);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	*fwerr = msgbuf->ioctl_resp_status;
52662306a36Sopenharmony_ci	return 0;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx,
53162306a36Sopenharmony_ci				 uint cmd, void *buf, uint len, int *fwerr)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	return brcmf_msgbuf_query_dcmd(drvr, ifidx, cmd, buf, len, fwerr);
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws,
53862306a36Sopenharmony_ci				struct sk_buff *skb, struct brcmf_if **ifp)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	return -ENODEV;
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic void brcmf_msgbuf_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic void
54862306a36Sopenharmony_cibrcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	u32 dma_sz;
55162306a36Sopenharmony_ci	void *dma_buf;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	brcmf_dbg(MSGBUF, "Removing flowring %d\n", flowid);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE;
55662306a36Sopenharmony_ci	dma_buf = msgbuf->flowrings[flowid]->buf_addr;
55762306a36Sopenharmony_ci	dma_free_coherent(msgbuf->drvr->bus_if->dev, dma_sz, dma_buf,
55862306a36Sopenharmony_ci			  msgbuf->flowring_dma_handle[flowid]);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	brcmf_flowring_delete(msgbuf->flow, flowid);
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic struct brcmf_msgbuf_work_item *
56562306a36Sopenharmony_cibrcmf_msgbuf_dequeue_work(struct brcmf_msgbuf *msgbuf)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	struct brcmf_msgbuf_work_item *work = NULL;
56862306a36Sopenharmony_ci	ulong flags;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	spin_lock_irqsave(&msgbuf->flowring_work_lock, flags);
57162306a36Sopenharmony_ci	if (!list_empty(&msgbuf->work_queue)) {
57262306a36Sopenharmony_ci		work = list_first_entry(&msgbuf->work_queue,
57362306a36Sopenharmony_ci					struct brcmf_msgbuf_work_item, queue);
57462306a36Sopenharmony_ci		list_del(&work->queue);
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci	spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	return work;
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic u32
58362306a36Sopenharmony_cibrcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf,
58462306a36Sopenharmony_ci				    struct brcmf_msgbuf_work_item *work)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	struct brcmf_pub *drvr = msgbuf->drvr;
58762306a36Sopenharmony_ci	struct msgbuf_tx_flowring_create_req *create;
58862306a36Sopenharmony_ci	struct brcmf_commonring *commonring;
58962306a36Sopenharmony_ci	void *ret_ptr;
59062306a36Sopenharmony_ci	u32 flowid;
59162306a36Sopenharmony_ci	void *dma_buf;
59262306a36Sopenharmony_ci	u32 dma_sz;
59362306a36Sopenharmony_ci	u64 address;
59462306a36Sopenharmony_ci	int err;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	flowid = work->flowid;
59762306a36Sopenharmony_ci	dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE;
59862306a36Sopenharmony_ci	dma_buf = dma_alloc_coherent(msgbuf->drvr->bus_if->dev, dma_sz,
59962306a36Sopenharmony_ci				     &msgbuf->flowring_dma_handle[flowid],
60062306a36Sopenharmony_ci				     GFP_KERNEL);
60162306a36Sopenharmony_ci	if (!dma_buf) {
60262306a36Sopenharmony_ci		bphy_err(drvr, "dma_alloc_coherent failed\n");
60362306a36Sopenharmony_ci		brcmf_flowring_delete(msgbuf->flow, flowid);
60462306a36Sopenharmony_ci		return BRCMF_FLOWRING_INVALID_ID;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	brcmf_commonring_config(msgbuf->flowrings[flowid],
60862306a36Sopenharmony_ci				BRCMF_H2D_TXFLOWRING_MAX_ITEM,
60962306a36Sopenharmony_ci				BRCMF_H2D_TXFLOWRING_ITEMSIZE, dma_buf);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
61262306a36Sopenharmony_ci	brcmf_commonring_lock(commonring);
61362306a36Sopenharmony_ci	ret_ptr = brcmf_commonring_reserve_for_write(commonring);
61462306a36Sopenharmony_ci	if (!ret_ptr) {
61562306a36Sopenharmony_ci		bphy_err(drvr, "Failed to reserve space in commonring\n");
61662306a36Sopenharmony_ci		brcmf_commonring_unlock(commonring);
61762306a36Sopenharmony_ci		brcmf_msgbuf_remove_flowring(msgbuf, flowid);
61862306a36Sopenharmony_ci		return BRCMF_FLOWRING_INVALID_ID;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	create = (struct msgbuf_tx_flowring_create_req *)ret_ptr;
62262306a36Sopenharmony_ci	create->msg.msgtype = MSGBUF_TYPE_FLOW_RING_CREATE;
62362306a36Sopenharmony_ci	create->msg.ifidx = work->ifidx;
62462306a36Sopenharmony_ci	create->msg.request_id = 0;
62562306a36Sopenharmony_ci	create->tid = brcmf_flowring_tid(msgbuf->flow, flowid);
62662306a36Sopenharmony_ci	create->flow_ring_id = cpu_to_le16(flowid +
62762306a36Sopenharmony_ci					   BRCMF_H2D_MSGRING_FLOWRING_IDSTART);
62862306a36Sopenharmony_ci	memcpy(create->sa, work->sa, ETH_ALEN);
62962306a36Sopenharmony_ci	memcpy(create->da, work->da, ETH_ALEN);
63062306a36Sopenharmony_ci	address = (u64)msgbuf->flowring_dma_handle[flowid];
63162306a36Sopenharmony_ci	create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32);
63262306a36Sopenharmony_ci	create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff);
63362306a36Sopenharmony_ci	create->max_items = cpu_to_le16(BRCMF_H2D_TXFLOWRING_MAX_ITEM);
63462306a36Sopenharmony_ci	create->len_item = cpu_to_le16(BRCMF_H2D_TXFLOWRING_ITEMSIZE);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	brcmf_dbg(MSGBUF, "Send Flow Create Req flow ID %d for peer %pM prio %d ifindex %d\n",
63762306a36Sopenharmony_ci		  flowid, work->da, create->tid, work->ifidx);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	err = brcmf_commonring_write_complete(commonring);
64062306a36Sopenharmony_ci	brcmf_commonring_unlock(commonring);
64162306a36Sopenharmony_ci	if (err) {
64262306a36Sopenharmony_ci		bphy_err(drvr, "Failed to write commonring\n");
64362306a36Sopenharmony_ci		brcmf_msgbuf_remove_flowring(msgbuf, flowid);
64462306a36Sopenharmony_ci		return BRCMF_FLOWRING_INVALID_ID;
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	return flowid;
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic void brcmf_msgbuf_flowring_worker(struct work_struct *work)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	struct brcmf_msgbuf *msgbuf;
65462306a36Sopenharmony_ci	struct brcmf_msgbuf_work_item *create;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	msgbuf = container_of(work, struct brcmf_msgbuf, flowring_work);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	while ((create = brcmf_msgbuf_dequeue_work(msgbuf))) {
65962306a36Sopenharmony_ci		brcmf_msgbuf_flowring_create_worker(msgbuf, create);
66062306a36Sopenharmony_ci		kfree(create);
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx,
66662306a36Sopenharmony_ci					struct sk_buff *skb)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	struct brcmf_msgbuf_work_item *create;
66962306a36Sopenharmony_ci	struct ethhdr *eh = (struct ethhdr *)(skb->data);
67062306a36Sopenharmony_ci	u32 flowid;
67162306a36Sopenharmony_ci	ulong flags;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	create = kzalloc(sizeof(*create), GFP_ATOMIC);
67462306a36Sopenharmony_ci	if (create == NULL)
67562306a36Sopenharmony_ci		return BRCMF_FLOWRING_INVALID_ID;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	flowid = brcmf_flowring_create(msgbuf->flow, eh->h_dest,
67862306a36Sopenharmony_ci				       skb->priority, ifidx);
67962306a36Sopenharmony_ci	if (flowid == BRCMF_FLOWRING_INVALID_ID) {
68062306a36Sopenharmony_ci		kfree(create);
68162306a36Sopenharmony_ci		return flowid;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	create->flowid = flowid;
68562306a36Sopenharmony_ci	create->ifidx = ifidx;
68662306a36Sopenharmony_ci	memcpy(create->sa, eh->h_source, ETH_ALEN);
68762306a36Sopenharmony_ci	memcpy(create->da, eh->h_dest, ETH_ALEN);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	spin_lock_irqsave(&msgbuf->flowring_work_lock, flags);
69062306a36Sopenharmony_ci	list_add_tail(&create->queue, &msgbuf->work_queue);
69162306a36Sopenharmony_ci	spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags);
69262306a36Sopenharmony_ci	schedule_work(&msgbuf->flowring_work);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	return flowid;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u16 flowid)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	struct brcmf_flowring *flow = msgbuf->flow;
70162306a36Sopenharmony_ci	struct brcmf_pub *drvr = msgbuf->drvr;
70262306a36Sopenharmony_ci	struct brcmf_commonring *commonring;
70362306a36Sopenharmony_ci	void *ret_ptr;
70462306a36Sopenharmony_ci	u32 count;
70562306a36Sopenharmony_ci	struct sk_buff *skb;
70662306a36Sopenharmony_ci	dma_addr_t physaddr;
70762306a36Sopenharmony_ci	u32 pktid;
70862306a36Sopenharmony_ci	struct msgbuf_tx_msghdr *tx_msghdr;
70962306a36Sopenharmony_ci	u64 address;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	commonring = msgbuf->flowrings[flowid];
71262306a36Sopenharmony_ci	if (!brcmf_commonring_write_available(commonring))
71362306a36Sopenharmony_ci		return;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	brcmf_commonring_lock(commonring);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	count = BRCMF_MSGBUF_TX_FLUSH_CNT2 - BRCMF_MSGBUF_TX_FLUSH_CNT1;
71862306a36Sopenharmony_ci	while (brcmf_flowring_qlen(flow, flowid)) {
71962306a36Sopenharmony_ci		skb = brcmf_flowring_dequeue(flow, flowid);
72062306a36Sopenharmony_ci		if (skb == NULL) {
72162306a36Sopenharmony_ci			bphy_err(drvr, "No SKB, but qlen %d\n",
72262306a36Sopenharmony_ci				 brcmf_flowring_qlen(flow, flowid));
72362306a36Sopenharmony_ci			break;
72462306a36Sopenharmony_ci		}
72562306a36Sopenharmony_ci		skb_orphan(skb);
72662306a36Sopenharmony_ci		if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
72762306a36Sopenharmony_ci					     msgbuf->tx_pktids, skb, ETH_HLEN,
72862306a36Sopenharmony_ci					     &physaddr, &pktid)) {
72962306a36Sopenharmony_ci			brcmf_flowring_reinsert(flow, flowid, skb);
73062306a36Sopenharmony_ci			bphy_err(drvr, "No PKTID available !!\n");
73162306a36Sopenharmony_ci			break;
73262306a36Sopenharmony_ci		}
73362306a36Sopenharmony_ci		ret_ptr = brcmf_commonring_reserve_for_write(commonring);
73462306a36Sopenharmony_ci		if (!ret_ptr) {
73562306a36Sopenharmony_ci			brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
73662306a36Sopenharmony_ci					       msgbuf->tx_pktids, pktid);
73762306a36Sopenharmony_ci			brcmf_flowring_reinsert(flow, flowid, skb);
73862306a36Sopenharmony_ci			break;
73962306a36Sopenharmony_ci		}
74062306a36Sopenharmony_ci		count++;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		tx_msghdr = (struct msgbuf_tx_msghdr *)ret_ptr;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci		tx_msghdr->msg.msgtype = MSGBUF_TYPE_TX_POST;
74562306a36Sopenharmony_ci		tx_msghdr->msg.request_id = cpu_to_le32(pktid + 1);
74662306a36Sopenharmony_ci		tx_msghdr->msg.ifidx = brcmf_flowring_ifidx_get(flow, flowid);
74762306a36Sopenharmony_ci		tx_msghdr->flags = BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3;
74862306a36Sopenharmony_ci		tx_msghdr->flags |= (skb->priority & 0x07) <<
74962306a36Sopenharmony_ci				    BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT;
75062306a36Sopenharmony_ci		tx_msghdr->seg_cnt = 1;
75162306a36Sopenharmony_ci		memcpy(tx_msghdr->txhdr, skb->data, ETH_HLEN);
75262306a36Sopenharmony_ci		tx_msghdr->data_len = cpu_to_le16(skb->len - ETH_HLEN);
75362306a36Sopenharmony_ci		address = (u64)physaddr;
75462306a36Sopenharmony_ci		tx_msghdr->data_buf_addr.high_addr = cpu_to_le32(address >> 32);
75562306a36Sopenharmony_ci		tx_msghdr->data_buf_addr.low_addr =
75662306a36Sopenharmony_ci			cpu_to_le32(address & 0xffffffff);
75762306a36Sopenharmony_ci		tx_msghdr->metadata_buf_len = 0;
75862306a36Sopenharmony_ci		tx_msghdr->metadata_buf_addr.high_addr = 0;
75962306a36Sopenharmony_ci		tx_msghdr->metadata_buf_addr.low_addr = 0;
76062306a36Sopenharmony_ci		atomic_inc(&commonring->outstanding_tx);
76162306a36Sopenharmony_ci		if (count >= BRCMF_MSGBUF_TX_FLUSH_CNT2) {
76262306a36Sopenharmony_ci			brcmf_commonring_write_complete(commonring);
76362306a36Sopenharmony_ci			count = 0;
76462306a36Sopenharmony_ci		}
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci	if (count)
76762306a36Sopenharmony_ci		brcmf_commonring_write_complete(commonring);
76862306a36Sopenharmony_ci	brcmf_commonring_unlock(commonring);
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cistatic void brcmf_msgbuf_txflow_worker(struct work_struct *worker)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct brcmf_msgbuf *msgbuf;
77562306a36Sopenharmony_ci	u32 flowid;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	msgbuf = container_of(worker, struct brcmf_msgbuf, txflow_work);
77862306a36Sopenharmony_ci	for_each_set_bit(flowid, msgbuf->flow_map, msgbuf->max_flowrings) {
77962306a36Sopenharmony_ci		clear_bit(flowid, msgbuf->flow_map);
78062306a36Sopenharmony_ci		brcmf_msgbuf_txflow(msgbuf, flowid);
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid,
78662306a36Sopenharmony_ci					bool force)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	struct brcmf_commonring *commonring;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	set_bit(flowid, msgbuf->flow_map);
79162306a36Sopenharmony_ci	commonring = msgbuf->flowrings[flowid];
79262306a36Sopenharmony_ci	if ((force) || (atomic_read(&commonring->outstanding_tx) <
79362306a36Sopenharmony_ci			BRCMF_MSGBUF_DELAY_TXWORKER_THRS))
79462306a36Sopenharmony_ci		queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	return 0;
79762306a36Sopenharmony_ci}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic int brcmf_msgbuf_tx_queue_data(struct brcmf_pub *drvr, int ifidx,
80162306a36Sopenharmony_ci				      struct sk_buff *skb)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
80462306a36Sopenharmony_ci	struct brcmf_flowring *flow = msgbuf->flow;
80562306a36Sopenharmony_ci	struct ethhdr *eh = (struct ethhdr *)(skb->data);
80662306a36Sopenharmony_ci	u32 flowid;
80762306a36Sopenharmony_ci	u32 queue_count;
80862306a36Sopenharmony_ci	bool force;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx);
81162306a36Sopenharmony_ci	if (flowid == BRCMF_FLOWRING_INVALID_ID) {
81262306a36Sopenharmony_ci		flowid = brcmf_msgbuf_flowring_create(msgbuf, ifidx, skb);
81362306a36Sopenharmony_ci		if (flowid == BRCMF_FLOWRING_INVALID_ID) {
81462306a36Sopenharmony_ci			return -ENOMEM;
81562306a36Sopenharmony_ci		} else {
81662306a36Sopenharmony_ci			brcmf_flowring_enqueue(flow, flowid, skb);
81762306a36Sopenharmony_ci			return 0;
81862306a36Sopenharmony_ci		}
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci	queue_count = brcmf_flowring_enqueue(flow, flowid, skb);
82162306a36Sopenharmony_ci	force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0);
82262306a36Sopenharmony_ci	brcmf_msgbuf_schedule_txdata(msgbuf, flowid, force);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	return 0;
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic void
82962306a36Sopenharmony_cibrcmf_msgbuf_configure_addr_mode(struct brcmf_pub *drvr, int ifidx,
83062306a36Sopenharmony_ci				 enum proto_addr_mode addr_mode)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	brcmf_flowring_configure_addr_mode(msgbuf->flow, ifidx, addr_mode);
83562306a36Sopenharmony_ci}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_cistatic void
83962306a36Sopenharmony_cibrcmf_msgbuf_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	brcmf_flowring_delete_peer(msgbuf->flow, ifidx, peer);
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cistatic void
84862306a36Sopenharmony_cibrcmf_msgbuf_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	brcmf_flowring_add_tdls_peer(msgbuf->flow, ifidx, peer);
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_cistatic void
85762306a36Sopenharmony_cibrcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	struct msgbuf_ioctl_resp_hdr *ioctl_resp;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	ioctl_resp = (struct msgbuf_ioctl_resp_hdr *)buf;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	msgbuf->ioctl_resp_status =
86462306a36Sopenharmony_ci			(s16)le16_to_cpu(ioctl_resp->compl_hdr.status);
86562306a36Sopenharmony_ci	msgbuf->ioctl_resp_ret_len = le16_to_cpu(ioctl_resp->resp_len);
86662306a36Sopenharmony_ci	msgbuf->ioctl_resp_pktid = le32_to_cpu(ioctl_resp->msg.request_id);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	brcmf_msgbuf_ioctl_resp_wake(msgbuf);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	if (msgbuf->cur_ioctlrespbuf)
87162306a36Sopenharmony_ci		msgbuf->cur_ioctlrespbuf--;
87262306a36Sopenharmony_ci	brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf);
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_cistatic void
87762306a36Sopenharmony_cibrcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	struct brcmf_commonring *commonring;
88062306a36Sopenharmony_ci	struct msgbuf_tx_status *tx_status;
88162306a36Sopenharmony_ci	u32 idx;
88262306a36Sopenharmony_ci	struct sk_buff *skb;
88362306a36Sopenharmony_ci	u16 flowid;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	tx_status = (struct msgbuf_tx_status *)buf;
88662306a36Sopenharmony_ci	idx = le32_to_cpu(tx_status->msg.request_id) - 1;
88762306a36Sopenharmony_ci	flowid = le16_to_cpu(tx_status->compl_hdr.flow_ring_id);
88862306a36Sopenharmony_ci	flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART;
88962306a36Sopenharmony_ci	skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
89062306a36Sopenharmony_ci				     msgbuf->tx_pktids, idx);
89162306a36Sopenharmony_ci	if (!skb)
89262306a36Sopenharmony_ci		return;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	set_bit(flowid, msgbuf->txstatus_done_map);
89562306a36Sopenharmony_ci	commonring = msgbuf->flowrings[flowid];
89662306a36Sopenharmony_ci	atomic_dec(&commonring->outstanding_tx);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx),
89962306a36Sopenharmony_ci			 skb, true);
90062306a36Sopenharmony_ci}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct brcmf_pub *drvr = msgbuf->drvr;
90662306a36Sopenharmony_ci	struct brcmf_commonring *commonring;
90762306a36Sopenharmony_ci	void *ret_ptr;
90862306a36Sopenharmony_ci	struct sk_buff *skb;
90962306a36Sopenharmony_ci	u16 alloced;
91062306a36Sopenharmony_ci	u32 pktlen;
91162306a36Sopenharmony_ci	dma_addr_t physaddr;
91262306a36Sopenharmony_ci	struct msgbuf_rx_bufpost *rx_bufpost;
91362306a36Sopenharmony_ci	u64 address;
91462306a36Sopenharmony_ci	u32 pktid;
91562306a36Sopenharmony_ci	u32 i;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT];
91862306a36Sopenharmony_ci	ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring,
91962306a36Sopenharmony_ci							      count,
92062306a36Sopenharmony_ci							      &alloced);
92162306a36Sopenharmony_ci	if (!ret_ptr) {
92262306a36Sopenharmony_ci		brcmf_dbg(MSGBUF, "Failed to reserve space in commonring\n");
92362306a36Sopenharmony_ci		return 0;
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	for (i = 0; i < alloced; i++) {
92762306a36Sopenharmony_ci		rx_bufpost = (struct msgbuf_rx_bufpost *)ret_ptr;
92862306a36Sopenharmony_ci		memset(rx_bufpost, 0, sizeof(*rx_bufpost));
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci		skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci		if (skb == NULL) {
93362306a36Sopenharmony_ci			bphy_err(drvr, "Failed to alloc SKB\n");
93462306a36Sopenharmony_ci			brcmf_commonring_write_cancel(commonring, alloced - i);
93562306a36Sopenharmony_ci			break;
93662306a36Sopenharmony_ci		}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci		pktlen = skb->len;
93962306a36Sopenharmony_ci		if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
94062306a36Sopenharmony_ci					     msgbuf->rx_pktids, skb, 0,
94162306a36Sopenharmony_ci					     &physaddr, &pktid)) {
94262306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
94362306a36Sopenharmony_ci			bphy_err(drvr, "No PKTID available !!\n");
94462306a36Sopenharmony_ci			brcmf_commonring_write_cancel(commonring, alloced - i);
94562306a36Sopenharmony_ci			break;
94662306a36Sopenharmony_ci		}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci		if (msgbuf->rx_metadata_offset) {
94962306a36Sopenharmony_ci			address = (u64)physaddr;
95062306a36Sopenharmony_ci			rx_bufpost->metadata_buf_len =
95162306a36Sopenharmony_ci				cpu_to_le16(msgbuf->rx_metadata_offset);
95262306a36Sopenharmony_ci			rx_bufpost->metadata_buf_addr.high_addr =
95362306a36Sopenharmony_ci				cpu_to_le32(address >> 32);
95462306a36Sopenharmony_ci			rx_bufpost->metadata_buf_addr.low_addr =
95562306a36Sopenharmony_ci				cpu_to_le32(address & 0xffffffff);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci			skb_pull(skb, msgbuf->rx_metadata_offset);
95862306a36Sopenharmony_ci			pktlen = skb->len;
95962306a36Sopenharmony_ci			physaddr += msgbuf->rx_metadata_offset;
96062306a36Sopenharmony_ci		}
96162306a36Sopenharmony_ci		rx_bufpost->msg.msgtype = MSGBUF_TYPE_RXBUF_POST;
96262306a36Sopenharmony_ci		rx_bufpost->msg.request_id = cpu_to_le32(pktid);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci		address = (u64)physaddr;
96562306a36Sopenharmony_ci		rx_bufpost->data_buf_len = cpu_to_le16((u16)pktlen);
96662306a36Sopenharmony_ci		rx_bufpost->data_buf_addr.high_addr =
96762306a36Sopenharmony_ci			cpu_to_le32(address >> 32);
96862306a36Sopenharmony_ci		rx_bufpost->data_buf_addr.low_addr =
96962306a36Sopenharmony_ci			cpu_to_le32(address & 0xffffffff);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci		ret_ptr += brcmf_commonring_len_item(commonring);
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	if (i)
97562306a36Sopenharmony_ci		brcmf_commonring_write_complete(commonring);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	return i;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_cistatic void
98262306a36Sopenharmony_cibrcmf_msgbuf_rxbuf_data_fill(struct brcmf_msgbuf *msgbuf)
98362306a36Sopenharmony_ci{
98462306a36Sopenharmony_ci	u32 fillbufs;
98562306a36Sopenharmony_ci	u32 retcount;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	fillbufs = msgbuf->max_rxbufpost - msgbuf->rxbufpost;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	while (fillbufs) {
99062306a36Sopenharmony_ci		retcount = brcmf_msgbuf_rxbuf_data_post(msgbuf, fillbufs);
99162306a36Sopenharmony_ci		if (!retcount)
99262306a36Sopenharmony_ci			break;
99362306a36Sopenharmony_ci		msgbuf->rxbufpost += retcount;
99462306a36Sopenharmony_ci		fillbufs -= retcount;
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_cistatic void
100062306a36Sopenharmony_cibrcmf_msgbuf_update_rxbufpost_count(struct brcmf_msgbuf *msgbuf, u16 rxcnt)
100162306a36Sopenharmony_ci{
100262306a36Sopenharmony_ci	msgbuf->rxbufpost -= rxcnt;
100362306a36Sopenharmony_ci	if (msgbuf->rxbufpost <= (msgbuf->max_rxbufpost -
100462306a36Sopenharmony_ci				  BRCMF_MSGBUF_RXBUFPOST_THRESHOLD))
100562306a36Sopenharmony_ci		brcmf_msgbuf_rxbuf_data_fill(msgbuf);
100662306a36Sopenharmony_ci}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_cistatic u32
101062306a36Sopenharmony_cibrcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf,
101162306a36Sopenharmony_ci			     u32 count)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	struct brcmf_pub *drvr = msgbuf->drvr;
101462306a36Sopenharmony_ci	struct brcmf_commonring *commonring;
101562306a36Sopenharmony_ci	void *ret_ptr;
101662306a36Sopenharmony_ci	struct sk_buff *skb;
101762306a36Sopenharmony_ci	u16 alloced;
101862306a36Sopenharmony_ci	u32 pktlen;
101962306a36Sopenharmony_ci	dma_addr_t physaddr;
102062306a36Sopenharmony_ci	struct msgbuf_rx_ioctl_resp_or_event *rx_bufpost;
102162306a36Sopenharmony_ci	u64 address;
102262306a36Sopenharmony_ci	u32 pktid;
102362306a36Sopenharmony_ci	u32 i;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
102662306a36Sopenharmony_ci	brcmf_commonring_lock(commonring);
102762306a36Sopenharmony_ci	ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring,
102862306a36Sopenharmony_ci							      count,
102962306a36Sopenharmony_ci							      &alloced);
103062306a36Sopenharmony_ci	if (!ret_ptr) {
103162306a36Sopenharmony_ci		bphy_err(drvr, "Failed to reserve space in commonring\n");
103262306a36Sopenharmony_ci		brcmf_commonring_unlock(commonring);
103362306a36Sopenharmony_ci		return 0;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	for (i = 0; i < alloced; i++) {
103762306a36Sopenharmony_ci		rx_bufpost = (struct msgbuf_rx_ioctl_resp_or_event *)ret_ptr;
103862306a36Sopenharmony_ci		memset(rx_bufpost, 0, sizeof(*rx_bufpost));
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci		skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_CTL_PKT_SIZE);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci		if (skb == NULL) {
104362306a36Sopenharmony_ci			bphy_err(drvr, "Failed to alloc SKB\n");
104462306a36Sopenharmony_ci			brcmf_commonring_write_cancel(commonring, alloced - i);
104562306a36Sopenharmony_ci			break;
104662306a36Sopenharmony_ci		}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci		pktlen = skb->len;
104962306a36Sopenharmony_ci		if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev,
105062306a36Sopenharmony_ci					     msgbuf->rx_pktids, skb, 0,
105162306a36Sopenharmony_ci					     &physaddr, &pktid)) {
105262306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
105362306a36Sopenharmony_ci			bphy_err(drvr, "No PKTID available !!\n");
105462306a36Sopenharmony_ci			brcmf_commonring_write_cancel(commonring, alloced - i);
105562306a36Sopenharmony_ci			break;
105662306a36Sopenharmony_ci		}
105762306a36Sopenharmony_ci		if (event_buf)
105862306a36Sopenharmony_ci			rx_bufpost->msg.msgtype = MSGBUF_TYPE_EVENT_BUF_POST;
105962306a36Sopenharmony_ci		else
106062306a36Sopenharmony_ci			rx_bufpost->msg.msgtype =
106162306a36Sopenharmony_ci				MSGBUF_TYPE_IOCTLRESP_BUF_POST;
106262306a36Sopenharmony_ci		rx_bufpost->msg.request_id = cpu_to_le32(pktid);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci		address = (u64)physaddr;
106562306a36Sopenharmony_ci		rx_bufpost->host_buf_len = cpu_to_le16((u16)pktlen);
106662306a36Sopenharmony_ci		rx_bufpost->host_buf_addr.high_addr =
106762306a36Sopenharmony_ci			cpu_to_le32(address >> 32);
106862306a36Sopenharmony_ci		rx_bufpost->host_buf_addr.low_addr =
106962306a36Sopenharmony_ci			cpu_to_le32(address & 0xffffffff);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci		ret_ptr += brcmf_commonring_len_item(commonring);
107262306a36Sopenharmony_ci	}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	if (i)
107562306a36Sopenharmony_ci		brcmf_commonring_write_complete(commonring);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	brcmf_commonring_unlock(commonring);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	return i;
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cistatic void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf)
108462306a36Sopenharmony_ci{
108562306a36Sopenharmony_ci	u32 count;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	count = msgbuf->max_ioctlrespbuf - msgbuf->cur_ioctlrespbuf;
108862306a36Sopenharmony_ci	count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, false, count);
108962306a36Sopenharmony_ci	msgbuf->cur_ioctlrespbuf += count;
109062306a36Sopenharmony_ci}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_cistatic void brcmf_msgbuf_rxbuf_event_post(struct brcmf_msgbuf *msgbuf)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	u32 count;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	count = msgbuf->max_eventbuf - msgbuf->cur_eventbuf;
109862306a36Sopenharmony_ci	count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, true, count);
109962306a36Sopenharmony_ci	msgbuf->cur_eventbuf += count;
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_cistatic void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	struct brcmf_pub *drvr = msgbuf->drvr;
110662306a36Sopenharmony_ci	struct msgbuf_rx_event *event;
110762306a36Sopenharmony_ci	u32 idx;
110862306a36Sopenharmony_ci	u16 buflen;
110962306a36Sopenharmony_ci	struct sk_buff *skb;
111062306a36Sopenharmony_ci	struct brcmf_if *ifp;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	event = (struct msgbuf_rx_event *)buf;
111362306a36Sopenharmony_ci	idx = le32_to_cpu(event->msg.request_id);
111462306a36Sopenharmony_ci	buflen = le16_to_cpu(event->event_data_len);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	if (msgbuf->cur_eventbuf)
111762306a36Sopenharmony_ci		msgbuf->cur_eventbuf--;
111862306a36Sopenharmony_ci	brcmf_msgbuf_rxbuf_event_post(msgbuf);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
112162306a36Sopenharmony_ci				     msgbuf->rx_pktids, idx);
112262306a36Sopenharmony_ci	if (!skb)
112362306a36Sopenharmony_ci		return;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	if (msgbuf->rx_dataoffset)
112662306a36Sopenharmony_ci		skb_pull(skb, msgbuf->rx_dataoffset);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	skb_trim(skb, buflen);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	ifp = brcmf_get_ifp(msgbuf->drvr, event->msg.ifidx);
113162306a36Sopenharmony_ci	if (!ifp || !ifp->ndev) {
113262306a36Sopenharmony_ci		bphy_err(drvr, "Received pkt for invalid ifidx %d\n",
113362306a36Sopenharmony_ci			 event->msg.ifidx);
113462306a36Sopenharmony_ci		goto exit;
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, ifp->ndev);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	brcmf_fweh_process_skb(ifp->drvr, skb, 0, GFP_KERNEL);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ciexit:
114262306a36Sopenharmony_ci	brcmu_pkt_buf_free_skb(skb);
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_cistatic void
114762306a36Sopenharmony_cibrcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf)
114862306a36Sopenharmony_ci{
114962306a36Sopenharmony_ci	struct brcmf_pub *drvr = msgbuf->drvr;
115062306a36Sopenharmony_ci	struct msgbuf_rx_complete *rx_complete;
115162306a36Sopenharmony_ci	struct sk_buff *skb;
115262306a36Sopenharmony_ci	u16 data_offset;
115362306a36Sopenharmony_ci	u16 buflen;
115462306a36Sopenharmony_ci	u16 flags;
115562306a36Sopenharmony_ci	u32 idx;
115662306a36Sopenharmony_ci	struct brcmf_if *ifp;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	rx_complete = (struct msgbuf_rx_complete *)buf;
116162306a36Sopenharmony_ci	data_offset = le16_to_cpu(rx_complete->data_offset);
116262306a36Sopenharmony_ci	buflen = le16_to_cpu(rx_complete->data_len);
116362306a36Sopenharmony_ci	idx = le32_to_cpu(rx_complete->msg.request_id);
116462306a36Sopenharmony_ci	flags = le16_to_cpu(rx_complete->flags);
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
116762306a36Sopenharmony_ci				     msgbuf->rx_pktids, idx);
116862306a36Sopenharmony_ci	if (!skb)
116962306a36Sopenharmony_ci		return;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	if (data_offset)
117262306a36Sopenharmony_ci		skb_pull(skb, data_offset);
117362306a36Sopenharmony_ci	else if (msgbuf->rx_dataoffset)
117462306a36Sopenharmony_ci		skb_pull(skb, msgbuf->rx_dataoffset);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	skb_trim(skb, buflen);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	if ((flags & BRCMF_MSGBUF_PKT_FLAGS_FRAME_MASK) ==
117962306a36Sopenharmony_ci	    BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_11) {
118062306a36Sopenharmony_ci		ifp = msgbuf->drvr->mon_if;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci		if (!ifp) {
118362306a36Sopenharmony_ci			bphy_err(drvr, "Received unexpected monitor pkt\n");
118462306a36Sopenharmony_ci			brcmu_pkt_buf_free_skb(skb);
118562306a36Sopenharmony_ci			return;
118662306a36Sopenharmony_ci		}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		brcmf_netif_mon_rx(ifp, skb);
118962306a36Sopenharmony_ci		return;
119062306a36Sopenharmony_ci	}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	ifp = brcmf_get_ifp(msgbuf->drvr, rx_complete->msg.ifidx);
119362306a36Sopenharmony_ci	if (!ifp || !ifp->ndev) {
119462306a36Sopenharmony_ci		bphy_err(drvr, "Received pkt for invalid ifidx %d\n",
119562306a36Sopenharmony_ci			 rx_complete->msg.ifidx);
119662306a36Sopenharmony_ci		brcmu_pkt_buf_free_skb(skb);
119762306a36Sopenharmony_ci		return;
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, ifp->ndev);
120162306a36Sopenharmony_ci	brcmf_netif_rx(ifp, skb);
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_cistatic void brcmf_msgbuf_process_gen_status(struct brcmf_msgbuf *msgbuf,
120562306a36Sopenharmony_ci					    void *buf)
120662306a36Sopenharmony_ci{
120762306a36Sopenharmony_ci	struct msgbuf_gen_status *gen_status = buf;
120862306a36Sopenharmony_ci	struct brcmf_pub *drvr = msgbuf->drvr;
120962306a36Sopenharmony_ci	int err;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	err = le16_to_cpu(gen_status->compl_hdr.status);
121262306a36Sopenharmony_ci	if (err)
121362306a36Sopenharmony_ci		bphy_err(drvr, "Firmware reported general error: %d\n", err);
121462306a36Sopenharmony_ci}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_cistatic void brcmf_msgbuf_process_ring_status(struct brcmf_msgbuf *msgbuf,
121762306a36Sopenharmony_ci					     void *buf)
121862306a36Sopenharmony_ci{
121962306a36Sopenharmony_ci	struct msgbuf_ring_status *ring_status = buf;
122062306a36Sopenharmony_ci	struct brcmf_pub *drvr = msgbuf->drvr;
122162306a36Sopenharmony_ci	int err;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	err = le16_to_cpu(ring_status->compl_hdr.status);
122462306a36Sopenharmony_ci	if (err) {
122562306a36Sopenharmony_ci		int ring = le16_to_cpu(ring_status->compl_hdr.flow_ring_id);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci		bphy_err(drvr, "Firmware reported ring %d error: %d\n", ring,
122862306a36Sopenharmony_ci			 err);
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_cistatic void
123362306a36Sopenharmony_cibrcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf,
123462306a36Sopenharmony_ci					       void *buf)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	struct brcmf_pub *drvr = msgbuf->drvr;
123762306a36Sopenharmony_ci	struct msgbuf_flowring_create_resp *flowring_create_resp;
123862306a36Sopenharmony_ci	u16 status;
123962306a36Sopenharmony_ci	u16 flowid;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	flowring_create_resp = (struct msgbuf_flowring_create_resp *)buf;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	flowid = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id);
124462306a36Sopenharmony_ci	flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART;
124562306a36Sopenharmony_ci	status =  le16_to_cpu(flowring_create_resp->compl_hdr.status);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	if (status) {
124862306a36Sopenharmony_ci		bphy_err(drvr, "Flowring creation failed, code %d\n", status);
124962306a36Sopenharmony_ci		brcmf_msgbuf_remove_flowring(msgbuf, flowid);
125062306a36Sopenharmony_ci		return;
125162306a36Sopenharmony_ci	}
125262306a36Sopenharmony_ci	brcmf_dbg(MSGBUF, "Flowring %d Create response status %d\n", flowid,
125362306a36Sopenharmony_ci		  status);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	brcmf_flowring_open(msgbuf->flow, flowid);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
125862306a36Sopenharmony_ci}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_cistatic void
126262306a36Sopenharmony_cibrcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf,
126362306a36Sopenharmony_ci					       void *buf)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	struct brcmf_pub *drvr = msgbuf->drvr;
126662306a36Sopenharmony_ci	struct msgbuf_flowring_delete_resp *flowring_delete_resp;
126762306a36Sopenharmony_ci	u16 status;
126862306a36Sopenharmony_ci	u16 flowid;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	flowring_delete_resp = (struct msgbuf_flowring_delete_resp *)buf;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	flowid = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id);
127362306a36Sopenharmony_ci	flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART;
127462306a36Sopenharmony_ci	status =  le16_to_cpu(flowring_delete_resp->compl_hdr.status);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	if (status) {
127762306a36Sopenharmony_ci		bphy_err(drvr, "Flowring deletion failed, code %d\n", status);
127862306a36Sopenharmony_ci		brcmf_flowring_delete(msgbuf->flow, flowid);
127962306a36Sopenharmony_ci		return;
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci	brcmf_dbg(MSGBUF, "Flowring %d Delete response status %d\n", flowid,
128262306a36Sopenharmony_ci		  status);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	brcmf_msgbuf_remove_flowring(msgbuf, flowid);
128562306a36Sopenharmony_ci}
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_cistatic void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	struct brcmf_pub *drvr = msgbuf->drvr;
129162306a36Sopenharmony_ci	struct msgbuf_common_hdr *msg;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	msg = (struct msgbuf_common_hdr *)buf;
129462306a36Sopenharmony_ci	switch (msg->msgtype) {
129562306a36Sopenharmony_ci	case MSGBUF_TYPE_GEN_STATUS:
129662306a36Sopenharmony_ci		brcmf_dbg(MSGBUF, "MSGBUF_TYPE_GEN_STATUS\n");
129762306a36Sopenharmony_ci		brcmf_msgbuf_process_gen_status(msgbuf, buf);
129862306a36Sopenharmony_ci		break;
129962306a36Sopenharmony_ci	case MSGBUF_TYPE_RING_STATUS:
130062306a36Sopenharmony_ci		brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RING_STATUS\n");
130162306a36Sopenharmony_ci		brcmf_msgbuf_process_ring_status(msgbuf, buf);
130262306a36Sopenharmony_ci		break;
130362306a36Sopenharmony_ci	case MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT:
130462306a36Sopenharmony_ci		brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT\n");
130562306a36Sopenharmony_ci		brcmf_msgbuf_process_flow_ring_create_response(msgbuf, buf);
130662306a36Sopenharmony_ci		break;
130762306a36Sopenharmony_ci	case MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT:
130862306a36Sopenharmony_ci		brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT\n");
130962306a36Sopenharmony_ci		brcmf_msgbuf_process_flow_ring_delete_response(msgbuf, buf);
131062306a36Sopenharmony_ci		break;
131162306a36Sopenharmony_ci	case MSGBUF_TYPE_IOCTLPTR_REQ_ACK:
131262306a36Sopenharmony_ci		brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTLPTR_REQ_ACK\n");
131362306a36Sopenharmony_ci		break;
131462306a36Sopenharmony_ci	case MSGBUF_TYPE_IOCTL_CMPLT:
131562306a36Sopenharmony_ci		brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTL_CMPLT\n");
131662306a36Sopenharmony_ci		brcmf_msgbuf_process_ioctl_complete(msgbuf, buf);
131762306a36Sopenharmony_ci		break;
131862306a36Sopenharmony_ci	case MSGBUF_TYPE_WL_EVENT:
131962306a36Sopenharmony_ci		brcmf_dbg(MSGBUF, "MSGBUF_TYPE_WL_EVENT\n");
132062306a36Sopenharmony_ci		brcmf_msgbuf_process_event(msgbuf, buf);
132162306a36Sopenharmony_ci		break;
132262306a36Sopenharmony_ci	case MSGBUF_TYPE_TX_STATUS:
132362306a36Sopenharmony_ci		brcmf_dbg(MSGBUF, "MSGBUF_TYPE_TX_STATUS\n");
132462306a36Sopenharmony_ci		brcmf_msgbuf_process_txstatus(msgbuf, buf);
132562306a36Sopenharmony_ci		break;
132662306a36Sopenharmony_ci	case MSGBUF_TYPE_RX_CMPLT:
132762306a36Sopenharmony_ci		brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n");
132862306a36Sopenharmony_ci		brcmf_msgbuf_process_rx_complete(msgbuf, buf);
132962306a36Sopenharmony_ci		break;
133062306a36Sopenharmony_ci	default:
133162306a36Sopenharmony_ci		bphy_err(drvr, "Unsupported msgtype %d\n", msg->msgtype);
133262306a36Sopenharmony_ci		break;
133362306a36Sopenharmony_ci	}
133462306a36Sopenharmony_ci}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_cistatic void brcmf_msgbuf_process_rx(struct brcmf_msgbuf *msgbuf,
133862306a36Sopenharmony_ci				    struct brcmf_commonring *commonring)
133962306a36Sopenharmony_ci{
134062306a36Sopenharmony_ci	void *buf;
134162306a36Sopenharmony_ci	u16 count;
134262306a36Sopenharmony_ci	u16 processed;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ciagain:
134562306a36Sopenharmony_ci	buf = brcmf_commonring_get_read_ptr(commonring, &count);
134662306a36Sopenharmony_ci	if (buf == NULL)
134762306a36Sopenharmony_ci		return;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	processed = 0;
135062306a36Sopenharmony_ci	while (count) {
135162306a36Sopenharmony_ci		brcmf_msgbuf_process_msgtype(msgbuf,
135262306a36Sopenharmony_ci					     buf + msgbuf->rx_dataoffset);
135362306a36Sopenharmony_ci		buf += brcmf_commonring_len_item(commonring);
135462306a36Sopenharmony_ci		processed++;
135562306a36Sopenharmony_ci		if (processed == BRCMF_MSGBUF_UPDATE_RX_PTR_THRS) {
135662306a36Sopenharmony_ci			brcmf_commonring_read_complete(commonring, processed);
135762306a36Sopenharmony_ci			processed = 0;
135862306a36Sopenharmony_ci		}
135962306a36Sopenharmony_ci		count--;
136062306a36Sopenharmony_ci	}
136162306a36Sopenharmony_ci	if (processed)
136262306a36Sopenharmony_ci		brcmf_commonring_read_complete(commonring, processed);
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	if (commonring->r_ptr == 0)
136562306a36Sopenharmony_ci		goto again;
136662306a36Sopenharmony_ci}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ciint brcmf_proto_msgbuf_rx_trigger(struct device *dev)
137062306a36Sopenharmony_ci{
137162306a36Sopenharmony_ci	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
137262306a36Sopenharmony_ci	struct brcmf_pub *drvr = bus_if->drvr;
137362306a36Sopenharmony_ci	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
137462306a36Sopenharmony_ci	struct brcmf_commonring *commonring;
137562306a36Sopenharmony_ci	void *buf;
137662306a36Sopenharmony_ci	u32 flowid;
137762306a36Sopenharmony_ci	int qlen;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
138062306a36Sopenharmony_ci	brcmf_msgbuf_process_rx(msgbuf, buf);
138162306a36Sopenharmony_ci	buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE];
138262306a36Sopenharmony_ci	brcmf_msgbuf_process_rx(msgbuf, buf);
138362306a36Sopenharmony_ci	buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE];
138462306a36Sopenharmony_ci	brcmf_msgbuf_process_rx(msgbuf, buf);
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	for_each_set_bit(flowid, msgbuf->txstatus_done_map,
138762306a36Sopenharmony_ci			 msgbuf->max_flowrings) {
138862306a36Sopenharmony_ci		clear_bit(flowid, msgbuf->txstatus_done_map);
138962306a36Sopenharmony_ci		commonring = msgbuf->flowrings[flowid];
139062306a36Sopenharmony_ci		qlen = brcmf_flowring_qlen(msgbuf->flow, flowid);
139162306a36Sopenharmony_ci		if ((qlen > BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) ||
139262306a36Sopenharmony_ci		    ((qlen) && (atomic_read(&commonring->outstanding_tx) <
139362306a36Sopenharmony_ci				BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS)))
139462306a36Sopenharmony_ci			brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true);
139562306a36Sopenharmony_ci	}
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	return 0;
139862306a36Sopenharmony_ci}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_civoid brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid)
140262306a36Sopenharmony_ci{
140362306a36Sopenharmony_ci	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
140462306a36Sopenharmony_ci	struct msgbuf_tx_flowring_delete_req *delete;
140562306a36Sopenharmony_ci	struct brcmf_commonring *commonring;
140662306a36Sopenharmony_ci	struct brcmf_commonring *commonring_del = msgbuf->flowrings[flowid];
140762306a36Sopenharmony_ci	struct brcmf_flowring *flow = msgbuf->flow;
140862306a36Sopenharmony_ci	void *ret_ptr;
140962306a36Sopenharmony_ci	u8 ifidx;
141062306a36Sopenharmony_ci	int err;
141162306a36Sopenharmony_ci	int retry = BRCMF_MAX_TXSTATUS_WAIT_RETRIES;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	/* make sure it is not in txflow */
141462306a36Sopenharmony_ci	brcmf_commonring_lock(commonring_del);
141562306a36Sopenharmony_ci	flow->rings[flowid]->status = RING_CLOSING;
141662306a36Sopenharmony_ci	brcmf_commonring_unlock(commonring_del);
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	/* wait for commonring txflow finished */
141962306a36Sopenharmony_ci	while (retry && atomic_read(&commonring_del->outstanding_tx)) {
142062306a36Sopenharmony_ci		usleep_range(5000, 10000);
142162306a36Sopenharmony_ci		retry--;
142262306a36Sopenharmony_ci	}
142362306a36Sopenharmony_ci	if (!retry) {
142462306a36Sopenharmony_ci		brcmf_err("timed out waiting for txstatus\n");
142562306a36Sopenharmony_ci		atomic_set(&commonring_del->outstanding_tx, 0);
142662306a36Sopenharmony_ci	}
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	/* no need to submit if firmware can not be reached */
142962306a36Sopenharmony_ci	if (drvr->bus_if->state != BRCMF_BUS_UP) {
143062306a36Sopenharmony_ci		brcmf_dbg(MSGBUF, "bus down, flowring will be removed\n");
143162306a36Sopenharmony_ci		brcmf_msgbuf_remove_flowring(msgbuf, flowid);
143262306a36Sopenharmony_ci		return;
143362306a36Sopenharmony_ci	}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
143662306a36Sopenharmony_ci	brcmf_commonring_lock(commonring);
143762306a36Sopenharmony_ci	ret_ptr = brcmf_commonring_reserve_for_write(commonring);
143862306a36Sopenharmony_ci	if (!ret_ptr) {
143962306a36Sopenharmony_ci		bphy_err(drvr, "FW unaware, flowring will be removed !!\n");
144062306a36Sopenharmony_ci		brcmf_commonring_unlock(commonring);
144162306a36Sopenharmony_ci		brcmf_msgbuf_remove_flowring(msgbuf, flowid);
144262306a36Sopenharmony_ci		return;
144362306a36Sopenharmony_ci	}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	delete = (struct msgbuf_tx_flowring_delete_req *)ret_ptr;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	ifidx = brcmf_flowring_ifidx_get(msgbuf->flow, flowid);
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	delete->msg.msgtype = MSGBUF_TYPE_FLOW_RING_DELETE;
145062306a36Sopenharmony_ci	delete->msg.ifidx = ifidx;
145162306a36Sopenharmony_ci	delete->msg.request_id = 0;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	delete->flow_ring_id = cpu_to_le16(flowid +
145462306a36Sopenharmony_ci					   BRCMF_H2D_MSGRING_FLOWRING_IDSTART);
145562306a36Sopenharmony_ci	delete->reason = 0;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	brcmf_dbg(MSGBUF, "Send Flow Delete Req flow ID %d, ifindex %d\n",
145862306a36Sopenharmony_ci		  flowid, ifidx);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	err = brcmf_commonring_write_complete(commonring);
146162306a36Sopenharmony_ci	brcmf_commonring_unlock(commonring);
146262306a36Sopenharmony_ci	if (err) {
146362306a36Sopenharmony_ci		bphy_err(drvr, "Failed to submit RING_DELETE, flowring will be removed\n");
146462306a36Sopenharmony_ci		brcmf_msgbuf_remove_flowring(msgbuf, flowid);
146562306a36Sopenharmony_ci	}
146662306a36Sopenharmony_ci}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci#ifdef DEBUG
146962306a36Sopenharmony_cistatic int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
147062306a36Sopenharmony_ci{
147162306a36Sopenharmony_ci	struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
147262306a36Sopenharmony_ci	struct brcmf_pub *drvr = bus_if->drvr;
147362306a36Sopenharmony_ci	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
147462306a36Sopenharmony_ci	struct brcmf_commonring *commonring;
147562306a36Sopenharmony_ci	u16 i;
147662306a36Sopenharmony_ci	struct brcmf_flowring_ring *ring;
147762306a36Sopenharmony_ci	struct brcmf_flowring_hash *hash;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
148062306a36Sopenharmony_ci	seq_printf(seq, "h2d_ctl_submit: rp %4u, wp %4u, depth %4u\n",
148162306a36Sopenharmony_ci		   commonring->r_ptr, commonring->w_ptr, commonring->depth);
148262306a36Sopenharmony_ci	commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT];
148362306a36Sopenharmony_ci	seq_printf(seq, "h2d_rx_submit:  rp %4u, wp %4u, depth %4u\n",
148462306a36Sopenharmony_ci		   commonring->r_ptr, commonring->w_ptr, commonring->depth);
148562306a36Sopenharmony_ci	commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE];
148662306a36Sopenharmony_ci	seq_printf(seq, "d2h_ctl_cmplt:  rp %4u, wp %4u, depth %4u\n",
148762306a36Sopenharmony_ci		   commonring->r_ptr, commonring->w_ptr, commonring->depth);
148862306a36Sopenharmony_ci	commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE];
148962306a36Sopenharmony_ci	seq_printf(seq, "d2h_tx_cmplt:   rp %4u, wp %4u, depth %4u\n",
149062306a36Sopenharmony_ci		   commonring->r_ptr, commonring->w_ptr, commonring->depth);
149162306a36Sopenharmony_ci	commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
149262306a36Sopenharmony_ci	seq_printf(seq, "d2h_rx_cmplt:   rp %4u, wp %4u, depth %4u\n",
149362306a36Sopenharmony_ci		   commonring->r_ptr, commonring->w_ptr, commonring->depth);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	seq_printf(seq, "\nh2d_flowrings: depth %u\n",
149662306a36Sopenharmony_ci		   BRCMF_H2D_TXFLOWRING_MAX_ITEM);
149762306a36Sopenharmony_ci	seq_puts(seq, "Active flowrings:\n");
149862306a36Sopenharmony_ci	for (i = 0; i < msgbuf->flow->nrofrings; i++) {
149962306a36Sopenharmony_ci		if (!msgbuf->flow->rings[i])
150062306a36Sopenharmony_ci			continue;
150162306a36Sopenharmony_ci		ring = msgbuf->flow->rings[i];
150262306a36Sopenharmony_ci		if (ring->status != RING_OPEN)
150362306a36Sopenharmony_ci			continue;
150462306a36Sopenharmony_ci		commonring = msgbuf->flowrings[i];
150562306a36Sopenharmony_ci		hash = &msgbuf->flow->hash[ring->hash_id];
150662306a36Sopenharmony_ci		seq_printf(seq, "id %3u: rp %4u, wp %4u, qlen %4u, blocked %u\n"
150762306a36Sopenharmony_ci				"        ifidx %u, fifo %u, da %pM\n",
150862306a36Sopenharmony_ci				i, commonring->r_ptr, commonring->w_ptr,
150962306a36Sopenharmony_ci				skb_queue_len(&ring->skblist), ring->blocked,
151062306a36Sopenharmony_ci				hash->ifidx, hash->fifo, hash->mac);
151162306a36Sopenharmony_ci	}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	return 0;
151462306a36Sopenharmony_ci}
151562306a36Sopenharmony_ci#else
151662306a36Sopenharmony_cistatic int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
151762306a36Sopenharmony_ci{
151862306a36Sopenharmony_ci	return 0;
151962306a36Sopenharmony_ci}
152062306a36Sopenharmony_ci#endif
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_cistatic void brcmf_msgbuf_debugfs_create(struct brcmf_pub *drvr)
152362306a36Sopenharmony_ci{
152462306a36Sopenharmony_ci	brcmf_debugfs_add_entry(drvr, "msgbuf_stats", brcmf_msgbuf_stats_read);
152562306a36Sopenharmony_ci}
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ciint brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
152862306a36Sopenharmony_ci{
152962306a36Sopenharmony_ci	struct brcmf_bus_msgbuf *if_msgbuf;
153062306a36Sopenharmony_ci	struct brcmf_msgbuf *msgbuf;
153162306a36Sopenharmony_ci	u64 address;
153262306a36Sopenharmony_ci	u32 count;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	if_msgbuf = drvr->bus_if->msgbuf;
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	if (if_msgbuf->max_flowrings >= BRCMF_FLOWRING_HASHSIZE) {
153762306a36Sopenharmony_ci		bphy_err(drvr, "driver not configured for this many flowrings %d\n",
153862306a36Sopenharmony_ci			 if_msgbuf->max_flowrings);
153962306a36Sopenharmony_ci		if_msgbuf->max_flowrings = BRCMF_FLOWRING_HASHSIZE - 1;
154062306a36Sopenharmony_ci	}
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL);
154362306a36Sopenharmony_ci	if (!msgbuf)
154462306a36Sopenharmony_ci		goto fail;
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	msgbuf->txflow_wq = create_singlethread_workqueue("msgbuf_txflow");
154762306a36Sopenharmony_ci	if (msgbuf->txflow_wq == NULL) {
154862306a36Sopenharmony_ci		bphy_err(drvr, "workqueue creation failed\n");
154962306a36Sopenharmony_ci		goto fail;
155062306a36Sopenharmony_ci	}
155162306a36Sopenharmony_ci	INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker);
155262306a36Sopenharmony_ci	count = BITS_TO_LONGS(if_msgbuf->max_flowrings);
155362306a36Sopenharmony_ci	count = count * sizeof(unsigned long);
155462306a36Sopenharmony_ci	msgbuf->flow_map = kzalloc(count, GFP_KERNEL);
155562306a36Sopenharmony_ci	if (!msgbuf->flow_map)
155662306a36Sopenharmony_ci		goto fail;
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	msgbuf->txstatus_done_map = kzalloc(count, GFP_KERNEL);
155962306a36Sopenharmony_ci	if (!msgbuf->txstatus_done_map)
156062306a36Sopenharmony_ci		goto fail;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	msgbuf->drvr = drvr;
156362306a36Sopenharmony_ci	msgbuf->ioctbuf = dma_alloc_coherent(drvr->bus_if->dev,
156462306a36Sopenharmony_ci					     BRCMF_TX_IOCTL_MAX_MSG_SIZE,
156562306a36Sopenharmony_ci					     &msgbuf->ioctbuf_handle,
156662306a36Sopenharmony_ci					     GFP_KERNEL);
156762306a36Sopenharmony_ci	if (!msgbuf->ioctbuf)
156862306a36Sopenharmony_ci		goto fail;
156962306a36Sopenharmony_ci	address = (u64)msgbuf->ioctbuf_handle;
157062306a36Sopenharmony_ci	msgbuf->ioctbuf_phys_hi = address >> 32;
157162306a36Sopenharmony_ci	msgbuf->ioctbuf_phys_lo = address & 0xffffffff;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	drvr->proto->hdrpull = brcmf_msgbuf_hdrpull;
157462306a36Sopenharmony_ci	drvr->proto->query_dcmd = brcmf_msgbuf_query_dcmd;
157562306a36Sopenharmony_ci	drvr->proto->set_dcmd = brcmf_msgbuf_set_dcmd;
157662306a36Sopenharmony_ci	drvr->proto->tx_queue_data = brcmf_msgbuf_tx_queue_data;
157762306a36Sopenharmony_ci	drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode;
157862306a36Sopenharmony_ci	drvr->proto->delete_peer = brcmf_msgbuf_delete_peer;
157962306a36Sopenharmony_ci	drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer;
158062306a36Sopenharmony_ci	drvr->proto->rxreorder = brcmf_msgbuf_rxreorder;
158162306a36Sopenharmony_ci	drvr->proto->debugfs_create = brcmf_msgbuf_debugfs_create;
158262306a36Sopenharmony_ci	drvr->proto->pd = msgbuf;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	init_waitqueue_head(&msgbuf->ioctl_resp_wait);
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	msgbuf->commonrings =
158762306a36Sopenharmony_ci		(struct brcmf_commonring **)if_msgbuf->commonrings;
158862306a36Sopenharmony_ci	msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings;
158962306a36Sopenharmony_ci	msgbuf->max_flowrings = if_msgbuf->max_flowrings;
159062306a36Sopenharmony_ci	msgbuf->flowring_dma_handle =
159162306a36Sopenharmony_ci		kcalloc(msgbuf->max_flowrings,
159262306a36Sopenharmony_ci			sizeof(*msgbuf->flowring_dma_handle), GFP_KERNEL);
159362306a36Sopenharmony_ci	if (!msgbuf->flowring_dma_handle)
159462306a36Sopenharmony_ci		goto fail;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	msgbuf->rx_dataoffset = if_msgbuf->rx_dataoffset;
159762306a36Sopenharmony_ci	msgbuf->max_rxbufpost = if_msgbuf->max_rxbufpost;
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	msgbuf->max_ioctlrespbuf = BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST;
160062306a36Sopenharmony_ci	msgbuf->max_eventbuf = BRCMF_MSGBUF_MAX_EVENTBUF_POST;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	msgbuf->tx_pktids = brcmf_msgbuf_init_pktids(NR_TX_PKTIDS,
160362306a36Sopenharmony_ci						     DMA_TO_DEVICE);
160462306a36Sopenharmony_ci	if (!msgbuf->tx_pktids)
160562306a36Sopenharmony_ci		goto fail;
160662306a36Sopenharmony_ci	msgbuf->rx_pktids = brcmf_msgbuf_init_pktids(NR_RX_PKTIDS,
160762306a36Sopenharmony_ci						     DMA_FROM_DEVICE);
160862306a36Sopenharmony_ci	if (!msgbuf->rx_pktids)
160962306a36Sopenharmony_ci		goto fail;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	msgbuf->flow = brcmf_flowring_attach(drvr->bus_if->dev,
161262306a36Sopenharmony_ci					     if_msgbuf->max_flowrings);
161362306a36Sopenharmony_ci	if (!msgbuf->flow)
161462306a36Sopenharmony_ci		goto fail;
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	brcmf_dbg(MSGBUF, "Feeding buffers, rx data %d, rx event %d, rx ioctl resp %d\n",
161862306a36Sopenharmony_ci		  msgbuf->max_rxbufpost, msgbuf->max_eventbuf,
161962306a36Sopenharmony_ci		  msgbuf->max_ioctlrespbuf);
162062306a36Sopenharmony_ci	count = 0;
162162306a36Sopenharmony_ci	do {
162262306a36Sopenharmony_ci		brcmf_msgbuf_rxbuf_data_fill(msgbuf);
162362306a36Sopenharmony_ci		if (msgbuf->max_rxbufpost != msgbuf->rxbufpost)
162462306a36Sopenharmony_ci			msleep(10);
162562306a36Sopenharmony_ci		else
162662306a36Sopenharmony_ci			break;
162762306a36Sopenharmony_ci		count++;
162862306a36Sopenharmony_ci	} while (count < 10);
162962306a36Sopenharmony_ci	brcmf_msgbuf_rxbuf_event_post(msgbuf);
163062306a36Sopenharmony_ci	brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf);
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	INIT_WORK(&msgbuf->flowring_work, brcmf_msgbuf_flowring_worker);
163362306a36Sopenharmony_ci	spin_lock_init(&msgbuf->flowring_work_lock);
163462306a36Sopenharmony_ci	INIT_LIST_HEAD(&msgbuf->work_queue);
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	return 0;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_cifail:
163962306a36Sopenharmony_ci	if (msgbuf) {
164062306a36Sopenharmony_ci		kfree(msgbuf->flow_map);
164162306a36Sopenharmony_ci		kfree(msgbuf->txstatus_done_map);
164262306a36Sopenharmony_ci		brcmf_msgbuf_release_pktids(msgbuf);
164362306a36Sopenharmony_ci		kfree(msgbuf->flowring_dma_handle);
164462306a36Sopenharmony_ci		if (msgbuf->ioctbuf)
164562306a36Sopenharmony_ci			dma_free_coherent(drvr->bus_if->dev,
164662306a36Sopenharmony_ci					  BRCMF_TX_IOCTL_MAX_MSG_SIZE,
164762306a36Sopenharmony_ci					  msgbuf->ioctbuf,
164862306a36Sopenharmony_ci					  msgbuf->ioctbuf_handle);
164962306a36Sopenharmony_ci		if (msgbuf->txflow_wq)
165062306a36Sopenharmony_ci			destroy_workqueue(msgbuf->txflow_wq);
165162306a36Sopenharmony_ci		kfree(msgbuf);
165262306a36Sopenharmony_ci	}
165362306a36Sopenharmony_ci	return -ENOMEM;
165462306a36Sopenharmony_ci}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_civoid brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr)
165862306a36Sopenharmony_ci{
165962306a36Sopenharmony_ci	struct brcmf_msgbuf *msgbuf;
166062306a36Sopenharmony_ci	struct brcmf_msgbuf_work_item *work;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
166362306a36Sopenharmony_ci	if (drvr->proto->pd) {
166462306a36Sopenharmony_ci		msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
166562306a36Sopenharmony_ci		cancel_work_sync(&msgbuf->flowring_work);
166662306a36Sopenharmony_ci		while (!list_empty(&msgbuf->work_queue)) {
166762306a36Sopenharmony_ci			work = list_first_entry(&msgbuf->work_queue,
166862306a36Sopenharmony_ci						struct brcmf_msgbuf_work_item,
166962306a36Sopenharmony_ci						queue);
167062306a36Sopenharmony_ci			list_del(&work->queue);
167162306a36Sopenharmony_ci			kfree(work);
167262306a36Sopenharmony_ci		}
167362306a36Sopenharmony_ci		kfree(msgbuf->flow_map);
167462306a36Sopenharmony_ci		kfree(msgbuf->txstatus_done_map);
167562306a36Sopenharmony_ci		if (msgbuf->txflow_wq)
167662306a36Sopenharmony_ci			destroy_workqueue(msgbuf->txflow_wq);
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci		brcmf_flowring_detach(msgbuf->flow);
167962306a36Sopenharmony_ci		dma_free_coherent(drvr->bus_if->dev,
168062306a36Sopenharmony_ci				  BRCMF_TX_IOCTL_MAX_MSG_SIZE,
168162306a36Sopenharmony_ci				  msgbuf->ioctbuf, msgbuf->ioctbuf_handle);
168262306a36Sopenharmony_ci		brcmf_msgbuf_release_pktids(msgbuf);
168362306a36Sopenharmony_ci		kfree(msgbuf->flowring_dma_handle);
168462306a36Sopenharmony_ci		kfree(msgbuf);
168562306a36Sopenharmony_ci		drvr->proto->pd = NULL;
168662306a36Sopenharmony_ci	}
168762306a36Sopenharmony_ci}
1688