665 lines
17 KiB
C
665 lines
17 KiB
C
/*+
|
||
* file: send.c
|
||
*
|
||
* Copyright (C) 1992-1995 by
|
||
* Digital Equipment Corporation, Maynard, Massachusetts.
|
||
* All rights reserved.
|
||
*
|
||
* This software is furnished under a license and may be used and copied
|
||
* only in accordance of the terms of such license and with the
|
||
* inclusion of the above copyright notice. This software or any other
|
||
* copies thereof may not be provided or otherwise made available to any
|
||
* other person. No title to and ownership of the software is hereby
|
||
* transferred.
|
||
*
|
||
* The information in this software is subject to change without notice
|
||
* and should not be construed as a commitment by digital equipment
|
||
* corporation.
|
||
*
|
||
* Digital assumes no responsibility for the use or reliability of its
|
||
* software on equipment which is not supplied by digital.
|
||
*
|
||
*
|
||
* Abstract: This file is part of the NDIS 4.0 miniport driver for
|
||
* Digital Equipment's DC21X4 Ethernet adapter family.
|
||
* It contains the code for submitting a packet for
|
||
* transmission.
|
||
*
|
||
* Author: Philippe Klein
|
||
*
|
||
* Revision History:
|
||
*
|
||
* phk 28-Aug-1994 creation date
|
||
*
|
||
-*/
|
||
|
||
#include <precomp.h>
|
||
#include <crc.h>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/*+
|
||
* DC21X4Send
|
||
*
|
||
* Routine Description:
|
||
*
|
||
* The DC21X4Send request instructs a MAC to transmit a packet through
|
||
* the adapter onto the medium.
|
||
*
|
||
* Arguments:
|
||
*
|
||
* MiniportAdapterContext -
|
||
* Packet - A pointer to a descriptor for the packet to transmit
|
||
*
|
||
* Return Value:
|
||
*
|
||
* The status of the operation.
|
||
*
|
||
-*/
|
||
|
||
extern
|
||
NDIS_STATUS
|
||
DC21X4Send(
|
||
IN NDIS_HANDLE MiniportAdapterContext,
|
||
IN PNDIS_PACKET Packet,
|
||
IN UINT Flags
|
||
)
|
||
{
|
||
|
||
PDC21X4_ADAPTER Adapter;
|
||
|
||
UINT PacketSize;
|
||
UCHAR PacketType;
|
||
|
||
PNDIS_BUFFER CurrentBuffer;
|
||
UINT NdisBufferCount;
|
||
UINT PhysicalSegmentCount;
|
||
UINT MapTableIndex;
|
||
|
||
PVOID TxmBuffer;
|
||
PUCHAR Tmp;
|
||
|
||
NDIS_STATUS NdisStatus;
|
||
NDIS_PHYSICAL_ADDRESS_UNIT PhysicalSegmentArray[DC21X4_MAX_SEGMENTS];
|
||
UINT BufferPhysicalSegments;
|
||
|
||
BOOLEAN FirstSegment;
|
||
BOOLEAN FirstBuffer;
|
||
|
||
PDC21X4_TRANSMIT_DESCRIPTOR FirstSegmentDescriptor=NULL;
|
||
PDC21X4_TRANSMIT_DESCRIPTOR LastSegmentDescriptor=NULL;
|
||
PDC21X4_TRANSMIT_DESCRIPTOR CurrentDescriptor=NULL;
|
||
|
||
UINT Length;
|
||
UINT Buffer;
|
||
UINT Segment;
|
||
|
||
UCHAR SendMode;
|
||
|
||
BOOLEAN GenerateCRC=FALSE;
|
||
|
||
ULONG TxmDescriptorCount = 0;
|
||
ULONG MapRegistersCount = 0;
|
||
ULONG MaxTransmitBufferCount = 0;
|
||
ULONG MinTransmitBufferCount = 0;
|
||
ULONG GoTransmitCount = 0;
|
||
|
||
#if _DBG
|
||
DbgPrint("DC21X4Send AdapterContext =%x Packet=%08x\n",
|
||
MiniportAdapterContext,Packet);
|
||
#endif
|
||
|
||
Adapter = (PDC21X4_ADAPTER)(MiniportAdapterContext);
|
||
|
||
//Check the link status
|
||
if (Adapter->LinkStatus == LinkFail) {
|
||
return NDIS_STATUS_NO_CABLE;
|
||
}
|
||
|
||
NdisQueryPacket(
|
||
Packet,
|
||
&PhysicalSegmentCount,
|
||
&NdisBufferCount,
|
||
&CurrentBuffer,
|
||
&PacketSize
|
||
);
|
||
|
||
if (PacketSize > DC21X4_MAX_FRAME_SIZE) {
|
||
return NDIS_STATUS_INVALID_PACKET;
|
||
}
|
||
|
||
// NT BUG: Clean up the msw of the PhysicalSegmentCount
|
||
PhysicalSegmentCount &= 0xFFFF;
|
||
|
||
#if _DBG
|
||
DbgPrint(" PacketSize= %d\n BufferCount= %d\n SegmentCount= %d\n",
|
||
PacketSize,NdisBufferCount,PhysicalSegmentCount);
|
||
DbgPrint(" FreeTxmDescCount = %d\n",Adapter->FreeTransmitDescriptorCount);
|
||
#endif
|
||
|
||
ASSERT(NdisBufferCount != 0);
|
||
|
||
// DC21040 Pass1 and Pass2:
|
||
// if SoftwareCRC mode is enabled, generate the CRC by software
|
||
// for packet > Transmit threshold
|
||
|
||
if (Adapter->SoftwareCRC) {
|
||
GenerateCRC = (PacketSize > Adapter->TxmThreshold);
|
||
}
|
||
|
||
// if the Ndis Packet is too fragmented or GenerateCRC is on,
|
||
// copy the packet into a single buffer
|
||
|
||
if ( (PhysicalSegmentCount > Adapter->PhysicalSegmentThreshold) || GenerateCRC ) {
|
||
|
||
if ((Adapter->MaxTransmitBufferInUse == DC21X4_NUMBER_OF_MAX_TRANSMIT_BUFFERS) ||
|
||
(Adapter->FreeTransmitDescriptorCount <= DC21X4_NUMBER_OF_SETUP_DESCRIPTORS)
|
||
) {
|
||
|
||
// All the Txm buffer are currently allocated or
|
||
// there is no free Txm descriptor in the ring
|
||
#if _DBG
|
||
DbgPrint ("No free Txm buffer or Txm desc...\n");
|
||
#endif
|
||
return NDIS_STATUS_RESOURCES;
|
||
}
|
||
else {
|
||
SendMode = CopyMaxBuffer;
|
||
}
|
||
}
|
||
|
||
// if the Ndis Packet is smaller than DC21X4_MIN_TXM_SIZE,
|
||
// copy the packet into a preallocated Txm buffer if the resource
|
||
// is available
|
||
|
||
else if ((PacketSize <= DC21X4_MIN_TXM_SIZE)
|
||
&& (Adapter->MinTransmitBufferInUse < DC21X4_NUMBER_OF_MIN_TRANSMIT_BUFFERS)
|
||
&& !Adapter->DontUseMinTransmitBuffer)
|
||
{
|
||
SendMode = CopyMinBuffer;
|
||
}
|
||
|
||
// Check if there are enough descriptors available in the ring to load
|
||
// the packet and enough free map registers to map the whole packet
|
||
|
||
else if ( (PhysicalSegmentCount >
|
||
((Adapter->FreeTransmitDescriptorCount - DC21X4_NUMBER_OF_SETUP_DESCRIPTORS) * NUMBER_OF_SEGMENT_PER_DESC))
|
||
||(PhysicalSegmentCount > Adapter->FreeMapRegisters)
|
||
){
|
||
|
||
// not enough descriptors in the ring
|
||
// or enough map registers to load the whole packet
|
||
#if _DBG
|
||
DbgPrint("Not enough txm desc or enough map registers\n");
|
||
DbgPrint("Phys. segment count=%d FreeTxDesc=%d FreeMapReg=%d\n",
|
||
PhysicalSegmentCount,
|
||
(Adapter->FreeTransmitDescriptorCount-DC21X4_NUMBER_OF_SETUP_DESCRIPTORS) * NUMBER_OF_SEGMENT_PER_DESC,
|
||
Adapter->FreeMapRegisters);
|
||
#endif
|
||
return NDIS_STATUS_RESOURCES;
|
||
}
|
||
else {
|
||
SendMode = MappedBuffer;
|
||
}
|
||
|
||
// For now, do not separately count multicast, broadcast and
|
||
// directed packets/bytes and avoid having to map the buffer, which
|
||
// is a very expensive operation. The mapping happens when we call
|
||
// NdisQueryBuffer with a VirtualAddress argument.
|
||
#if 0
|
||
NdisQueryBuffer(
|
||
CurrentBuffer,
|
||
&TxmBuffer,
|
||
&Length
|
||
);
|
||
|
||
ASSERT(Length >= ETH_LENGTH_OF_ADDRESS);
|
||
|
||
PacketType = CHECK_PACKET_TYPE(TxmBuffer);
|
||
#else
|
||
PacketType = TXM_DIRECTED_FRAME;
|
||
#endif
|
||
|
||
|
||
// Until Send and Request are serialized
|
||
// the Enqueue pointer which can modified in both
|
||
// send and filter routines should be protected by a SpinLock
|
||
|
||
if (Adapter->FullDuplex) {
|
||
NdisDprAcquireSpinLock(&Adapter->EnqueueSpinLock);
|
||
}
|
||
|
||
|
||
switch (SendMode) {
|
||
|
||
case CopyMaxBuffer:
|
||
|
||
// Copy the Packet into a Max Txm Buffer
|
||
|
||
TxmBuffer = (PVOID)Adapter->MaxTransmitBuffer[Adapter->MaxTransmitBufferIndex].Va;
|
||
|
||
#if _DBG
|
||
DbgPrint ("Copy packet %x into a Max Txm buffer [%d]\n",Packet,Adapter->MaxTransmitBufferIndex);
|
||
#endif
|
||
|
||
CopyFromPacketToBuffer (
|
||
Packet,
|
||
0,
|
||
TxmBuffer,
|
||
PacketSize,
|
||
&Length
|
||
);
|
||
|
||
ASSERT (Length == PacketSize);
|
||
ASSERT(Length >= ETH_LENGTH_OF_ADDRESS);
|
||
|
||
CurrentDescriptor = Adapter->EnqueueTransmitDescriptor;
|
||
|
||
Adapter->EnqueueTransmitDescriptor = CurrentDescriptor->Next;
|
||
|
||
MaxTransmitBufferCount++;
|
||
TxmDescriptorCount++;
|
||
|
||
|
||
//Clear all the Descriptor Control word but the SECOND_ADDR_CHAINED flag;
|
||
CurrentDescriptor->Control &= DC21X4_TDES_SECOND_ADDR_CHAINED;
|
||
|
||
// If GenerateCRC, add the software generated CRC
|
||
// to the end of the buffer
|
||
|
||
if (GenerateCRC) {
|
||
|
||
Tmp = (PUCHAR)TxmBuffer;
|
||
*(UNALIGNED ULONG *)&Tmp[PacketSize] =
|
||
CRC32 (
|
||
&Tmp[0],
|
||
PacketSize
|
||
);
|
||
PacketSize += sizeof(UINT);
|
||
CurrentDescriptor->Control |= DC21X4_TDES_ADD_CRC_DISABLE;
|
||
#if _DBG
|
||
DbgPrint(" Software CRC = %02x %02x %02x %02x\n",
|
||
Tmp[PacketSize-4],Tmp[PacketSize-3],
|
||
Tmp[PacketSize-2],Tmp[PacketSize-1]);
|
||
#endif
|
||
}
|
||
|
||
CurrentDescriptor->Control |= (
|
||
DC21X4_TDES_FIRST_SEGMENT
|
||
| PacketSize);
|
||
|
||
FirstSegmentDescriptor = CurrentDescriptor;
|
||
LastSegmentDescriptor = FirstSegmentDescriptor;
|
||
|
||
CurrentDescriptor->FirstBufferAddress =
|
||
Adapter->MaxTransmitBuffer[Adapter->MaxTransmitBufferIndex].Pa;
|
||
|
||
NdisFlushBuffer(
|
||
Adapter->MaxTransmitBuffer[Adapter->MaxTransmitBufferIndex].FlushBuffer,
|
||
TRUE
|
||
);
|
||
|
||
Adapter->MaxTransmitBufferIndex++;
|
||
Adapter->MaxTransmitBufferIndex &= (DC21X4_NUMBER_OF_MAX_TRANSMIT_BUFFERS-1);
|
||
|
||
#if _DBG
|
||
DbgPrint (" [%08x]\n %08x\n %08x\n %08x\n",
|
||
CurrentDescriptor,
|
||
CurrentDescriptor->Status,
|
||
CurrentDescriptor->Control,
|
||
CurrentDescriptor->FirstBufferAddress);
|
||
#endif
|
||
NdisStatus = NDIS_STATUS_PENDING;
|
||
// NdisStatus = NDIS_STATUS_SUCCESS; // hapi stress test pb
|
||
|
||
break;
|
||
|
||
|
||
case CopyMinBuffer:
|
||
|
||
// Copy the Packet into a Min Txm Buffer
|
||
|
||
TxmBuffer = (PVOID)Adapter->MinTransmitBuffer[Adapter->MinTransmitBufferIndex].Va;
|
||
|
||
#if _DBG
|
||
DbgPrint ("Copy packet %x into a Min Txm buffer [%d]\n",
|
||
Packet,Adapter->MinTransmitBufferIndex);
|
||
#endif
|
||
|
||
CopyFromPacketToBuffer (
|
||
Packet,
|
||
0,
|
||
TxmBuffer,
|
||
PacketSize,
|
||
&Length
|
||
);
|
||
|
||
ASSERT (Length == PacketSize);
|
||
|
||
ASSERT(Length >= ETH_LENGTH_OF_ADDRESS);
|
||
|
||
CurrentDescriptor = Adapter->EnqueueTransmitDescriptor;
|
||
|
||
Adapter->EnqueueTransmitDescriptor = CurrentDescriptor->Next;
|
||
|
||
MinTransmitBufferCount++;
|
||
TxmDescriptorCount++;
|
||
|
||
|
||
//Clear all the Descriptor Control word but the SECOND_ADDR_CHAINED flag;
|
||
CurrentDescriptor->Control &= DC21X4_TDES_SECOND_ADDR_CHAINED;
|
||
|
||
|
||
CurrentDescriptor->Control |= (
|
||
DC21X4_TDES_FIRST_SEGMENT
|
||
| PacketSize);
|
||
|
||
FirstSegmentDescriptor = CurrentDescriptor;
|
||
LastSegmentDescriptor = FirstSegmentDescriptor;
|
||
|
||
CurrentDescriptor->FirstBufferAddress =
|
||
Adapter->MinTransmitBuffer[Adapter->MinTransmitBufferIndex].Pa;
|
||
|
||
NdisFlushBuffer(
|
||
Adapter->MinTransmitBuffer[Adapter->MinTransmitBufferIndex].FlushBuffer,
|
||
TRUE
|
||
);
|
||
|
||
Adapter->MinTransmitBufferIndex++;
|
||
Adapter->MinTransmitBufferIndex &= (DC21X4_NUMBER_OF_MIN_TRANSMIT_BUFFERS-1);
|
||
|
||
#if _DBG
|
||
DbgPrint (" [%08x]\n %08x\n %08x\n %08x\n",
|
||
CurrentDescriptor,
|
||
CurrentDescriptor->Status,
|
||
CurrentDescriptor->Control,
|
||
CurrentDescriptor->FirstBufferAddress);
|
||
#endif
|
||
|
||
NdisStatus = NDIS_STATUS_PENDING;
|
||
// NdisStatus = NDIS_STATUS_SUCCESS; // hapi stress test pb
|
||
|
||
break;
|
||
|
||
|
||
case MappedBuffer:
|
||
|
||
FirstSegment = TRUE;
|
||
FirstBuffer = TRUE;
|
||
|
||
FirstSegmentDescriptor = Adapter->EnqueueTransmitDescriptor;
|
||
LastSegmentDescriptor = FirstSegmentDescriptor;
|
||
|
||
MapTableIndex = FirstSegmentDescriptor->MapTableIndex;
|
||
|
||
FirstSegmentDescriptor->Control &= DC21X4_TDES_SECOND_ADDR_CHAINED ;
|
||
FirstSegmentDescriptor->Control |= DC21X4_TDES_FIRST_SEGMENT;
|
||
|
||
|
||
for (Buffer=0; Buffer<NdisBufferCount; Buffer++) {
|
||
|
||
// Get the mapping of the physical segments
|
||
// of the current buffer
|
||
#if _DBG
|
||
DbgPrint("NdisMStartBufferPhysicalMapping (%d)\n",MapTableIndex);
|
||
DbgPrint("MapRegisterIndex = %d\n",Adapter->MapRegisterIndex);
|
||
DbgPrint("FreeMapRegisters = %d\n",Adapter->FreeMapRegisters);
|
||
#endif
|
||
|
||
NdisMStartBufferPhysicalMapping(
|
||
Adapter->MiniportAdapterHandle,
|
||
CurrentBuffer,
|
||
Adapter->MapRegisterIndex,
|
||
TRUE,
|
||
PhysicalSegmentArray,
|
||
&BufferPhysicalSegments
|
||
);
|
||
|
||
if (BufferPhysicalSegments) {
|
||
|
||
// Save the CurrentBuffer address for NdisCompleteBufferPhysicalMapping
|
||
|
||
Adapter->PhysicalMapping[MapTableIndex].Register = Adapter->MapRegisterIndex;
|
||
Adapter->PhysicalMapping[MapTableIndex].Buffer = CurrentBuffer;
|
||
Adapter->PhysicalMapping[MapTableIndex].Valid = TRUE;
|
||
|
||
Adapter->MapRegisterIndex++;
|
||
if (Adapter->MapRegisterIndex >= Adapter->AllocMapRegisters) {
|
||
Adapter->MapRegisterIndex = 0;
|
||
}
|
||
|
||
MapRegistersCount++;
|
||
|
||
// Put the physical segments for this buffer into
|
||
// the transmit descriptors.
|
||
#if _DBG
|
||
DbgPrint(" Nb segments = %d\n",BufferPhysicalSegments);
|
||
#endif
|
||
for (Segment=0; Segment<BufferPhysicalSegments;) {
|
||
|
||
ASSERT (NdisGetPhysicalAddressHigh(PhysicalSegmentArray[Segment].PhysicalAddress) == 0);
|
||
|
||
if (FirstBuffer) {
|
||
|
||
FirstBuffer = FALSE;
|
||
|
||
CurrentDescriptor = Adapter->EnqueueTransmitDescriptor;
|
||
LastSegmentDescriptor = CurrentDescriptor;
|
||
|
||
// Point to the next descriptor in the ring;
|
||
Adapter->EnqueueTransmitDescriptor= (Adapter->EnqueueTransmitDescriptor)->Next;
|
||
|
||
TxmDescriptorCount++;
|
||
|
||
if (FirstSegment) {
|
||
|
||
// The ownership bit of the first segment descriptor will be changed
|
||
// after the entire frame is fully mapped into the transmit ring
|
||
|
||
FirstSegment = FALSE;
|
||
|
||
}
|
||
else {
|
||
|
||
//Clear all the Descriptor Control word but the SECOND_ADDR_CHAINED flag;
|
||
CurrentDescriptor->Control &= DC21X4_TDES_SECOND_ADDR_CHAINED ;
|
||
|
||
// set the ownership bit to DC21X4
|
||
CurrentDescriptor->Status = DESC_OWNED_BY_DC21X4;
|
||
}
|
||
|
||
//First BufferSize
|
||
CurrentDescriptor->Control |= PhysicalSegmentArray[Segment].Length;
|
||
|
||
//First Buffer Address
|
||
CurrentDescriptor->FirstBufferAddress =
|
||
NdisGetPhysicalAddressLow(PhysicalSegmentArray[Segment].PhysicalAddress);
|
||
|
||
MapTableIndex++;
|
||
|
||
}
|
||
else {
|
||
|
||
FirstBuffer=TRUE;
|
||
|
||
MapTableIndex = (Adapter->EnqueueTransmitDescriptor)->MapTableIndex;
|
||
|
||
if (CurrentDescriptor->Control & DC21X4_TDES_SECOND_ADDR_CHAINED) {
|
||
continue;
|
||
}
|
||
else {
|
||
|
||
// Second BufferSize
|
||
CurrentDescriptor->Control |=
|
||
(PhysicalSegmentArray[Segment].Length << TDES_SECOND_BUFFER_SIZE_BIT_NUMBER);
|
||
|
||
// Second Buffer Address
|
||
CurrentDescriptor->SecondBufferAddress =
|
||
NdisGetPhysicalAddressLow(PhysicalSegmentArray[Segment].PhysicalAddress);
|
||
}
|
||
}
|
||
Segment++;
|
||
}
|
||
}
|
||
|
||
else {
|
||
|
||
// No physical segments in this Ndis buffer
|
||
|
||
NdisMCompleteBufferPhysicalMapping(
|
||
Adapter->MiniportAdapterHandle,
|
||
CurrentBuffer,
|
||
Adapter->MapRegisterIndex
|
||
);
|
||
|
||
}
|
||
|
||
NdisFlushBuffer(
|
||
CurrentBuffer,
|
||
TRUE
|
||
);
|
||
|
||
// Get the Next Buffer;
|
||
|
||
NdisGetNextBuffer(
|
||
CurrentBuffer,
|
||
&CurrentBuffer
|
||
);
|
||
|
||
}
|
||
|
||
NdisStatus = NDIS_STATUS_PENDING;
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
// Save the information needed by the Txm interrupt handler
|
||
// to complete the Send request
|
||
|
||
LastSegmentDescriptor->Packet = Packet;
|
||
LastSegmentDescriptor->PacketType = PacketType;
|
||
LastSegmentDescriptor->PacketSize = PacketSize;
|
||
LastSegmentDescriptor->SendStatus = SendMode;
|
||
|
||
LastSegmentDescriptor->Control |= DC21X4_TDES_LAST_SEGMENT;
|
||
|
||
LastSegmentDescriptor->Control |= DC21X4_TDES_INTERRUPT_ON_COMPLETION;
|
||
|
||
GoTransmitCount++;
|
||
|
||
// Desc Pointer of last segment descriptor points the first segment descriptor
|
||
LastSegmentDescriptor->DescPointer = FirstSegmentDescriptor;
|
||
|
||
// Desc Pointer of first segment descriptor points the last segment descriptor
|
||
FirstSegmentDescriptor->DescPointer = LastSegmentDescriptor;
|
||
|
||
if (Adapter->FullDuplex) {
|
||
NdisDprReleaseSpinLock(&Adapter->EnqueueSpinLock);
|
||
NdisDprAcquireSpinLock(&Adapter->FullDuplexSpinLock);
|
||
}
|
||
|
||
Adapter->FreeTransmitDescriptorCount -= TxmDescriptorCount;
|
||
|
||
Adapter->FreeMapRegisters -= MapRegistersCount;
|
||
|
||
Adapter->MaxTransmitBufferInUse += MaxTransmitBufferCount;
|
||
|
||
Adapter->MinTransmitBufferInUse += MinTransmitBufferCount;
|
||
|
||
Adapter->GeneralOptional[GO_TRANSMIT_QUEUE_LENGTH] += GoTransmitCount;
|
||
|
||
if (Adapter->FullDuplex) {
|
||
NdisDprReleaseSpinLock(&Adapter->FullDuplexSpinLock);
|
||
}
|
||
|
||
// Set the Ownership bit off the First_Segment descriptor;
|
||
FirstSegmentDescriptor->Status = DESC_OWNED_BY_DC21X4;
|
||
|
||
if (!Adapter->DisableTransmitPolling) {
|
||
|
||
// Poll Transmit the adapter
|
||
|
||
DC21X4_WRITE_PORT(
|
||
DC21X4_TXM_POLL_DEMAND,
|
||
1
|
||
);
|
||
}
|
||
|
||
return NdisStatus;
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/*+
|
||
*
|
||
* CRC32
|
||
*
|
||
* Routine Description:
|
||
*
|
||
* Generate a CRC-32 from the data stream
|
||
*
|
||
* Arguments:
|
||
*
|
||
* Data - the data stream
|
||
* Len - the length of the stream
|
||
*
|
||
*Return Value:
|
||
*
|
||
* CRC-32
|
||
*
|
||
-*/
|
||
extern
|
||
ULONG
|
||
CRC32 (
|
||
IN PUCHAR Data,
|
||
IN UINT Len
|
||
)
|
||
{
|
||
ULONG Crc = 0xffffffff;
|
||
|
||
while (Len--) {
|
||
Crc = CrcTable[(Crc ^ *Data++) & 0xFF] ^ (Crc >> 8);
|
||
}
|
||
|
||
return ~Crc;
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/*+
|
||
*
|
||
* NdisSendPackets
|
||
*
|
||
*
|
||
-*/
|
||
NDIS_STATUS
|
||
DC21X4SendPackets(
|
||
IN NDIS_HANDLE MiniportAdapterContext,
|
||
IN PPNDIS_PACKET PacketArray,
|
||
IN UINT NumberOfPackets
|
||
) {
|
||
return NDIS_STATUS_SUCCESS;
|
||
}
|
||
|