/*+ * 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 #include /*+ * 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; BufferMapRegisterIndex); 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; SegmentEnqueueTransmitDescriptor; 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; }