/* * Copyright (c) 2013-2018 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. */ #include "htc_debug.h" #include "htc_internal.h" #include /* qdf_nbuf_t */ /* HTC Control message receive timeout msec */ #define HTC_CONTROL_RX_TIMEOUT 6000 #if defined(WLAN_DEBUG) || defined(DEBUG) void debug_dump_bytes(uint8_t *buffer, uint16_t length, char *pDescription) { int8_t stream[60]; int8_t byteOffsetStr[10]; uint32_t i; uint16_t offset, count, byteOffset; A_PRINTF("<---------Dumping %d Bytes : %s ------>\n", length, pDescription); count = 0; offset = 0; byteOffset = 0; for (i = 0; i < length; i++) { A_SNPRINTF(stream + offset, (sizeof(stream) - offset), "%02X ", buffer[i]); count++; offset += 3; if (count == 16) { count = 0; offset = 0; A_SNPRINTF(byteOffsetStr, sizeof(byteOffset), "%4.4X", byteOffset); A_PRINTF("[%s]: %s\n", byteOffsetStr, stream); qdf_mem_zero(stream, 60); byteOffset += 16; } } if (offset != 0) { A_SNPRINTF(byteOffsetStr, sizeof(byteOffset), "%4.4X", byteOffset); A_PRINTF("[%s]: %s\n", byteOffsetStr, stream); } A_PRINTF("<------------------------------------------------->\n"); } #else void debug_dump_bytes(uint8_t *buffer, uint16_t length, char *pDescription) { } #endif static A_STATUS htc_process_trailer(HTC_TARGET *target, uint8_t *pBuffer, int Length, HTC_ENDPOINT_ID FromEndpoint); static void do_recv_completion_pkt(HTC_ENDPOINT *pEndpoint, HTC_PACKET *pPacket) { if (pEndpoint->EpCallBacks.EpRecv == NULL) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HTC ep %d has NULL recv callback on packet %pK\n", pEndpoint->Id, pPacket)); if (pPacket) qdf_nbuf_free(pPacket->pPktContext); } else { AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("HTC calling ep %d recv callback on packet %pK\n", pEndpoint->Id, pPacket)); pEndpoint->EpCallBacks.EpRecv(pEndpoint->EpCallBacks.pContext, pPacket); } } static void do_recv_completion(HTC_ENDPOINT *pEndpoint, HTC_PACKET_QUEUE *pQueueToIndicate) { HTC_PACKET *pPacket; if (HTC_QUEUE_EMPTY(pQueueToIndicate)) { /* nothing to indicate */ return; } while (!HTC_QUEUE_EMPTY(pQueueToIndicate)) { pPacket = htc_packet_dequeue(pQueueToIndicate); do_recv_completion_pkt(pEndpoint, pPacket); } } void htc_control_rx_complete(void *Context, HTC_PACKET *pPacket) { /* TODO, can't really receive HTC control messages yet.... */ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Invalid call to htc_control_rx_complete\n")); } void htc_unblock_recv(HTC_HANDLE HTCHandle) { /* TODO find the Need in new model */ } void htc_enable_recv(HTC_HANDLE HTCHandle) { /* TODO find the Need in new model */ } void htc_disable_recv(HTC_HANDLE HTCHandle) { /* TODO find the Need in new model */ } int htc_get_num_recv_buffers(HTC_HANDLE HTCHandle, HTC_ENDPOINT_ID Endpoint) { HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); HTC_ENDPOINT *pEndpoint = &target->endpoint[Endpoint]; return HTC_PACKET_QUEUE_DEPTH(&pEndpoint->RxBufferHoldQueue); } HTC_PACKET *allocate_htc_packet_container(HTC_TARGET *target) { HTC_PACKET *pPacket; LOCK_HTC_RX(target); if (NULL == target->pHTCPacketStructPool) { UNLOCK_HTC_RX(target); return NULL; } pPacket = target->pHTCPacketStructPool; target->pHTCPacketStructPool = (HTC_PACKET *) pPacket->ListLink.pNext; UNLOCK_HTC_RX(target); pPacket->ListLink.pNext = NULL; return pPacket; } void free_htc_packet_container(HTC_TARGET *target, HTC_PACKET *pPacket) { pPacket->ListLink.pPrev = NULL; LOCK_HTC_RX(target); if (NULL == target->pHTCPacketStructPool) { target->pHTCPacketStructPool = pPacket; pPacket->ListLink.pNext = NULL; } else { pPacket->ListLink.pNext = (DL_LIST *) target->pHTCPacketStructPool; target->pHTCPacketStructPool = pPacket; } UNLOCK_HTC_RX(target); } #ifdef RX_SG_SUPPORT qdf_nbuf_t rx_sg_to_single_netbuf(HTC_TARGET *target) { qdf_nbuf_t skb; uint8_t *anbdata; uint8_t *anbdata_new; uint32_t anblen; qdf_nbuf_t new_skb = NULL; uint32_t sg_queue_len; qdf_nbuf_queue_t *rx_sg_queue = &target->RxSgQueue; sg_queue_len = qdf_nbuf_queue_len(rx_sg_queue); if (sg_queue_len <= 1) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("rx_sg_to_single_netbuf: invalid sg queue len %u\n")); goto _failed; } new_skb = qdf_nbuf_alloc(target->ExpRxSgTotalLen, 0, 4, false); if (new_skb == NULL) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("rx_sg_to_single_netbuf: can't allocate %u size netbuf\n", target->ExpRxSgTotalLen)); goto _failed; } qdf_nbuf_peek_header(new_skb, &anbdata_new, &anblen); skb = qdf_nbuf_queue_remove(rx_sg_queue); do { qdf_nbuf_peek_header(skb, &anbdata, &anblen); qdf_mem_copy(anbdata_new, anbdata, qdf_nbuf_len(skb)); qdf_nbuf_put_tail(new_skb, qdf_nbuf_len(skb)); anbdata_new += qdf_nbuf_len(skb); qdf_nbuf_free(skb); skb = qdf_nbuf_queue_remove(rx_sg_queue); } while (skb != NULL); RESET_RX_SG_CONFIG(target); return new_skb; _failed: while ((skb = qdf_nbuf_queue_remove(rx_sg_queue)) != NULL) qdf_nbuf_free(skb); RESET_RX_SG_CONFIG(target); return NULL; } #endif #ifdef CONFIG_WIN #define HTC_MSG_NACK_SUSPEND 7 #endif QDF_STATUS htc_rx_completion_handler(void *Context, qdf_nbuf_t netbuf, uint8_t pipeID) { QDF_STATUS status = QDF_STATUS_SUCCESS; HTC_FRAME_HDR *HtcHdr; HTC_TARGET *target = (HTC_TARGET *) Context; uint8_t *netdata; uint32_t netlen; HTC_ENDPOINT *pEndpoint; HTC_PACKET *pPacket; uint16_t payloadLen; uint32_t trailerlen = 0; uint8_t htc_ep_id; struct htc_init_info *info; #ifdef RX_SG_SUPPORT LOCK_HTC_RX(target); if (target->IsRxSgInprogress) { target->CurRxSgTotalLen += qdf_nbuf_len(netbuf); qdf_nbuf_queue_add(&target->RxSgQueue, netbuf); if (target->CurRxSgTotalLen == target->ExpRxSgTotalLen) { netbuf = rx_sg_to_single_netbuf(target); if (netbuf == NULL) { UNLOCK_HTC_RX(target); goto _out; } } else { netbuf = NULL; UNLOCK_HTC_RX(target); goto _out; } } UNLOCK_HTC_RX(target); #endif netdata = qdf_nbuf_data(netbuf); netlen = qdf_nbuf_len(netbuf); HtcHdr = (HTC_FRAME_HDR *) netdata; do { htc_ep_id = HTC_GET_FIELD(HtcHdr, HTC_FRAME_HDR, ENDPOINTID); if (htc_ep_id >= ENDPOINT_MAX) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HTC Rx: invalid EndpointID=%d\n", htc_ep_id)); debug_dump_bytes((uint8_t *) HtcHdr, sizeof(HTC_FRAME_HDR), "BAD HTC Header"); status = QDF_STATUS_E_FAILURE; QDF_BUG(0); break; } pEndpoint = &target->endpoint[htc_ep_id]; /* * If this endpoint that received a message from the target has * a to-target HIF pipe whose send completions are polled rather * than interrupt driven, this is a good point to ask HIF to * check whether it has any completed sends to handle. */ if (pEndpoint->ul_is_polled) htc_send_complete_check(pEndpoint, 1); payloadLen = HTC_GET_FIELD(HtcHdr, HTC_FRAME_HDR, PAYLOADLEN); if (netlen < (payloadLen + HTC_HDR_LENGTH)) { #ifdef RX_SG_SUPPORT LOCK_HTC_RX(target); target->IsRxSgInprogress = true; qdf_nbuf_queue_init(&target->RxSgQueue); qdf_nbuf_queue_add(&target->RxSgQueue, netbuf); target->ExpRxSgTotalLen = (payloadLen + HTC_HDR_LENGTH); target->CurRxSgTotalLen += netlen; UNLOCK_HTC_RX(target); netbuf = NULL; break; #else AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HTC Rx: insufficient length, got:%d expected =%zu\n", netlen, payloadLen + HTC_HDR_LENGTH)); debug_dump_bytes((uint8_t *) HtcHdr, sizeof(HTC_FRAME_HDR), "BAD RX packet length"); status = QDF_STATUS_E_FAILURE; QDF_BUG(0); break; #endif } #ifdef HTC_EP_STAT_PROFILING LOCK_HTC_RX(target); INC_HTC_EP_STAT(pEndpoint, RxReceived, 1); UNLOCK_HTC_RX(target); #endif /* if (IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) { */ { uint8_t temp; A_STATUS temp_status; /* get flags to check for trailer */ temp = HTC_GET_FIELD(HtcHdr, HTC_FRAME_HDR, FLAGS); if (temp & HTC_FLAGS_RECV_TRAILER) { /* extract the trailer length */ temp = HTC_GET_FIELD(HtcHdr, HTC_FRAME_HDR, CONTROLBYTES0); if ((temp < sizeof(HTC_RECORD_HDR)) || (temp > payloadLen)) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("htc_rx_completion_handler, invalid header (payloadlength should be :%d, CB[0] is:%d)\n", payloadLen, temp)); status = QDF_STATUS_E_INVAL; break; } trailerlen = temp; /* process trailer data that follows HDR + * application payload */ temp_status = htc_process_trailer(target, ((uint8_t *) HtcHdr + HTC_HDR_LENGTH + payloadLen - temp), temp, htc_ep_id); if (A_FAILED(temp_status)) { status = QDF_STATUS_E_FAILURE; break; } } } if (((int)payloadLen - (int)trailerlen) <= 0) { /* 0 length packet with trailer data, just drop these */ break; } if (htc_ep_id == ENDPOINT_0) { uint16_t message_id; HTC_UNKNOWN_MSG *htc_msg; bool wow_nack; /* remove HTC header */ qdf_nbuf_pull_head(netbuf, HTC_HDR_LENGTH); netdata = qdf_nbuf_data(netbuf); netlen = qdf_nbuf_len(netbuf); htc_msg = (HTC_UNKNOWN_MSG *) netdata; message_id = HTC_GET_FIELD(htc_msg, HTC_UNKNOWN_MSG, MESSAGEID); switch (message_id) { default: /* handle HTC control message */ if (target->CtrlResponseProcessing) { /* this is a fatal error, target should * not be sending unsolicited messages * on the endpoint 0 */ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HTC Rx Ctrl still processing\n")); status = QDF_STATUS_E_FAILURE; QDF_BUG(false); break; } LOCK_HTC_RX(target); target->CtrlResponseLength = min((int)netlen, HTC_MAX_CONTROL_MESSAGE_LENGTH); qdf_mem_copy(target->CtrlResponseBuffer, netdata, target->CtrlResponseLength); /* Requester will clear this flag */ target->CtrlResponseProcessing = true; UNLOCK_HTC_RX(target); qdf_event_set(&target->ctrl_response_valid); break; #ifdef HTC_MSG_WAKEUP_FROM_SUSPEND_ID case HTC_MSG_WAKEUP_FROM_SUSPEND_ID: AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("Received initial wake up")); LOCK_HTC_CREDIT(target); htc_credit_record(HTC_INITIAL_WAKE_UP, pEndpoint->TxCredits, HTC_PACKET_QUEUE_DEPTH( &pEndpoint->TxQueue)); UNLOCK_HTC_CREDIT(target); info = &target->HTCInitInfo; if (info && info->target_initial_wakeup_cb) info->target_initial_wakeup_cb( info->target_psoc); else AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("No initial wake up cb")); break; #endif case HTC_MSG_SEND_SUSPEND_COMPLETE: wow_nack = false; LOCK_HTC_CREDIT(target); htc_credit_record(HTC_SUSPEND_ACK, pEndpoint->TxCredits, HTC_PACKET_QUEUE_DEPTH( &pEndpoint->TxQueue)); UNLOCK_HTC_CREDIT(target); target->HTCInitInfo.TargetSendSuspendComplete( target->HTCInitInfo.target_psoc, wow_nack); break; case HTC_MSG_NACK_SUSPEND: wow_nack = true; LOCK_HTC_CREDIT(target); htc_credit_record(HTC_SUSPEND_ACK, pEndpoint->TxCredits, HTC_PACKET_QUEUE_DEPTH( &pEndpoint->TxQueue)); UNLOCK_HTC_CREDIT(target); target->HTCInitInfo.TargetSendSuspendComplete( target->HTCInitInfo.target_psoc, wow_nack); break; } qdf_nbuf_free(netbuf); netbuf = NULL; break; } /* the current message based HIF architecture allocates net bufs * for recv packets since this layer bridges that HIF to upper * layers , which expects HTC packets, we form the packets here * TODO_FIXME */ pPacket = allocate_htc_packet_container(target); if (NULL == pPacket) { status = QDF_STATUS_E_RESOURCES; break; } pPacket->Status = QDF_STATUS_SUCCESS; pPacket->Endpoint = htc_ep_id; pPacket->pPktContext = netbuf; pPacket->pBuffer = qdf_nbuf_data(netbuf) + HTC_HDR_LENGTH; pPacket->ActualLength = netlen - HTC_HEADER_LEN - trailerlen; qdf_nbuf_pull_head(netbuf, HTC_HEADER_LEN); qdf_nbuf_set_pktlen(netbuf, pPacket->ActualLength); do_recv_completion_pkt(pEndpoint, pPacket); /* recover the packet container */ free_htc_packet_container(target, pPacket); netbuf = NULL; } while (false); #ifdef RX_SG_SUPPORT _out: #endif if (netbuf != NULL) qdf_nbuf_free(netbuf); return status; } A_STATUS htc_add_receive_pkt_multiple(HTC_HANDLE HTCHandle, HTC_PACKET_QUEUE *pPktQueue) { HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); HTC_ENDPOINT *pEndpoint; HTC_PACKET *pFirstPacket; A_STATUS status = A_OK; HTC_PACKET *pPacket; pFirstPacket = htc_get_pkt_at_head(pPktQueue); if (NULL == pFirstPacket) { A_ASSERT(false); return A_EINVAL; } AR_DEBUG_ASSERT(pFirstPacket->Endpoint < ENDPOINT_MAX); AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+- htc_add_receive_pkt_multiple : endPointId: %d, cnt:%d, length: %d\n", pFirstPacket->Endpoint, HTC_PACKET_QUEUE_DEPTH(pPktQueue), pFirstPacket->BufferLength)); pEndpoint = &target->endpoint[pFirstPacket->Endpoint]; LOCK_HTC_RX(target); do { if (HTC_STOPPING(target)) { status = A_ERROR; break; } /* store receive packets */ HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pEndpoint->RxBufferHoldQueue, pPktQueue); } while (false); UNLOCK_HTC_RX(target); if (A_FAILED(status)) { /* walk through queue and mark each one canceled */ HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue, pPacket) { pPacket->Status = QDF_STATUS_E_CANCELED; } HTC_PACKET_QUEUE_ITERATE_END; do_recv_completion(pEndpoint, pPktQueue); } return status; } A_STATUS htc_add_receive_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket) { HTC_PACKET_QUEUE queue; INIT_HTC_PACKET_QUEUE_AND_ADD(&queue, pPacket); return htc_add_receive_pkt_multiple(HTCHandle, &queue); } void htc_flush_rx_hold_queue(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint) { HTC_PACKET *pPacket; LOCK_HTC_RX(target); while (1) { pPacket = htc_packet_dequeue(&pEndpoint->RxBufferHoldQueue); if (pPacket == NULL) break; UNLOCK_HTC_RX(target); pPacket->Status = QDF_STATUS_E_CANCELED; pPacket->ActualLength = 0; AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("Flushing RX packet:%pK, length:%d, ep:%d\n", pPacket, pPacket->BufferLength, pPacket->Endpoint)); /* give the packet back */ do_recv_completion_pkt(pEndpoint, pPacket); LOCK_HTC_RX(target); } UNLOCK_HTC_RX(target); } void htc_recv_init(HTC_TARGET *target) { /* Initialize ctrl_response_valid to block */ qdf_event_create(&target->ctrl_response_valid); } /* polling routine to wait for a control packet to be received */ QDF_STATUS htc_wait_recv_ctrl_message(HTC_TARGET *target) { /* int count = HTC_TARGET_MAX_RESPONSE_POLL; */ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCWaitCtrlMessageRecv\n")); /* Wait for BMI request/response transaction to complete */ if (qdf_wait_single_event(&target->ctrl_response_valid, HTC_CONTROL_RX_TIMEOUT)) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to receive control message\n")); return QDF_STATUS_E_FAILURE; } LOCK_HTC_RX(target); /* caller will clear this flag */ target->CtrlResponseProcessing = true; UNLOCK_HTC_RX(target); AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCWaitCtrlMessageRecv success\n")); return QDF_STATUS_SUCCESS; } static A_STATUS htc_process_trailer(HTC_TARGET *target, uint8_t *pBuffer, int Length, HTC_ENDPOINT_ID FromEndpoint) { HTC_RECORD_HDR *pRecord; uint8_t htc_rec_id; uint8_t htc_rec_len; uint8_t *pRecordBuf; uint8_t *pOrigBuffer; int origLength; A_STATUS status; AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+htc_process_trailer (length:%d)\n", Length)); if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) AR_DEBUG_PRINTBUF(pBuffer, Length, "Recv Trailer"); pOrigBuffer = pBuffer; origLength = Length; status = A_OK; while (Length > 0) { if (Length < sizeof(HTC_RECORD_HDR)) { status = A_EPROTO; break; } /* these are byte aligned structs */ pRecord = (HTC_RECORD_HDR *) pBuffer; Length -= sizeof(HTC_RECORD_HDR); pBuffer += sizeof(HTC_RECORD_HDR); htc_rec_len = HTC_GET_FIELD(pRecord, HTC_RECORD_HDR, LENGTH); htc_rec_id = HTC_GET_FIELD(pRecord, HTC_RECORD_HDR, RECORDID); if (htc_rec_len > Length) { /* no room left in buffer for record */ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("invalid record length: %d (id:%d) buffer has: %d bytes left\n", htc_rec_len, htc_rec_id, Length)); status = A_EPROTO; break; } /* start of record follows the header */ pRecordBuf = pBuffer; switch (htc_rec_id) { case HTC_RECORD_CREDITS: AR_DEBUG_ASSERT(htc_rec_len >= sizeof(HTC_CREDIT_REPORT)); htc_process_credit_rpt(target, (HTC_CREDIT_REPORT *) pRecordBuf, htc_rec_len / (sizeof(HTC_CREDIT_REPORT)), FromEndpoint); break; #ifdef HIF_SDIO case HTC_RECORD_LOOKAHEAD: /* Process in HIF layer */ break; case HTC_RECORD_LOOKAHEAD_BUNDLE: /* Process in HIF layer */ break; #endif /* HIF_SDIO */ default: AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HTC unhandled record: id:%d length:%d\n", htc_rec_id, htc_rec_len)); break; } if (A_FAILED(status)) { break; } /* advance buffer past this record for next time around */ pBuffer += htc_rec_len; Length -= htc_rec_len; } if (A_FAILED(status)) debug_dump_bytes(pOrigBuffer, origLength, "BAD Recv Trailer"); AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-htc_process_trailer\n")); return status; }