| /* |
| * Copyright (c) 2013, 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. |
| */ |
| /**======================================================================== |
| |
| \file htc_smd.c |
| \brief htc smd module implmentation |
| |
| ========================================================================*/ |
| /**========================================================================= |
| 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 |
| -------- --- ----------------------------------------- |
| 01/03/2013 Ganesh HTC SMD module implementation |
| Kondabattini |
| ==========================================================================*/ |
| |
| |
| #include "htc_api.h" |
| #include "htc_smd.h" |
| #include "htc_services.h" |
| #include "vos_memory.h" |
| #include "vos_mq.h" |
| #include "wmi_unified.h" |
| |
| #define HTC_LOGD(args...) \ |
| VOS_TRACE( VOS_MODULE_ID_HTC, VOS_TRACE_LEVEL_INFO, ## args) |
| #define HTC_LOGE(args...) \ |
| VOS_TRACE( VOS_MODULE_ID_HTC, VOS_TRACE_LEVEL_ERROR, ## args) |
| #define HTC_LOGP(args...) \ |
| VOS_TRACE( VOS_MODULE_ID_HTC, VOS_TRACE_LEVEL_FATAL, ## args) |
| |
| /******************************************* |
| ************* Declarations **************** |
| *******************************************/ |
| static tp_htc_handle g_htc_handle; |
| |
| /******************************************* |
| ********** Function Definition************* |
| *******************************************/ |
| |
| /* function: htc_smd_open_callback |
| * Descriptin: |
| * Args: msg |
| * Retruns: nothing |
| */ |
| static void htc_smd_open_callback(t_htc_msg *msg) |
| { |
| tp_htc_handle htc_handle; |
| HTC_LOGD("Enter"); |
| if (NULL == msg) { |
| WARN_ON(1); |
| HTC_LOGE("Invalid parameters received."); |
| return; |
| } |
| /* extract our context from the message */ |
| htc_handle = (tp_htc_handle)msg->pContext; |
| |
| /*-------------------------------------------------------------------- |
| Sanity check |
| --------------------------------------------------------------------*/ |
| if ((NULL == htc_handle) || (HTC_CB_MAGIC != htc_handle->htc_magic)) { |
| HTC_LOGE("Invalid parameters received."); |
| return; |
| } |
| |
| if (HTC_SMD_STATE_OPEN_PENDING != htc_handle->htc_smd_state) { |
| HTC_LOGE("Invoked from invalid state."); |
| return; |
| } |
| |
| /* notified registered client that the channel is open */ |
| htc_handle->htc_smd_state = HTC_SMD_STATE_OPEN; |
| |
| /* signal event for WCTS_OpenTransport to proceed */ |
| complete(&htc_handle->htc_smd_event); |
| } |
| |
| |
| /* function: htc_get_end_point |
| * Descriptin: extracts pointer to endpoint from wmi cmd id |
| * Args: htc_handle, wmi cmd id |
| * Retruns: point to endpoint |
| */ |
| HTC_ENDPOINT* htc_get_end_point(t_htc_handle *htc_handle, HTC_SERVICE_ID id) |
| { |
| /* currently we don't get HTT message from SMD driver. |
| HTT message will be routed by DXE */ |
| if (id >= WMI_EVT_GRP_START_ID(WMI_GRP_START)) { |
| return &htc_handle->end_point[HTC_ENDPOINT_WMI]; |
| } else { |
| return &htc_handle->end_point[HTC_ENDPOINT_INIT]; |
| } |
| } |
| |
| /* function: htc_smd_call_endpoint |
| * Descriptin: |
| * Args: htc_handle, rx_buf |
| * Retruns: nothing |
| */ |
| void htc_smd_call_endpoint(tp_htc_handle htc_handle, adf_nbuf_t rx_buf) |
| { |
| unsigned short cmd_id; |
| HTC_ENDPOINT *endpoint; |
| unsigned char *data = adf_nbuf_data(rx_buf); |
| unsigned int long len = adf_nbuf_len(rx_buf); |
| |
| HTC_LOGD("Enter"); |
| cmd_id = data[1] << 8 | data[0]; |
| endpoint = htc_get_end_point(htc_handle, cmd_id); |
| |
| HTC_LOGD("WMI command id = %hu, endpoint = %p," |
| "endpoint->Id = %d", cmd_id, |
| endpoint, endpoint->Id); |
| |
| if (endpoint && endpoint->EpCallBacks.EpRecv) { |
| vos_mem_zero(&htc_handle->htc_packet, sizeof (HTC_PACKET)); |
| htc_handle->htc_packet.Status = 0; |
| htc_handle->htc_packet.pPktContext = rx_buf; |
| htc_handle->htc_packet.pBuffer = data; |
| htc_handle->htc_packet.ActualLength = len; |
| htc_handle->htc_packet.Endpoint = endpoint->Id; |
| HTC_LOGD("calling endpoint id %d", endpoint->Id); |
| endpoint->EpCallBacks.EpRecv(endpoint->EpCallBacks.pContext, |
| &htc_handle->htc_packet); |
| /*TODO: freee rx_buf ??*/ |
| } else { |
| HTC_LOGE("received unknown message from FW with id = 0x%x\n", cmd_id); |
| adf_nbuf_free(rx_buf); |
| } |
| |
| HTC_LOGD("Exit"); |
| } |
| |
| /* function: htc_smd_read_callback |
| * Descriptin: |
| * Args: htc_handle |
| * Retruns: nothing |
| */ |
| static void htc_smd_read_callback(tp_htc_handle htc_handle) |
| { |
| int packet_size; |
| int available; |
| int bytes_read; |
| adf_nbuf_t rx_buf; |
| void *data; |
| |
| HTC_LOGD("Enter"); |
| |
| if ((NULL == htc_handle) || (HTC_CB_MAGIC != htc_handle->htc_magic)) { |
| HTC_LOGE("Invalid parameters."); |
| return; |
| } |
| |
| /* iterate until no more packets are available */ |
| while (1) { |
| /* check the length of the next packet */ |
| packet_size = smd_cur_packet_size(htc_handle->smd_channel); |
| if (0 == packet_size) { |
| /* No more data to be read */ |
| return; |
| } |
| |
| /* Check how much of the data is available */ |
| available = smd_read_avail(htc_handle->smd_channel); |
| if (available < packet_size) { |
| return; |
| } |
| |
| rx_buf = adf_nbuf_alloc(htc_handle->osdev, packet_size, 0, 1, 0); |
| data = adf_nbuf_data(rx_buf); |
| bytes_read = smd_read(htc_handle->smd_channel, |
| data, |
| packet_size); |
| |
| if (bytes_read != packet_size) { |
| HTC_LOGE("Failed to read the data from the SMD."); |
| adf_nbuf_free(rx_buf); |
| WARN_ON(1); |
| return; |
| } |
| |
| adf_nbuf_set_pktlen(rx_buf, (uint32_t)packet_size); |
| |
| HTC_LOGD("packet_size = %d", packet_size); |
| |
| htc_smd_call_endpoint(htc_handle, rx_buf); |
| } |
| |
| HTC_LOGD("Exit"); |
| } |
| |
| /* function: htc_smd_write_callback |
| * Descriptin: |
| * Args: htc_handle |
| * Retruns: nothing |
| */ |
| static void htc_smd_write_callback(tp_htc_handle htc_handle) |
| { |
| vos_list_node_t *pnode; |
| t_htc_buffer *bufq; |
| HTC_PACKET *htc_packet; |
| void *buf; |
| int len; |
| int available; |
| int written; |
| HTC_ENDPOINT *endpoint; |
| adf_nbuf_t tx_buf; |
| |
| HTC_LOGD("Enter"); |
| |
| if ((NULL == htc_handle) || (HTC_CB_MAGIC != htc_handle->htc_magic)) { |
| HTC_LOGE("Invalid parameter"); |
| return; |
| } |
| |
| /* if we are not deferred, then there are no pending packets */ |
| if (HTC_SMD_STATE_DEFERRED != htc_handle->htc_smd_state) { |
| HTC_LOGD("No Pending packets"); |
| 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 (VOS_STATUS_SUCCESS == |
| vos_list_peek_front(&htc_handle->htc_write_pending_q, &pnode)) { |
| bufq = container_of(pnode, t_htc_buffer, node); |
| htc_packet = bufq->htc_packet; |
| tx_buf = GET_HTC_PACKET_NET_BUF_CONTEXT(htc_packet); |
| buf = adf_nbuf_data(tx_buf); |
| len = adf_nbuf_len(tx_buf); |
| endpoint = &htc_handle->end_point[htc_packet->Endpoint]; |
| available = smd_write_avail(htc_handle->smd_channel); |
| if (available < len) { |
| /* channel has no room for the next packet so we are done */ |
| HTC_LOGD("channel has no room for next packet"); |
| return; |
| } |
| |
| /* there is room for the next message, so we can now remove |
| it from the deferred message queue and send it */ |
| vos_list_remove_front(&htc_handle->htc_write_pending_q, &pnode); |
| |
| /* note that pNode will be the same as when we peeked, so |
| there is no need to update pBuffer or len */ |
| |
| written = smd_write(htc_handle->smd_channel, buf, len); |
| if (written != len) { |
| /* Something went wrong */ |
| HTC_LOGE("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 */ |
| } |
| |
| /* we'll continue to iterate until the channel is full or all |
| of the deferred messages have been sent */ |
| htc_packet->Status = 0; |
| endpoint->EpCallBacks.EpTxComplete(endpoint->EpCallBacks.pContext, htc_packet); |
| } |
| |
| /* 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 */ |
| htc_handle->htc_smd_state = HTC_SMD_STATE_OPEN; |
| smd_disable_read_intr(htc_handle->smd_channel); |
| |
| HTC_LOGD("Exit"); |
| } |
| |
| /* function: htc_smd_data_callback |
| * Descriptin: |
| * Args: pMsg |
| * Retruns: nothing |
| */ |
| static void htc_smd_data_callback(t_htc_msg *pMsg) |
| { |
| /* extract our context from the message */ |
| tp_htc_handle htc_handle = pMsg->pContext; |
| |
| HTC_LOGD("Enter"); |
| |
| /* process any incoming messages */ |
| htc_smd_read_callback(htc_handle); |
| |
| /* send any deferred messages */ |
| htc_smd_write_callback(htc_handle); |
| |
| HTC_LOGD("Exit"); |
| } |
| |
| /* function: htc_post_ctrl_msg |
| * Descriptin: |
| * Args: htc_handle, pMsg |
| * Retruns: failure or success |
| */ |
| int htc_post_ctrl_msg(void *htc_handle, t_htc_msg *pMsg) |
| { |
| vos_msg_t msg; |
| |
| HTC_LOGD("Enter"); |
| |
| if (NULL == pMsg) |
| { |
| HTC_LOGP("NULL message pointer"); |
| WARN_ON(1); |
| return -EINVAL; |
| } |
| |
| msg.type = 0; //This field is not used because VOSS doesn't check it. |
| msg.reserved = 0; |
| msg.bodyval = 0; |
| msg.bodyptr = pMsg; |
| if(!VOS_IS_STATUS_SUCCESS(vos_mq_post_message(VOS_MQ_ID_HTC, &msg))) |
| { |
| HTC_LOGE("fail to post msg %d", pMsg->type); |
| return -EINVAL; |
| } |
| else |
| { |
| HTC_LOGD("Successfully posted msg to MC thread"); |
| } |
| HTC_LOGD("Exit"); |
| return 0; |
| } |
| |
| /* function: htc_smd_notify_callback |
| * Descriptin: |
| * Args: data, event |
| * Retruns: nothing |
| */ |
| static void htc_smd_notify_callback(void *data, unsigned event) |
| { |
| t_htc_msg *htc_msg; |
| tp_htc_handle htc_handle = (tp_htc_handle) data; |
| |
| HTC_LOGD("Enter"); |
| |
| if (NULL == htc_handle) |
| { |
| HTC_LOGE("Invalid htc_handle"); |
| WARN_ON(1); |
| return; |
| } |
| |
| if (HTC_CB_MAGIC != htc_handle->htc_magic) { |
| HTC_LOGE("Received unexpected SMD event"); |
| return; |
| } |
| |
| /* Serialize processing in the control thread */ |
| switch (event) { |
| case SMD_EVENT_OPEN: |
| HTC_LOGD("received SMD_EVENT_OPEN from SMD"); |
| /* If the prev state was 'remote closed' then it is a Riva 'restart', |
| * subsystem restart re-init |
| */ |
| if (HTC_SMD_STATE_REM_CLOSED == htc_handle->htc_smd_state) |
| { |
| HTC_LOGD("received SMD_EVENT_OPEN in HTC_SMD_STATE_REM_CLOSED state"); |
| /* call subsystem restart re-init function */ |
| vos_wlanReInit(); |
| return; |
| } |
| htc_msg = &htc_handle->htc_open_msg; |
| break; |
| |
| case SMD_EVENT_DATA: |
| if (HTC_SMD_STATE_REM_CLOSED == htc_handle->htc_smd_state) |
| { |
| HTC_LOGD("received smd data in HTC_SMD_STATE_REM_CLOSED state"); |
| /* we should not be getting any data now */ |
| return; |
| } |
| HTC_LOGD("received SMD_EVENT_DATA from SMD"); |
| htc_msg = &htc_handle->htc_data_msg; |
| break; |
| |
| case SMD_EVENT_CLOSE: |
| HTC_LOGD("received SMD_EVENT_CLOSE from SMD"); |
| /* 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 */ |
| htc_handle->htc_smd_state = HTC_SMD_STATE_REM_CLOSED; |
| /* subsystem restart: shutdown */ |
| vos_wlanShutdown(); |
| return; |
| |
| case SMD_EVENT_STATUS: |
| HTC_LOGD("received SMD_EVENT_STATUS from SMD"); |
| return; |
| |
| case SMD_EVENT_REOPEN_READY: |
| HTC_LOGD("received SMD_EVENT_REOPEN_READY from SMD"); |
| |
| /* TODO: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 */ |
| complete(&htc_handle->htc_smd_event); |
| return; |
| |
| default: |
| HTC_LOGD("Unexpected event %u received from SMD", event); |
| return; |
| } |
| |
| htc_post_ctrl_msg(htc_handle, htc_msg); |
| HTC_LOGD("Exit"); |
| } |
| |
| /* function: htc_create |
| * Descriptin: |
| * Args: name -> HTC_SMD_CTRL_PORT |
| osdev |
| * Retruns: tp_htc_handle |
| */ |
| HTC_HANDLE HTCCreate(void *hHIF, HTC_INIT_INFO *pInfo, adf_os_device_t osdev) |
| { |
| int status = 0; |
| tp_htc_handle htc_handle = NULL; |
| |
| HTC_LOGD("Enter"); |
| |
| /* This open is coming after a SSR, we don't need to reopen SMD, |
| * the SMD port was never closed during SSR*/ |
| if (g_htc_handle) { |
| HTC_LOGD("\n SMD port is already opened"); |
| |
| htc_handle = g_htc_handle; |
| if (HTC_CB_MAGIC != htc_handle->htc_magic) { |
| HTC_LOGP("\n Invalid magic"); |
| return NULL; |
| } |
| htc_handle->htc_smd_state = HTC_SMD_STATE_OPEN; |
| |
| /*TODO: if caller of this function registers notify callback |
| then call the function here |
| */ |
| #if 0 |
| pWCTSCb->wctsNotifyCB((WCTS_HandleType)pWCTSCb, |
| WCTS_EVENT_OPEN, |
| pWCTSCb->wctsNotifyCBData); |
| #endif |
| /* we initially don't want read interrupts |
| (we only want them if we get into deferred write mode) */ |
| smd_disable_read_intr(htc_handle->smd_channel); |
| |
| return htc_handle; |
| } |
| |
| /* allocate the memory for HTC handle */ |
| htc_handle = vos_mem_malloc(sizeof (t_htc_handle)); |
| if (NULL == htc_handle) { |
| HTC_LOGE("Memory allocation failed for htc_handle"); |
| return NULL; |
| } |
| |
| vos_mem_zero(htc_handle, sizeof(t_htc_handle)); |
| init_completion(&htc_handle->htc_smd_event); |
| |
| |
| vos_list_init(&htc_handle->htc_write_pending_q); |
| |
| /* initialize the remaining fields */ |
| htc_handle->osdev = osdev; |
| htc_handle->htc_magic = HTC_CB_MAGIC; |
| htc_handle->htc_smd_state = HTC_SMD_STATE_OPEN_PENDING; |
| htc_handle->smd_channel = NULL; |
| |
| /* since SMD will callback in interrupt context, we will used |
| * canned messages to serialize the SMD events into a thread |
| * context |
| */ |
| htc_handle->htc_open_msg.callback = htc_smd_open_callback; |
| htc_handle->htc_open_msg.pContext = htc_handle; |
| htc_handle->htc_data_msg.callback = htc_smd_data_callback; |
| htc_handle->htc_data_msg.pContext = htc_handle; |
| |
| HTC_LOGD("htc_handle = %p", htc_handle); |
| |
| INIT_COMPLETION(htc_handle->htc_smd_event); |
| /* Open the SMD channel */ |
| status = smd_named_open_on_edge(HTC_SMD_CTRL_PORT, SMD_APPS_WCNSS, |
| &htc_handle->smd_channel, htc_handle, |
| htc_smd_notify_callback); |
| if (0 != status) { |
| HTC_LOGE("Failed to open SMD Channel"); |
| goto fail; |
| } |
| /* Wait for the event */ |
| status = wait_for_completion_interruptible_timeout( |
| &htc_handle->htc_smd_event, |
| msecs_to_jiffies(HTC_SMD_OPEN_TIMEOUT)); |
| |
| if (!status) { |
| HTC_LOGE("Timeout occurred while waiting for SMD_SS_OPENED event."); |
| /* since we opened one end of the channel, close it */ |
| status = smd_close(htc_handle->smd_channel); |
| if (0 != status) { |
| HTC_LOGE("smd_close failed."); |
| } |
| goto fail; |
| } |
| |
| /* we initially don't want read interrupts |
| (we only want them if we get into deferred write mode) */ |
| smd_disable_read_intr(htc_handle->smd_channel); |
| |
| /* we have successfully opened the SMD channel */ |
| g_htc_handle = htc_handle; |
| HTC_LOGD("Exit"); |
| return (void*)htc_handle; |
| |
| fail: |
| /* Failed to open the SMD Channel */ |
| HTC_LOGE("Failure"); |
| vos_mem_free(htc_handle); |
| return NULL; |
| } |
| |
| /* function: htc_connect_service |
| * Descriptin: |
| * Args: htc_handle, pConnectReq, pConnectResp |
| * Retruns: success or failure |
| */ |
| A_STATUS HTCConnectService(HTC_HANDLE HTCHandle, |
| HTC_SERVICE_CONNECT_REQ *pConnectReq, |
| HTC_SERVICE_CONNECT_RESP *pConnectResp) |
| { |
| tp_htc_handle htc_handle = (tp_htc_handle)HTCHandle; |
| HTC_ENDPOINT *endpoint = NULL; |
| |
| HTC_LOGD("Enter"); |
| |
| WARN_ON(!pConnectReq->ServiceID); |
| |
| switch (pConnectReq->ServiceID) { |
| case CFG_NV_SVC: |
| HTC_LOGD("CFG_NV_SVC"); |
| endpoint = &htc_handle->end_point[HTC_ENDPOINT_INIT]; |
| pConnectResp->Endpoint = HTC_ENDPOINT_INIT; |
| break; |
| case WMI_CONTROL_SVC: |
| HTC_LOGD("WMI_CONTROL_SVC"); |
| endpoint = &htc_handle->end_point[HTC_ENDPOINT_WMI]; |
| pConnectResp->Endpoint = HTC_ENDPOINT_WMI; |
| break; |
| default: |
| HTC_LOGD("Invalid service"); |
| return -EINVAL; |
| } |
| |
| pConnectResp->ConnectRespCode = HTC_SERVICE_SUCCESS; |
| endpoint->ServiceID = pConnectReq->ServiceID; |
| endpoint->EpCallBacks = pConnectReq->EpCallbacks; |
| endpoint->Id = pConnectResp->Endpoint; |
| /*TODO: */ |
| //endpoint->target = htc_handle; |
| |
| HTC_LOGD("Exit"); |
| return 0; |
| } |
| |
| /* function: htc_start |
| * Descriptin: |
| * Args: htc_handle |
| * Retruns: success or failure |
| */ |
| A_STATUS HTCStart(HTC_HANDLE HTCHandle) |
| { |
| tp_htc_handle htc_handle = (tp_htc_handle)HTCHandle; |
| if (HTC_SMD_STATE_OPEN == htc_handle->htc_smd_state) { |
| HTC_LOGD("htc_start success"); |
| /* TODO: ? */ |
| return A_OK; |
| } |
| else { |
| HTC_LOGD("smd is not in open state; htc_start failed"); |
| return A_ERROR; |
| } |
| } |
| |
| /* function: htc_stop |
| * Descriptin: |
| * Args: htc_handle |
| * Retruns: nothing |
| */ |
| void HTCStop(HTC_HANDLE HTCHandle) |
| { |
| /* TODO: ? */ |
| return; |
| } |
| |
| /* function: htc_destroy |
| * Descriptin: Closes the SMD session. |
| * Args: HTC context |
| * Retruns: success or failure |
| */ |
| void HTCDestroy(HTC_HANDLE HTCHandle) |
| { |
| tp_htc_handle htc_handle = (tp_htc_handle) HTCHandle; |
| vos_list_node_t *pnode = NULL; |
| t_htc_buffer *bufq = NULL; |
| HTC_PACKET *htc_packet; |
| int smd_status; |
| int status; |
| HTC_ENDPOINT *endpoint; |
| |
| if ((NULL == htc_handle) || (HTC_CB_MAGIC != htc_handle->htc_magic)) { |
| HTC_LOGE("Invalid parameter"); |
| return; |
| } |
| |
| /*Free the buffers in the pending queue.*/ |
| while (VOS_STATUS_SUCCESS == |
| vos_list_remove_front(&htc_handle->htc_write_pending_q, &pnode)) { |
| bufq = container_of(pnode, t_htc_buffer, node); |
| htc_packet = (HTC_PACKET*)bufq->htc_packet; |
| |
| endpoint = &htc_handle->end_point[htc_packet->Endpoint]; |
| endpoint->EpCallBacks.EpRecv(endpoint->EpCallBacks.pContext, htc_packet); |
| vos_mem_free(bufq); |
| } |
| |
| |
| INIT_COMPLETION(htc_handle->htc_smd_event); |
| smd_status = smd_close(htc_handle->smd_channel); |
| if (0 != smd_status) { |
| HTC_LOGE("smd_close failed with status %d", smd_status); |
| /* 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 = wait_for_completion_interruptible_timeout( |
| &htc_handle->htc_smd_event, |
| msecs_to_jiffies(HTC_SMD_OPEN_TIMEOUT)); |
| if (!status) { |
| HTC_LOGE("failed to receive SMD_EVENT_REOPEN_READY %d", smd_status); |
| } |
| |
| /* 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); |
| } |
| |
| /* Reset the state */ |
| htc_handle->htc_smd_state = HTC_SMD_STATE_CLOSED; |
| |
| /* TODO: If caller registers any function then call it */ |
| #if 0 |
| /* channel has (hopefully) been closed */ |
| htc_handle->wctsNotifyCB((WCTS_HandleType)pWCTSCb, |
| WCTS_EVENT_CLOSE, |
| pWCTSCb->wctsNotifyCBData); |
| #endif |
| /* release the resource */ |
| htc_handle->htc_magic = 0; |
| vos_mem_free(htc_handle); |
| |
| g_htc_handle = NULL; |
| |
| return; |
| } |
| |
| /* function: htc_send_packet |
| * Descriptin: |
| * Args: handle, htc_pakcet |
| * Retruns: success or failure |
| */ |
| A_STATUS HTCSendPkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket) |
| { |
| tp_htc_handle htc_handle = (tp_htc_handle)HTCHandle; |
| HTC_ENDPOINT *endpoint = &htc_handle->end_point[pPacket->Endpoint]; |
| adf_nbuf_t txBuf = (adf_nbuf_t)GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); |
| t_htc_buffer *bufq; |
| int len; |
| int written = 0; |
| int available; |
| |
| if ((NULL == htc_handle) || (HTC_CB_MAGIC != htc_handle->htc_magic) || |
| (NULL == pPacket)) { |
| HTC_LOGE("Invalid parameters received"); |
| WARN_ON(1); |
| return A_ERROR; |
| } |
| |
| /* the SMD API uses int instead of uint, so change types here */ |
| len = adf_nbuf_len(txBuf); |
| |
| if (HTC_SMD_STATE_OPEN == htc_handle->htc_smd_state) { |
| available = smd_write_avail(htc_handle->smd_channel); |
| if (available >= len) { |
| written = smd_write(htc_handle->smd_channel, adf_nbuf_data(txBuf), len); |
| } |
| } else if (HTC_SMD_STATE_DEFERRED == htc_handle->htc_smd_state) { |
| HTC_LOGD("FIFO space not available, the packets will be queued"); |
| } else { |
| HTC_LOGE("Channel in invalid state %d", htc_handle->htc_smd_state); |
| /* force following logic to reclaim the buffer */ |
| written = -1; |
| } |
| |
| if (-1 == written) { |
| /*Something wrong*/ |
| HTC_LOGD("Failed to send message over the bus"); |
| WARN_ON(1); |
| return A_ERROR; |
| } else if (written == len) { |
| HTC_LOGD("Message sent"); |
| /* Message sent! No deferred state, free the buffer*/ |
| if ((NULL != endpoint) && (NULL != endpoint->EpCallBacks.EpTxComplete)) { |
| HTC_LOGD("calling EpTxComplete"); |
| pPacket->Status = A_OK; //SUCCESS |
| endpoint->EpCallBacks.EpTxComplete(endpoint->EpCallBacks.pContext, |
| pPacket); |
| } |
| } else { |
| /* This much data cannot be written at this time, |
| queue the rest of the data for later*/ |
| HTC_LOGD("Defer message"); |
| bufq = vos_mem_malloc(sizeof(t_htc_buffer)); |
| if (NULL == bufq) { |
| HTC_LOGE("Cannot allocate memory for queuing the buffer"); |
| WARN_ON(1); |
| return A_MEMORY_NOT_AVAIL; |
| } |
| bufq->htc_packet = pPacket; |
| vos_list_insert_back(&htc_handle->htc_write_pending_q, &bufq->node); |
| |
| /* 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 */ |
| if (HTC_SMD_STATE_DEFERRED != htc_handle->htc_smd_state) { |
| |
| /* Mark the state as deferred. |
| */ |
| htc_handle->htc_smd_state = HTC_SMD_STATE_DEFERRED; |
| |
| smd_enable_read_intr(htc_handle->smd_channel); |
| } |
| } |
| |
| return A_OK; |
| } |