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