static int ProcessCreditCounterReadBuffer(u8 *pBuffer, int Length) { int credits = 0; /* theory of how this works: * We read the credit decrement register multiple times on a byte-wide basis. * The number of times (32) aligns the I/O operation to be a multiple of 4 bytes and provides a * reasonable chance to acquire "all" pending credits in a single I/O operation. * * Once we obtain the filled buffer, we can walk through it looking for credit decrement transitions. * Each non-zero byte represents a single credit decrement (which is a credit given back to the host) * For example if the target provides 3 credits and added 4 more during the 32-byte read operation the following * pattern "could" appear: * * 0x3 0x2 0x1 0x0 0x0 0x0 0x0 0x0 0x1 0x0 0x1 0x0 0x1 0x0 0x1 0x0 ......rest zeros * <---------> <-----------------------------> * \_ credits aleady there \_ target adding 4 more credits * * The total available credits would be 7, since there are 7 non-zero bytes in the buffer. * * */ if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) { DebugDumpBytes(pBuffer, Length, "GMBOX Credit read buffer"); } while (Length) { if (*pBuffer != 0) { credits++; } Length--; pBuffer++; } return credits; }
static A_STATUS HTCProcessTrailer(HTC_TARGET *target, A_UINT8 *pBuffer, int Length, HTC_ENDPOINT_ID FromEndpoint) { HTC_RECORD_HDR *pRecord; A_UINT8 htc_rec_id; A_UINT8 htc_rec_len; A_UINT8 *pRecordBuf; A_UINT8 *pOrigBuffer; int origLength; A_STATUS status; AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCProcessTrailer (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)); HTCProcessCreditRpt(target, (HTC_CREDIT_REPORT *)pRecordBuf, htc_rec_len / (sizeof(HTC_CREDIT_REPORT)), FromEndpoint); break; default: AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (" 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)) { DebugDumpBytes(pOrigBuffer,origLength,"BAD Recv Trailer"); } AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCProcessTrailer \n")); return status; }
/* process pending interrupts synchronously */ static int ProcessPendingIRQs(AR6K_DEVICE *pDev, bool *pDone, bool *pASyncProcessing) { int status = 0; u8 host_int_status = 0; u32 lookAhead = 0; AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+ProcessPendingIRQs: (dev: 0x%lX)\n", (unsigned long)pDev)); /*** NOTE: the HIF implementation guarantees that the context of this call allows * us to perform SYNCHRONOUS I/O, that is we can block, sleep or call any API that * can block or switch thread/task ontexts. * This is a fully schedulable context. * */ do { if (pDev->IrqEnableRegisters.int_status_enable == 0) { /* interrupt enables have been cleared, do not try to process any pending interrupts that * may result in more bus transactions. The target may be unresponsive at this * point. */ break; } if (pDev->GetPendingEventsFunc != NULL) { HIF_PENDING_EVENTS_INFO events; #ifdef THREAD_X events.Polling= 0; #endif /* the HIF layer uses a special mechanism to get events * get this synchronously */ status = pDev->GetPendingEventsFunc(pDev->HIFDevice, &events, NULL); if (status) { break; } if (events.Events & HIF_RECV_MSG_AVAIL) { lookAhead = events.LookAhead; if (0 == lookAhead) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" ProcessPendingIRQs1 lookAhead is zero! \n")); } } if (!(events.Events & HIF_OTHER_EVENTS) || !(pDev->IrqEnableRegisters.int_status_enable & OTHER_INTS_ENABLED)) { /* no need to read the register table, no other interesting interrupts. * Some interfaces (like SPI) can shadow interrupt sources without * requiring the host to do a full table read */ break; } /* otherwise fall through and read the register table */ } /* * Read the first 28 bytes of the HTC register table. This will yield us * the value of different int status registers and the lookahead * registers. * length = sizeof(int_status) + sizeof(cpu_int_status) + * sizeof(error_int_status) + sizeof(counter_int_status) + * sizeof(mbox_frame) + sizeof(rx_lookahead_valid) + * sizeof(hole) + sizeof(rx_lookahead) + * sizeof(int_status_enable) + sizeof(cpu_int_status_enable) + * sizeof(error_status_enable) + * sizeof(counter_int_status_enable); * */ #ifdef CONFIG_MMC_SDHCI_S3C pDev->IrqProcRegisters.host_int_status = 0; pDev->IrqProcRegisters.rx_lookahead_valid = 0; pDev->IrqProcRegisters.host_int_status2 = 0; pDev->IrqProcRegisters.rx_lookahead[0] = 0; pDev->IrqProcRegisters.rx_lookahead[1] = 0xaaa5555; #endif /* CONFIG_MMC_SDHCI_S3C */ status = HIFReadWrite(pDev->HIFDevice, HOST_INT_STATUS_ADDRESS, (u8 *)&pDev->IrqProcRegisters, AR6K_IRQ_PROC_REGS_SIZE, HIF_RD_SYNC_BYTE_INC, NULL); if (status) { break; } #ifdef ATH_DEBUG_MODULE if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_IRQ)) { DevDumpRegisters(pDev, &pDev->IrqProcRegisters, &pDev->IrqEnableRegisters); } #endif /* Update only those registers that are enabled */ host_int_status = pDev->IrqProcRegisters.host_int_status & pDev->IrqEnableRegisters.int_status_enable; if (NULL == pDev->GetPendingEventsFunc) { /* only look at mailbox status if the HIF layer did not provide this function, * on some HIF interfaces reading the RX lookahead is not valid to do */ if (host_int_status & (1 << HTC_MAILBOX)) { /* mask out pending mailbox value, we use "lookAhead" as the real flag for * mailbox processing below */ host_int_status &= ~(1 << HTC_MAILBOX); if (pDev->IrqProcRegisters.rx_lookahead_valid & (1 << HTC_MAILBOX)) { /* mailbox has a message and the look ahead is valid */ lookAhead = pDev->IrqProcRegisters.rx_lookahead[HTC_MAILBOX]; if (0 == lookAhead) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" ProcessPendingIRQs2, lookAhead is zero! \n")); } } } } else { /* not valid to check if the HIF has another mechanism for reading mailbox pending status*/ host_int_status &= ~(1 << HTC_MAILBOX); } if (pDev->GMboxEnabled) { /*call GMBOX layer to process any interrupts of interest */ status = DevCheckGMboxInterrupts(pDev); } } while (false); do { /* did the interrupt status fetches succeed? */ if (status) { break; } if ((0 == host_int_status) && (0 == lookAhead)) { /* nothing to process, the caller can use this to break out of a loop */ *pDone = true; break; } if (lookAhead != 0) { int fetched = 0; AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("Pending mailbox message, LookAhead: 0x%X\n",lookAhead)); /* Mailbox Interrupt, the HTC layer may issue async requests to empty the * mailbox... * When emptying the recv mailbox we use the async handler above called from the * completion routine of the callers read request. This can improve performance * by reducing context switching when we rapidly pull packets */ status = pDev->MessagePendingCallback(pDev->HTCContext, &lookAhead, 1, pASyncProcessing, &fetched); if (status) { break; } if (!fetched) { /* HTC could not pull any messages out due to lack of resources */ /* force DSR handler to ack the interrupt */ *pASyncProcessing = false; pDev->RecheckIRQStatusCnt = 0; } } /* now handle the rest of them */ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, (" Valid interrupt source(s) for OTHER interrupts: 0x%x\n", host_int_status)); if (HOST_INT_STATUS_CPU_GET(host_int_status)) { /* CPU Interrupt */ status = DevServiceCPUInterrupt(pDev); if (status){ break; } } if (HOST_INT_STATUS_ERROR_GET(host_int_status)) { /* Error Interrupt */ status = DevServiceErrorInterrupt(pDev); if (status){ break; } } if (HOST_INT_STATUS_COUNTER_GET(host_int_status)) { /* Counter Interrupt */ status = DevServiceCounterInterrupt(pDev); if (status){ break; } } } while (false); /* an optimization to bypass reading the IRQ status registers unecessarily which can re-wake * the target, if upper layers determine that we are in a low-throughput mode, we can * rely on taking another interrupt rather than re-checking the status registers which can * re-wake the target. * * NOTE : for host interfaces that use the special GetPendingEventsFunc, this optimization cannot * be used due to possible side-effects. For example, SPI requires the host to drain all * messages from the mailbox before exiting the ISR routine. */ if (!(*pASyncProcessing) && (pDev->RecheckIRQStatusCnt == 0) && (pDev->GetPendingEventsFunc == NULL)) { AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("Bypassing IRQ Status re-check, forcing done \n")); *pDone = true; } AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-ProcessPendingIRQs: (done:%d, async:%d) status=%d \n", *pDone, *pASyncProcessing, status)); return status; }
/* Start HTC, enable interrupts and let the target know host has finished setup */ A_STATUS HTCStart(HTC_HANDLE HTCHandle) { HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); HTC_PACKET *pPacket; A_STATUS status; AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCStart Enter\n")); printk("HTCStart Enter\n"); /* make sure interrupts are disabled at the chip level, * this function can be called again from a reboot of the target without shutting down HTC */ DevDisableInterrupts(&target->Device); /* make sure state is cleared again */ target->HTCStateFlags = 0; /* now that we are starting, push control receive buffers into the * HTC control endpoint */ while (1) { pPacket = HTC_ALLOC_CONTROL_RX(target); if (NULL == pPacket) { break; } HTCAddReceivePkt((HTC_HANDLE)target,pPacket); } do { AR_DEBUG_ASSERT(target->InitCredits != NULL); AR_DEBUG_ASSERT(target->EpCreditDistributionListHead != NULL); AR_DEBUG_ASSERT(target->EpCreditDistributionListHead->pNext != NULL); /* call init credits callback to do the distribution , * NOTE: the first entry in the distribution list is ENDPOINT_0, so * we pass the start of the list after this one. */ target->InitCredits(target->pCredDistContext, target->EpCreditDistributionListHead->pNext, target->TargetCredits); if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_TRC)) { DumpCreditDistStates(target); } /* the caller is done connecting to services, so we can indicate to the * target that the setup phase is complete */ status = HTCSendSetupComplete(target); if (A_FAILED(status)) { break; } /* unmask interrupts */ status = DevUnmaskInterrupts(&target->Device); if (A_FAILED(status)) { HTCStop(target); } } while (FALSE); AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCStart Exit\n")); printk("HTCStart Exit\n"); return status; }
/* process pending interrupts synchronously */ static A_STATUS ProcessPendingIRQs(AR6K_DEVICE *pDev, A_BOOL *pDone, A_BOOL *pASyncProcessing) { A_STATUS status = A_OK; A_UINT8 host_int_status = 0; A_UINT32 lookAhead = 0; AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+ProcessPendingIRQs: (dev: 0x%X)\n", (A_UINT32)pDev)); /*** NOTE: the HIF implementation guarantees that the context of this call allows * us to perform SYNCHRONOUS I/O, that is we can block, sleep or call any API that * can block or switch thread/task ontexts. * This is a fully schedulable context. * */ do { if (pDev->IrqEnableRegisters.int_status_enable == 0) { /* interrupt enables have been cleared, do not try to process any pending interrupts that * may result in more bus transactions. The target may be unresponsive at this * point. */ break; } if (pDev->GetPendingEventsFunc != NULL) { HIF_PENDING_EVENTS_INFO events; /* the HIF layer uses a special mechanism to get events * get this synchronously */ status = pDev->GetPendingEventsFunc(pDev->HIFDevice, &events, NULL); if (A_FAILED(status)) { break; } if (events.Events & HIF_RECV_MSG_AVAIL) { lookAhead = events.LookAhead; if (0 == lookAhead) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" ProcessPendingIRQs1 lookAhead is zero! \n")); } } if (!(events.Events & HIF_OTHER_EVENTS) || !(pDev->IrqEnableRegisters.int_status_enable & OTHER_INTS_ENABLED)) { /* no need to read the register table, no other interesting interrupts. * Some interfaces (like SPI) can shadow interrupt sources without * requiring the host to do a full table read */ break; } /* otherwise fall through and read the register table */ } /* * Read the first 28 bytes of the HTC register table. This will yield us * the value of different int status registers and the lookahead * registers. * length = sizeof(int_status) + sizeof(cpu_int_status) + * sizeof(error_int_status) + sizeof(counter_int_status) + * sizeof(mbox_frame) + sizeof(rx_lookahead_valid) + * sizeof(hole) + sizeof(rx_lookahead) + * sizeof(int_status_enable) + sizeof(cpu_int_status_enable) + * sizeof(error_status_enable) + * sizeof(counter_int_status_enable); * */ status = HIFReadWrite(pDev->HIFDevice, HOST_INT_STATUS_ADDRESS, (A_UINT8 *)&pDev->IrqProcRegisters, AR6K_IRQ_PROC_REGS_SIZE, HIF_RD_SYNC_BYTE_INC, NULL); if (A_FAILED(status)) { break; } if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_IRQ)) { DevDumpRegisters(&pDev->IrqProcRegisters, &pDev->IrqEnableRegisters); } /* Update only those registers that are enabled */ host_int_status = pDev->IrqProcRegisters.host_int_status & pDev->IrqEnableRegisters.int_status_enable; if (NULL == pDev->GetPendingEventsFunc) { /* only look at mailbox status if the HIF layer did not provide this function, * on some HIF interfaces reading the RX lookahead is not valid to do */ if (host_int_status & (1 << HTC_MAILBOX)) { /* mask out pending mailbox value, we use "lookAhead" as the real flag for * mailbox processing below */ host_int_status &= ~(1 << HTC_MAILBOX); if (pDev->IrqProcRegisters.rx_lookahead_valid & (1 << HTC_MAILBOX)) { /* mailbox has a message and the look ahead is valid */ lookAhead = pDev->IrqProcRegisters.rx_lookahead[HTC_MAILBOX]; if (0 == lookAhead) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" ProcessPendingIRQs2, lookAhead is zero! \n")); } } } } else { /* not valid to check if the HIF has another mechanism for reading mailbox pending status*/ host_int_status &= ~(1 << HTC_MAILBOX); } } while (FALSE); do { /* did the interrupt status fetches succeed? */ if (A_FAILED(status)) { break; } if ((0 == host_int_status) && (0 == lookAhead)) { /* nothing to process, the caller can use this to break out of a loop */ *pDone = TRUE; break; } if (lookAhead != 0) { AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("Pending mailbox message, LookAhead: 0x%X\n",lookAhead)); /* Mailbox Interrupt, the HTC layer may issue async requests to empty the * mailbox... * When emptying the recv mailbox we use the async handler above called from the * completion routine of the callers read request. This can improve performance * by reducing context switching when we rapidly pull packets */ status = pDev->MessagePendingCallback(pDev->HTCContext, lookAhead, pASyncProcessing); if (A_FAILED(status)) { break; } } /* now handle the rest of them */ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, (" Valid interrupt source(s) for OTHER interrupts: 0x%x\n", host_int_status)); if (HOST_INT_STATUS_CPU_GET(host_int_status)) { /* CPU Interrupt */ status = DevServiceCPUInterrupt(pDev); if (A_FAILED(status)){ break; } } if (HOST_INT_STATUS_ERROR_GET(host_int_status)) { /* Error Interrupt */ status = DevServiceErrorInterrupt(pDev); if (A_FAILED(status)){ break; } } if (HOST_INT_STATUS_COUNTER_GET(host_int_status)) { /* Counter Interrupt */ status = DevServiceCounterInterrupt(pDev); if (A_FAILED(status)){ break; } } } while (FALSE); AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-ProcessPendingIRQs: (done:%d, async:%d) status=%d \n", *pDone, *pASyncProcessing, status)); return status; }
static void ar6000_hci_pkt_recv(void *pContext, HTC_PACKET *pPacket) { AR6K_HCI_BRIDGE_INFO *pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)pContext; struct sk_buff *skb; AR_SOFTC_DEV_T *arDev = pHcidevInfo->ar->arDev[0]; A_ASSERT(pHcidevInfo != NULL); skb = (struct sk_buff *)pPacket->pPktContext; A_ASSERT(skb != NULL); do { if (A_FAILED(pPacket->Status)) { break; } AR_DEBUG_PRINTF(ATH_DEBUG_HCI_RECV, ("HCI Bridge, packet received type : %d len:%d \n", HCI_GET_PACKET_TYPE(pPacket),pPacket->ActualLength)); /* set the actual buffer position in the os buffer, HTC recv buffers posted to HCI are set * to fill the front of the buffer */ A_NETBUF_PUT(skb,pPacket->ActualLength + pHcidevInfo->HCIProps.HeadRoom); A_NETBUF_PULL(skb,pHcidevInfo->HCIProps.HeadRoom); if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_HCI_DUMP)) { AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("<<< Recv HCI %s packet len:%d \n", (HCI_GET_PACKET_TYPE(pPacket) == HCI_EVENT_TYPE) ? "EVENT" : "ACL", skb->len)); AR_DEBUG_PRINTBUF(skb->data, skb->len,"BT HCI RECV Packet Dump"); } if (pHcidevInfo->HciNormalMode) { /* indicate the packet */ if (bt_indicate_recv(pHcidevInfo,HCI_GET_PACKET_TYPE(pPacket),skb)) { /* bt stack accepted the packet */ skb = NULL; } break; } /* for testing, indicate packet to the network stack */ #ifdef EXPORT_HCI_BRIDGE_INTERFACE skb->dev = (struct net_device *)(pHcidevInfo->HCITransHdl.netDevice); if ((((struct net_device *)pHcidevInfo->HCITransHdl.netDevice)->flags & IFF_UP) == IFF_UP) { skb->protocol = eth_type_trans(skb, (struct net_device *)(pHcidevInfo->HCITransHdl.netDevice)); #else skb->dev = arDev->arNetDev; if ((arDev->arNetDev->flags & IFF_UP) == IFF_UP) { skb->protocol = eth_type_trans(skb, arDev->arNetDev); #endif netif_rx(skb); skb = NULL; } } while (FALSE); FreeHTCStruct(pHcidevInfo,pPacket); if (skb != NULL) { /* packet was not accepted, free it */ FreeBtOsBuf(pHcidevInfo,skb); } } static void ar6000_hci_pkt_refill(void *pContext, HCI_TRANSPORT_PACKET_TYPE Type, int BuffersAvailable) { AR6K_HCI_BRIDGE_INFO *pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)pContext; int refillCount; if (Type == HCI_ACL_TYPE) { refillCount = MAX_ACL_RECV_BUFS - BuffersAvailable; } else { refillCount = MAX_EVT_RECV_BUFS - BuffersAvailable; } if (refillCount > 0) { RefillRecvBuffers(pHcidevInfo,Type,refillCount); } }
static INLINE A_STATUS HTCProcessTrailer(HTC_TARGET *target, A_UINT8 *pBuffer, int Length, A_UINT32 *pNextLookAhead, HTC_ENDPOINT_ID FromEndpoint) { HTC_RECORD_HDR *pRecord; A_UINT8 *pRecordBuf; HTC_LOOKAHEAD_REPORT *pLookAhead; A_UINT8 *pOrigBuffer; int origLength; A_STATUS status; AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCProcessTrailer (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); if (pRecord->Length > 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", pRecord->Length, pRecord->RecordID, Length)); status = A_EPROTO; break; } /* start of record follows the header */ pRecordBuf = pBuffer; switch (pRecord->RecordID) { case HTC_RECORD_CREDITS: AR_DEBUG_ASSERT(pRecord->Length >= sizeof(HTC_CREDIT_REPORT)); HTCProcessCreditRpt(target, (HTC_CREDIT_REPORT *)pRecordBuf, pRecord->Length / (sizeof(HTC_CREDIT_REPORT)), FromEndpoint); break; case HTC_RECORD_LOOKAHEAD: AR_DEBUG_ASSERT(pRecord->Length >= sizeof(HTC_LOOKAHEAD_REPORT)); pLookAhead = (HTC_LOOKAHEAD_REPORT *)pRecordBuf; if ((pLookAhead->PreValid == ((~pLookAhead->PostValid) & 0xFF)) && (pNextLookAhead != NULL)) { AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" LookAhead Report Found (pre valid:0x%X, post valid:0x%X) \n", pLookAhead->PreValid, pLookAhead->PostValid)); /* look ahead bytes are valid, copy them over */ ((A_UINT8 *)pNextLookAhead)[0] = pLookAhead->LookAhead[0]; ((A_UINT8 *)pNextLookAhead)[1] = pLookAhead->LookAhead[1]; ((A_UINT8 *)pNextLookAhead)[2] = pLookAhead->LookAhead[2]; ((A_UINT8 *)pNextLookAhead)[3] = pLookAhead->LookAhead[3]; if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) { DebugDumpBytes((A_UINT8 *)pNextLookAhead,4,"Next Look Ahead"); } } break; default: AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (" unhandled record: id:%d length:%d \n", pRecord->RecordID, pRecord->Length)); break; } if (A_FAILED(status)) { break; } /* advance buffer past this record for next time around */ pBuffer += pRecord->Length; Length -= pRecord->Length; } if (A_FAILED(status)) { DebugDumpBytes(pOrigBuffer,origLength,"BAD Recv Trailer"); } AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCProcessTrailer \n")); return status; }
/* process a received message (i.e. strip off header, process any trailer data) * note : locks must be released when this function is called */ static A_STATUS HTCProcessRecvHeader(HTC_TARGET *target, HTC_PACKET *pPacket, A_UINT32 *pNextLookAhead) { A_UINT8 temp; A_UINT8 *pBuf; A_STATUS status = A_OK; A_UINT16 payloadLen; A_UINT32 lookAhead; pBuf = pPacket->pBuffer; AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCProcessRecvHeader \n")); if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) { AR_DEBUG_PRINTBUF(pBuf,pPacket->ActualLength,"HTC Recv PKT"); } do { /* note, we cannot assume the alignment of pBuffer, so we use the safe macros to * retrieve 16 bit fields */ payloadLen = A_GET_UINT16_FIELD(pBuf, HTC_FRAME_HDR, PayloadLen); ((A_UINT8 *)&lookAhead)[0] = pBuf[0]; ((A_UINT8 *)&lookAhead)[1] = pBuf[1]; ((A_UINT8 *)&lookAhead)[2] = pBuf[2]; ((A_UINT8 *)&lookAhead)[3] = pBuf[3]; if (lookAhead != pPacket->HTCReserved) { /* somehow the lookahead that gave us the full read length did not * reflect the actual header in the pending message */ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HTCProcessRecvHeader, lookahead mismatch! \n")); DebugDumpBytes((A_UINT8 *)&pPacket->HTCReserved,4,"Expected Message LookAhead"); DebugDumpBytes(pBuf,sizeof(HTC_FRAME_HDR),"Current Frame Header"); #ifdef HTC_CAPTURE_LAST_FRAME DebugDumpBytes((A_UINT8 *)&target->LastFrameHdr,sizeof(HTC_FRAME_HDR),"Last Frame Header"); if (target->LastTrailerLength != 0) { DebugDumpBytes(target->LastTrailer, target->LastTrailerLength, "Last trailer"); } #endif status = A_EPROTO; break; } /* get flags */ temp = A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, Flags); if (temp & HTC_FLAGS_RECV_TRAILER) { /* this packet has a trailer */ /* extract the trailer length in control byte 0 */ temp = A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, ControlBytes[0]); if ((temp < sizeof(HTC_RECORD_HDR)) || (temp > payloadLen)) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HTCProcessRecvHeader, invalid header (payloadlength should be :%d, CB[0] is:%d) \n", payloadLen, temp)); status = A_EPROTO; break; } /* process trailer data that follows HDR + application payload */ status = HTCProcessTrailer(target, (pBuf + HTC_HDR_LENGTH + payloadLen - temp), temp, pNextLookAhead, pPacket->Endpoint); if (A_FAILED(status)) { break; } #ifdef HTC_CAPTURE_LAST_FRAME A_MEMCPY(target->LastTrailer, (pBuf + HTC_HDR_LENGTH + payloadLen - temp), temp); target->LastTrailerLength = temp; #endif /* trim length by trailer bytes */ pPacket->ActualLength -= temp; } #ifdef HTC_CAPTURE_LAST_FRAME else { target->LastTrailerLength = 0; } #endif /* if we get to this point, the packet is good */ /* remove header and adjust length */ pPacket->pBuffer += HTC_HDR_LENGTH; pPacket->ActualLength -= HTC_HDR_LENGTH; } while (FALSE); if (A_FAILED(status)) { /* dump the whole packet */ DebugDumpBytes(pBuf,pPacket->ActualLength,"BAD HTC Recv PKT"); } else { #ifdef HTC_CAPTURE_LAST_FRAME A_MEMCPY(&target->LastFrameHdr,pBuf,sizeof(HTC_FRAME_HDR)); #endif if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) { if (pPacket->ActualLength > 0) { AR_DEBUG_PRINTBUF(pPacket->pBuffer,pPacket->ActualLength,"HTC - Application Msg"); } } } AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCProcessRecvHeader \n")); return status; }
static void usb_hif_usb_recv_bundle_complete(struct urb *urb) { HIF_URB_CONTEXT *urb_context = (HIF_URB_CONTEXT *) urb->context; A_STATUS status = A_OK; adf_nbuf_t buf = NULL; HIF_USB_PIPE *pipe = urb_context->pipe; A_UINT8 *netdata, *netdata_new; A_UINT32 netlen, netlen_new; HTC_FRAME_HDR *HtcHdr; A_UINT16 payloadLen; adf_nbuf_t new_skb = NULL; AR_DEBUG_PRINTF(USB_HIF_DEBUG_BULK_IN, ( "+%s: recv pipe: %d, stat:%d,len:%d urb:0x%p\n", __func__, pipe->logical_pipe_num, urb->status, urb->actual_length, urb)); /* this urb is not pending anymore */ usb_hif_remove_pending_transfer(urb_context); do { if (urb->status != 0) { status = A_ECOMM; switch (urb->status) { case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* NOTE: no need to spew these errors when * device is removed * or urb is killed due to driver shutdown */ status = A_ECANCELED; break; default: AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ( "%s recv pipe: %d (ep:0x%2.2X), failed:%d\n", __func__, pipe->logical_pipe_num, pipe->ep_address, urb->status)); break; } break; } if (urb->actual_length == 0) break; buf = urb_context->buf; if (AR_DEBUG_LVL_CHECK(USB_HIF_DEBUG_DUMP_DATA)) { A_UINT8 *data; A_UINT32 len; adf_nbuf_peek_header(buf, &data, &len); DebugDumpBytes(data, len, "hif recv data"); } adf_nbuf_peek_header(buf, &netdata, &netlen); netlen = urb->actual_length; do { #if defined(AR6004_1_0_ALIGN_WAR) A_UINT8 extra_pad; A_UINT16 act_frame_len; #endif A_UINT16 frame_len; /* Hack into HTC header for bundle processing */ HtcHdr = (HTC_FRAME_HDR *) netdata; if (HtcHdr->EndpointID >= ENDPOINT_MAX) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("athusb: Rx: invalid EndpointID=%d\n", HtcHdr->EndpointID)); break; } payloadLen = HtcHdr->PayloadLen; payloadLen = A_LE2CPU16(payloadLen); #if defined(AR6004_1_0_ALIGN_WAR) act_frame_len = (HTC_HDR_LENGTH + payloadLen); if (HtcHdr->EndpointID == 0 || HtcHdr->EndpointID == 1) { /* assumption: target won't pad on HTC endpoint * 0 & 1. */ extra_pad = 0; } else { extra_pad = A_GET_UINT8_FIELD((A_UINT8 *) HtcHdr, HTC_FRAME_HDR, ControlBytes[1]); } #endif if (payloadLen > HIF_USB_RX_BUFFER_SIZE) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("athusb: payloadLen too long %u\n", payloadLen)); break; } #if defined(AR6004_1_0_ALIGN_WAR) frame_len = (act_frame_len + extra_pad); #else frame_len = (HTC_HDR_LENGTH + payloadLen); #endif if (netlen >= frame_len) { /* allocate a new skb and copy */ #if defined(AR6004_1_0_ALIGN_WAR) new_skb = adf_nbuf_alloc(NULL, act_frame_len, 0, 4, FALSE); if (new_skb == NULL) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ( "athusb: allocate skb (len=%u) failed\n", act_frame_len)); break; } adf_nbuf_peek_header(new_skb, &netdata_new, &netlen_new); adf_os_mem_copy(netdata_new, netdata, act_frame_len); adf_nbuf_put_tail(new_skb, act_frame_len); #else new_skb = adf_nbuf_alloc(NULL, frame_len, 0, 4, FALSE); if (new_skb == NULL) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ( "athusb: allocate skb (len=%u) failed\n", frame_len)); break; } adf_nbuf_peek_header(new_skb, &netdata_new, &netlen_new); adf_os_mem_copy(netdata_new, netdata, frame_len); adf_nbuf_put_tail(new_skb, frame_len); #endif skb_queue_tail(&pipe->io_comp_queue, new_skb); new_skb = NULL; netdata += frame_len; netlen -= frame_len; } else { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ( "athusb: subframe length %d not fitted into bundle packet length %d\n" , netlen, frame_len)); break; } } while (netlen); schedule_work(&pipe->io_complete_work); } while (FALSE); if (urb_context->buf == NULL) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("athusb: buffer in urb_context is NULL\n")); } /* reset urb_context->buf ==> seems not necessary */ usb_hif_free_urb_to_pipe(urb_context->pipe, urb_context); if (A_SUCCESS(status)) { if (pipe->urb_cnt >= pipe->urb_cnt_thresh) { /* our free urbs are piling up, post more transfers */ usb_hif_post_recv_bundle_transfers(pipe, 0 /* pass zero for not allocating urb-buffer again */ ); } } AR_DEBUG_PRINTF(USB_HIF_DEBUG_BULK_IN, ("-%s\n", __func__)); }
static void usb_hif_usb_recv_complete(struct urb *urb) { HIF_URB_CONTEXT *urb_context = (HIF_URB_CONTEXT *) urb->context; A_STATUS status = A_OK; adf_nbuf_t buf = NULL; HIF_USB_PIPE *pipe = urb_context->pipe; AR_DEBUG_PRINTF(USB_HIF_DEBUG_BULK_IN, ( "+%s: recv pipe: %d, stat:%d,len:%d urb:0x%p\n", __func__, pipe->logical_pipe_num, urb->status, urb->actual_length, urb)); /* this urb is not pending anymore */ usb_hif_remove_pending_transfer(urb_context); do { if (urb->status != 0) { status = A_ECOMM; switch (urb->status) { #ifdef RX_SG_SUPPORT case -EOVERFLOW: urb->actual_length = HIF_USB_RX_BUFFER_SIZE; status = A_OK; break; #endif case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* NOTE: no need to spew these errors when * device is removed * or urb is killed due to driver shutdown */ status = A_ECANCELED; break; default: AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ( "%s recv pipe: %d (ep:0x%2.2X), failed:%d\n", __func__, pipe->logical_pipe_num, pipe->ep_address, urb->status)); break; } break; } if (urb->actual_length == 0) break; buf = urb_context->buf; /* we are going to pass it up */ urb_context->buf = NULL; adf_nbuf_put_tail(buf, urb->actual_length); if (AR_DEBUG_LVL_CHECK(USB_HIF_DEBUG_DUMP_DATA)) { A_UINT8 *data; A_UINT32 len; adf_nbuf_peek_header(buf, &data, &len); DebugDumpBytes(data, len, "hif recv data"); } /* note: queue implements a lock */ skb_queue_tail(&pipe->io_comp_queue, buf); schedule_work(&pipe->io_complete_work); } while (FALSE); usb_hif_cleanup_recv_urb(urb_context); if (A_SUCCESS(status)) { if (pipe->urb_cnt >= pipe->urb_cnt_thresh) { /* our free urbs are piling up, post more transfers */ usb_hif_post_recv_transfers(pipe, HIF_USB_RX_BUFFER_SIZE); } } AR_DEBUG_PRINTF(USB_HIF_DEBUG_BULK_IN, ("-%s\n", __func__)); }