/*++ Copyright (c) 2001 Microsoft Corporation Module Name: Offload.c Abstract: This file contains all the functions needed by TCP/IP checksum and segmentation of Large TCP packets task offloading. Actually thses functions should be implemented by hardware, and the purpose of this file is just to demonstrate how to use OID_TCP_TASK_OFFLOAD to enable/disable task offload capabilities. Revision History Who When What ------ --------- ---------- 02-19-2001 Create Notes: --*/ #include "precomp.h" #ifdef OFFLOAD #define PROTOCOL_TCP 6 // // This miniport uses shared memory to handle offload tasks, so it tries to allocate // shared memory of 64K, 32K, 16K. First it tries to allocate 64K, if fails, then // it tries 32K and so on. If successed, than keeps the size in adapter, which is used // to decide the maximum offload size in large send. If all the tries fail, then this // miniport cann't support any offload task. // ULONG LargeSendSharedMemArray[LARGE_SEND_MEM_SIZE_OPTION] = {64*1024, 32*1024, 16*1024}; // // if x is aabb(where aa, bb are hex bytes), we want net_short (x) to be bbaa. // USHORT net_short( ULONG NaturalData ) { USHORT ShortData = (USHORT)NaturalData; return (ShortData << 8) | (ShortData >> 8); } // // if x is aabbccdd (where aa, bb, cc, dd are hex bytes) // we want net_long(x) to be ddccbbaa. A small and fast way to do this is // to first byteswap it to get bbaaddcc and then swap high and low words. // ULONG net_long( ULONG NaturalData ) { ULONG ByteSwapped; ByteSwapped = ((NaturalData & 0x00ff00ff) << 8) | ((NaturalData & 0xff00ff00) >> 8); return (ByteSwapped << 16) | (ByteSwapped >> 16); } // // calculate the checksum for pseudo-header // #define PHXSUM(s,d,p,l) (UINT)( (UINT)*(USHORT *)&(s) + \ (UINT)*(USHORT *)((char *)&(s) + sizeof(USHORT)) + \ (UINT)*(USHORT *)&(d) + \ (UINT)*(USHORT *)((char *)&(d) + sizeof(USHORT)) + \ (UINT)((USHORT)net_short((p))) + \ (UINT)((USHORT)net_short((USHORT)(l))) ) #define IP_HEADER_LENGTH(pIpHdr) \ ( (ULONG)((pIpHdr->iph_verlen & 0x0F) << 2) ) #define TCP_HEADER_LENGTH(pTcpHdr) \ ( (USHORT)(((*((PUCHAR)(&(pTcpHdr->tcp_flags))) & 0xF0) >> 4) << 2) ) /*++ Routine Description: Copy data in a packet to the specified location Arguments: BytesToCopy The number of bytes need to copy CurrentBuffer The buffer to start to copy StartVa The start address to copy the data to Offset The start offset in the buffer to copy the data HeadersLength The length of the headers which has already been copied. Return Value: The number of bytes actually copied --*/ ULONG MpCopyData( ULONG BytesToCopy, PNDIS_BUFFER *CurrentBuffer, PVOID StartVa, PULONG Offset, ULONG HeadersLength ) { ULONG CurrLength; PUCHAR pSrc; PUCHAR pDest; ULONG BytesCopied = 0; ULONG CopyLength; DBGPRINT(MP_TRACE, ("--> MpCopyData\n")); pDest = StartVa; while (*CurrentBuffer && BytesToCopy != 0) { #ifdef NDIS51_MINIPORT NdisQueryBufferSafe( *CurrentBuffer, &pSrc, &CurrLength, NormalPagePriority); if (pSrc == NULL) { BytesCopied = 0; break; } #else NdisQueryBuffer( *CurrentBuffer, &pSrc, &CurrLength); #endif // // Current buffer length is greater than the offset to the buffer // if (CurrLength > *Offset) { pSrc += *Offset; CurrLength -= *Offset; CopyLength = CurrLength > BytesToCopy ? BytesToCopy : CurrLength; NdisMoveMemory(pDest, pSrc, CopyLength); BytesCopied += CopyLength; if (CurrLength > BytesToCopy) { *Offset += BytesToCopy; break; } BytesToCopy -= CopyLength; pDest += CopyLength; *Offset = 0; } else { *Offset -= CurrLength; } NdisGetNextBuffer( *CurrentBuffer, CurrentBuffer); } ASSERT(BytesCopied + HeadersLength <= NIC_MAX_PACKET_SIZE); // // Zero out the padding bytes if necessary // if (BytesCopied + HeadersLength < NIC_MIN_PACKET_SIZE) { NdisZeroMemory(pDest, NIC_MIN_PACKET_SIZE - (BytesCopied + HeadersLength)); } DBGPRINT(MP_TRACE, ("<-- MpCopyData\n")); return BytesCopied; } /*++ Routine Description: Dump packet information for debug purpose Arguments: pPkt Pointer to the packet Return Value: None --*/ VOID e100DumpPkt ( IN PNDIS_PACKET Packet ) { PNDIS_BUFFER pPrevBuffer; PNDIS_BUFFER pBuffer; do { // // Get first buffer of the packet // pBuffer = Packet->Private.Head; pPrevBuffer = NULL; // // Scan the buffer chain // while (pBuffer != NULL) { PVOID pVa = NULL; ULONG BufLen = 0; BufLen = NdisBufferLength (pBuffer); pVa = NdisBufferVirtualAddress(pBuffer); pPrevBuffer = pBuffer; pBuffer = pBuffer->Next; if (pVa == NULL) { continue; } DBGPRINT(MP_WARN, ("Mdl %p, Va %p. Len %x\n", pPrevBuffer, pVa, BufLen)); Dump( (CHAR* )pVa, BufLen, 0, 1 ); } } while (FALSE); } /*++ Routine Description: Calculate the IP checksum Arguments: Packet Pointer to the packet IpHdrOffset Offset of IP header from the beginning of the packet Return Value: None --*/ VOID CalculateIpChecksum( PUCHAR StartVa, ULONG IpHdrOffset ) { IPHeader *pIpHdr; ULONG IpHdrLen; ULONG TempXsum = 0; pIpHdr = (IPHeader *)(StartVa + IpHdrOffset); IpHdrLen = IP_HEADER_LENGTH(pIpHdr); XSUM(TempXsum, StartVa, IpHdrLen, IpHdrOffset); pIpHdr->iph_xsum = ~(USHORT)TempXsum; } /*++ Routine Description: Calculate the UDP checksum Arguments: Packet Pointer to the packet IpHdrOffset Offset of IP header from the beginning of the packet Return Value: None --*/ VOID CalculateUdpChecksum( PNDIS_PACKET pPacket, ULONG IpHdrOffset ) { DBGPRINT(MP_WARN, ("UdpChecksum is not handled\n")); } /*++ Routine Description: Calculate the TCP checksum Arguments: Packet Pointer to the packet IpHdrOffset Offset of IP header from the beginning of the packet Return Value: None --*/ VOID CalculateTcpChecksum( PVOID StartVa, ULONG PacketLength, ULONG IpHdrOffset ) { ULONG Offset; IPHeader *pIpHdr; ULONG IpHdrLength; TCPHeader *pTcpHdr; USHORT PseudoXsum; ULONG TmpXsum; DBGPRINT(MP_TRACE, ("===> CalculateTcpChecksum\n")); // // Find IP header and get IP header length in byte // MDL won't split headers // Offset = IpHdrOffset; pIpHdr = (IPHeader *) ((PUCHAR)StartVa + Offset); IpHdrLength = IP_HEADER_LENGTH(pIpHdr); // // If that is not tcp protocol, we can not do anything. // So just return to the caller // if (((pIpHdr->iph_verlen & 0xF0) >> 4) != 4 && pIpHdr->iph_protocol != PROTOCOL_TCP) { return; } // // Locate the TCP header // Offset += IpHdrLength; pTcpHdr = (TCPHeader *) ((PUCHAR)StartVa + Offset); // // Calculate the checksum for the tcp header and payload // PseudoXsum = pTcpHdr->tcp_xsum; pTcpHdr->tcp_xsum = 0; TmpXsum = PseudoXsum; XSUM(TmpXsum, StartVa, PacketLength - Offset, Offset); // // Now we got the checksum, need to put the checksum back to MDL // pTcpHdr->tcp_xsum = (USHORT)(~TmpXsum); DBGPRINT(MP_TRACE, ("<=== CalculateTcpChecksum\n")); } /*++ Routine Description: Do the checksum offloading Arguments: Packet Pointer to the packet IpHdrOffset Offset of IP header from the beginning of the packet Return Value: None --*/ VOID CalculateChecksum( PVOID StartVa, ULONG PacketLength, PNDIS_PACKET Packet, ULONG IpHdrOffset ) { ULONG ChecksumPktInfo; PNDIS_TCP_IP_CHECKSUM_PACKET_INFO pChecksumPktInfo; // // Check for protocol // if (NDIS_PROTOCOL_ID_TCP_IP != NDIS_GET_PACKET_PROTOCOL_TYPE(Packet)) { DBGPRINT(MP_TRACE, ("Packet's protocol is wrong.\n")); return; } // // Query per packet information // ChecksumPktInfo = PtrToUlong( NDIS_PER_PACKET_INFO_FROM_PACKET( Packet, TcpIpChecksumPacketInfo)); // DBGPRINT(MP_WARN, ("Checksum info: %lu\n", ChecksumPktInfo)); pChecksumPktInfo = (PNDIS_TCP_IP_CHECKSUM_PACKET_INFO) & ChecksumPktInfo; // // Check per packet information // if (pChecksumPktInfo->Transmit.NdisPacketChecksumV4 == 0) { DBGPRINT(MP_TRACE, ("NdisPacketChecksumV4 is not set.\n")); return; } // // do tcp checksum // if (pChecksumPktInfo->Transmit.NdisPacketTcpChecksum) { CalculateTcpChecksum(StartVa, PacketLength, IpHdrOffset); } // // do udp checksum // if (pChecksumPktInfo->Transmit.NdisPacketUdpChecksum) { CalculateUdpChecksum(Packet, IpHdrOffset); } // // do ip checksum // if (pChecksumPktInfo->Transmit.NdisPacketIpChecksum) { CalculateIpChecksum(StartVa, IpHdrOffset); } } /*++ Routine Description: MiniportSendPackets handler Arguments: MiniportAdapterContext Pointer to our adapter PacketArray Set of packets to send NumOfPackets Self-explanatory Return Value: None --*/ VOID MPOffloadSendPackets( IN NDIS_HANDLE MiniportAdapterContext, IN PPNDIS_PACKET PacketArray, IN UINT NumOfPackets ) { PMP_ADAPTER Adapter; NDIS_STATUS Status; UINT PacketCount; ULONG IpHdrOffset; DBGPRINT(MP_TRACE, ("====> MPOffloadSendPackets\n")); Adapter = (PMP_ADAPTER)MiniportAdapterContext; NdisAcquireSpinLock(&Adapter->SendLock); // // Is this adapter ready for sending? // if (MP_IS_NOT_READY(Adapter)) { // // There is link // if (MP_TEST_FLAG(Adapter, fMP_ADAPTER_LINK_DETECTION)) { for (PacketCount = 0; PacketCount < NumOfPackets; PacketCount++) { InsertTailQueue(&Adapter->SendWaitQueue, MP_GET_PACKET_MR( PacketArray[PacketCount] ) ); Adapter->nWaitSend++; DBGPRINT(MP_WARN, ("MpOffloadSendPackets: link detection - queue packet "PTR_FORMAT"\n", PacketArray[PacketCount])); } NdisReleaseSpinLock(&Adapter->SendLock); return; } // // Adapter is not ready and there is not link // Status = MP_GET_STATUS_FROM_FLAGS(Adapter); NdisReleaseSpinLock(&Adapter->SendLock); for (PacketCount = 0; PacketCount < NumOfPackets; PacketCount++) { NdisMSendComplete( MP_GET_ADAPTER_HANDLE(Adapter), PacketArray[PacketCount], Status); } return; } // // Adapter is ready, send these packets // for (PacketCount = 0; PacketCount < NumOfPackets; PacketCount++) { // // queue is not empty or tcb is not available // if (!IsQueueEmpty(&Adapter->SendWaitQueue) || !MP_TCB_RESOURCES_AVAIABLE(Adapter) || MP_TEST_FLAG(Adapter, fMP_SHARED_MEM_IN_USE)) { InsertTailQueue(&Adapter->SendWaitQueue, MP_GET_PACKET_MR( PacketArray[PacketCount] ) ); Adapter->nWaitSend++; } else { MpOffloadSendPacket(Adapter, PacketArray[PacketCount], FALSE); } } NdisReleaseSpinLock(&Adapter->SendLock); DBGPRINT(MP_TRACE, ("<==== MPOffloadSendPackets\n")); return; } /*++ Routine Description: Do the work to send a packet Assumption: Send spinlock has been acquired and shared mem is available Arguments: Adapter Pointer to our adapter Packet The packet bFromQueue TRUE if it's taken from the send wait queue Return Value: NDIS_STATUS_SUCCESS NDIS_STATUS_PENDING Put into the send wait queue NDIS_STATUS_HARD_ERRORS --*/ NDIS_STATUS MpOffloadSendPacket( IN PMP_ADAPTER Adapter, IN PNDIS_PACKET Packet, IN BOOLEAN bFromQueue ) { NDIS_STATUS Status = NDIS_STATUS_PENDING; PMP_TCB pMpTcb = NULL; ULONG BytesCopied; ULONG NumOfPackets; // Mimiced frag list if map registers are used, on the local stack as it's not so big MP_FRAG_LIST FragList; // Pointer to either the scatter gather or the local mimiced frag list PMP_FRAG_LIST pFragList; NDIS_PHYSICAL_ADDRESS SendPa; ULONG BytesToCopy; ULONG Offset; PNDIS_PACKET_EXTENSION PktExt; ULONG mss; PNDIS_BUFFER NdisBuffer; ULONG PacketLength; PVOID CopyStartVa; ULONG IpHdrOffset; PUCHAR StartVa; PNDIS_BUFFER FirstBuffer; DBGPRINT(MP_TRACE, ("--> MpOffloadSendPacket, Pkt= "PTR_FORMAT"\n", Packet)); // //Check is shared memory available, just double check // if (MP_TEST_FLAG(Adapter, fMP_SHARED_MEM_IN_USE)) { DBGPRINT(MP_WARN, ("Shared mem is in use.\n")); if (bFromQueue) { InsertHeadQueue(&Adapter->SendWaitQueue, MP_GET_PACKET_MR(Packet)); } else { InsertTailQueue(&Adapter->SendWaitQueue, MP_GET_PACKET_MR(Packet)); } DBGPRINT(MP_TRACE, ("<-- MpOffloadSendPacket\n")); return Status; } MP_SET_FLAG(Adapter, fMP_SHARED_MEM_IN_USE); // // Get maximum segment size // PktExt = NDIS_PACKET_EXTENSION_FROM_PACKET(Packet); mss = PtrToUlong(PktExt->NdisPacketInfo[TcpLargeSendPacketInfo]); // // Copy NIC_MAX_PACKET_SIZE bytes of data from NDIS buffer // to the shared memory // NdisQueryPacket( Packet, NULL, NULL, &FirstBuffer, &PacketLength ); Offset = 0; NdisBuffer = FirstBuffer; BytesToCopy = NIC_MAX_PACKET_SIZE; CopyStartVa = Adapter->OffloadSharedMem.StartVa; BytesCopied = MpCopyData(BytesToCopy, &NdisBuffer, CopyStartVa, &Offset, 0); #ifdef NDIS51_MINIPORT // // MpCopyPacket may return 0 if system resources are low or exhausted // if (BytesCopied == 0) { DBGPRINT(MP_ERROR, ("Calling NdisMSendComplete with NDIS_STATUS_RESOURCES, Pkt= "PTR_FORMAT"\n", Packet)); NdisReleaseSpinLock(&Adapter->SendLock); NdisMSendComplete( MP_GET_ADAPTER_HANDLE(Adapter), Packet, NDIS_STATUS_RESOURCES); NdisAcquireSpinLock(&Adapter->SendLock); MP_CLEAR_FLAG(Adapter, fMP_SHARED_MEM_IN_USE); return NDIS_STATUS_RESOURCES; } #endif StartVa = CopyStartVa; SendPa = Adapter->OffloadSharedMem.PhyAddr; IpHdrOffset = Adapter->EncapsulationFormat.EncapsulationHeaderSize; // // Check if large send capability is on and this is a large packet // if (Adapter->NicTaskOffload.LargeSendOffload && mss > 0) { ULONG IpHeaderLen; ULONG TcpHdrOffset; ULONG HeadersLen; IPHeader * IpHdr; TCPHeader * TcpHdr; USHORT TcpHeaderLen; ULONG IpSegmentLen; ULONG TcpDataLen; ULONG LastPacketDataLen; int SeqNum; BOOLEAN IsFinSet = FALSE; BOOLEAN IsPushSet = FALSE; BOOLEAN IsFirstSlot = TRUE; ULONG TmpXsum; ULONG BytesSent = 0; ULONG TmpPxsum; IpHdr = (IPHeader *)((PUCHAR)CopyStartVa + IpHdrOffset); IpHeaderLen = IP_HEADER_LENGTH(IpHdr); // // The packet must be a TCP packet // ASSERT(IpHdr->iph_protocol == PROTOCOL_TCP); TcpHdrOffset = IpHdrOffset + IpHeaderLen; TcpHdr = (TCPHeader *)((PUCHAR)CopyStartVa + TcpHdrOffset); TcpHeaderLen = TCP_HEADER_LENGTH(TcpHdr); HeadersLen = TcpHdrOffset + TcpHeaderLen; // // This length include IP, TCP headers and TCP data // IpSegmentLen = net_short(IpHdr->iph_length); // // get the pseudo-header 1's complement sum // TmpPxsum = TcpHdr->tcp_xsum; ASSERT(IpSegmentLen == PacketLength - IpHdrOffset); IsFinSet = (BOOLEAN)(TcpHdr->tcp_flags & TCP_FLAG_FIN); IsPushSet = (BOOLEAN)(TcpHdr->tcp_flags & TCP_FLAG_PUSH); SeqNum = net_long(TcpHdr->tcp_seq); TcpDataLen = IpSegmentLen - TcpHeaderLen - IpHeaderLen; ASSERT(TcpDataLen <= Adapter->LargeSendInfo.MaxOffLoadSize) NumOfPackets = TcpDataLen / mss + 1; ASSERT (NumOfPackets >= Adapter->LargeSendInfo.MinSegmentCount); LastPacketDataLen = TcpDataLen % mss; NdisBuffer = FirstBuffer; BytesSent = 0; // // The next copy start with offset of (mss+HeadersLen) corresponding to first buf // BytesCopied = (BytesCopied >= mss + HeadersLen)? (mss + HeadersLen):BytesCopied; Offset = BytesCopied; // // Send out all the packets from the large TCP packet // while (NumOfPackets--) { TmpXsum = 0; // // Is the first packet? // if (IsFirstSlot) { if (NumOfPackets == 0) { PktExt->NdisPacketInfo[TcpLargeSendPacketInfo] = UlongToPtr(BytesCopied); } else { if (IsFinSet) { TcpHdr->tcp_flags &= ~TCP_FLAG_FIN; } if (IsPushSet) { TcpHdr->tcp_flags &= ~TCP_FLAG_PUSH; } } BytesCopied -= HeadersLen; IsFirstSlot = FALSE; } // // Not the first packet // else { // // copy headers // NdisMoveMemory (StartVa, CopyStartVa, HeadersLen); IpHdr = (IPHeader *)((PUCHAR)StartVa + IpHdrOffset); TcpHdr = (TCPHeader *) ((PUCHAR)StartVa + TcpHdrOffset); // // Last packet // if (NumOfPackets == 0) { BytesToCopy = LastPacketDataLen; PktExt->NdisPacketInfo[TcpLargeSendPacketInfo] = UlongToPtr(BytesSent + LastPacketDataLen); } else { BytesToCopy = mss; // clear flag if (IsFinSet) { TcpHdr->tcp_flags &= ~TCP_FLAG_FIN; } if (IsPushSet) { TcpHdr->tcp_flags &= ~TCP_FLAG_PUSH; } } BytesCopied = MpCopyData( BytesToCopy, &NdisBuffer, StartVa + HeadersLen, &Offset, HeadersLen); #ifdef NDIS51_MINIPORT // // MpCopyData may return 0 if system resources are low or exhausted // if (BytesCopied == 0) { PktExt->NdisPacketInfo[TcpLargeSendPacketInfo] = UlongToPtr(BytesSent); DBGPRINT(MP_WARN, ("Calling NdisMSendComplete with NDIS_STATUS_SUCCESS(Part of the data is sent), Pkt= "PTR_FORMAT"\n", Packet)); NdisReleaseSpinLock(&Adapter->SendLock); NdisMSendComplete( MP_GET_ADAPTER_HANDLE(Adapter), Packet, NDIS_STATUS_SUCCESS); NdisAcquireSpinLock(&Adapter->SendLock); return NDIS_STATUS_RESOURCES; } #endif } IpHdr->iph_length = net_short(TcpHeaderLen + IpHeaderLen + BytesCopied); TcpHdr->tcp_seq = net_long(SeqNum); SeqNum += BytesCopied; // // calculate ip checksum and tcp checksum // IpHdr->iph_xsum = 0; XSUM(TmpXsum, StartVa, IpHeaderLen, IpHdrOffset); IpHdr->iph_xsum = ~(USHORT)(TmpXsum); TmpXsum = TmpPxsum + net_short((USHORT)(BytesCopied + TcpHeaderLen)); TcpHdr->tcp_xsum = 0; XSUM(TmpXsum, StartVa, BytesCopied + TcpHeaderLen, TcpHdrOffset); TcpHdr->tcp_xsum = ~(USHORT)(TmpXsum); BytesSent += BytesCopied; BytesCopied += HeadersLen; // // get TCB for the slot // pMpTcb = Adapter->CurrSendTail; ASSERT(!MP_TEST_FLAG(pMpTcb, fMP_TCB_IN_USE)); // // Set up the frag list, only one fragment after it's coalesced // pFragList = &FragList; pFragList->NumberOfElements = 1; pFragList->Elements[0].Address = SendPa; pFragList->Elements[0].Length = (BytesCopied >= NIC_MIN_PACKET_SIZE) ? BytesCopied : NIC_MIN_PACKET_SIZE; pMpTcb->Packet = NULL; MP_SET_FLAG(pMpTcb, fMP_TCB_IN_USE); // // Call the NIC specific send handler, it only needs to deal with the frag list // Status = NICSendPacket(Adapter, pMpTcb, pFragList); Adapter->nBusySend++; NdisInterlockedIncrement(&Adapter->SharedMemRefCount); // // Update the CopyVa and SendPa // SendPa.QuadPart += BytesCopied; StartVa += BytesCopied; Adapter->CurrSendTail = Adapter->CurrSendTail->Next; // // out of resouces, which will send complete part of the packet // if (Adapter->nBusySend >= Adapter->NumTcb) { PktExt->NdisPacketInfo[TcpLargeSendPacketInfo] = UlongToPtr(BytesSent); break; } } // while } // // This is not a large packet or large send capability is not on // else { // // get TCB for the slot // pMpTcb = Adapter->CurrSendTail; ASSERT(!MP_TEST_FLAG(pMpTcb, fMP_TCB_IN_USE)); // // Set up the frag list, only one fragment after it's coalesced // pFragList = &FragList; pFragList->NumberOfElements = 1; pFragList->Elements[0].Address = SendPa; pFragList->Elements[0].Length = (BytesCopied >= NIC_MIN_PACKET_SIZE) ? BytesCopied : NIC_MIN_PACKET_SIZE; pMpTcb->Packet = NULL; if (Adapter->NicChecksumOffload.DoXmitTcpChecksum && Adapter->NicTaskOffload.ChecksumOffload) { CalculateChecksum(CopyStartVa, BytesCopied, Packet, Adapter->EncapsulationFormat.EncapsulationHeaderSize); } MP_SET_FLAG(pMpTcb, fMP_TCB_IN_USE); // // Call the NIC specific send handler, it only needs to deal with the frag list // Status = NICSendPacket(Adapter, pMpTcb, pFragList); Adapter->nBusySend++; NdisInterlockedIncrement(&Adapter->SharedMemRefCount); ASSERT(Adapter->nBusySend <= Adapter->NumTcb); Adapter->CurrSendTail = Adapter->CurrSendTail->Next; } NdisReleaseSpinLock(&Adapter->SendLock); DBGPRINT(MP_TRACE, ("Calling NdisMSendComplete, Pkt= "PTR_FORMAT"\n", Packet)); NdisMSendComplete( MP_GET_ADAPTER_HANDLE(Adapter), Packet, Status); NdisAcquireSpinLock(&Adapter->SendLock); DBGPRINT(MP_TRACE, ("<-- MpOffloadSendPacket\n")); return Status; } /*++ Routine Description: Recycle a MP_TCB and complete the packet if necessary Assumption: Send spinlock has been acquired Arguments: Adapter Pointer to our adapter pMpTcb Pointer to MP_TCB Return Value: None --*/ VOID MP_OFFLOAD_FREE_SEND_PACKET( IN PMP_ADAPTER Adapter, IN PMP_TCB pMpTcb ) { PNDIS_BUFFER CurrBuffer; ASSERT(MP_TEST_FLAG(pMpTcb, fMP_TCB_IN_USE)); pMpTcb->Packet = NULL; pMpTcb->Count = 0; MP_CLEAR_FLAGS(pMpTcb); Adapter->CurrSendHead = Adapter->CurrSendHead->Next; Adapter->nBusySend--; NdisInterlockedDecrement(&Adapter->SharedMemRefCount); if (Adapter->SharedMemRefCount == 0) { MP_CLEAR_FLAG(Adapter, fMP_SHARED_MEM_IN_USE); } ASSERT(Adapter->nBusySend >= 0); } /*++ Routine Description: Disable the existing capabilities before protocol is setting the new capabilities Arguments: Adapter Pointer to our adapter Return Value: None --*/ VOID DisableOffload( IN PMP_ADAPTER Adapter ) { // // Disable the capabilities of the miniports // NdisZeroMemory(&(Adapter->NicTaskOffload), sizeof(NIC_TASK_OFFLOAD)); NdisZeroMemory(&(Adapter->NicChecksumOffload), sizeof(NIC_CHECKSUM_OFFLOAD)); } #endif // OFFLOAD