| /* |
| * Copyright (c) 2012-2015,2017 The Linux Foundation. All rights reserved. |
| * |
| * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| * |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /* |
| * This file was originally distributed by Qualcomm Atheros, Inc. |
| * under proprietary terms before Copyright ownership was assigned |
| * to the Linux Foundation. |
| */ |
| |
| /*=========================================================================== |
| |
| W L A N _ Q C T _ C T S . C |
| |
| OVERVIEW: |
| |
| This software unit holds the implementation of the WLAN Control Transport |
| Service Layer. |
| |
| The functions externalized by this module are to be called by the DAL Core |
| that wishes to use a platform agnostic API to communicate with the WLAN SS. |
| |
| DEPENDENCIES: |
| |
| Are listed for each API below. |
| |
| |
| ===========================================================================*/ |
| |
| /*=========================================================================== |
| |
| EDIT HISTORY FOR FILE |
| |
| |
| This section contains comments describing changes made to the module. |
| Notice that changes are listed in reverse chronological order. |
| |
| |
| $Header$$DateTime$$Author$ |
| |
| |
| when who what, where, why |
| ---------- --- -------------------------------------------------------- |
| 2011-02-28 jtj Linux/Android implementation which utilizes SMD |
| 2010-08-09 mss Created module |
| |
| ===========================================================================*/ |
| |
| |
| |
| /*=========================================================================== |
| |
| INCLUDE FILES FOR MODULE |
| |
| ===========================================================================*/ |
| |
| /*---------------------------------------------------------------------------- |
| * Include Files |
| * -------------------------------------------------------------------------*/ |
| #include "wlan_qct_wdi_cts.h" |
| #include "wlan_qct_pal_msg.h" |
| #include "wlan_qct_pal_api.h" |
| #include "wlan_qct_pal_trace.h" |
| #include "wlan_qct_os_list.h" |
| #include "wlan_qct_wdi.h" |
| #include "wlan_qct_wdi_i.h" |
| #ifdef CONFIG_ANDROID |
| #ifdef EXISTS_MSM_SMD |
| #include <mach/msm_smd.h> |
| #else |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)) |
| #include <soc/qcom/smd.h> |
| #endif |
| #endif |
| #include <linux/delay.h> |
| #else |
| #include "msm_smd.h" |
| #endif |
| #include "vos_api.h" |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) |
| #include <linux/wcnss_wlan.h> |
| #endif |
| |
| /* Global context for CTS handle, it is required to keep this |
| * transport open during SSR shutdown */ |
| static WCTS_HandleType gwctsHandle; |
| /*---------------------------------------------------------------------------- |
| * Preprocessor Definitions and Constants |
| * -------------------------------------------------------------------------*/ |
| |
| /* Magic value to validate a WCTS CB (value is little endian ASCII: WCTS */ |
| #define WCTS_CB_MAGIC 0x53544357 |
| |
| /* time to wait for SMD channel to close (in msecs) */ |
| #define WCTS_SMD_CLOSE_TIMEOUT 5000 |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) |
| #define smd_disable_read_intr(a) |
| #define smd_enable_read_intr(a) |
| #endif |
| |
| /* Global Variable for WDI SMD stats */ |
| static struct WdiSmdStats gWdiSmdStats; |
| /*---------------------------------------------------------------------------- |
| * Type Declarations |
| * -------------------------------------------------------------------------*/ |
| |
| /*--------------------------------------------------------------------------- |
| WDI CTS Buffer Type |
| ---------------------------------------------------------------------------*/ |
| typedef struct |
| { |
| /*Node for linking pending buffers into a linked list */ |
| wpt_list_node node; |
| |
| /*Buffer associated with the request */ |
| void* pBuffer; |
| |
| /*Buffer Size*/ |
| int bufferSize; |
| |
| } WCTS_BufferType; |
| |
| /*---------------------------------------------------------------------------- |
| * Global Data Definitions |
| * -------------------------------------------------------------------------*/ |
| |
| /*---------------------------------------------------------------------------- |
| * Static Variable Definitions |
| * -------------------------------------------------------------------------*/ |
| |
| /*---------------------------------------------------------------------------- |
| * Static Function Declarations and Definitions |
| * -------------------------------------------------------------------------*/ |
| |
| |
| |
| /** |
| @brief Callback function for serializing WCTS Open |
| processing in the control context |
| @param |
| |
| pMsg - pointer to the message |
| |
| @see |
| @return void |
| */ |
| static void |
| WCTS_PALOpenCallback |
| ( |
| wpt_msg *pMsg |
| ) |
| { |
| WCTS_ControlBlockType* pWCTSCb; |
| /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ |
| |
| /* extract our context from the message */ |
| pWCTSCb = pMsg->pContext; |
| |
| /*-------------------------------------------------------------------- |
| Sanity check |
| --------------------------------------------------------------------*/ |
| if ((NULL == pWCTSCb) || (WCTS_CB_MAGIC != pWCTSCb->wctsMagic)) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_PALOpenCallback: Invalid parameters received."); |
| return; |
| } |
| |
| if (WCTS_STATE_OPEN_PENDING != pWCTSCb->wctsState) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_PALOpenCallback: Invoke from invalid state %d.", |
| pWCTSCb->wctsState); |
| return; |
| } |
| |
| /* notified registered client that the channel is open */ |
| pWCTSCb->wctsState = WCTS_STATE_OPEN; |
| pWCTSCb->wctsNotifyCB((WCTS_HandleType)pWCTSCb, |
| WCTS_EVENT_OPEN, |
| pWCTSCb->wctsNotifyCBData); |
| |
| /* signal event for WCTS_OpenTransport to proceed */ |
| wpalEventSet(&pWCTSCb->wctsEvent); |
| |
| }/*WCTS_PALOpenCallback*/ |
| |
| |
| |
| /** |
| @brief Callback function for serializing WCTS Read |
| processing in the control context |
| |
| @param pWCTSCb WCTS Control Block |
| |
| @see |
| @return void |
| */ |
| static void |
| WCTS_PALReadCallback |
| ( |
| WCTS_ControlBlockType* pWCTSCb |
| ) |
| { |
| void* buffer; |
| int packet_size; |
| int bytes_read; |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) |
| struct data_msg *msg; |
| #else |
| int available; |
| #endif |
| |
| /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ |
| |
| /*-------------------------------------------------------------------- |
| Sanity check |
| --------------------------------------------------------------------*/ |
| if ((NULL == pWCTSCb) || (WCTS_CB_MAGIC != pWCTSCb->wctsMagic)) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_PALReadCallback: Invalid parameter received."); |
| return; |
| } |
| |
| /* iterate until no more packets are available */ |
| while (1) { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) |
| spin_lock_bh(&pWCTSCb->wctsDataMsg.data_queue_lock); |
| if (list_empty(&pWCTSCb->wctsDataMsg.data_queue)) { |
| spin_unlock_bh(&pWCTSCb->wctsDataMsg.data_queue_lock); |
| return; |
| } |
| |
| msg = list_first_entry(&pWCTSCb->wctsDataMsg.data_queue, |
| struct data_msg, list); |
| list_del(&msg->list); |
| spin_unlock_bh(&pWCTSCb->wctsDataMsg.data_queue_lock); |
| |
| buffer = msg->buffer; |
| packet_size = msg->buf_len; |
| bytes_read = msg->buf_len; |
| wpalMemoryFree(msg); |
| #else |
| /* check the length of the next packet */ |
| packet_size = smd_cur_packet_size(pWCTSCb->wctsChannel); |
| if (0 == packet_size) { |
| /* No more data to be read */ |
| return; |
| } |
| |
| /* Check how much of the data is available */ |
| available = smd_read_avail(pWCTSCb->wctsChannel); |
| if (available < packet_size) { |
| /* Entire packet not yet ready to be read -- |
| There will be another notification when it is ready */ |
| return; |
| } |
| |
| buffer = wpalMemoryAllocate(packet_size); |
| if (NULL == buffer) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_PALReadCallback: Memory allocation failure"); |
| WPAL_ASSERT(0); |
| return; |
| } |
| |
| bytes_read = smd_read(pWCTSCb->wctsChannel, |
| buffer, |
| packet_size); |
| |
| if (bytes_read != packet_size) { |
| /*Some problem, do not forward it to WDI.*/ |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_PALReadCallback: Failed to read data from SMD"); |
| wpalMemoryFree(buffer); |
| WPAL_ASSERT(0); |
| return; |
| } |
| #endif |
| |
| /* forward the message to the registered handler */ |
| pWCTSCb->wctsRxMsgCB((WCTS_HandleType)pWCTSCb, |
| buffer, |
| packet_size, |
| pWCTSCb->wctsRxMsgCBData); |
| |
| /* Free the allocated buffer*/ |
| wpalMemoryZero(buffer, bytes_read); |
| wpalMemoryFree(buffer); |
| } |
| |
| } /*WCTS_PALReadCallback*/ |
| |
| |
| |
| /** |
| @brief Callback function for serializing WCTS Write |
| processing in the control context |
| |
| @param pWCTSCb WCTS Control Block |
| |
| @see |
| @return void |
| */ |
| static void |
| WCTS_PALWriteCallback |
| ( |
| WCTS_ControlBlockType* pWCTSCb |
| ) |
| { |
| wpt_list_node* pNode; |
| WCTS_BufferType* pBufferQueue; |
| void* pBuffer; |
| int len; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)) |
| int available; |
| int written; |
| #endif |
| |
| /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ |
| |
| /*-------------------------------------------------------------------- |
| Sanity check |
| --------------------------------------------------------------------*/ |
| if ((NULL == pWCTSCb) || (WCTS_CB_MAGIC != pWCTSCb->wctsMagic)) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_PALWriteCallback: Invalid parameter received."); |
| return; |
| } |
| |
| /* if we are not deferred, then there are no pending packets */ |
| if (WCTS_STATE_DEFERRED != pWCTSCb->wctsState) { |
| return; |
| } |
| |
| /* Keep sending deferred messages as long as there is room in |
| the channel. Note that we initially peek at the head of the |
| list to access the parameters for the next message; we don't |
| actually remove the next message from the deferred list until |
| we know the channel can handle it */ |
| while (eWLAN_PAL_STATUS_SUCCESS == |
| wpal_list_peek_front(&pWCTSCb->wctsPendingQueue, &pNode)) { |
| pBufferQueue = container_of(pNode, WCTS_BufferType, node); |
| pBuffer = pBufferQueue->pBuffer; |
| len = pBufferQueue->bufferSize; |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) |
| if (wcnss_smd_tx(pWCTSCb->wctsChannel, pBuffer, len)) |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_PALWriteCallback: channel write failure"); |
| #else |
| available = smd_write_avail(pWCTSCb->wctsChannel); |
| if (available < len) { |
| /* channel has no room for the next packet so we are done */ |
| return; |
| } |
| #endif |
| |
| /* there is room for the next message, so we can now remove |
| it from the deferred message queue and send it */ |
| wpal_list_remove_front(&pWCTSCb->wctsPendingQueue, &pNode); |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)) |
| /* note that pNode will be the same as when we peeked, so |
| there is no need to update pBuffer or len */ |
| |
| written = smd_write(pWCTSCb->wctsChannel, pBuffer, len); |
| if (written != len) { |
| /* Something went wrong */ |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_PALWriteCallback: channel write failure"); |
| |
| /* we were unable to send the message that was at the head |
| of the deferred list. there is nothing else we can do |
| other than drop it, so we will just fall through to the |
| "success" processing. |
| hopefully the client can recover from this since there is |
| nothing else we can do here */ |
| } |
| #endif |
| |
| /* whether we had success or failure, reclaim all memory */ |
| wpalMemoryZero(pBuffer, len); |
| wpalMemoryFree(pBuffer); |
| wpalMemoryFree(pBufferQueue); |
| |
| /* we'll continue to iterate until the channel is full or all |
| of the deferred messages have been sent */ |
| } |
| |
| /* if we've exited the loop, then we have drained the deferred queue. |
| set the state to indicate we are no longer deferred, and turn off |
| the remote read interrupt */ |
| pWCTSCb->wctsState = WCTS_STATE_OPEN; |
| smd_disable_read_intr(pWCTSCb->wctsChannel); |
| |
| } /*WCTS_PALWriteCallback*/ |
| |
| |
| |
| /** |
| @brief Callback function for serializing SMD DATA Event |
| processing in the control context |
| @param |
| |
| pMsg - pointer to the message |
| |
| @see |
| @return void |
| */ |
| static void |
| WCTS_PALDataCallback |
| ( |
| wpt_msg *pMsg |
| ) |
| { |
| WCTS_ControlBlockType* pWCTSCb; |
| /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ |
| |
| /* extract our context from the message */ |
| pWCTSCb = pMsg->pContext; |
| |
| /* process any incoming messages */ |
| WCTS_PALReadCallback(pWCTSCb); |
| |
| /* send any deferred messages */ |
| WCTS_PALWriteCallback(pWCTSCb); |
| |
| } /*WCTS_PALDataCallback*/ |
| |
| /** |
| @brief This helper function is used to clean up the pending |
| messages in the transport queue |
| |
| @param wctsHandlehandle: transport handle |
| |
| @see |
| @return 0 for success |
| */ |
| wpt_uint32 |
| WCTS_ClearPendingQueue |
| ( |
| WCTS_HandleType wctsHandle |
| ) |
| { |
| WCTS_ControlBlockType* pWCTSCb = (WCTS_ControlBlockType*) wctsHandle; |
| wpt_list_node* pNode = NULL; |
| WCTS_BufferType* pBufferQueue = NULL; |
| void* pBuffer = NULL; |
| |
| /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ |
| |
| if ((NULL == pWCTSCb) || (WCTS_CB_MAGIC != pWCTSCb->wctsMagic)) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_ClearPendingQueue: Invalid parameters received."); |
| return eWLAN_PAL_STATUS_E_INVAL; |
| } |
| |
| /*Free the buffers in the pending queue.*/ |
| while (eWLAN_PAL_STATUS_SUCCESS == |
| wpal_list_remove_front(&pWCTSCb->wctsPendingQueue, &pNode)) { |
| pBufferQueue = container_of(pNode, WCTS_BufferType, node); |
| pBuffer = pBufferQueue->pBuffer; |
| wpalMemoryFree(pBuffer); |
| wpalMemoryFree(pBufferQueue); |
| } |
| return eWLAN_PAL_STATUS_SUCCESS; |
| |
| }/*WCTS_ClearPendingQueue*/ |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) |
| int WCTS_smd_resp_process(struct rpmsg_device *rpdev, |
| void *data, int len, void *priv, u32 addr) |
| { |
| WCTS_ControlBlockType* wcts_cb = (WCTS_ControlBlockType*) priv; |
| struct data_msg *msg; |
| |
| gWdiSmdStats.smd_event_data++; |
| |
| msg = wpalMemoryAllocate(sizeof(*msg)); |
| if (!msg) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_smd_resp_process: Memory allocation failed"); |
| return 0; |
| } |
| |
| msg->buf_len = len; |
| msg->buffer = wpalMemoryAllocate(len); |
| if (!msg->buffer) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_smd_resp_process: Memory allocation failure"); |
| WPAL_ASSERT(0); |
| return 0; |
| } |
| |
| vos_mem_copy(msg->buffer, data, msg->buf_len); |
| |
| spin_lock_bh(&wcts_cb->wctsDataMsg.data_queue_lock); |
| list_add_tail(&msg->list, &wcts_cb->wctsDataMsg.data_queue); |
| spin_unlock_bh(&wcts_cb->wctsDataMsg.data_queue_lock); |
| |
| wpalPostCtrlMsg(WDI_GET_PAL_CTX(), &wcts_cb->wctsDataMsg); |
| |
| return 0; |
| } |
| |
| int WCTS_driver_state_process(void *priv, enum wcnss_driver_state state) |
| { |
| WCTS_ControlBlockType* wcts_cb = (WCTS_ControlBlockType*) priv; |
| wpt_msg *pal_msg; |
| |
| switch (state) { |
| case WCNSS_SMD_OPEN: |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, |
| "%s: received WCNSS_SMD_OPEN from SMD", __func__); |
| /* If the prev state was 'remote closed' then it is a Riva 'restart', |
| * subsystem restart re-init |
| */ |
| if (WCTS_STATE_REM_CLOSED == wcts_cb->wctsState) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, |
| "%s: received WCNSS_SMD_OPEN in WCTS_STATE_REM_CLOSED state", |
| __func__); |
| /* call subsystem restart re-init function */ |
| wpalDriverReInit(); |
| return 0; |
| } |
| gWdiSmdStats.smd_event_open++; |
| pal_msg = &wcts_cb->wctsOpenMsg; |
| break; |
| case WCNSS_SMD_CLOSE: |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, |
| "%s: received WCNSS_SMD_CLOSE from SMD", __func__); |
| /* SMD channel was closed from the remote side, |
| * this would happen only when Riva crashed and SMD is |
| * closing the channel on behalf of Riva */ |
| vos_spin_lock_acquire(&wcts_cb->wctsStateLock); |
| wcts_cb->wctsState = WCTS_STATE_REM_CLOSED; |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, |
| "%s: received WCNSS_SMD_CLOSE WLAN driver going down now", |
| __func__); |
| vos_spin_lock_release(&wcts_cb->wctsStateLock); |
| |
| /* subsystem restart: shutdown */ |
| wpalDriverShutdown(); |
| gWdiSmdStats.smd_event_close++; |
| return 0; |
| } |
| |
| /* serialize this event */ |
| wpalPostCtrlMsg(WDI_GET_PAL_CTX(), pal_msg); |
| return 0; |
| } |
| #else |
| void |
| WCTS_NotifyCallback |
| ( |
| void *data, |
| unsigned event |
| ) |
| { |
| wpt_msg *palMsg; |
| WCTS_ControlBlockType* pWCTSCb = (WCTS_ControlBlockType*) data; |
| |
| /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ |
| |
| /*-------------------------------------------------------------------- |
| Sanity check |
| --------------------------------------------------------------------*/ |
| if (WCTS_CB_MAGIC != pWCTSCb->wctsMagic) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "%s: Received unexpected SMD event %u", |
| __func__, event); |
| |
| /* TODO_PRIMA what error recovery options do we have? */ |
| return; |
| } |
| |
| /* Serialize processing in the control thread */ |
| switch (event) { |
| case SMD_EVENT_OPEN: |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, |
| "%s: received SMD_EVENT_OPEN from SMD", __func__); |
| /* If the prev state was 'remote closed' then it is a Riva 'restart', |
| * subsystem restart re-init |
| */ |
| if (WCTS_STATE_REM_CLOSED == pWCTSCb->wctsState) |
| { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, |
| "%s: received SMD_EVENT_OPEN in WCTS_STATE_REM_CLOSED state", |
| __func__); |
| /* call subsystem restart re-init function */ |
| wpalDriverReInit(); |
| return; |
| } |
| gWdiSmdStats.smd_event_open++; |
| palMsg = &pWCTSCb->wctsOpenMsg; |
| break; |
| |
| case SMD_EVENT_DATA: |
| if (WCTS_STATE_REM_CLOSED == pWCTSCb->wctsState) |
| { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "%s: received SMD data when the state is remote closed ", |
| __func__); |
| /* we should not be getting any data now */ |
| return; |
| } |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, |
| "%s: received SMD_EVENT_DATA from SMD", __func__); |
| palMsg = &pWCTSCb->wctsDataMsg; |
| gWdiSmdStats.smd_event_data++; |
| break; |
| |
| case SMD_EVENT_CLOSE: |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, |
| "%s: received SMD_EVENT_CLOSE from SMD", __func__); |
| /* SMD channel was closed from the remote side, |
| * this would happen only when Riva crashed and SMD is |
| * closing the channel on behalf of Riva */ |
| vos_spin_lock_acquire(&pWCTSCb->wctsStateLock); |
| pWCTSCb->wctsState = WCTS_STATE_REM_CLOSED; |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, |
| "%s: received SMD_EVENT_CLOSE WLAN driver going down now", |
| __func__); |
| vos_spin_lock_release(&pWCTSCb->wctsStateLock); |
| |
| /* subsystem restart: shutdown */ |
| wpalDriverShutdown(); |
| gWdiSmdStats.smd_event_close++; |
| return; |
| |
| case SMD_EVENT_STATUS: |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, |
| "%s: received SMD_EVENT_STATUS from SMD", __func__); |
| gWdiSmdStats.smd_event_status++; |
| return; |
| |
| case SMD_EVENT_REOPEN_READY: |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, |
| "%s: received SMD_EVENT_REOPEN_READY from SMD", __func__); |
| |
| /* unlike other events which occur when our kernel threads are |
| running, this one is received when the threads are closed and |
| the rmmod thread is waiting. so just unblock that thread */ |
| wpalEventSet(&pWCTSCb->wctsEvent); |
| gWdiSmdStats.smd_event_reopen_ready++; |
| return; |
| |
| default: |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "%s: Unexpected event %u received from SMD", |
| __func__, event); |
| gWdiSmdStats.smd_event_err++; |
| |
| return; |
| } |
| |
| /* serialize this event */ |
| wpalPostCtrlMsg(WDI_GET_PAL_CTX(), palMsg); |
| |
| } /*WCTS_NotifyCallback*/ |
| #endif |
| |
| /*---------------------------------------------------------------------------- |
| * Externalized Function Definitions |
| * -------------------------------------------------------------------------*/ |
| |
| |
| /** |
| @brief This function is used by the DAL Core to initialize the Control |
| Transport for processing. It must be called prior to calling any |
| other APIs of the Control Transport. |
| |
| |
| @param szName: unique name for the channel that is to be opened |
| uSize: size of the channel that must be opened (should fit the |
| largest size of packet that the Dal Core wishes to send) |
| wctsCBs: a list of callbacks that the CT needs to use to send |
| notification and messages back to DAL |
| |
| @see |
| @return A handle that must be used for further communication with the CTS. |
| This is an opaque structure for the caller and it will be used in |
| all communications to and from the CTS. |
| |
| */ |
| WCTS_HandleType |
| WCTS_OpenTransport |
| ( |
| const wpt_uint8* szName, |
| wpt_uint32 uSize, |
| WCTS_TransportCBsType* wctsCBs |
| ) |
| { |
| WCTS_ControlBlockType* pWCTSCb; |
| int smdstatus; |
| |
| /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ |
| |
| /*--------------------------------------------------------------------- |
| Sanity check |
| ---------------------------------------------------------------------*/ |
| if ((NULL == wctsCBs) || (NULL == szName) || |
| (NULL == wctsCBs->wctsNotifyCB) || (NULL == wctsCBs->wctsRxMsgCB)) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_OpenTransport: Invalid parameters received."); |
| |
| return NULL; |
| } |
| |
| /* This open is coming after a SSR, we don't need to reopen SMD, |
| * the SMD port was never closed during SSR*/ |
| if (gwctsHandle) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, |
| "WCTS_OpenTransport port is already open"); |
| |
| pWCTSCb = gwctsHandle; |
| if (WCTS_CB_MAGIC != pWCTSCb->wctsMagic) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_FATAL, |
| "WCTS_OpenTransport: Invalid magic."); |
| return NULL; |
| } |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) |
| /* Need to open smd channel in case of SSR in rpmsg */ |
| smdstatus = vos_smd_open(szName, pWCTSCb); |
| #endif |
| pWCTSCb->wctsState = WCTS_STATE_OPEN; |
| |
| pWCTSCb->wctsNotifyCB((WCTS_HandleType)pWCTSCb, |
| WCTS_EVENT_OPEN, |
| pWCTSCb->wctsNotifyCBData); |
| |
| /* we initially don't want read interrupts |
| (we only want them if we get into deferred write mode) */ |
| smd_disable_read_intr(pWCTSCb->wctsChannel); |
| |
| return (WCTS_HandleType)pWCTSCb; |
| } |
| |
| /* allocate a ControlBlock to hold all context */ |
| pWCTSCb = wpalMemoryAllocate(sizeof(*pWCTSCb)); |
| if (NULL == pWCTSCb) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_OpenTransport: Memory allocation failure."); |
| return NULL; |
| } |
| |
| /* make sure the control block is initialized. in particular we need |
| to make sure the embedded event and list structures are initialized |
| to prevent "magic number" tests from being run against uninitialized |
| values */ |
| wpalMemoryZero(pWCTSCb, sizeof(*pWCTSCb)); |
| |
| /*Initialise the event*/ |
| wpalEventInit(&pWCTSCb->wctsEvent); |
| |
| /* save the user-supplied information */ |
| pWCTSCb->wctsNotifyCB = wctsCBs->wctsNotifyCB; |
| pWCTSCb->wctsNotifyCBData = wctsCBs->wctsNotifyCBData; |
| pWCTSCb->wctsRxMsgCB = wctsCBs->wctsRxMsgCB; |
| pWCTSCb->wctsRxMsgCBData = wctsCBs->wctsRxMsgCBData; |
| |
| /* initialize the remaining fields */ |
| wpal_list_init(&pWCTSCb->wctsPendingQueue); |
| pWCTSCb->wctsMagic = WCTS_CB_MAGIC; |
| vos_spin_lock_init(&pWCTSCb->wctsStateLock); |
| pWCTSCb->wctsState = WCTS_STATE_OPEN_PENDING; |
| pWCTSCb->wctsChannel = NULL; |
| |
| /* since SMD will callback in interrupt context, we will used |
| * canned messages to serialize the SMD events into a thread |
| * context |
| */ |
| pWCTSCb->wctsOpenMsg.callback = WCTS_PALOpenCallback; |
| pWCTSCb->wctsOpenMsg.pContext = pWCTSCb; |
| pWCTSCb->wctsOpenMsg.type= WPAL_MC_MSG_SMD_NOTIF_OPEN_SIG; |
| |
| pWCTSCb->wctsDataMsg.callback = WCTS_PALDataCallback; |
| pWCTSCb->wctsDataMsg.pContext = pWCTSCb; |
| pWCTSCb->wctsDataMsg.type = WPAL_MC_MSG_SMD_NOTIF_DATA_SIG; |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) |
| INIT_LIST_HEAD(&pWCTSCb->wctsDataMsg.data_queue); |
| spin_lock_init(&pWCTSCb->wctsDataMsg.data_queue_lock); |
| #endif |
| |
| /*--------------------------------------------------------------------- |
| Open the SMD channel |
| ---------------------------------------------------------------------*/ |
| smdstatus = vos_smd_open(szName, pWCTSCb); |
| if (0 != smdstatus) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "%s: smd_named_open_on_edge failed with status %d", |
| __func__, smdstatus); |
| goto fail; |
| } |
| |
| /* we initially don't want read interrupts |
| (we only want them if we get into deferred write mode) */ |
| smd_disable_read_intr(pWCTSCb->wctsChannel); |
| |
| /* we have successfully opened the SMD channel */ |
| gwctsHandle = pWCTSCb; |
| return (WCTS_HandleType)pWCTSCb; |
| |
| fail: |
| /* we were unable to open the SMD channel */ |
| pWCTSCb->wctsMagic = 0; |
| wpalMemoryFree(pWCTSCb); |
| return NULL; |
| |
| }/*WCTS_OpenTransport*/ |
| |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) |
| void wcts_close_channel(WCTS_HandleType wcts_handle) |
| { |
| WCTS_ControlBlockType* wcts_cb = (WCTS_ControlBlockType*) wcts_handle; |
| |
| wcnss_close_channel(wcts_cb->wctsChannel); |
| } |
| #endif |
| /** |
| @brief This function is used by the DAL Core to close the |
| Control Transport when its services are no longer |
| needed. Full close notification will be receive |
| asynchronously on the notification callback |
| registered on Open |
| |
| |
| @param wctsHandlehandle: received upon open |
| |
| @see |
| @return 0 for success |
| */ |
| wpt_uint32 |
| WCTS_CloseTransport |
| ( |
| WCTS_HandleType wctsHandle |
| ) |
| { |
| WCTS_ControlBlockType* pWCTSCb = (WCTS_ControlBlockType*) wctsHandle; |
| wpt_list_node* pNode = NULL; |
| WCTS_BufferType* pBufferQueue = NULL; |
| void* pBuffer = NULL; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)) |
| wpt_status status; |
| int smdstatus; |
| #endif |
| |
| /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ |
| |
| if ((NULL == pWCTSCb) || (WCTS_CB_MAGIC != pWCTSCb->wctsMagic)) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_CloseTransport: Invalid parameters received."); |
| return eWLAN_PAL_STATUS_E_INVAL; |
| } |
| |
| /*Free the buffers in the pending queue.*/ |
| while (eWLAN_PAL_STATUS_SUCCESS == |
| wpal_list_remove_front(&pWCTSCb->wctsPendingQueue, &pNode)) { |
| pBufferQueue = container_of(pNode, WCTS_BufferType, node); |
| pBuffer = pBufferQueue->pBuffer; |
| wpalMemoryFree(pBuffer); |
| wpalMemoryFree(pBufferQueue); |
| } |
| |
| /* Reset the state */ |
| pWCTSCb->wctsState = WCTS_STATE_CLOSED; |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)) |
| wpalEventReset(&pWCTSCb->wctsEvent); |
| smdstatus = smd_close(pWCTSCb->wctsChannel); |
| if (0 != smdstatus) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "%s: smd_close failed with status %d", |
| __func__, smdstatus); |
| /* SMD did not successfully close the channel, therefore we |
| won't receive an asynchronous close notification so don't |
| bother to wait for an event that won't come */ |
| |
| } else { |
| /* close command was sent -- wait for the callback to complete */ |
| status = wpalEventWait(&pWCTSCb->wctsEvent, WCTS_SMD_CLOSE_TIMEOUT); |
| if (eWLAN_PAL_STATUS_SUCCESS != status) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "%s: failed to receive SMD_EVENT_REOPEN_READY", |
| __func__); |
| } |
| |
| /* During the close sequence we deregistered from SMD. As part |
| of deregistration SMD will call back into our driver with an |
| event to let us know the channel is closed. We need to |
| insert a brief delay to allow that thread of execution to |
| exit our module. Otherwise our module may be unloaded while |
| there is still code running within the address space, and |
| that code will crash when the memory is unmapped */ |
| msleep(50); |
| } |
| #else |
| wcts_close_channel(wctsHandle); |
| wlan_unregister_driver(); |
| #endif |
| |
| /* channel has (hopefully) been closed */ |
| pWCTSCb->wctsNotifyCB((WCTS_HandleType)pWCTSCb, |
| WCTS_EVENT_CLOSE, |
| pWCTSCb->wctsNotifyCBData); |
| vos_spin_lock_destroy(&pWCTSCb->wctsStateLock); |
| /* release the resource */ |
| pWCTSCb->wctsMagic = 0; |
| wpalMemoryFree(pWCTSCb); |
| gwctsHandle = NULL; |
| |
| return eWLAN_PAL_STATUS_SUCCESS; |
| |
| }/*WCTS_CloseTransport*/ |
| |
| |
| |
| /** |
| @brief This function is used by the DAL Core to to send a |
| message over to the WLAN sub-system. |
| |
| Once a buffer has been passed into the Send Message |
| API, CT takes full ownership of it and it is responsible for |
| freeing the associated resources. (This prevents a memcpy in |
| case of a deferred write) |
| |
| The messages transported through the CT on both RX and TX are |
| flat memory buffers that can be accessed and manipulated |
| through standard memory functions. |
| |
| @param wctsHandlehandle: received upon open |
| pMsg: the message to be sent |
| uLen: the length of the message |
| |
| @see |
| @return 0 for success |
| */ |
| wpt_uint32 |
| WCTS_SendMessage |
| ( |
| WCTS_HandleType wctsHandle, |
| void* pMsg, |
| wpt_uint32 uLen |
| ) |
| { |
| WCTS_ControlBlockType* pWCTSCb = (WCTS_ControlBlockType*) wctsHandle; |
| WCTS_BufferType* pBufferQueue; |
| int len; |
| int written = 0; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)) |
| int available; |
| #endif |
| |
| /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ |
| |
| /*-------------------------------------------------------------------- |
| Sanity check |
| --------------------------------------------------------------------*/ |
| if ((NULL == pWCTSCb) || (WCTS_CB_MAGIC != pWCTSCb->wctsMagic) || |
| (NULL == pMsg) || (0 == uLen) || (0x7fffffff < uLen)) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_SendMessage: Invalid parameters received."); |
| WPAL_ASSERT(0); |
| if (NULL != pMsg) { |
| wpalMemoryFree(pMsg); |
| } |
| return eWLAN_PAL_STATUS_E_INVAL; |
| } |
| |
| /* the SMD API uses int instead of uint, so change types here */ |
| len = (int)uLen; |
| |
| if (WCTS_STATE_OPEN == pWCTSCb->wctsState) { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) |
| if (wcnss_smd_tx(pWCTSCb->wctsChannel, pMsg, len)) |
| written = -1; |
| else |
| written = len; |
| #else |
| available = smd_write_avail(pWCTSCb->wctsChannel); |
| if (available >= len) { |
| written = smd_write(pWCTSCb->wctsChannel, pMsg, len); |
| } |
| #endif |
| } else if (WCTS_STATE_DEFERRED == pWCTSCb->wctsState) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO, |
| "WCTS_SendMessage: FIFO space not available, the packets will be queued"); |
| } else { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_SendMessage: Channel in illegal state [%d].", |
| pWCTSCb->wctsState); |
| /* force following logic to reclaim the buffer */ |
| written = -1; |
| } |
| |
| if (-1 == written) { |
| /*Something wrong*/ |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_SendMessage: Failed to send message over the bus."); |
| wpalMemoryFree(pMsg); |
| return eWLAN_PAL_STATUS_E_FAILURE; |
| } else if (written == len) { |
| /* Message sent! No deferred state, free the buffer*/ |
| wpalMemoryZero(pMsg, len); |
| wpalMemoryFree(pMsg); |
| } else { |
| /* This much data cannot be written at this time, |
| queue the rest of the data for later*/ |
| pBufferQueue = wpalMemoryAllocate(sizeof(WCTS_BufferType)); |
| if (NULL == pBufferQueue) { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "WCTS_SendMessage: Cannot allocate memory for queuing the buffer"); |
| wpalMemoryFree(pMsg); |
| WPAL_ASSERT(0); |
| return eWLAN_PAL_STATUS_E_NOMEM; |
| } |
| |
| pBufferQueue->bufferSize = len; |
| pBufferQueue->pBuffer = pMsg; |
| |
| if (eWLAN_PAL_STATUS_E_FAILURE == |
| wpal_list_insert_back(&pWCTSCb->wctsPendingQueue, |
| &pBufferQueue->node)) |
| { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "pBufferQueue wpal_list_insert_back failed"); |
| wpalMemoryFree(pMsg); |
| wpalMemoryFree(pBufferQueue); |
| WPAL_ASSERT(0); |
| return eWLAN_PAL_STATUS_E_NOMEM; |
| } |
| |
| /* if we are not already in the deferred state, then transition |
| to that state. when we do so, we enable the remote read |
| interrupt so that we'll be notified when messages are read |
| from the remote end */ |
| vos_spin_lock_acquire(&pWCTSCb->wctsStateLock); |
| if ((WCTS_STATE_DEFERRED != pWCTSCb->wctsState) && |
| (WCTS_STATE_REM_CLOSED != pWCTSCb->wctsState)) { |
| |
| /* Mark the state as deferred.*/ |
| pWCTSCb->wctsState = WCTS_STATE_DEFERRED; |
| |
| smd_enable_read_intr(pWCTSCb->wctsChannel); |
| } |
| vos_spin_lock_release(&pWCTSCb->wctsStateLock); |
| |
| /*indicate to client that message was placed in deferred queue*/ |
| return eWLAN_PAL_STATUS_E_RESOURCES; |
| } |
| |
| return eWLAN_PAL_STATUS_SUCCESS; |
| |
| }/*WCTS_SendMessage*/ |
| |
| void WCTS_Dump_Smd_status(void) |
| { |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "Smd Read Stats: %d", gWdiSmdStats.smd_event_data); |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "Smd Open Stats: %d", gWdiSmdStats.smd_event_open); |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "Smd Close Stats: %d", gWdiSmdStats.smd_event_close); |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "Smd Status Stats: %d", gWdiSmdStats.smd_event_status); |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "Smd Reopen Stats: %d", gWdiSmdStats.smd_event_reopen_ready); |
| WPAL_TRACE(eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_ERROR, |
| "Smd Error Stats: %d", gWdiSmdStats.smd_event_err); |
| } |