VOID NdisprotReceiveNetBufferLists( IN NDIS_HANDLE ProtocolBindingContext, IN PNET_BUFFER_LIST pNetBufferLists, IN NDIS_PORT_NUMBER PortNumber, IN ULONG NumberOfNetBufferLists, IN ULONG ReceiveFlags ) /*++ Routine Description: Protocol entry point called by NDIS if the driver below uses NDIS 6 net buffer list indications. If the miniport allows us to hold on to this net buffer list, we use it as is, otherwise we make a copy. Arguments: ProtocolBindingContext - pointer to open context pNetBufferLists - a list of the Net Buffer lists being indicated up. PortNumber - Port on which the Net Bufer list was received NumberOfNetBufferLists - the number of NetBufferLists in this indication ReceiveFlags - indicates whether the NetBufferLists can be pended in the protocol driver. Return Value: --*/ { PNDISPROT_OPEN_CONTEXT pOpenContext; PMDL pMdl = NULL; UINT BufferLength; PNDISPROT_ETH_HEADER pEthHeader = NULL; PNET_BUFFER_LIST pCopyNetBufList; PUCHAR pCopyBuf; ULONG TotalLength; ULONG BytesCopied; PNET_BUFFER_LIST pNetBufList; PNET_BUFFER_LIST pNetBufListOrig = NULL; PNET_BUFFER_LIST pNextNetBufList; PNET_BUFFER_LIST pReturnNetBufList = NULL; PNET_BUFFER_LIST pLastReturnNetBufList = NULL; NTSTATUS NtStatus; BOOLEAN bAcceptedReceive; ULONG Offset; ULONG ReturnFlags = 0; BOOLEAN DispatchLevel; BOOLEAN NoReadIRP = FALSE; UNREFERENCED_PARAMETER(PortNumber); UNREFERENCED_PARAMETER(NumberOfNetBufferLists); pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext; if (NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags)) { NDIS_SET_RETURN_FLAG(ReturnFlags, NDIS_RETURN_FLAGS_DISPATCH_LEVEL); } NPROT_STRUCT_ASSERT(pOpenContext, oc); if ((pOpenContext->State == NdisprotPausing) || (pOpenContext->State == NdisprotPaused)) { if (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags) == TRUE) { NdisReturnNetBufferLists(pOpenContext->BindingHandle, pNetBufferLists, ReturnFlags); } return; } pNetBufList = pNetBufferLists; while (pNetBufList != NULL) { pNextNetBufList = NET_BUFFER_LIST_NEXT_NBL (pNetBufList); NBL_CLEAR_PROT_RSVD_FLAG(pNetBufList, NBL_PROT_RSVD_FLAGS); bAcceptedReceive = FALSE; // // Get first MDL and data length in the list // pMdl = NET_BUFFER_CURRENT_MDL(NET_BUFFER_LIST_FIRST_NB(pNetBufList)); TotalLength = NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(pNetBufList)); Offset = NET_BUFFER_CURRENT_MDL_OFFSET(NET_BUFFER_LIST_FIRST_NB(pNetBufList)); BufferLength = 0; do { ASSERT(pMdl != NULL); if (pMdl) { NdisQueryMdl( pMdl, &pEthHeader, &BufferLength, NormalPagePriority); } if (pEthHeader == NULL) { // // The system is low on resources. Set up to handle failure // below. // BufferLength = 0; break; } if (BufferLength == 0) { break; } ASSERT(BufferLength > Offset); BufferLength -= Offset; pEthHeader = (PNDISPROT_ETH_HEADER)((PUCHAR)pEthHeader + Offset); if (BufferLength < sizeof(NDISPROT_ETH_HEADER)) { DEBUGP(DL_WARN, ("ReceiveNetBufferList: Open %p, runt nbl %p, first buffer length %d\n", pOpenContext, pNetBufList, BufferLength)); break; } // // Check the EtherType. If the Ether type indicates presence of // a tag, then the "real" Ether type is 4 bytes further down. // if (pEthHeader->EthType == NPROT_8021P_TAG_TYPE) { USHORT UNALIGNED *pEthType; if (BufferLength < (sizeof(NDISPROT_ETH_HEADER) + 4)) { break; } pEthType = (USHORT UNALIGNED *)((PUCHAR)&pEthHeader->EthType + 4); if (*pEthType != Globals.EthType) { break; } } else if (pEthHeader->EthType != Globals.EthType) { break; } bAcceptedReceive = TRUE; DEBUGP(DL_LOUD, ("ReceiveNetBufferList: Open %p, interesting nbl %p\n", pOpenContext, pNetBufList)); // // If the miniport is out of resources, we can't queue // this list of net buffer list - make a copy if this is so. // DispatchLevel = NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags); NoReadIRP = NPROT_IS_LIST_EMPTY(&pOpenContext->PendedReads); if (NoReadIRP || NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags)) { bAcceptedReceive = FALSE; pCopyNetBufList = ndisprotAllocateReceiveNetBufferList( pOpenContext, TotalLength, &pCopyBuf); if (pCopyNetBufList == NULL) { DEBUGP(DL_FATAL, ("ReceiveNetBufferList: Open %p, failed to" " alloc copy, %d bytes\n", pOpenContext, TotalLength)); break; } NBL_SET_PROT_RSVD_FLAG(pCopyNetBufList, NPROT_ALLOCATED_NBL); // // Copy the data to the new allocated NetBufferList // NtStatus = NdisCopyFromNetBufferToNetBuffer(NET_BUFFER_LIST_FIRST_NB(pCopyNetBufList), 0, TotalLength, NET_BUFFER_LIST_FIRST_NB(pNetBufList), 0, &BytesCopied); if (NtStatus != STATUS_SUCCESS) { DEBUGP(DL_FATAL, ("ReceiveNetBufferList: Open %p, failed to" " copy the data, %d bytes\n", pOpenContext, TotalLength)); // // Free the NetBufferList and memory allocate before // ndisprotFreeReceiveNetBufferList(pOpenContext, pCopyNetBufList, DispatchLevel); break; } NPROT_ASSERT(BytesCopied == TotalLength); // // The other members of NET_BUFFER_DATA structure are already initialized properly during allocation. // NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(pCopyNetBufList)) = BytesCopied; // //save a copy for no Read IRP case // if(NoReadIRP) { pNetBufListOrig = pNetBufList; } pNetBufList = pCopyNetBufList; } // // Queue this up and service any pending Read IRPs. // ndisprotQueueReceiveNetBufferList(pOpenContext, pNetBufList, DispatchLevel); } while (FALSE); // // Ndisprot is not interested this NetBufferList, return the // NetBufferList back to the miniport if the miniport gave us // ownership of it // if ((bAcceptedReceive == FALSE) && (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags) == TRUE)) { // Restore pNetBufList if it was overwritten earlier if (pNetBufListOrig != NULL) { pNetBufList = pNetBufListOrig; pNetBufListOrig = NULL; } if (pReturnNetBufList == NULL) { pReturnNetBufList = pNetBufList; } else { NET_BUFFER_LIST_NEXT_NBL(pLastReturnNetBufList) = pNetBufList; } pLastReturnNetBufList = pNetBufList; NET_BUFFER_LIST_NEXT_NBL(pNetBufList) = NULL; } pNetBufList = pNextNetBufList; } // end of the for loop if (pReturnNetBufList != NULL) { NdisReturnNetBufferLists(pOpenContext->BindingHandle, pReturnNetBufList, ReturnFlags); } }
// NDIS packet reception notification procedure void SlNdisReceiveNetBufferListsProc(NDIS_HANDLE protocol_binding_context, NET_BUFFER_LIST *net_buffer_lists, NDIS_PORT_NUMBER port_number, ULONG NumberOfNetBufferLists, ULONG receive_flags) { SL_ADAPTER *a = (SL_ADAPTER *)protocol_binding_context; UINT i; UINT return_flags = 0; NET_BUFFER_LIST *nbl; UCHAR *tmp_buffer; UINT tmp_size; if (net_buffer_lists == NULL || NumberOfNetBufferLists == 0) { return; } if (a->AdapterHandle2 == NULL) { a->AdapterHandle2 = a->AdapterHandle; } if (NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(receive_flags)) { NDIS_SET_RETURN_FLAG(return_flags, NDIS_RETURN_FLAGS_DISPATCH_LEVEL); } if (a->Halt || a->Device == NULL || a->Device->Halting || a->Ready == false || a->AdapterHandle == NULL) { goto LABEL_CLEANUP; } tmp_buffer = a->TmpBuffer; tmp_size = sizeof(a->TmpBuffer); nbl = net_buffer_lists; SlLockList(a->Device->FileList); { if (a->Halt == false) { for (i = 0;i < SL_LIST_NUM(a->Device->FileList);i++) { // Lock the receive queue SL_FILE *f = SL_LIST_DATA(a->Device->FileList, i); SlLock(f->RecvLock); } while (nbl != NULL) { NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); bool is_vlan = false; UCHAR vlan_tag[2]; if (NET_BUFFER_LIST_INFO(nbl, Ieee8021QNetBufferListInfo) != 0) { NDIS_NET_BUFFER_LIST_8021Q_INFO qinfo; qinfo.Value = NET_BUFFER_LIST_INFO(nbl, Ieee8021QNetBufferListInfo); if (qinfo.TagHeader.VlanId != 0) { USHORT tag_us; is_vlan = true; tag_us = (qinfo.TagHeader.UserPriority & 0x07 << 13) | (qinfo.TagHeader.CanonicalFormatId & 0x01 << 12) | (qinfo.TagHeader.VlanId & 0x0FFF); vlan_tag[0] = ((UCHAR *)(&tag_us))[1]; vlan_tag[1] = ((UCHAR *)(&tag_us))[0]; } } while (nb != NULL) { UINT size = NET_BUFFER_DATA_LENGTH(nb); if (size >= 14 && size <= tmp_size && size <= (UINT)((is_vlan == false) ? SL_MAX_PACKET_SIZE : (SL_MAX_PACKET_SIZE - 4))) { UCHAR *ptr = NdisGetDataBuffer(nb, size, tmp_buffer, 1, 0); if (ptr != NULL) { // Insert the queue to all waiting files for (i = 0;i < SL_LIST_NUM(a->Device->FileList);i++) { SL_FILE *f = SL_LIST_DATA(a->Device->FileList, i); if (f->NumRecvPackets < SL_MAX_PACKET_QUEUED) { SL_PACKET *q = SlMalloc(sizeof(SL_PACKET)); if (is_vlan == false) { // Normal packet SlCopy(q->Data, ptr, size); q->Size = size; } else { // Insert a tag in the case of IEEE802.1Q packet SlCopy(q->Data, ptr, 12); q->Data[12] = 0x81; q->Data[13] = 0x00; SlCopy(&q->Data[14], vlan_tag, 2); SlCopy(&q->Data[16], &ptr[12], size - 12); q->Size = size + 4; } q->Next = NULL; if (f->RecvPacketHead == NULL) { f->RecvPacketHead = q; } else { f->RecvPacketTail->Next = q; } f->RecvPacketTail = q; f->NumRecvPackets++; } } } } nb = NET_BUFFER_NEXT_NB(nb); } nbl = NET_BUFFER_LIST_NEXT_NBL(nbl); } // Hit the event for (i = 0;i < SL_LIST_NUM(a->Device->FileList);i++) { SL_FILE *f = SL_LIST_DATA(a->Device->FileList, i); // Unlock the receive queue SlUnlock(f->RecvLock); SlSet(f->Event); } } } SlUnlockList(a->Device->FileList); LABEL_CLEANUP: if (NDIS_TEST_RECEIVE_CAN_PEND(receive_flags)) { NdisReturnNetBufferLists(a->AdapterHandle2, net_buffer_lists, return_flags); } }