diff options
Diffstat (limited to 'drivers/net/wireless/bcmdhd/dhd_msgbuf.c')
-rw-r--r-- | drivers/net/wireless/bcmdhd/dhd_msgbuf.c | 1755 |
1 files changed, 1755 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcmdhd/dhd_msgbuf.c b/drivers/net/wireless/bcmdhd/dhd_msgbuf.c new file mode 100644 index 000000000000..76745f9d4a23 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_msgbuf.c @@ -0,0 +1,1755 @@ +/* + * Header file describing the internal (inter-module) DHD interfaces. + * + * Provides type definitions and function prototypes used to link the + * DHD OS, bus, and protocol modules. + * + * Copyright (C) 1999-2016, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_msgbuf.c 452261 2014-01-29 19:30:23Z $ + */ +#include <typedefs.h> +#include <osl.h> + +#include <bcmutils.h> +#include <circularbuf.h> +#include <bcmmsgbuf.h> +#include <bcmendian.h> + +#include <dngl_stats.h> +#include <dhd.h> +#include <dhd_proto.h> +#include <dhd_bus.h> +#include <dhd_dbg.h> + + +#ifdef PROP_TXSTATUS +#include <wlfc_proto.h> +#include <dhd_wlfc.h> +#endif +#include <pcie_core.h> +#include <bcmpcie.h> + +#define RETRIES 2 /* # of retries to retrieve matching ioctl response */ +#define IOCTL_HDR_LEN 12 + +#define DEFAULT_RX_BUFFERS_TO_POST 255 +#define RXBUFPOST_THRESHOLD 16 +#define RX_BUF_BURST 8 + +#define DHD_STOP_QUEUE_THRESHOLD 24 +#define DHD_START_QUEUE_THRESHOLD 32 +#define MAX_INLINE_IOCTL_LEN 64 /* anything beyond this len will not be inline reqst */ + +/* Required for Native to PktId mapping incase of 64bit hosts */ +#define MAX_PKTID_ITEMS (2048) + +/* Given packet pointer and physical address, macro should return unique 32 bit pktid */ +/* And given 32bit pktid, macro should return packet pointer and physical address */ +extern void *pktid_map_init(void *osh, uint32 count); +extern void pktid_map_uninit(void *pktid_map_handle); +extern uint32 pktid_map_unique(void *pktid_map_handle, + void *pkt, dmaaddr_t physaddr, uint32 physlen, uint32 dma); +extern void *pktid_get_packet(void *pktid_map_handle, + uint32 id, dmaaddr_t *physaddr, uint32 *physlen); + +#define NATIVE_TO_PKTID_INIT(osh, count) pktid_map_init(osh, count) +#define NATIVE_TO_PKTID_UNINIT(pktid_map_handle) pktid_map_uninit(pktid_map_handle) + +#define NATIVE_TO_PKTID(pktid_map_handle, pkt, pa, pa_len, dma) \ + pktid_map_unique((pktid_map_handle), (void *)(pkt), (pa), (uint32) (pa_len), (uint32)dma) +#define PKTID_TO_NATIVE(pktid_map_handle, id, pa, pa_len) \ + pktid_get_packet((pktid_map_handle), (uint32)(id), (void *)&(pa), (uint32 *) &(pa_len)) + +#define MODX(x, n) ((x) & ((n) -1)) +#define align(x, n) (MODX(x, n) ? ((x) - MODX(x, n) + (n)) : ((x) - MODX(x, n))) +#define RX_DMA_OFFSET 8 +#define IOCT_RETBUF_SIZE (RX_DMA_OFFSET + WLC_IOCTL_MAXLEN) + +typedef struct dhd_prot { + uint32 reqid; + uint16 hdr_len; + uint32 lastcmd; + uint32 pending; + uint16 rxbufpost; + uint16 max_rxbufpost; + uint16 active_tx_count; + uint16 max_tx_count; + dmaaddr_t htod_physaddr; + dmaaddr_t dtoh_physaddr; + bool txflow_en; + circularbuf_t *dtohbuf; + circularbuf_t *htodbuf; + uint32 rx_dataoffset; + void* retbuf; + dmaaddr_t retbuf_phys; + void* ioctbuf; /* For holding ioct request buf */ + dmaaddr_t ioctbuf_phys; /* physical address for ioctbuf */ + dhd_mb_ring_t mb_ring_fn; + void *htod_ring; + void *dtoh_ring; + /* Flag to check if splitbuf support is enabled. */ + /* Set to False at dhd_prot_attach. Set to True at dhd_prot_init */ + bool htodsplit; + bool dtohsplit; + /* H2D/D2H Ctrl rings */ + dmaaddr_t htod_ctrl_physaddr; /* DMA mapped physical addr ofr H2D ctrl ring */ + dmaaddr_t dtoh_ctrl_physaddr; /* DMA mapped phys addr for D2H ctrl ring */ + circularbuf_t *htod_ctrlbuf; /* Cbuf handle for H2D ctrl ring */ + circularbuf_t *dtoh_ctrlbuf; /* Cbuf handle for D2H ctrl ring */ + void *htod_ctrl_ring; /* address for H2D control buf */ + void *dtoh_ctrl_ring; /* address for D2H control buf */ + + + uint16 ioctl_seq_no; + uint16 data_seq_no; + void *pktid_map_handle; +} dhd_prot_t; + +static int dhdmsgbuf_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, + void *buf, uint len, uint8 action); +static int dhd_msgbuf_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, + void *buf, uint len, uint8 action); +static int dhdmsgbuf_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len, void* buf, void* retbuf); +static int dhd_msgbuf_init_dtoh(dhd_pub_t *dhd); + +static int dhd_msgbuf_rxbuf_post(dhd_pub_t *dhd); +static int dhd_msgbuf_init_htod(dhd_pub_t *dhd); +static int dhd_msgbuf_init_htod_ctrl(dhd_pub_t *dhd); +static int dhd_msgbuf_init_dtoh_ctrl(dhd_pub_t *dhd); +static int dhd_prot_rxbufpost(dhd_pub_t *dhd, uint32 count); +static void dhd_prot_return_rxbuf(dhd_pub_t *dhd, uint16 rxcnt); +static void dhd_prot_rxcmplt_process(dhd_pub_t *dhd, void* buf); +static void dhd_prot_event_process(dhd_pub_t *dhd, uint8* buf, uint16 len); +static void dhd_prot_process_msgtype(dhd_pub_t *dhd, uint8* buf, uint16 len); +static void dhd_process_msgtype(dhd_pub_t *dhd, uint8* buf, uint16 len); + +static void dhd_prot_txstatus_process(dhd_pub_t *dhd, void * buf); +static void dhd_prot_ioctcmplt_process(dhd_pub_t *dhd, void * buf); +void* dhd_alloc_circularbuf_space(dhd_pub_t *dhd, circularbuf_t *handle, uint16 msglen, uint path); +static int dhd_fillup_ioct_reqst(dhd_pub_t *dhd, uint16 len, uint cmd, void* buf, int ifidx); +static int dhd_fillup_ioct_reqst_ptrbased(dhd_pub_t *dhd, uint16 len, uint cmd, void* buf, + int ifidx); +static INLINE void dhd_prot_packet_free(dhd_pub_t *dhd, uint32 pktid); +static INLINE void *dhd_prot_packet_get(dhd_pub_t *dhd, uint32 pktid); + +/* Linkage, sets prot link and updates hdrlen in pub */ +int dhd_prot_attach(dhd_pub_t *dhd) +{ + uint alloced = 0; + + dhd_prot_t *msg_buf; + if (!(msg_buf = (dhd_prot_t *)DHD_OS_PREALLOC(dhd, DHD_PREALLOC_PROT, + sizeof(dhd_prot_t)))) { + DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + goto fail; + } + memset(msg_buf, 0, sizeof(dhd_prot_t)); + + msg_buf->hdr_len = sizeof(ioctl_req_hdr_t) + sizeof(cmn_msg_hdr_t) + sizeof(ret_buf_t); + msg_buf->dtohbuf = MALLOC(dhd->osh, sizeof(circularbuf_t)); + msg_buf->htodbuf = MALLOC(dhd->osh, sizeof(circularbuf_t)); + + memset(msg_buf->dtohbuf, 0, sizeof(circularbuf_t)); + memset(msg_buf->htodbuf, 0, sizeof(circularbuf_t)); + + dhd->prot = msg_buf; + dhd->maxctl = WLC_IOCTL_MAXLEN + msg_buf->hdr_len; + + /* ret buf for ioctl */ + msg_buf->retbuf = DMA_ALLOC_CONSISTENT(dhd->osh, IOCT_RETBUF_SIZE, 4, + &alloced, &msg_buf->retbuf_phys, NULL); + if (msg_buf->retbuf == NULL) { + ASSERT(0); + return BCME_NOMEM; + } + + ASSERT(MODX((unsigned long)msg_buf->retbuf, 4) == 0); + + msg_buf->ioctbuf = DMA_ALLOC_CONSISTENT(dhd->osh, MSGBUF_MAX_MSG_SIZE, 4, + &alloced, &msg_buf->ioctbuf_phys, NULL); + + if (msg_buf->ioctbuf == NULL) { + ASSERT(0); + return BCME_NOMEM; + } + + ASSERT(MODX((unsigned long)msg_buf->ioctbuf, 4) == 0); + + msg_buf->pktid_map_handle = NATIVE_TO_PKTID_INIT(dhd->osh, MAX_PKTID_ITEMS); + if (msg_buf->pktid_map_handle == NULL) { + ASSERT(0); + return BCME_NOMEM; + } + + msg_buf->htod_ring = DMA_ALLOC_CONSISTENT(dhd->osh, HOST_TO_DNGL_MSGBUF_SZ, 4, + &alloced, &msg_buf->htod_physaddr, NULL); + if (msg_buf->htod_ring == NULL) { + ASSERT(0); + return BCME_NOMEM; + } + + ASSERT(MODX((unsigned long)msg_buf->htod_ring, 4) == 0); + + msg_buf->dtoh_ring = DMA_ALLOC_CONSISTENT(dhd->osh, DNGL_TO_HOST_MSGBUF_SZ, 4, + &alloced, &msg_buf->dtoh_physaddr, NULL); + if (msg_buf->dtoh_ring == NULL) { + ASSERT(0); + return BCME_NOMEM; + } + + ASSERT(MODX((unsigned long)msg_buf->dtoh_ring, 4) == 0); + + /* At this point we assume splitbuf is not supported by dongle */ + msg_buf->htodsplit = FALSE; + msg_buf->dtohsplit = FALSE; + + + return 0; + +fail: +#ifndef CONFIG_DHD_USE_STATIC_BUF + if (msg_buf != NULL) + MFREE(dhd->osh, msg_buf, sizeof(dhd_prot_t)); +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + return BCME_NOMEM; +} + +/* Unlink, frees allocated protocol memory (including dhd_prot) */ +void dhd_prot_detach(dhd_pub_t *dhd) +{ + /* Stop the protocol module */ + if (dhd->prot) { + + if (dhd->prot->dtoh_ring) { + DMA_FREE_CONSISTENT(dhd->osh, dhd->prot->dtoh_ring, + DNGL_TO_HOST_MSGBUF_SZ, dhd->prot->dtoh_physaddr, NULL); + + dhd->prot->dtoh_ring = NULL; + PHYSADDRHISET(dhd->prot->dtoh_physaddr, 0); + PHYSADDRLOSET(dhd->prot->dtoh_physaddr, 0); + } + + if (dhd->prot->htod_ring) { + DMA_FREE_CONSISTENT(dhd->osh, dhd->prot->htod_ring, + HOST_TO_DNGL_MSGBUF_SZ, dhd->prot->htod_physaddr, NULL); + + dhd->prot->htod_ring = NULL; + PHYSADDRHISET(dhd->prot->htod_physaddr, 0); + PHYSADDRLOSET(dhd->prot->htod_physaddr, 0); + } + + if (dhd->prot->dtohbuf) { + MFREE(dhd->osh, dhd->prot->dtohbuf, sizeof(circularbuf_t)); + dhd->prot->dtohbuf = NULL; + } + + if (dhd->prot->htodbuf) { + MFREE(dhd->osh, dhd->prot->htodbuf, sizeof(circularbuf_t)); + dhd->prot->htodbuf = NULL; + } + + if (dhd->prot->htod_ctrl_ring) { + DMA_FREE_CONSISTENT(dhd->osh, dhd->prot->htod_ctrl_ring, + HOST_TO_DNGL_CTRLRING_SZ, dhd->prot->htod_ctrl_physaddr, NULL); + + dhd->prot->htod_ctrl_ring = NULL; + dhd->prot->htod_ctrl_physaddr = 0; + } + + if (dhd->prot->dtoh_ctrl_ring) { + DMA_FREE_CONSISTENT(dhd->osh, dhd->prot->dtoh_ctrl_ring, + DNGL_TO_HOST_CTRLRING_SZ, dhd->prot->dtoh_ctrl_physaddr, NULL); + + dhd->prot->dtoh_ctrl_ring = NULL; + dhd->prot->dtoh_ctrl_physaddr = 0; + } + + if (dhd->prot->htod_ctrlbuf) { + MFREE(dhd->osh, dhd->prot->htod_ctrlbuf, sizeof(circularbuf_t)); + dhd->prot->htod_ctrlbuf = NULL; + } + + if (dhd->prot->dtoh_ctrlbuf) { + MFREE(dhd->osh, dhd->prot->dtoh_ctrlbuf, sizeof(circularbuf_t)); + dhd->prot->dtoh_ctrlbuf = NULL; + } + + if (dhd->prot->retbuf) { + DMA_FREE_CONSISTENT(dhd->osh, dhd->prot->retbuf, + IOCT_RETBUF_SIZE, dhd->prot->retbuf_phys, NULL); + dhd->prot->retbuf = NULL; + } + + if (dhd->prot->ioctbuf) { + DMA_FREE_CONSISTENT(dhd->osh, dhd->prot->ioctbuf, + MSGBUF_MAX_MSG_SIZE, dhd->prot->ioctbuf_phys, NULL); + + dhd->prot->ioctbuf = NULL; + } + + NATIVE_TO_PKTID_UNINIT(dhd->prot->pktid_map_handle); + +#ifndef CONFIG_DHD_USE_STATIC_BUF + MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t)); +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + + dhd->prot = NULL; + } +} + +void +dhd_prot_rx_dataoffset(dhd_pub_t *dhd, uint32 rx_offset) +{ + dhd_prot_t *prot = dhd->prot; + prot->rx_dataoffset = rx_offset; +} + + +/* Initialize protocol: sync w/dongle state. + * Sets dongle media info (iswl, drv_version, mac address). + */ +int dhd_prot_init(dhd_pub_t *dhd) +{ + int ret = 0; + wlc_rev_info_t revinfo; + dhd_prot_t *prot = dhd->prot; + uint32 shared_flags; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + dhd_bus_cmn_readshared(dhd->bus, &prot->max_tx_count, TOTAL_LFRAG_PACKET_CNT); + if (prot->max_tx_count == 0) { + /* This can happen if LFrag pool is not enabled for the LFRAG's */ + /* on the dongle. Let's use some default value */ + prot->max_tx_count = 64; + } + DHD_INFO(("%s:%d: MAX_TX_COUNT = %d\n", __FUNCTION__, __LINE__, prot->max_tx_count)); + + dhd_bus_cmn_readshared(dhd->bus, &prot->max_rxbufpost, MAX_HOST_RXBUFS); + if (prot->max_rxbufpost == 0) { + /* This would happen if the dongle firmware is not */ + /* using the latest shared structure template */ + prot->max_rxbufpost = DEFAULT_RX_BUFFERS_TO_POST; + } + DHD_INFO(("%s:%d: MAX_RXBUFPOST = %d\n", __FUNCTION__, __LINE__, prot->max_rxbufpost)); + + prot->active_tx_count = 0; + prot->txflow_en = FALSE; + prot->mb_ring_fn = dhd_bus_get_mbintr_fn(dhd->bus); + prot->data_seq_no = 0; + prot->ioctl_seq_no = 0; + /* initialise msgbufs */ + shared_flags = dhd_bus_get_sharedflags(dhd->bus); + if (shared_flags & PCIE_SHARED_HTOD_SPLIT) { + prot->htodsplit = TRUE; + if (dhd_msgbuf_init_htod_ctrl(dhd) == BCME_NOMEM) + { + prot->htodsplit = FALSE; + DHD_ERROR(("%s:%d: HTOD ctrl ring alloc failed!\n", + __FUNCTION__, __LINE__)); + } + } + if (shared_flags & PCIE_SHARED_DTOH_SPLIT) { + prot->dtohsplit = TRUE; + if (dhd_msgbuf_init_dtoh_ctrl(dhd) == BCME_NOMEM) + { + prot->dtohsplit = FALSE; + DHD_ERROR(("%s:%d: DTOH ctrl ring alloc failed!\n", + __FUNCTION__, __LINE__)); + } + } + ret = dhd_msgbuf_init_htod(dhd); + ret = dhd_msgbuf_init_dtoh(dhd); + ret = dhd_msgbuf_rxbuf_post(dhd); + + + /* Get the device rev info */ + memset(&revinfo, 0, sizeof(revinfo)); + ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo), FALSE, 0); + if (ret < 0) + goto done; +#if defined(WL_CFG80211) + if (dhd_download_fw_on_driverload) +#endif /* defined(WL_CFG80211) */ + ret = dhd_preinit_ioctls(dhd); + /* Always assumes wl for now */ + dhd->iswl = TRUE; +done: + return ret; + +} + +static INLINE void BCMFASTPATH +dhd_prot_packet_free(dhd_pub_t *dhd, uint32 pktid) +{ + void *PKTBUF; + dmaaddr_t pa; + uint32 pa_len; + PKTBUF = PKTID_TO_NATIVE(dhd->prot->pktid_map_handle, pktid, pa, pa_len); + DMA_UNMAP(dhd->osh, (uint) pa, (uint) pa_len, DMA_TX, 0, 0); + PKTFREE(dhd->osh, PKTBUF, TRUE); + return; +} + +static INLINE void * BCMFASTPATH +dhd_prot_packet_get(dhd_pub_t *dhd, uint32 pktid) +{ + void *PKTBUF; + ulong pa; + uint32 pa_len; + PKTBUF = PKTID_TO_NATIVE(dhd->prot->pktid_map_handle, pktid, pa, pa_len); + DMA_UNMAP(dhd->osh, (uint) pa, (uint) pa_len, DMA_RX, 0, 0); + return PKTBUF; +} + +static int BCMFASTPATH +dhd_msgbuf_rxbuf_post(dhd_pub_t *dhd) +{ + dhd_prot_t *prot = dhd->prot; + unsigned long flags; + uint32 fillbufs; + uint32 i; + fillbufs = prot->max_rxbufpost - prot->rxbufpost; + + for (i = 0; i < fillbufs; ) { + int retcount; + uint32 buf_count = (fillbufs - i) > RX_BUF_BURST ? RX_BUF_BURST : (fillbufs - i); + + flags = dhd_os_spin_lock(dhd); + retcount = dhd_prot_rxbufpost(dhd, buf_count); + if (retcount > 0) { + prot->rxbufpost += (uint16)retcount; + i += (uint16)retcount; + dhd_os_spin_unlock(dhd, flags); + } else { + dhd_os_spin_unlock(dhd, flags); + break; + } + } + + return 0; +} + +static int BCMFASTPATH +dhd_prot_rxbufpost(dhd_pub_t *dhd, uint32 count) +{ + void *p; + uint16 pktsz = 2048; + uint32 i; + rxdesc_msghdr_t *rxbuf_post; + rx_lenptr_tup_t *rx_tup; + dmaaddr_t physaddr; + uint32 pktlen; + uint32 msglen = sizeof(rxdesc_msghdr_t) + count * sizeof(rx_lenptr_tup_t); + + dhd_prot_t *prot = dhd->prot; + circularbuf_t *htod_msgbuf = (circularbuf_t *)prot->htodbuf; + + rxbuf_post = (rxdesc_msghdr_t *)dhd_alloc_circularbuf_space(dhd, + htod_msgbuf, (uint16)msglen, HOST_TO_DNGL_DATA); + if (rxbuf_post == NULL) { + DHD_INFO(("%s:%d: HTOD Msgbuf Not available\n", + __FUNCTION__, __LINE__)); + return -1; + } + + /* CMN msg header */ + rxbuf_post->msg.msglen = htol16((uint16)msglen); + rxbuf_post->msg.msgtype = MSG_TYPE_RXBUF_POST; + rxbuf_post->msg.ifidx = 0; + rxbuf_post->msg.u.seq.seq_no = htol16(++prot->data_seq_no); + + /* RX specific hdr */ + rxbuf_post->rsvd0 = 0; + rxbuf_post->rsvd1 = 0; + rxbuf_post->descnt = (uint8)count; + + rx_tup = (rx_lenptr_tup_t *) &(rxbuf_post->rx_tup[0]); + + for (i = 0; i < count; i++) { + if ((p = PKTGET(dhd->osh, pktsz, FALSE)) == NULL) { + DHD_ERROR(("%s:%d: PKTGET for rxbuf failed\n", __FUNCTION__, __LINE__)); + printf("%s:%d: PKTGET for rxbuf failed. Need to handle this gracefully\n", + __FUNCTION__, __LINE__); + return -1; + } + + pktlen = PKTLEN(dhd->osh, p); + physaddr = DMA_MAP(dhd->osh, PKTDATA(dhd->osh, p), pktlen, DMA_RX, 0, 0); + if (physaddr == 0) { + DHD_ERROR(("Something really bad, unless 0 is a valid phyaddr\n")); + ASSERT(0); + } + /* Each bufid-len-ptr tuple */ + rx_tup->rxbufid = htol32(NATIVE_TO_PKTID(dhd->prot->pktid_map_handle, + p, physaddr, pktlen, DMA_RX)); + rx_tup->len = htol16((uint16)PKTLEN(dhd->osh, p)); + rx_tup->rsvd2 = 0; + rx_tup->ret_buf.high_addr = htol32(PHYSADDRHI(physaddr)); + rx_tup->ret_buf.low_addr = htol32(PHYSADDRLO(physaddr)); + + rx_tup++; + } + + /* Since, we are filling the data directly into the bufptr obtained + * from the msgbuf, we can directly call the write_complete + */ + circularbuf_write_complete(htod_msgbuf, (uint16)msglen); + + return count; +} + +void BCMFASTPATH +dhd_msgbuf_ringbell(void *ctx) +{ + dhd_pub_t *dhd = (dhd_pub_t *) ctx; + dhd_prot_t *prot = dhd->prot; + circularbuf_t *htod_msgbuf = (circularbuf_t *)prot->htodbuf; + + /* Following will take care of writing both the Write and End pointers (32 bits) */ + dhd_bus_cmn_writeshared(dhd->bus, &(CIRCULARBUF_WRITE_PTR(htod_msgbuf)), + sizeof(uint32), HOST_TO_DNGL_WPTR); + + prot->mb_ring_fn(dhd->bus, *(uint32 *) &(CIRCULARBUF_WRITE_PTR(htod_msgbuf))); +} + +void BCMFASTPATH +dhd_ctrlbuf_ringbell(void *ctx) +{ + dhd_pub_t *dhd = (dhd_pub_t *) ctx; + dhd_prot_t *prot = dhd->prot; + circularbuf_t *htod_ctrlbuf = (circularbuf_t *)prot->htod_ctrlbuf; + + /* Following will take care of writing both the Write and End pointers (32 bits) */ + dhd_bus_cmn_writeshared(dhd->bus, &(CIRCULARBUF_WRITE_PTR(htod_ctrlbuf)), + sizeof(uint32), HTOD_CTRL_WPTR); + + prot->mb_ring_fn(dhd->bus, *(uint32 *) &(CIRCULARBUF_WRITE_PTR(htod_ctrlbuf))); +} + +static int +dhd_msgbuf_init_htod(dhd_pub_t *dhd) +{ + dhd_prot_t *prot = dhd->prot; + circularbuf_t *htod_msgbuf = (circularbuf_t *)prot->htodbuf; + + circularbuf_init(htod_msgbuf, prot->htod_ring, HOST_TO_DNGL_MSGBUF_SZ); + circularbuf_register_cb(htod_msgbuf, dhd_msgbuf_ringbell, (void *)dhd); + dhd_bus_cmn_writeshared(dhd->bus, &prot->htod_physaddr, + sizeof(prot->htod_physaddr), HOST_TO_DNGL_BUF_ADDR); + + dhd_bus_cmn_writeshared(dhd->bus, &(CIRCULARBUF_WRITE_PTR(htod_msgbuf)), + sizeof(uint32), HOST_TO_DNGL_WPTR); + + return 0; + +} +static int +dhd_msgbuf_init_dtoh(dhd_pub_t *dhd) +{ + dhd_prot_t *prot = dhd->prot; + circularbuf_t *dtoh_msgbuf = (circularbuf_t *)prot->dtohbuf; + + prot->rxbufpost = 0; + circularbuf_init(dtoh_msgbuf, prot->dtoh_ring, DNGL_TO_HOST_MSGBUF_SZ); + dhd_bus_cmn_writeshared(dhd->bus, &prot->dtoh_physaddr, + sizeof(prot->dtoh_physaddr), DNGL_TO_HOST_BUF_ADDR); + + dhd_bus_cmn_writeshared(dhd->bus, &CIRCULARBUF_READ_PTR(dtoh_msgbuf), + sizeof(uint16), DNGL_TO_HOST_RPTR); + + /* One dummy interrupt to the device. This would trigger */ + /* the msgbuf initializations at the device side. */ + /* Send dummy intr to device here, only if support for split data/ctrl rings is disabled */ + /* Else send the dummy initialization intr at dtoh ctrl buf init */ + + dhd_bus_ringbell(dhd->bus, PCIE_INTB); + return 0; +} + +/* Allocate space for HTOD ctrl ring on host and initialize handle/doorbell for the same */ +static int dhd_msgbuf_init_htod_ctrl(dhd_pub_t *dhd) +{ + uint alloced; + dhd_prot_t *prot = dhd->prot; + prot->htod_ctrlbuf = MALLOC(dhd->osh, sizeof(circularbuf_t)); + memset(prot->htod_ctrlbuf, 0, sizeof(circularbuf_t)); + + prot->htod_ctrl_ring = DMA_ALLOC_CONSISTENT(dhd->osh, HOST_TO_DNGL_CTRLRING_SZ, 4, + &alloced, &prot->htod_ctrl_physaddr, NULL); + if (prot->htod_ctrl_ring == NULL) { + return BCME_NOMEM; + } + + ASSERT(MODX((unsigned long)prot->htod_ctrl_ring, 4) == 0); + + circularbuf_init(prot->htod_ctrlbuf, prot->htod_ctrl_ring, HOST_TO_DNGL_CTRLRING_SZ); + circularbuf_register_cb(prot->htod_ctrlbuf, dhd_ctrlbuf_ringbell, (void *)dhd); + dhd_bus_cmn_writeshared(dhd->bus, &prot->htod_ctrl_physaddr, + sizeof(prot->htod_ctrl_physaddr), HOST_TO_DNGL_CTRLBUF_ADDR); + + dhd_bus_cmn_writeshared(dhd->bus, &(CIRCULARBUF_WRITE_PTR(prot->htod_ctrlbuf)), + sizeof(uint32), HTOD_CTRL_WPTR); + + return 0; +} +/* Allocate space for DTOH ctrl ring on host and initialize msgbuf handle in dhd_prot_t */ +static int dhd_msgbuf_init_dtoh_ctrl(dhd_pub_t *dhd) +{ + uint alloced; + dhd_prot_t *prot = dhd->prot; + prot->dtoh_ctrlbuf = MALLOC(dhd->osh, sizeof(circularbuf_t)); + memset(prot->dtoh_ctrlbuf, 0, sizeof(circularbuf_t)); + + prot->dtoh_ctrl_ring = DMA_ALLOC_CONSISTENT(dhd->osh, DNGL_TO_HOST_CTRLRING_SZ, 4, + &alloced, &prot->dtoh_ctrl_physaddr, NULL); + if (prot->dtoh_ctrl_ring == NULL) { + return BCME_NOMEM; + } + ASSERT(MODX((unsigned long)prot->dtoh_ctrl_ring, 4) == 0); + + circularbuf_init(prot->dtoh_ctrlbuf, prot->dtoh_ctrl_ring, DNGL_TO_HOST_CTRLRING_SZ); + dhd_bus_cmn_writeshared(dhd->bus, &prot->dtoh_ctrl_physaddr, + sizeof(prot->dtoh_ctrl_physaddr), DNGL_TO_HOST_CTRLBUF_ADDR); + + dhd_bus_cmn_writeshared(dhd->bus, &(CIRCULARBUF_READ_PTR(prot->dtoh_ctrlbuf)), + sizeof(uint32), DTOH_CTRL_RPTR); + return 0; +} + +int BCMFASTPATH +dhd_prot_process_msgbuf(dhd_pub_t *dhd) +{ + dhd_prot_t *prot = dhd->prot; + circularbuf_t *dtoh_msgbuf = (circularbuf_t *)prot->dtohbuf; + + dhd_bus_cmn_readshared(dhd->bus, &CIRCULARBUF_WRITE_PTR(dtoh_msgbuf), DNGL_TO_HOST_WPTR); + + /* Process all the messages - DTOH direction */ + while (TRUE) { + uint8 *src_addr; + uint16 src_len; + + src_addr = circularbuf_get_read_ptr(dtoh_msgbuf, &src_len); + if (src_addr == NULL) + break; + + /* Prefetch data to populate the cache */ + OSL_PREFETCH(src_addr); + + dhd_prot_process_msgtype(dhd, src_addr, src_len); + circularbuf_read_complete(dtoh_msgbuf, src_len); + + /* Write to dngl rd ptr */ + dhd_bus_cmn_writeshared(dhd->bus, &CIRCULARBUF_READ_PTR(dtoh_msgbuf), + sizeof(uint16), DNGL_TO_HOST_RPTR); + } + + return 0; +} + +int BCMFASTPATH +dhd_prot_process_ctrlbuf(dhd_pub_t * dhd) +{ + dhd_prot_t *prot = dhd->prot; + circularbuf_t *dtoh_ctrlbuf = (circularbuf_t *)prot->dtoh_ctrlbuf; + + dhd_bus_cmn_readshared(dhd->bus, &CIRCULARBUF_WRITE_PTR(dtoh_ctrlbuf), DTOH_CTRL_WPTR); + + /* Process all the messages - DTOH direction */ + while (TRUE) { + uint8 *src_addr; + uint16 src_len; + + src_addr = circularbuf_get_read_ptr(dtoh_ctrlbuf, &src_len); + if (src_addr == NULL) { + break; + } + /* Prefetch data to populate the cache */ + OSL_PREFETCH(src_addr); + + dhd_prot_process_msgtype(dhd, src_addr, src_len); + circularbuf_read_complete(dtoh_ctrlbuf, src_len); + + /* Write to dngl rd ptr */ + dhd_bus_cmn_writeshared(dhd->bus, &CIRCULARBUF_READ_PTR(dtoh_ctrlbuf), + sizeof(uint16), DTOH_CTRL_RPTR); + } + + return 0; +} + +static void BCMFASTPATH +dhd_prot_process_msgtype(dhd_pub_t *dhd, uint8* buf, uint16 len) +{ + dhd_prot_t *prot = dhd->prot; + uint32 cur_dma_len = 0; + + DHD_TRACE(("%s: process msgbuf of len %d\n", __FUNCTION__, len)); + + while (len > 0) { + ASSERT(len > (sizeof(cmn_msg_hdr_t) + prot->rx_dataoffset)); + if (prot->rx_dataoffset) { + cur_dma_len = *(uint32 *) buf; + ASSERT(cur_dma_len <= len); + buf += prot->rx_dataoffset; + len -= (uint16)prot->rx_dataoffset; + } + else { + cur_dma_len = len; + } + dhd_process_msgtype(dhd, buf, (uint16)cur_dma_len); + len -= (uint16)cur_dma_len; + buf += cur_dma_len; + } +} + + +static void +dhd_check_sequence_num(cmn_msg_hdr_t *msg) +{ + static uint32 ioctl_seq_no_old = 0; + static uint32 data_seq_no_old = 0; + + switch (msg->msgtype) { + case MSG_TYPE_IOCTL_CMPLT: + if (msg->u.seq.seq_no && msg->u.seq.seq_no != (ioctl_seq_no_old + 1)) + { + DHD_ERROR(("Error in IOCTL MsgBuf Sequence number!!" + "new seq no %u, old seq number %u\n", + msg->u.seq.seq_no, ioctl_seq_no_old)); + } + ioctl_seq_no_old = msg->u.seq.seq_no; + break; + + case MSG_TYPE_RX_CMPLT: + case MSG_TYPE_WL_EVENT : + case MSG_TYPE_TX_STATUS : + case MSG_TYPE_LOOPBACK: + if (msg->u.seq.seq_no && msg->u.seq.seq_no != (data_seq_no_old + 1)) + { + DHD_ERROR(("Error in DATA MsgBuf Sequence number!!" + "new seq no %u old seq number %u\n", + msg->u.seq.seq_no, data_seq_no_old)); + } + data_seq_no_old = msg->u.seq.seq_no; + break; + + default: + printf("Unknown MSGTYPE in %s \n", __FUNCTION__); + break; + + } +} + +static void BCMFASTPATH +dhd_process_msgtype(dhd_pub_t *dhd, uint8* buf, uint16 len) +{ + uint16 pktlen = len; + uint16 msglen; + uint8 msgtype; + cmn_msg_hdr_t *msg = NULL; + while (pktlen > 0) { + msg = (cmn_msg_hdr_t *)buf; + msgtype = msg->msgtype; + msglen = msg->msglen; + + /* Prefetch data to populate the cache */ + OSL_PREFETCH(buf+msglen); + + dhd_check_sequence_num(msg); + + DHD_INFO(("msgtype %d, msglen is %d \n", msgtype, msglen)); + switch (msgtype) { + case MSG_TYPE_IOCTL_CMPLT: + DHD_INFO((" MSG_TYPE_IOCTL_CMPLT\n")); + dhd_prot_ioctcmplt_process(dhd, buf); + break; + case MSG_TYPE_RX_CMPLT: + DHD_INFO((" MSG_TYPE_RX_CMPLT\n")); + dhd_prot_rxcmplt_process(dhd, buf); + break; + case MSG_TYPE_WL_EVENT: + DHD_INFO((" MSG_TYPE_WL_EVENT\n")); + dhd_prot_event_process(dhd, buf, msglen); + break; + case MSG_TYPE_TX_STATUS: + DHD_INFO((" MSG_TYPE_TX_STATUS\n")); + dhd_prot_txstatus_process(dhd, buf); + break; + case MSG_TYPE_LOOPBACK: + bcm_print_bytes("LPBK RESP: ", (uint8 *)msg, msglen); + DHD_ERROR((" MSG_TYPE_LOOPBACK, len %d\n", msglen)); + break; + default : + DHD_ERROR(("Unknown state in %s," + "rxoffset %d\n", __FUNCTION__, dhd->prot->rx_dataoffset)); + bcm_print_bytes("UNKNOWN msg", (uchar *)msg, msglen); + break; + + } + + DHD_INFO(("pktlen is %d, msglen is %d\n", pktlen, msglen)); + if (pktlen < msglen) { + return; + } + pktlen = pktlen - msglen; + buf = buf + msglen; + } +} +static void +dhd_prot_ioctcmplt_process(dhd_pub_t *dhd, void * buf) +{ + uint32 retlen, status, inline_data = 0; + uint32 pkt_id, xt_id; + + ioct_resp_hdr_t * ioct_resp = (ioct_resp_hdr_t *)buf; + retlen = ltoh32(ioct_resp->ret_len); + pkt_id = ltoh32(ioct_resp->pkt_id); + xt_id = ltoh32(ioct_resp->xt_id); + status = ioct_resp->status; + if (retlen <= 4) { + inline_data = ltoh32(ioct_resp->inline_data); + } else { + OSL_CACHE_INV((void *) dhd->prot->retbuf, retlen); + } + DHD_CTL(("status from the pkt_id is %d, ioctl is %d, ret_len is %d, xt_id %d\n", + pkt_id, status, retlen, xt_id)); + + if (retlen == 0) + retlen = 1; + + dhd_bus_update_retlen(dhd->bus, retlen, pkt_id, status, inline_data); + dhd_os_ioctl_resp_wake(dhd); +} + +static void BCMFASTPATH +dhd_prot_txstatus_process(dhd_pub_t *dhd, void * buf) +{ + dhd_prot_t *prot = dhd->prot; + txstatus_hdr_t * txstatus; + unsigned long flags; + uint32 pktid; + + /* locks required to protect circular buffer accesses */ + flags = dhd_os_spin_lock(dhd); + + txstatus = (txstatus_hdr_t *)buf; + pktid = ltoh32(txstatus->pktid); + + prot->active_tx_count--; + + ASSERT(pktid != 0); + dhd_prot_packet_free(dhd, pktid); + + if (prot->txflow_en == TRUE) { + /* If the pktpool availability is above the high watermark, */ + /* let's resume the flow of packets to dongle. */ + if ((prot->max_tx_count - prot->active_tx_count) > DHD_START_QUEUE_THRESHOLD) { + dhd_bus_start_queue(dhd->bus); + prot->txflow_en = FALSE; + } + } + + dhd_os_spin_unlock(dhd, flags); + return; +} + +static void +dhd_prot_event_process(dhd_pub_t *dhd, uint8* buf, uint16 len) +{ + wl_event_hdr_t * evnt; + uint32 bufid; + uint16 buflen; + int ifidx = 0; + uint pkt_count = 1; + void* pkt; + unsigned long flags; + + /* Event complete header */ + evnt = (wl_event_hdr_t *)buf; + bufid = ltoh32(evnt->rxbufid); + buflen = ltoh16(evnt->retbuf_len); + + /* Post another rxbuf to the device */ + dhd_prot_return_rxbuf(dhd, 1); + + /* locks required to protect pktid_map */ + flags = dhd_os_spin_lock(dhd); + + pkt = dhd_prot_packet_get(dhd, ltoh32(bufid)); + + dhd_os_spin_unlock(dhd, flags); + + /* DMA RX offset updated through shared area */ + if (dhd->prot->rx_dataoffset) + PKTPULL(dhd->osh, pkt, dhd->prot->rx_dataoffset); + + PKTSETLEN(dhd->osh, pkt, buflen); + + /* remove WL header */ + PKTPULL(dhd->osh, pkt, 4); /* WL Header */ + + dhd_bus_rx_frame(dhd->bus, pkt, ifidx, pkt_count); +} + +static void BCMFASTPATH +dhd_prot_rxcmplt_process(dhd_pub_t *dhd, void* buf) +{ + rxcmplt_hdr_t *rxcmplt_h; + rxcmplt_tup_t *rx_tup; + uint32 bufid; + uint16 buflen, cmpltcnt; + uint16 data_offset; /* offset at which data starts */ + void * pkt; + int ifidx = 0; + uint pkt_count = 0; + uint32 i; + void *pkthead = NULL; + void *pkttail = NULL; + + /* RXCMPLT HDR */ + rxcmplt_h = (rxcmplt_hdr_t *)buf; + cmpltcnt = ltoh16(rxcmplt_h->rxcmpltcnt); + + /* Post another set of rxbufs to the device */ + dhd_prot_return_rxbuf(dhd, cmpltcnt); + ifidx = rxcmplt_h->msg.ifidx; + + rx_tup = (rxcmplt_tup_t *) &(rxcmplt_h->rx_tup[0]); + for (i = 0; i < cmpltcnt; i++) { + unsigned long flags; + + bufid = ltoh32(rx_tup->rxbufid); + buflen = ltoh16(rx_tup->retbuf_len); + + /* offset from which data starts is populated in rxstatus0 */ + data_offset = ltoh16(rx_tup->data_offset); + + /* locks required to protect pktid_map */ + flags = dhd_os_spin_lock(dhd); + pkt = dhd_prot_packet_get(dhd, ltoh32(bufid)); + dhd_os_spin_unlock(dhd, flags); + + /* data_offset from buf start */ + if (data_offset) { + /* data offset given from dongle after split rx */ + PKTPULL(dhd->osh, pkt, data_offset); /* data offset */ + } else { + /* DMA RX offset updated through shared area */ + if (dhd->prot->rx_dataoffset) + PKTPULL(dhd->osh, pkt, dhd->prot->rx_dataoffset); + } + + /* Actual length of the packet */ + PKTSETLEN(dhd->osh, pkt, buflen); + + /* remove WL header */ + PKTPULL(dhd->osh, pkt, 4); /* WL Header */ + + pkt_count++; + rx_tup++; + + /* Chain the packets and release in one shot to dhd_linux. */ + /* Interface and destination checks are not required here. */ + PKTSETNEXT(dhd->osh, pkt, NULL); + if (pkttail == NULL) { + pkthead = pkttail = pkt; + } else { + PKTSETNEXT(dhd->osh, pkttail, pkt); + pkttail = pkt; + } + } + + if (pkthead) { + /* Release the packets to dhd_linux */ + dhd_bus_rx_frame(dhd->bus, pkthead, ifidx, pkt_count); + } +} +/* Stop protocol: sync w/dongle state. */ +void dhd_prot_stop(dhd_pub_t *dhd) +{ + /* nothing to do for pcie */ +} + +/* Add any protocol-specific data header. + * Caller must reserve prot_hdrlen prepend space. + */ +void dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *PKTBUF) +{ + return; +} + +#define PKTBUF pktbuf + +int BCMFASTPATH +dhd_prot_txdata(dhd_pub_t *dhd, void *PKTBUF, uint8 ifidx) +{ + unsigned long flags; + dhd_prot_t *prot = dhd->prot; + circularbuf_t *htod_msgbuf = (circularbuf_t *)prot->htodbuf; + txdescr_msghdr_t *txdesc = NULL; + tx_lenptr_tup_t *tx_tup; + dmaaddr_t physaddr; + uint8 *pktdata; + uint8 *etherhdr; + uint16 pktlen; + uint16 hdrlen; + uint32 pktid; + + /* Extract the data pointer and length information */ + pktdata = PKTDATA(dhd->osh, PKTBUF); + pktlen = (uint16)PKTLEN(dhd->osh, PKTBUF); + + /* Extract the ethernet header and adjust the data pointer and length */ + etherhdr = pktdata; + pktdata += ETHER_HDR_LEN; + pktlen -= ETHER_HDR_LEN; + + + flags = dhd_os_spin_lock(dhd); + + /* Map the data pointer to a DMA-able address */ + physaddr = DMA_MAP(dhd->osh, pktdata, pktlen, DMA_TX, 0, 0); + if (physaddr == 0) { + DHD_ERROR(("Something really bad, unless 0 is a valid phyaddr\n")); + ASSERT(0); + } + + /* Create a unique 32-bit packet id */ + pktid = NATIVE_TO_PKTID(dhd->prot->pktid_map_handle, PKTBUF, physaddr, pktlen, DMA_TX); + + /* Reserve space in the circular buffer */ + hdrlen = sizeof(txdescr_msghdr_t) + (1 * sizeof(tx_lenptr_tup_t)); + + txdesc = (txdescr_msghdr_t *)dhd_alloc_circularbuf_space(dhd, + htod_msgbuf, hdrlen, HOST_TO_DNGL_DATA); + if (txdesc == NULL) { + dhd_prot_packet_free(dhd, pktid); + dhd_os_spin_unlock(dhd, flags); + + DHD_INFO(("%s:%d: HTOD Msgbuf Not available TxCount = %d\n", + __FUNCTION__, __LINE__, prot->active_tx_count)); + return BCME_NORESOURCE; + } + + /* Form the Tx descriptor message buffer */ + + /* Common message hdr */ + txdesc->txcmn.msg.msglen = htol16(hdrlen); + txdesc->txcmn.msg.msgtype = MSG_TYPE_TX_POST; + txdesc->txcmn.msg.u.seq.seq_no = htol16(++prot->data_seq_no); + + /* Ethernet header */ + txdesc->txcmn.hdrlen = htol16(ETHER_HDR_LEN); + bcopy(etherhdr, txdesc->txhdr, ETHER_HDR_LEN); + + /* Packet ID */ + txdesc->txcmn.pktid = htol32(pktid); + + /* Descriptor count - Linux needs only one */ + txdesc->txcmn.descrcnt = 0x1; + + tx_tup = (tx_lenptr_tup_t *) &(txdesc->tx_tup); + + /* Descriptor - 0 */ + tx_tup->pktlen = htol16(pktlen); + tx_tup->ret_buf.high_addr = htol32(PHYSADDRHI(physaddr)); + tx_tup->ret_buf.low_addr = htol32(PHYSADDRLO(physaddr)); + /* Descriptor 1 - should be filled here - if required */ + + /* Reserved for future use */ + txdesc->txcmn.priority = (uint8)PKTPRIO(PKTBUF); + txdesc->txcmn.flowid = 0; + txdesc->txcmn.msg.ifidx = ifidx; + + /* Since, we are filling the data directly into the bufptr obtained + * from the circularbuf, we can directly call the write_complete + */ + circularbuf_write_complete(htod_msgbuf, hdrlen); + + prot->active_tx_count++; + + /* If we have accounted for most of the lfrag packets on the dongle, */ + /* it's time to stop the packet flow - Assert flow control. */ + if ((prot->max_tx_count - prot->active_tx_count) < DHD_STOP_QUEUE_THRESHOLD) { + dhd_bus_stop_queue(dhd->bus); + prot->txflow_en = TRUE; + } + + dhd_os_spin_unlock(dhd, flags); + + return BCME_OK; +} + +#undef PKTBUF /* Only defined in the above routine */ +int dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pkt, uchar *buf, uint *len) +{ + return 0; +} + +static void BCMFASTPATH +dhd_prot_return_rxbuf(dhd_pub_t *dhd, uint16 rxcnt) +{ + dhd_prot_t *prot = dhd->prot; + + prot->rxbufpost -= rxcnt; + if (prot->rxbufpost <= (prot->max_rxbufpost - RXBUFPOST_THRESHOLD)) + dhd_msgbuf_rxbuf_post(dhd); + + return; +} + +/* Use protocol to issue ioctl to dongle */ +int dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len) +{ + dhd_prot_t *prot = dhd->prot; + int ret = -1; + uint8 action; + + if ((dhd->busstate == DHD_BUS_DOWN) || dhd->hang_was_sent) { + DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); + goto done; + } + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + ASSERT(len <= WLC_IOCTL_MAXLEN); + + if (len > WLC_IOCTL_MAXLEN) + goto done; + + if (prot->pending == TRUE) { + DHD_ERROR(("packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n", + ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd, + (unsigned long)prot->lastcmd)); + if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) { + DHD_TRACE(("iovar cmd=%s\n", (char*)buf)); + } + goto done; + } + + prot->pending = TRUE; + prot->lastcmd = ioc->cmd; + action = ioc->set; + if (action & WL_IOCTL_ACTION_SET) { + ret = dhd_msgbuf_set_ioctl(dhd, ifidx, ioc->cmd, buf, len, action); + } else { + ret = dhdmsgbuf_query_ioctl(dhd, ifidx, ioc->cmd, buf, len, action); + if (ret > 0) + ioc->used = ret; + } + /* Too many programs assume ioctl() returns 0 on success */ + if (ret >= 0) + ret = 0; + else { + DHD_INFO(("%s: status ret value is %d \n", __FUNCTION__, ret)); + dhd->dongle_error = ret; + } + + /* Intercept the wme_dp ioctl here */ + if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) { + int slen, val = 0; + + slen = strlen("wme_dp") + 1; + if (len >= (int)(slen + sizeof(int))) + bcopy(((char *)buf + slen), &val, sizeof(int)); + dhd->wme_dp = (uint8) ltoh32(val); + } + + prot->pending = FALSE; + +done: + return ret; + +} + +int +dhdmsgbuf_lpbk_req(dhd_pub_t *dhd, uint len) +{ + unsigned long flags; + dhd_prot_t *prot = dhd->prot; + circularbuf_t *htod_msgbuf; + + ioct_reqst_hdr_t *ioct_rqst; + + uint16 hdrlen = sizeof(ioct_reqst_hdr_t); + uint16 msglen = len + hdrlen; + + if (dhd->prot->htodsplit) + htod_msgbuf = (circularbuf_t *) prot->htod_ctrlbuf; + else + htod_msgbuf = (circularbuf_t *) prot->htodbuf; + + if (msglen > MSGBUF_MAX_MSG_SIZE) + msglen = MSGBUF_MAX_MSG_SIZE; + + msglen = align(msglen, 4); + + /* locks required to protect circular buffer accesses */ + flags = dhd_os_spin_lock(dhd); + + if (dhd->prot->htodsplit) { + ioct_rqst = (ioct_reqst_hdr_t *)dhd_alloc_circularbuf_space(dhd, + htod_msgbuf, msglen, HOST_TO_DNGL_CTRL); + } + else { + ioct_rqst = (ioct_reqst_hdr_t *)dhd_alloc_circularbuf_space(dhd, + htod_msgbuf, msglen, HOST_TO_DNGL_DATA); + } + + if (ioct_rqst == NULL) { + dhd_os_spin_unlock(dhd, flags); + return 0; + } + + { + uint8 *ptr; + uint16 i; + + ptr = (uint8 *)ioct_rqst; + for (i = 0; i < msglen; i++) { + ptr[i] = i % 256; + } + } + + + /* Common msg buf hdr */ + ioct_rqst->msg.msglen = htol16(msglen); + ioct_rqst->msg.msgtype = MSG_TYPE_LOOPBACK; + ioct_rqst->msg.ifidx = 0; + ioct_rqst->msg.u.seq.seq_no = htol16(++prot->data_seq_no); + + bcm_print_bytes("LPBK REQ: ", (uint8 *)ioct_rqst, msglen); + + circularbuf_write_complete(htod_msgbuf, msglen); + + dhd_os_spin_unlock(dhd, flags); + + return 0; +} + + +static int +dhdmsgbuf_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action) +{ + dhd_prot_t *prot = dhd->prot; + + int ret = 0; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* Respond "bcmerror" and "bcmerrorstr" with local cache */ + if (cmd == WLC_GET_VAR && buf) + { + if (!strcmp((char *)buf, "bcmerrorstr")) + { + strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN); + goto done; + } + else if (!strcmp((char *)buf, "bcmerror")) + { + *(int *)buf = dhd->dongle_error; + goto done; + } + } + + /* Fill up msgbuf for ioctl req */ + if (len < MAX_INLINE_IOCTL_LEN) { + /* Inline ioct resuest */ + ret = dhd_fillup_ioct_reqst(dhd, (uint16)len, cmd, buf, ifidx); + } else { + /* Non inline ioct resuest */ + ret = dhd_fillup_ioct_reqst_ptrbased(dhd, (uint16)len, cmd, buf, ifidx); + } + + DHD_INFO(("ACTION %d ifdix %d cmd %d len %d \n", + action, ifidx, cmd, len)); + + /* wait for interrupt and get first fragment */ + ret = dhdmsgbuf_cmplt(dhd, prot->reqid, len, buf, prot->retbuf); + +done: + return ret; +} +static int +dhdmsgbuf_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len, void* buf, void* retbuf) +{ + dhd_prot_t *prot = dhd->prot; + ioct_resp_hdr_t ioct_resp; + uint8* data; + int retlen; + int msgbuf_len = 0; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + retlen = dhd_bus_rxctl(dhd->bus, (uchar*)&ioct_resp, msgbuf_len); + + if (retlen <= 0) + return -1; + + /* get ret buf */ + if (buf != NULL) { + if (retlen <= 4) { + bcopy((void*)&ioct_resp.inline_data, buf, retlen); + DHD_INFO(("%s: data is %d, ret_len is %d\n", + __FUNCTION__, ioct_resp.inline_data, retlen)); + } + else { + data = (uint8*)retbuf; + bcopy((void*)&data[prot->rx_dataoffset], buf, retlen); + } + } + return ioct_resp.status; +} +static int +dhd_msgbuf_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action) +{ + dhd_prot_t *prot = dhd->prot; + + int ret = 0; + + DHD_TRACE(("%s: Enter \n", __FUNCTION__)); + DHD_TRACE(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len)); + + if (dhd->busstate == DHD_BUS_DOWN) { + DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); + return -EIO; + } + + /* don't talk to the dongle if fw is about to be reloaded */ + if (dhd->hang_was_sent) { + DHD_ERROR(("%s: HANG was sent up earlier. Not talking to the chip\n", + __FUNCTION__)); + return -EIO; + } + + /* Fill up msgbuf for ioctl req */ + if (len < MAX_INLINE_IOCTL_LEN) { + /* Inline ioct resuest */ + ret = dhd_fillup_ioct_reqst(dhd, (uint16)len, cmd, buf, ifidx); + } else { + /* Non inline ioct resuest */ + ret = dhd_fillup_ioct_reqst_ptrbased(dhd, (uint16)len, cmd, buf, ifidx); + } + + DHD_INFO(("ACTIOn %d ifdix %d cmd %d len %d \n", + action, ifidx, cmd, len)); + + ret = dhdmsgbuf_cmplt(dhd, prot->reqid, len, buf, prot->retbuf); + + return ret; +} +/* Handles a protocol control response asynchronously */ +int dhd_prot_ctl_complete(dhd_pub_t *dhd) +{ + return 0; +} + +/* Check for and handle local prot-specific iovar commands */ +int dhd_prot_iovar_op(dhd_pub_t *dhd, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + return BCME_UNSUPPORTED; +} + +/* Add prot dump output to a buffer */ +void dhd_prot_dump(dhd_pub_t *dhd, struct bcmstrbuf *strbuf) +{ + +} + +/* Update local copy of dongle statistics */ +void dhd_prot_dstats(dhd_pub_t *dhd) +{ + return; +} + +int dhd_process_pkt_reorder_info(dhd_pub_t *dhd, uchar *reorder_info_buf, + uint reorder_info_len, void **pkt, uint32 *free_buf_count) +{ + return 0; +} +/* post a dummy message to interrupt dongle */ +/* used to process cons commands */ +int +dhd_post_dummy_msg(dhd_pub_t *dhd) +{ + unsigned long flags; + hostevent_hdr_t *hevent = NULL; + uint16 msglen = sizeof(hostevent_hdr_t); + + dhd_prot_t *prot = dhd->prot; + circularbuf_t *htod_msgbuf; + + /* locks required to protect circular buffer accesses */ + flags = dhd_os_spin_lock(dhd); + if (dhd->prot->htodsplit) { + htod_msgbuf = (circularbuf_t *)prot->htod_ctrlbuf; + hevent = (hostevent_hdr_t *)dhd_alloc_circularbuf_space(dhd, + htod_msgbuf, msglen, HOST_TO_DNGL_CTRL); + } + else { + htod_msgbuf = (circularbuf_t *)prot->htodbuf; + hevent = (hostevent_hdr_t *)dhd_alloc_circularbuf_space(dhd, + htod_msgbuf, msglen, HOST_TO_DNGL_DATA); + } + + if (hevent == NULL) { + dhd_os_spin_unlock(dhd, flags); + return -1; + } + + /* CMN msg header */ + hevent->msg.msglen = htol16(msglen); + hevent->msg.msgtype = MSG_TYPE_HOST_EVNT; + hevent->msg.ifidx = 0; + hevent->msg.u.seq.seq_no = htol16(++prot->data_seq_no); + + /* Event payload */ + hevent->evnt_pyld = htol32(HOST_EVENT_CONS_CMD); + + /* Since, we are filling the data directly into the bufptr obtained + * from the msgbuf, we can directly call the write_complete + */ + circularbuf_write_complete(htod_msgbuf, msglen); + dhd_os_spin_unlock(dhd, flags); + + return 0; +} +void * BCMFASTPATH +dhd_alloc_circularbuf_space(dhd_pub_t *dhd, circularbuf_t *handle, uint16 msglen, uint path) +{ + void * ret_buf; + + ret_buf = circularbuf_reserve_for_write(handle, msglen); + if (ret_buf == NULL) { + /* Try again after updating the read ptr from dongle */ + if (path == HOST_TO_DNGL_DATA) + dhd_bus_cmn_readshared(dhd->bus, &(CIRCULARBUF_READ_PTR(handle)), + HOST_TO_DNGL_RPTR); + else if (path == HOST_TO_DNGL_CTRL) + dhd_bus_cmn_readshared(dhd->bus, &(CIRCULARBUF_READ_PTR(handle)), + HTOD_CTRL_RPTR); + else + DHD_ERROR(("%s:%d: Unknown path value \n", __FUNCTION__, __LINE__)); + ret_buf = circularbuf_reserve_for_write(handle, msglen); + if (ret_buf == NULL) { + DHD_INFO(("%s:%d: HTOD Msgbuf Not available \n", __FUNCTION__, __LINE__)); + return NULL; + } + } + + return ret_buf; +} +INLINE bool +dhd_prot_dtohsplit(dhd_pub_t* dhd) +{ + return dhd->prot->dtohsplit; +} +static int +dhd_fillup_ioct_reqst(dhd_pub_t *dhd, uint16 len, uint cmd, void* buf, int ifidx) +{ + dhd_prot_t *prot = dhd->prot; + ioct_reqst_hdr_t *ioct_rqst; + uint16 hdrlen = sizeof(ioct_reqst_hdr_t); + uint16 msglen = len + hdrlen; + circularbuf_t *htod_msgbuf; + unsigned long flags; + uint16 rqstlen = len; + + /* Limit ioct request to MSGBUF_MAX_MSG_SIZE bytes including hdrs */ + if (rqstlen + hdrlen > MSGBUF_MAX_MSG_SIZE) + rqstlen = MSGBUF_MAX_MSG_SIZE - hdrlen; + + /* Messge = hdr + rqstbuf */ + msglen = rqstlen + hdrlen; + + /* align it to 4 bytes, so that all start addr form cbuf is 4 byte aligned */ + msglen = align(msglen, 4); + + /* locks required to protect circular buffer accesses */ + flags = dhd_os_spin_lock(dhd); + + /* Request for cbuf space */ + if (dhd->prot->htodsplit) { + htod_msgbuf = (circularbuf_t *)prot->htod_ctrlbuf; + ioct_rqst = (ioct_reqst_hdr_t *)dhd_alloc_circularbuf_space(dhd, + htod_msgbuf, msglen, HOST_TO_DNGL_CTRL); + } + else { + htod_msgbuf = (circularbuf_t *)prot->htodbuf; + ioct_rqst = (ioct_reqst_hdr_t *)dhd_alloc_circularbuf_space(dhd, + htod_msgbuf, msglen, HOST_TO_DNGL_DATA); + } + + if (ioct_rqst == NULL) { + dhd_os_spin_unlock(dhd, flags); + return -1; + } + + /* Common msg buf hdr */ + ioct_rqst->msg.msglen = htol16(msglen); + ioct_rqst->msg.msgtype = MSG_TYPE_IOCTL_REQ; + ioct_rqst->msg.ifidx = (uint8)ifidx; + ioct_rqst->msg.u.seq.seq_no = htol16(++prot->ioctl_seq_no); + + /* Ioctl specific Message buf header */ + ioct_rqst->ioct_hdr.cmd = htol32(cmd); + ioct_rqst->ioct_hdr.pkt_id = htol32(++prot->reqid); + ioct_rqst->ioct_hdr.retbuf_len = htol16(len); + ioct_rqst->ioct_hdr.xt_id = (uint16)ioct_rqst->ioct_hdr.pkt_id; + DHD_CTL(("sending IOCTL_REQ cmd %d, pkt_id %d xt_id %d\n", + ioct_rqst->ioct_hdr.cmd, ioct_rqst->ioct_hdr.pkt_id, ioct_rqst->ioct_hdr.xt_id)); + + /* Ret buf ptr */ + ioct_rqst->ret_buf.high_addr = htol32(PHYSADDRHI(prot->retbuf_phys)); + ioct_rqst->ret_buf.low_addr = htol32(PHYSADDRLO(prot->retbuf_phys)); + + /* copy ioct payload */ + if (buf) + memcpy(&ioct_rqst[1], buf, rqstlen); + + /* upd wrt ptr and raise interrupt */ + circularbuf_write_complete(htod_msgbuf, msglen); + dhd_os_spin_unlock(dhd, flags); + + return 0; +} +/* Non inline ioct request */ +/* Form a ioctl request first as per ioctptr_reqst_hdr_t header in the circular buffer */ +/* Form a separate request buffer where a 4 byte cmn header is added in the front */ +/* buf contents from parent function is copied to remaining section of this buffer */ +static int +dhd_fillup_ioct_reqst_ptrbased(dhd_pub_t *dhd, uint16 len, uint cmd, void* buf, int ifidx) +{ + dhd_prot_t *prot = dhd->prot; + ioctptr_reqst_hdr_t *ioct_rqst; + uint16 msglen = sizeof(ioctptr_reqst_hdr_t); + circularbuf_t * htod_msgbuf; + cmn_msg_hdr_t * ioct_buf; /* For ioctl payload */ + uint16 alignlen, rqstlen = len; + unsigned long flags; + + /* Limit ioct request to MSGBUF_MAX_MSG_SIZE bytes including hdrs */ + if ((rqstlen + sizeof(cmn_msg_hdr_t)) > MSGBUF_MAX_MSG_SIZE) + rqstlen = MSGBUF_MAX_MSG_SIZE - sizeof(cmn_msg_hdr_t); + + /* align it to 4 bytes, so that all start addr form cbuf is 4 byte aligned */ + alignlen = align(rqstlen, 4); + + /* locks required to protect circular buffer accesses */ + flags = dhd_os_spin_lock(dhd); + /* Request for cbuf space */ + if (dhd->prot->htodsplit) { + htod_msgbuf = (circularbuf_t *)prot->htod_ctrlbuf; + ioct_rqst = (ioctptr_reqst_hdr_t*)dhd_alloc_circularbuf_space(dhd, + htod_msgbuf, msglen, HOST_TO_DNGL_CTRL); + } + else { + htod_msgbuf = (circularbuf_t *)prot->htodbuf; + ioct_rqst = (ioctptr_reqst_hdr_t*)dhd_alloc_circularbuf_space(dhd, + htod_msgbuf, msglen, HOST_TO_DNGL_DATA); + } + if (ioct_rqst == NULL) { + dhd_os_spin_unlock(dhd, flags); + return -1; + } + + /* Common msg buf hdr */ + ioct_rqst->msg.msglen = htol16(msglen); + ioct_rqst->msg.msgtype = MSG_TYPE_IOCTLPTR_REQ; + ioct_rqst->msg.ifidx = (uint8)ifidx; + ioct_rqst->msg.u.seq.seq_no = htol16(++prot->ioctl_seq_no); + + /* Ioctl specific Message buf header */ + ioct_rqst->ioct_hdr.cmd = htol32(cmd); + ioct_rqst->ioct_hdr.pkt_id = htol32(++prot->reqid); + ioct_rqst->ioct_hdr.retbuf_len = htol16(len); + ioct_rqst->ioct_hdr.xt_id = (uint16)ioct_rqst->ioct_hdr.pkt_id; + + DHD_CTL(("sending IOCTL_PTRREQ cmd %d, pkt_id %d xt_id %d\n", + ioct_rqst->ioct_hdr.cmd, ioct_rqst->ioct_hdr.pkt_id, ioct_rqst->ioct_hdr.xt_id)); + + /* Ret buf ptr */ + ioct_rqst->ret_buf.high_addr = htol32(PHYSADDRHI(prot->retbuf_phys)); + ioct_rqst->ret_buf.low_addr = htol32(PHYSADDRLO(prot->retbuf_phys)); + + /* copy ioct payload */ + ioct_buf = (cmn_msg_hdr_t *) prot->ioctbuf; + ioct_buf->msglen = htol16(alignlen + sizeof(cmn_msg_hdr_t)); + ioct_buf->msgtype = MSG_TYPE_IOCT_PYLD; + + if (buf) { + memcpy(&ioct_buf[1], buf, rqstlen); + OSL_CACHE_FLUSH((void *) prot->ioctbuf, rqstlen+sizeof(cmn_msg_hdr_t)); + } + + if ((ulong)ioct_buf % 4) + printf("host ioct address unaligned !!!!! \n"); + + /* populate ioctl buffer info */ + ioct_rqst->ioct_hdr.buflen = htol16(alignlen + sizeof(cmn_msg_hdr_t)); + ioct_rqst->ioct_buf.high_addr = htol32(PHYSADDRHI(prot->ioctbuf_phys)); + ioct_rqst->ioct_buf.low_addr = htol32(PHYSADDRLO(prot->ioctbuf_phys)); + + /* upd wrt ptr and raise interrupt */ + circularbuf_write_complete(htod_msgbuf, msglen); + + dhd_os_spin_unlock(dhd, flags); + + return 0; +} + +/* Packet to PacketID mapper */ +typedef struct { + ulong native; + dmaaddr_t pa; + uint32 pa_len; + uchar dma; +} pktid_t; + +typedef struct { + void *osh; + void *mwbmap_hdl; + pktid_t *pktid_list; + uint32 count; +} pktid_map_t; + + +void *pktid_map_init(void *osh, uint32 count) +{ + pktid_map_t *handle; + + handle = (pktid_map_t *) MALLOC(osh, sizeof(pktid_map_t)); + if (handle == NULL) { + printf("%s:%d: MALLOC failed for size %d\n", + __FUNCTION__, __LINE__, (uint32) sizeof(pktid_map_t)); + return NULL; + } + handle->osh = osh; + handle->count = count; + handle->mwbmap_hdl = bcm_mwbmap_init(osh, count); + if (handle->mwbmap_hdl == NULL) { + printf("%s:%d: bcm_mwbmap_init failed for count %d\n", + __FUNCTION__, __LINE__, count); + MFREE(osh, handle, sizeof(pktid_map_t)); + return NULL; + } + + handle->pktid_list = (pktid_t *) MALLOC(osh, sizeof(pktid_t) * (count+1)); + if (handle->pktid_list == NULL) { + printf("%s:%d: MALLOC failed for count %d / total = %d\n", + __FUNCTION__, __LINE__, count, (uint32) sizeof(pktid_t) * count); + bcm_mwbmap_fini(osh, handle->mwbmap_hdl); + MFREE(osh, handle, sizeof(pktid_map_t)); + return NULL; + } + + return handle; +} + +void +pktid_map_uninit(void *pktid_map_handle) +{ + pktid_map_t *handle = (pktid_map_t *) pktid_map_handle; + uint32 ix; + + if (handle != NULL) { + void *osh = handle->osh; + for (ix = 0; ix < MAX_PKTID_ITEMS; ix++) + { + if (!bcm_mwbmap_isfree(handle->mwbmap_hdl, ix)) { + /* Mark the slot as free */ + bcm_mwbmap_free(handle->mwbmap_hdl, ix); + /* + Here we can do dma unmapping for 32 bit also. + Since this in removal path, it will not affect performance + */ + DMA_UNMAP(osh, (uint) handle->pktid_list[ix+1].pa, + (uint) handle->pktid_list[ix+1].pa_len, + handle->pktid_list[ix+1].dma, 0, 0); + PKTFREE(osh, + (unsigned long*)handle->pktid_list[ix+1].native, TRUE); + } + } + bcm_mwbmap_fini(osh, handle->mwbmap_hdl); + MFREE(osh, handle->pktid_list, sizeof(pktid_t) * (handle->count+1)); + MFREE(osh, handle, sizeof(pktid_map_t)); + } + return; +} + +uint32 BCMFASTPATH +pktid_map_unique(void *pktid_map_handle, void *pkt, dmaaddr_t physaddr, uint32 physlen, uint32 dma) +{ + uint32 id; + pktid_map_t *handle = (pktid_map_t *) pktid_map_handle; + + if (handle == NULL) { + printf("%s:%d: Error !!! pktid_map_unique called without initing pktid_map\n", + __FUNCTION__, __LINE__); + return 0; + } + id = bcm_mwbmap_alloc(handle->mwbmap_hdl); + if (id == BCM_MWBMAP_INVALID_IDX) { + printf("%s:%d: bcm_mwbmap_alloc failed. Free Count = %d\n", + __FUNCTION__, __LINE__, bcm_mwbmap_free_cnt(handle->mwbmap_hdl)); + return 0; + } + + /* id=0 is invalid as we use this for error checking in the dongle */ + id += 1; + handle->pktid_list[id].native = (ulong) pkt; + handle->pktid_list[id].pa = physaddr; + handle->pktid_list[id].pa_len = (uint32) physlen; + handle->pktid_list[id].dma = dma; + + return id; +} + +void * BCMFASTPATH +pktid_get_packet(void *pktid_map_handle, uint32 id, dmaaddr_t *physaddr, uint32 *physlen) +{ + void *native = NULL; + pktid_map_t *handle = (pktid_map_t *) pktid_map_handle; + if (handle == NULL) { + printf("%s:%d: Error !!! pktid_get_packet called without initing pktid_map\n", + __FUNCTION__, __LINE__); + return NULL; + } + + /* Debug check */ + if (bcm_mwbmap_isfree(handle->mwbmap_hdl, (id-1))) { + printf("%s:%d: Error !!!. How can the slot (%d) be free if the app is using it.\n", + __FUNCTION__, __LINE__, (id-1)); + return NULL; + } + + native = (void *) handle->pktid_list[id].native; + *physaddr = handle->pktid_list[id].pa; + *physlen = (uint32) handle->pktid_list[id].pa_len; + + /* Mark the slot as free */ + bcm_mwbmap_free(handle->mwbmap_hdl, (id-1)); + + return native; +} |