/*! \internal \brief Detaches the current thread from kmq \note Obtains ::cs_kmq_global */ void kmqint_detach_this_thread(void) { kmq_queue * q; q = (kmq_queue *) TlsGetValue(kmq_tls_queue); if(q) { kmq_message_ref * r; kmq_message * m; EnterCriticalSection(&q->cs); if (q->flags & KMQ_QUEUE_FLAG_DETACHING) { #ifdef DEBUG assert(FALSE); #endif LeaveCriticalSection(&q->cs); return; } q->flags |= KMQ_QUEUE_FLAG_DELETED | KMQ_QUEUE_FLAG_DETACHING; QGET(q, &r); while(r) { m = r->msg; LeaveCriticalSection(&q->cs); EnterCriticalSection(&cs_kmq_msg); EnterCriticalSection(&cs_kmq_msg_ref); kmqint_put_message_ref(r); LeaveCriticalSection(&cs_kmq_msg_ref); m->nFailed++; if(m->nCompleted + m->nFailed == m->nSent) { kmqint_put_message(m); } LeaveCriticalSection(&cs_kmq_msg); EnterCriticalSection(&q->cs); QGET(q, &r); } CloseHandle(q->wait_o); q->wait_o = NULL; q->flags &= ~KMQ_QUEUE_FLAG_DETACHING; LeaveCriticalSection(&q->cs); /* For now, we don't free the queue. */ /* TODO: before we can free the queue, we have to go through all the message type subscriptions and ad-hoc subscriptions and make sure no subscriptions exist which refer to this message queue. */ } }
/* Assumes that the context has been deleted from all relevant lists */ static void free_context(kherr_context * c) { kherr_context * ch; kherr_event * e; assert(IS_KHERR_CTX(c)); #ifdef DEBUG_CONTEXT if (IsDebuggerPresent()) kherr_debug_printf(L"Freeing context 0x%x\n", c); #endif EnterCriticalSection(&cs_error); if (c->desc_event) free_event(c->desc_event); c->desc_event = NULL; TPOPCHILD(c, &ch); while(ch) { free_context(ch); TPOPCHILD(c, &ch); } QGET(c, &e); while(e) { free_event(e); QGET(c, &e); } c->serial = 0; LPUSH(&ctx_free_list,c); LeaveCriticalSection(&cs_error); #ifdef DEBUG_CONTEXT if (IsDebuggerPresent()) kherr_debug_printf(L"Done with context 0x%x\n", c); #endif }
/*! \internal \brief Get the topmost message ref for a queue \note Obtains kmq_queue::cs */ void kmqint_get_queue_message_ref(kmq_queue * q, kmq_message_ref ** r) { EnterCriticalSection(&q->cs); if (q->flags & KMQ_QUEUE_FLAG_DELETED) { *r = NULL; } else { QGET(q,r); if(QTOP(q)) SetEvent(q->wait_o); } LeaveCriticalSection(&q->cs); }
DWORD WINAPI kmqint_completion_thread_proc(LPVOID p) { kmq_message * m; kherr_context * ctx; PDESCTHREAD(L"Msg completion thread", L"KMQ"); EnterCriticalSection(&cs_compl); do { if (QTOP(&kmq_completion_xfer) == NULL) { LeaveCriticalSection(&cs_compl); WaitForSingleObject(compl_wx, INFINITE); EnterCriticalSection(&cs_compl); /* go through the loop again before checking the queue */ } else { QGET(&kmq_completion_xfer, &m); LeaveCriticalSection(&cs_compl); EnterCriticalSection(&cs_kmq_msg); ctx = m->err_ctx; if (ctx) kherr_push_context(ctx); kmqint_put_message(m); if (ctx) kherr_pop_context(); LeaveCriticalSection(&cs_kmq_msg); EnterCriticalSection(&cs_compl); } } while(compl_continue); LeaveCriticalSection(&cs_compl); ExitThread(0); }
/*FUNCTION*------------------------------------------------------------- * * Function Name : MACNET_process_rx_bds * Returned Value : void * Comments : * Processes received buffers. * * NOTE: For MACNET, a frame consists of one or more buffers. * For ENET, a packet consists of one or more fragments * Therefore, a frame = a packet, and a buffer = a fragment. * * *END*-----------------------------------------------------------------*/ static void MACNET_process_rx_bds ( /* [IN] the Ethernet state structure */ ENET_CONTEXT_STRUCT_PTR enet_ptr ) { /* Body */ MACNET_CONTEXT_STRUCT_PTR macnet_context_ptr = (MACNET_CONTEXT_STRUCT_PTR) enet_ptr->MAC_CONTEXT_PTR; VENET_BD_STRUCT_PTR bd_ptr; PCB_PTR pcb_ptr=NULL; uint_16 rx_status; uint_32 i,length; int_32 ilength; uchar_ptr buffer, small_packet, large_packet; boolean buffer_is_valid, consumed, first, last; //_DCACHE_INVALIDATE(); // temporary - I can't find fast actual part of memory for cache invalidation /* Dequeue all received buffers (fragments) */ while (macnet_context_ptr->ActiveRxBDs > 0) { bd_ptr = &macnet_context_ptr->MACNET_RX_RING_PTR[macnet_context_ptr->NextRxBD]; _DCACHE_INVALIDATE_MBYTES((pointer)bd_ptr, sizeof(ENET_BD_STRUCT)); if ((bd_ptr->CONTROL & HOST_TO_BE_SHORT_CONST(ENET_BD_ETHER_RX_EMPTY)) != 0) { break; // Currently being written to by MII } BD_INC(macnet_context_ptr->NextRxBD,macnet_context_ptr->NumRxBDs); macnet_context_ptr->ActiveRxBDs--; ENET_INC_STATS(COMMON.ST_RX_TOTAL); rx_status = SHORT_BE_TO_HOST(bd_ptr->CONTROL); length = SHORT_BE_TO_HOST(bd_ptr->LENGTH); buffer = (uchar_ptr)LONG_BE_TO_HOST((uint_32)(bd_ptr->BUFFER)); //buffer = LONG_BE_TO_HOST(bd_ptr->BUFFER); consumed = FALSE; first = macnet_context_ptr->CurrentRxFrag == 0; last = TRUE; // Start by validating the received buffer // a buffer is valid if: // (it is error free) AND ( (it is the first buffer in a frame) OR (it is a subsequent buffer in a frame we want ) ) // buffer_is_valid = FALSE; if (rx_status & ENET_BD_ETHER_RX_TRUNCATED) { ENET_INC_STATS(ST_RX_RUNT); } else if (rx_status & ENET_BD_ETHER_RX_LAST_FRAME) { // Some error bits can only be checked on the last buffer // Overrun needs to be checked first, If this bit is // set, the other status bits, M, LG, NO, CR, and CL lose their normal meaning and are zero. This bit // is valid only if the L-bit is set. if (rx_status & ENET_BD_ETHER_RX_OVERRUN) { ENET_INC_STATS(ST_RX_OVERRUN); } else if (rx_status & ENET_BD_ETHER_RX_LENGTH_VIOLATION) { ENET_INC_STATS(ST_RX_GIANT); } else if (rx_status & ENET_BD_ETHER_RX_NON_OCTET) { ENET_INC_STATS(ST_RX_ALIGN); } else if (rx_status & ENET_BD_ETHER_RX_CRC_ERROR) { ENET_INC_STATS(ST_RX_FCS); } else { #if BSPCFG_ENABLE_ENET_HISTOGRAM uint_32 index = length>> ENET_HISTOGRAM_SHIFT; if (index < ENET_HISTOGRAM_ENTRIES) { ENET_INC_STATS(RX_HISTOGRAM[index]); } #endif // Good buffer (with Last bit set). buffer_is_valid = TRUE; // Remove CRC and compute buffer length ilength = length - (ENET_FRAMESIZE_TAIL + (macnet_context_ptr->CurrentRxFrag * enet_ptr->PARAM_PTR->RX_BUFFER_SIZE)); // The last fragment may contain nothing but part of the CRC. If this is the case, length will be <= 0, // and if length < 0, the previous fragment contains part of the CRC, which needs to be removed. if (ilength < 0) { length = 0; if (!first) { macnet_context_ptr->FRAGS[macnet_context_ptr->CurrentRxFrag-1].LENGTH += ilength; } else { // Should never get here - would mean we received a really small packet which should have already been // caught above. But if we do, discard it. buffer_is_valid = TRUE; } } else { length = (uint_32) ilength; } } } else { // Good buffer (without Last bit set). Can only be processed if we are not currently discarding. buffer_is_valid = TRUE; last = FALSE; } if (!buffer_is_valid) { // Was a buffer with an error ENET_INC_STATS(COMMON.ST_RX_ERRORS); MACNET_discard_current_rx(enet_ptr,macnet_context_ptr,last); } else { // If it is the first buffer in the frame, we have to see if the frame is of interest. if (first) { // make sure we don't examine a cached copy of the buffer _DCACHE_INVALIDATE_MBYTES(buffer, length); // the frame is of interest if there is a receiver registered for the destination and protocol. We can do this on the first buffer // of the frame, since the ethernet header information always fits in the first buffer of a frame. macnet_context_ptr->CurrentRxECB = ENET_find_receiver(enet_ptr, (ENET_HEADER_PTR) buffer, &length); } // If we have an Rx ECB, it means we want this packet if (macnet_context_ptr->CurrentRxECB ) { if (!first) { // If it is not the first packet, the cache is still valid. We could have put the invalidate before the first "if (first)", // but that would mean we may invalidate the cache on buffers that we know we are discarding. _DCACHE_INVALIDATE_MBYTES(buffer, length); } // Add the packet to the list of fragments in the PCB macnet_context_ptr->FRAGS[macnet_context_ptr->CurrentRxFrag].FRAGMENT = buffer; macnet_context_ptr->FRAGS[macnet_context_ptr->CurrentRxFrag].LENGTH = length; macnet_context_ptr->CurrentRxFrag++; consumed = TRUE; // Now, there are three cases: // !l - start or mid frame buffer - store it for later coalecing into a large buffer // !f, l - end of multi-buffer packet - coalece into a large buffer // f, l - single buffer packet - ready to process if (!last) { // Not the last frame in the packet. Have to make sure there is room for at least one // more fragment. If not, the packet is too large if (macnet_context_ptr->CurrentRxFrag >= MACNET_MAX_FRAGS) { MACNET_discard_current_rx(enet_ptr,macnet_context_ptr,last); ENET_INC_STATS(RX_FRAGS_EXCEEDED); } } else { // last buffer (may or may not also be the first buffer) so process it // There is a receiver, so need to allocate a receive PCB QGET(macnet_context_ptr->RxPCBHead, macnet_context_ptr->RxPCBTail, pcb_ptr); if (pcb_ptr) { // allocation successful, initialize new receive PCB pcb_ptr->PRIVATE = (pointer)enet_ptr; pcb_ptr->FREE = MACNET_rx_free; // Check to see if it is a large packet if (!first) { // Coalese large_packet = ENET_Dequeue_Buffer((pointer *)&macnet_context_ptr->LARGE_BUFFERS); if (large_packet) { // need to compute the length of this fragment, because the length on a BD with the L bit set // is not the length of the buffer, it is the length of the frame. pcb_ptr->FRAG[0].FRAGMENT = large_packet; pcb_ptr->FRAG[0].LENGTH = 0; pcb_ptr->FREE = MACNET_rx_free_large; for (i=0;i<macnet_context_ptr->CurrentRxFrag;i++) { if (macnet_context_ptr->FRAGS[i].LENGTH ) { _mem_copy(macnet_context_ptr->FRAGS[i].FRAGMENT, large_packet, macnet_context_ptr->FRAGS[i].LENGTH ); large_packet += macnet_context_ptr->FRAGS[i].LENGTH; pcb_ptr->FRAG[0].LENGTH += macnet_context_ptr->FRAGS[i].LENGTH; } ENET_Enqueue_Buffer((pointer*)&macnet_context_ptr->RX_BUFFERS, macnet_context_ptr->FRAGS[i].FRAGMENT); macnet_context_ptr->FRAGS[i].FRAGMENT = NULL; } ENET_INC_STATS(ST_RX_COPY_LARGE); } else { ENET_INC_STATS(RX_LARGE_BUFFERS_EXHAUSTED); MACNET_discard_current_rx(enet_ptr,macnet_context_ptr,last); QADD(macnet_context_ptr->RxPCBHead, macnet_context_ptr->RxPCBTail, pcb_ptr); pcb_ptr=NULL; } // Check to see if it is a small packet } else { pcb_ptr->FRAG[0].LENGTH = length; pcb_ptr->FRAG[0].FRAGMENT = buffer; if (length < MACNET_SMALL_PACKET_SIZE) { small_packet = ENET_Dequeue_Buffer((pointer *)&macnet_context_ptr->SMALL_BUFFERS); if (small_packet) { _mem_copy( macnet_context_ptr->FRAGS[0].FRAGMENT, small_packet, length ); pcb_ptr->FRAG[0].FRAGMENT = small_packet; pcb_ptr->FREE = MACNET_rx_free_small; consumed = FALSE; ENET_INC_STATS(ST_RX_COPY_SMALL); } } } if (pcb_ptr) { pcb_ptr->FRAG[1].LENGTH = 0; pcb_ptr->FRAG[1].FRAGMENT = NULL; #if ENETCFG_SUPPORT_PTP if (macnet_context_ptr->PTP_PRIV->PTIMER_PRESENT) { MACNET_ptp_store_rxstamp(enet_ptr, pcb_ptr, bd_ptr); } #endif /* ENETCFG_SUPPORT_PTP */ // Call the receiver's service function to pass the PCB to the receiver macnet_context_ptr->CurrentRxECB->SERVICE(pcb_ptr, macnet_context_ptr->CurrentRxECB->PRIVATE); // and clear the current ECB and PCB info, as it's gone to the receiver now. macnet_context_ptr->CurrentRxECB = NULL; macnet_context_ptr->CurrentRxFrag = 0; } } else { // Couldn't get a Receive PCB, so need to discard buffers until last buffer in frame. MACNET_discard_current_rx(enet_ptr,macnet_context_ptr,last); ENET_INC_STATS(RX_PCBS_EXHAUSTED); } } } } if (!consumed) { ENET_INC_STATS(COMMON.ST_RX_DISCARDED); ENET_Enqueue_Buffer((pointer*)&macnet_context_ptr->RX_BUFFERS, buffer); } }