A_STATUS HIFDevSendBuffer(HIF_SDIO_DEVICE *pDev, unsigned int transferID, a_uint8_t pipe, unsigned int nbytes, adf_nbuf_t buf) { A_STATUS status; A_UINT32 paddedLength; int frag_count = 0, i, head_data_len; struct HIFSendContext *pSendContext; unsigned char *pData; A_UINT32 request = HIF_WR_ASYNC_BLOCK_INC; A_UINT8 mboxIndex = HIFDevMapPipeToMailBox(pDev, pipe); paddedLength = DEV_CALC_SEND_PADDED_LEN(pDev, nbytes); #ifdef ENABLE_MBOX_DUMMY_SPACE_FEATURE A_ASSERT(paddedLength - nbytes < HIF_DUMMY_SPACE_MASK + 1); /* * two most significant bytes to save dummy data count * data written into the dummy space will not put into the final mbox FIFO * */ request |= ((paddedLength - nbytes) << 16); #endif frag_count = adf_nbuf_get_num_frags(buf); if (frag_count > 1){ /* header data length should be total sending length substract internal data length of netbuf */ /* * | HIFSendContext | fragments except internal buffer | netbuf->data */ head_data_len = sizeof(struct HIFSendContext) + (nbytes - adf_nbuf_get_frag_len(buf, frag_count - 1)); } else { /* * | HIFSendContext | netbuf->data */ head_data_len = sizeof(struct HIFSendContext); } /* Check whether head room is enough to save extra head data */ if ((head_data_len <= adf_nbuf_headroom(buf)) && (adf_nbuf_tailroom(buf) >= (paddedLength - nbytes))){ pSendContext = (struct HIFSendContext*)adf_nbuf_push_head(buf, head_data_len); pSendContext->bNewAlloc = FALSE; } else { pSendContext = (struct HIFSendContext*)adf_os_mem_alloc(NULL, sizeof(struct HIFSendContext) + paddedLength); pSendContext->bNewAlloc = TRUE; } pSendContext->netbuf = buf; pSendContext->pDev = pDev; pSendContext->transferID = transferID; pSendContext->head_data_len = head_data_len; /* * Copy data to head part of netbuf or head of allocated buffer. * if buffer is new allocated, the last buffer should be copied also. * It assume last fragment is internal buffer of netbuf * sometime total length of fragments larger than nbytes */ pData = (unsigned char *)pSendContext + sizeof(struct HIFSendContext); for (i = 0; i < (pSendContext->bNewAlloc ? frag_count : frag_count - 1); i ++){ int frag_len = adf_nbuf_get_frag_len(buf, i); unsigned char *frag_addr = adf_nbuf_get_frag_vaddr(buf, i); if (frag_len > nbytes){ frag_len = nbytes; } memcpy(pData, frag_addr, frag_len); pData += frag_len; nbytes -= frag_len; if (nbytes <= 0) { break; } } /* Reset pData pointer and send out */ pData = (unsigned char *)pSendContext + sizeof(struct HIFSendContext); status = HIFReadWrite(pDev->HIFDevice, pDev->MailBoxInfo.MboxProp[mboxIndex].ExtendedAddress, (char*) pData, paddedLength, request, (void*)pSendContext); if (status == A_PENDING){ /* * it will return A_PENDING in native HIF implementation, * which should be treated as successful result here. */ status = A_OK; } /* release buffer or move back data pointer when failed */ if (status != A_OK){ if (pSendContext->bNewAlloc){ adf_os_mem_free(pSendContext); } else { adf_nbuf_pull_head(buf, head_data_len); } } return status; }
LOCAL void _HTC_SendMsg(htc_handle_t htcHandle, HTC_ENDPOINT_ID EndpointID, adf_nbuf_t pBuffers) { HTC_FRAME_HDR *pHTCHdr; int totsz; HTC_CONTEXT *pHTC = (HTC_CONTEXT *)htcHandle; HTC_BUF_CONTEXT *ctx; ctx = (HTC_BUF_CONTEXT *)adf_nbuf_get_priv(pBuffers); /* init total size (this does not include the space we will put in for the HTC header) */ totsz = adf_nbuf_len(pBuffers); /* the first buffer stores the header */ /* back up buffer by a header size when we pass it down, by agreed upon convention the caller * points the buffer to it's payload and leaves head room for the HTC header * Note: in HTCSendDoneHandler(), we undo this so that the caller get's it's buffer * back untainted */ pHTCHdr = (HTC_FRAME_HDR *)adf_nbuf_push_head(pBuffers, HTC_HDR_LENGTH); /* flag that this is the header buffer that was modified */ ctx->htc_flags |= HTC_FLAGS_BUF_HDR; /* mark where this buffer came from */ ctx->end_point = EndpointID; /* the header start is ALWAYS aligned since we DMA it directly */ /* set some fields, the rest of them will be filled below when we check for * trailer space */ pHTCHdr->Flags = 0; pHTCHdr->EndpointID = EndpointID; /* check opportunistically if we can return any reports via a trailer */ do { int room,i,totalReportBytes; A_UINT32 creditsPendingMap, compareMask; HTC_CREDIT_REPORT *pCreditRpt; HTC_RECORD_HDR *pRecHdr; int pipeMaxLen; A_UINT32 roomForPipeMaxLen; /* figure out how much room the last buffer can spare */ pipeMaxLen = HIF_get_max_msg_len(pHTC->hifHandle, pHTC->Endpoints[EndpointID].DownLinkPipeID); roomForPipeMaxLen = pipeMaxLen - adf_nbuf_headroom(pBuffers) - adf_nbuf_len(pBuffers); if ( roomForPipeMaxLen < 0 ) { roomForPipeMaxLen = 0; } room = adf_os_min( adf_nbuf_tailroom(pBuffers), roomForPipeMaxLen); if (room < (int)(sizeof(HTC_CREDIT_REPORT) + sizeof(HTC_RECORD_HDR))) { /* no room for any reports */ break; } /* note, a record header only has 8 bit fields, so this is safe. * we need an uncached pointer here too */ totalReportBytes = 0; /* get a copy */ creditsPendingMap = pHTC->EpCreditPendingMap; /* test pending map to see if we can send a report , if any * credits are available, we might as well send them on the * unused space in the buffer */ if (creditsPendingMap) { pRecHdr = (HTC_RECORD_HDR *)adf_nbuf_put_tail(pBuffers, sizeof(HTC_RECORD_HDR)); /* set the ID, the length will be updated with the number of credit reports we * can fit (see below) */ pRecHdr->RecordID = HTC_RECORD_CREDITS; pRecHdr->Length = 0; /* the credit report follows the record header */ totalReportBytes += sizeof(HTC_RECORD_HDR); room -= sizeof(HTC_RECORD_HDR); /* walkthrough pending credits map and build the records */ for (i = 0; (creditsPendingMap != 0) && (room >= (int)sizeof(HTC_CREDIT_REPORT)); i++) { compareMask = (1 << i); if (compareMask & creditsPendingMap) { pCreditRpt = (HTC_CREDIT_REPORT *)adf_nbuf_put_tail(pBuffers, sizeof(HTC_CREDIT_REPORT)); /* clear pending mask, we are going to return all these credits */ creditsPendingMap &= ~(compareMask); /* add this record */ pCreditRpt->EndpointID = i; pCreditRpt->Credits = (A_UINT8)pHTC->Endpoints[i].CreditsToReturn; /* remove pending credits, we always send deltas */ pHTC->Endpoints[i].CreditsToReturn = 0; /* adjust new threshold for this endpoint if needed */ CHECK_AND_ADJUST_CREDIT_THRESHOLD(&pHTC->Endpoints[i]); /* update this record length */ pRecHdr->Length += sizeof(HTC_CREDIT_REPORT); room -= sizeof(HTC_CREDIT_REPORT); totalReportBytes += sizeof(HTC_CREDIT_REPORT); if ( room < sizeof(HTC_CREDIT_REPORT) ) { break; } } } /* update new pending credits map */ pHTC->EpCreditPendingMap = creditsPendingMap; } if (totalReportBytes <= 0) { break; } /* must fit into a byte, this should never actually happen since * the maximum possible number of endpoints is 32. * The trailer can have at most 1 credit record with up to 32 reports in the record. * The trailer can have at most 1 lookahead record with only 1 lookahead report in the record. */ /* set header option bytes */ pHTCHdr->ControlBytes[0] = totalReportBytes; /* HTC frame contains a trailer */ pHTCHdr->Flags |= HTC_FLAGS_RECV_TRAILER; /* increment total size by the reports we added */ totsz += totalReportBytes; /* adjust the last buffer we used for adding on the trailer */ } while (FALSE); if (totsz == 0) { } /* set length for message (this includes any reports that were added above) */ pHTCHdr->PayloadLen = adf_os_htons(totsz); HIF_send_buffer(pHTC->hifHandle, pHTC->Endpoints[EndpointID].DownLinkPipeID, pBuffers); }