/*++ Copyright (c) 1992 Microsoft Corporation Module Name: reset.c Abstract: This is the file containing the reset code for the 3Com Etherlink/MC and Etherlink 16 Ethernet adapter. This driver conforms to the NDIS 3.0 interface. Author: Johnson R. Apacible (JohnsonA) 10-June-1991 Environment: Kernel Mode - Or whatever is the equivalent on OS/2 and DOS. Revision History: --*/ #include // // So we can trace things... // #include #include #include #define STATIC STATIC VOID ElnkAbortPendingQueue( IN PELNK_ADAPTER Adapter, IN BOOLEAN AbortOpens ); STATIC VOID ElnkSetConfigurationBlock( IN PELNK_ADAPTER Adapter ); STATIC BOOLEAN SetConfigurationBlockAndInit( IN PELNK_ADAPTER Adapter ); STATIC VOID SetupSharedMemory( IN PELNK_ADAPTER Adapter ); STATIC BOOLEAN ElnkPowerUpInit( IN PELNK_ADAPTER Adapter ); extern VOID ChangeAddressDispatch( IN PELNK_ADAPTER Adapter, IN UINT AddressCount, IN CHAR Addresses[][ETH_LENGTH_OF_ADDRESS], IN PELNK_OPEN Open, IN BOOLEAN Set ); STATIC BOOLEAN ElnkInitialInit( IN PELNK_ADAPTER Adapter, IN UINT ElnkInterruptVector ); STATIC VOID DoResetIndications( IN PELNK_ADAPTER Adapter, IN NDIS_STATUS Status ); VOID ResetAdapterVariables( IN PELNK_ADAPTER Adapter ); VOID Elnk16GenerateIdPattern( IN PELNK_ADAPTER Adapter ); // // Common Elnkmc and Elnk16 routines // BOOLEAN ElnkSyncStartReceive( IN PVOID Context ) /*++ Routine Description: This function is used to synchronize with the ISR the starting of a receive unit. Arguments: see NDIS 3.0 spec. Notes: returns TRUE on success, else FALSE. --*/ { PELNK_ADAPTER Adapter = (PELNK_ADAPTER)Context; IF_LOG('w'); WRITE_ADAPTER_REGISTER( Adapter, OFFSET_SCB_RD, (USHORT)(Adapter->RfdOffset) ); WRITE_ADAPTER_REGISTER( Adapter, OFFSET_SCBCMD, RUC_START ); ELNK_CA; Adapter->RuRestarted = TRUE; return(TRUE); } BOOLEAN ElnkSyncAbort( IN PVOID Context ) /*++ Routine Description: This function is used to synchronize with the ISR the starting of a abort of a command block. Arguments: see NDIS 3.0 spec. Notes: returns TRUE on success, else FALSE. --*/ { PELNK_ADAPTER Adapter = (PELNK_ADAPTER)Context; WRITE_ADAPTER_REGISTER( Adapter, OFFSET_SCBCMD, CUC_ABORT | RUC_ABORT ); ELNK_CA; return(TRUE); } BOOLEAN ElnkSyncReset( IN PVOID Context ) /*++ Routine Description: This function is used to synchronize with the ISR the starting of a reset command block. Arguments: see NDIS 3.0 spec. Notes: returns TRUE on success, else FALSE. --*/ { PELNK_ADAPTER Adapter = (PELNK_ADAPTER)Context; WRITE_ADAPTER_REGISTER( Adapter, OFFSET_SCBCMD, SCB_COMMAND_RESET ); ELNK_CA; return(TRUE); } STATIC NDIS_STATUS ElnkReset( IN NDIS_HANDLE MacBindingHandle ) /*++ Routine Description: The ElnkReset request instructs the MAC to issue a hardware reset to the network adapter. The MAC also resets its software state. See the description of NdisReset for a detailed description of this request. Arguments: MacBindingHandle - The context value returned by the MAC when the adapter was opened. In reality, it is a pointer to ELNK_OPEN. Return Value: The function value is the status of the operation. --*/ { // // Holds the status that should be returned to the caller. // NDIS_STATUS StatusToReturn = NDIS_STATUS_PENDING; PELNK_ADAPTER Adapter = PELNK_ADAPTER_FROM_BINDING_HANDLE(MacBindingHandle); // // Hold the locks while we update the reference counts on the // adapter and the open. // NdisAcquireSpinLock(&Adapter->Lock); Adapter->References++; if (!Adapter->ResetInProgress) { PELNK_OPEN Open = PELNK_OPEN_FROM_BINDING_HANDLE(MacBindingHandle); if (!Open->BindingShuttingDown) { Open->References++; SetupForReset( Adapter, PELNK_OPEN_FROM_BINDING_HANDLE(MacBindingHandle) ); Open->References--; } else { StatusToReturn = NDIS_STATUS_CLOSING; } } else { Adapter->References--; NdisReleaseSpinLock(&Adapter->Lock); return(NDIS_STATUS_RESET_IN_PROGRESS); } ELNK_DO_DEFERRED(Adapter); return(StatusToReturn); } STATIC VOID ElnkSetConfigurationBlock( IN PELNK_ADAPTER Adapter ) /*++ Routine Description: This routine simply fills the configuration block with the information necessary for initialization. NOTE: This routine assumes that it is called with the lock acquired OR that only a single thread of execution is accessing the particular adapter. Arguments: Adapter - The adapter which holds the initialization block to initialize. Return Value: None. --*/ { UINT PacketFilters; UINT i; PCONFIG_CB Configuration; Configuration = &Adapter->MulticastBlock->Parm.Config; NdisZeroMappedMemory( (PUCHAR)Configuration, sizeof(CONFIG_CB) ); // // Setup default configuration values // Adapter->OldParameterField = DEFAULT_PARM5; // // Set up the address filtering. // // First get hold of the combined packet filter. // if (Adapter->FilterDB != NULL) { PacketFilters = ETH_QUERY_FILTER_CLASSES(Adapter->FilterDB); } else { PacketFilters = 0; } // // this code was removed as it isn't necessary and causes the cards to // not be able to send packets unless the packet filter is changed. // #if 0 if (PacketFilters & NDIS_PACKET_TYPE_PROMISCUOUS) { // // If one binding is promiscuous there is no point in // setting up any other filtering. Every packet is // going to be accepted by the hardware. // Adapter->OldParameterField |= CONFIG_PROMISCUOUS; } else if (PacketFilters & NDIS_PACKET_TYPE_ALL_MULTICAST) { // // Simulate All Multicast // Adapter->OldParameterField |= CONFIG_PROMISCUOUS; } else if (PacketFilters & NDIS_PACKET_TYPE_BROADCAST) { // // Enable broadcast packets. // Adapter->OldParameterField &= ~CONFIG_BROADCAST; } if (!Adapter->IsExternal) { Adapter->OldParameterField |= CONFIG_INTERNAL; } // // see if we need to change adapter default configuration // NdisWriteRegisterUshort(&Configuration->Parameter1, DEFAULT_PARM1); NdisWriteRegisterUshort(&Configuration->Parameter2, DEFAULT_PARM2); NdisWriteRegisterUshort(&Configuration->Parameter3, DEFAULT_PARM3); NdisWriteRegisterUshort(&Configuration->Parameter4, DEFAULT_PARM4); NdisWriteRegisterUshort(&Configuration->Parameter6, DEFAULT_PARM6); NdisWriteRegisterUshort( &Configuration->Parameter5, Adapter->OldParameterField ); NdisWriteRegisterUshort(&Adapter->MulticastBlock->Status, CB_STATUS_FREE); NdisWriteRegisterUshort(&Adapter->MulticastBlock->Command, CB_CONFIG); NdisWriteRegisterUshort(&Adapter->MulticastBlock->NextCbOffset, ELNK_NULL); ElnkSubmitCommandBlockAndWait(Adapter); #endif // // Do Individual Address Setup // NdisWriteRegisterUshort(&Adapter->MulticastBlock->Status, CB_STATUS_FREE); NdisWriteRegisterUshort(&Adapter->MulticastBlock->Command, CB_SETUP); NdisWriteRegisterUshort(&Adapter->MulticastBlock->NextCbOffset, ELNK_NULL); for (i = 0; i < ETH_LENGTH_OF_ADDRESS; i++) { NdisWriteRegisterUchar( &Adapter->MulticastBlock->Parm.Setup.StationAddress[i], Adapter->CurrentAddress[i] ); } ElnkSubmitCommandBlockAndWait(Adapter); // // Now Query the Multicast Addresses // if (PacketFilters & NDIS_PACKET_TYPE_MULTICAST) { UINT NumberOfMulticastAddresses; NDIS_STATUS Status; EthQueryGlobalFilterAddresses( &Status, Adapter->FilterDB, ETH_LENGTH_OF_ADDRESS * ELNK_MAXIMUM_MULTICAST, &NumberOfMulticastAddresses, Adapter->PrivateMulticastBuffer ); if (Status == NDIS_STATUS_SUCCESS) { ChangeAddressDispatch( Adapter, NumberOfMulticastAddresses, Adapter->PrivateMulticastBuffer, ELNK_BOGUS_OPEN, TRUE ); } } } VOID ElnkStartAdapterReset( IN PELNK_ADAPTER Adapter ) /*++ Routine Description: This is the first phase of resetting the adapter hardware. It makes the following assumptions: 1) That the hardware has been stopped. 2) That it can not be preempted. 3) That no other adapter activity can occur. When this routine is finished all of the adapter information will be as if the driver was just initialized. Spinlock assumed held. Arguments: Adapter - The adapter whose hardware is to be reset. Return Value: None. --*/ { // // Go through the various transmit lists and abort every packet. // { UINT i; PNDIS_PACKET Packet; PELNK_RESERVED Reserved; PELNK_OPEN Open; PNDIS_PACKET Next; for ( i = 0; i < 3; i++ ) { switch (i) { case 0: Next = Adapter->FirstLoopBack; break; case 1: Next = Adapter->FirstFinishTransmit; break; case 2: Next = Adapter->FirstStagePacket; break; } while (Next) { Packet = Next; Reserved = PELNK_RESERVED_FROM_PACKET(Packet); Next = Reserved->Next; Open = PELNK_OPEN_FROM_BINDING_HANDLE(Reserved->MacBindingHandle); // // The completion of the packet is one less reason // to keep the open around. // ASSERT(Open->References); Open->References--; NdisReleaseSpinLock(&Adapter->Lock); NdisCompleteSend( Open->NdisBindingContext, Packet, NDIS_STATUS_REQUEST_ABORTED ); NdisAcquireSpinLock(&Adapter->Lock); } } } SetConfigurationBlockAndInit(Adapter); } STATIC BOOLEAN SetConfigurationBlockAndInit( IN PELNK_ADAPTER Adapter ) /*++ Routine Description: It is this routines responsibility to make sure that the Configuration block is filled and the adapter is initialized *but not* started. NOTE: This routine assumes that it is called with the lock acquired OR that only a single thread of execution is working with this particular adapter. Arguments: Adapter - The adapter whose hardware is to be initialized. Return Value: TRUE is reset successful, FALSE otherwise. --*/ { BOOLEAN StatusOfReset = FALSE; // // We have 2 ways of doing reset, one for initial power up // and the other when we have already setup the scb // ResetAdapterVariables(Adapter); StatusOfReset = ElnkPowerUpInit(Adapter); if (StatusOfReset) { // // Setup the shared memory structures // SetupSharedMemory(Adapter); // // Fill in the adapter's initialization block. // ElnkSetConfigurationBlock(Adapter); ELNK_ENABLE_INTERRUPT; DoResetIndications(Adapter, NDIS_STATUS_SUCCESS); if (!Adapter->FirstReset) { NdisSetTimer( &Adapter->DeadmanTimer, 5000 ); } else { Adapter->FirstReset = FALSE; } } else { DoResetIndications(Adapter, NDIS_STATUS_FAILURE); } return(StatusOfReset); } VOID ElnkStartChip( IN PELNK_ADAPTER Adapter, IN PELNK_RECEIVE_INFO ReceiveInfo ) /*++ Routine Description: This routine is used to start an already initialized Elnk. Arguments: Adapter - The adapter for the Elnk to start. ReceiveInfo - Pointer to the first receive entry to be used by the adapter. Return Value: None. --*/ { // // If the memory is not mapped, then we can do nothing // if (!Adapter->MemoryIsMapped) { return; } // // Start the receive unit // Adapter->RfdOffset = ReceiveInfo->RfdOffset; ELNK_WAIT; NdisSynchronizeWithInterrupt( &(Adapter->Interrupt), (PVOID)ElnkSyncStartReceive, (PVOID)(Adapter) ); ELNK_WAIT; } VOID ElnkStopChip( IN PELNK_ADAPTER Adapter ) /*++ Routine Description: This routine is used to stop the Elnk. Arguments: Adapter - The Elnk adapter to stop. Return Value: None. --*/ { UCHAR CurrentCsr; // // If the adapter has previously been reset, we need to stop both the // CU and the RU // if (!Adapter->FirstReset) { NdisSynchronizeWithInterrupt( &(Adapter->Interrupt), (PVOID)ElnkSyncAbort, (PVOID)(Adapter) ); } ELNK_READ_UCHAR( Adapter, ELNK_CSR, &CurrentCsr ); ELNK_WRITE_UCHAR( Adapter, ELNK_CSR, CurrentCsr & ~CSR_INTEN ); } STATIC BOOLEAN ElnkPowerUpInit( IN PELNK_ADAPTER Adapter ) /*++ Routine Description: This routine initializes the card and reads and sets relevant information from and to the 82586. NOTE: This routine assumes that it is called with the lock acquired OR that only a single thread of execution is working with this particular adapter. Arguments: Adapter - The adapter whose hardware is to be initialized. Return Value: TRUE is reset successful, FALSE otherwise. --*/ { NDIS_STATUS Status; NDIS_PHYSICAL_ADDRESS PhysicalAddress; // // Get station address // ElnkGetStationAddress( Adapter ); // // Check for validity of the address // if (((Adapter->NetworkAddress[0] == 0xFF) && (Adapter->NetworkAddress[1] == 0xFF) && (Adapter->NetworkAddress[2] == 0xFF) && (Adapter->NetworkAddress[3] == 0xFF) && (Adapter->NetworkAddress[4] == 0xFF) && (Adapter->NetworkAddress[5] == 0xFF)) || ((Adapter->NetworkAddress[0] == 0x00) && (Adapter->NetworkAddress[1] == 0x00) && (Adapter->NetworkAddress[2] == 0x00) && (Adapter->NetworkAddress[3] == 0x00) && (Adapter->NetworkAddress[4] == 0x00) && (Adapter->NetworkAddress[5] == 0x00))) { ElnkLogError( Adapter, startChip, NDIS_ERROR_CODE_INVALID_VALUE_FROM_ADAPTER, 0); return(FALSE); } // // Do Memory Mapping // if (!Adapter->MemoryIsMapped) { NdisSetPhysicalAddressHigh(PhysicalAddress, 0); NdisSetPhysicalAddressLow(PhysicalAddress, Adapter->SharedRamPhys); NdisMapIoSpace( &Status, (PVOID *)(&Adapter->SharedRam), Adapter->NdisAdapterHandle, PhysicalAddress, Adapter->SharedRamSize * 1024 ); if (Status != NDIS_STATUS_SUCCESS) { ElnkLogError( Adapter, startChip, NDIS_ERROR_CODE_RESOURCE_CONFLICT, 0 ); return(FALSE); } Adapter->MemoryIsMapped = TRUE; } // // everything must be in a single 64K segment // Adapter->Scp = (PSCP) ELNK_GET_HOST_ADDRESS(Adapter, OFFSET_SCP); Adapter->Iscp = (PISCP) ELNK_GET_HOST_ADDRESS(Adapter, OFFSET_ISCP); Adapter->Scb = (PSCB) ELNK_GET_HOST_ADDRESS(Adapter, OFFSET_SCB); Adapter->MulticastBlock = (PNON_TRANSMIT_CB) ELNK_GET_HOST_ADDRESS(Adapter, OFFSET_MULTICAST); Adapter->TransmitQueue = (PTRANSMIT_CB) Adapter->SharedRam; Adapter->ReceiveQueue = (PRECEIVE_FRAME_DESCRIPTOR) ( (PUCHAR)(Adapter->TransmitQueue) + Adapter->NumberOfTransmitBuffers * (sizeof(TRANSMIT_CB) + ELNK_OFFSET_TO_NEXT_BUFFER)); if ELNKDEBUG { DPrint2("Shared Ram = %lx\n",Adapter->SharedRam); DPrint2("Scp = %lx\n",Adapter->Scp); DPrint2("IScp = %lx\n",Adapter->Iscp); DPrint2("Scb = %lx\n",Adapter->Scb); DPrint2("MulticastBlock = %lx\n",Adapter->MulticastBlock); DPrint2("****** Adapter = %lx\n",Adapter); } #if ELNKMC // // Reset Chip // ELNK_WRITE_UCHAR( Adapter, ELNK_CSR, CSR_RESET | CSR_BANK_SELECT_MASK ); NdisStallExecution(1000); ELNK_WRITE_UCHAR( Adapter, ELNK_CSR, CSR_BANK_SELECT_MASK | CSR_INTEN ); // // Do a Channel Attention to wake up card. When card wakes up, it will // be very hungry and will try to get its food from the reset vector at // location offset + 3FF6 for the address of the ISCP // // // Setup the Reset vector First // NdisWriteRegisterUshort(&Adapter->Scp->SysBus, 0); NdisWriteRegisterUshort(&Adapter->Scp->IscpBase, 0); NdisWriteRegisterUshort( &Adapter->Scp->IscpOffset, OFFSET_ISCP ); // // Setup the ISCP // NdisWriteRegisterUlong(&Adapter->Iscp->ScbBaseAddress, 0); NdisWriteRegisterUshort( &Adapter->Iscp->ScbOffset, OFFSET_SCB ); NdisWriteRegisterUshort(&Adapter->Iscp->Busy, 0x01); // // Put the Scb in a known state // NdisWriteRegisterUshort(&Adapter->Scb->Status, CB_STATUS_FREE); NdisWriteRegisterUshort(&Adapter->Scb->Command, 0); NdisWriteRegisterUshort(&Adapter->Scb->CommandListOffset, ELNK_NULL); NdisWriteRegisterUshort(&Adapter->Scb->RFAOffset, ELNK_NULL); NdisWriteRegisterUshort(&Adapter->Scb->CrcErrors, 0); NdisWriteRegisterUshort(&Adapter->Scb->AlignmentErrors, 0); NdisWriteRegisterUshort(&Adapter->Scb->ResourceErrors, 0); NdisWriteRegisterUshort(&Adapter->Scb->OverrunErrors, 0); // // Do Channel Attention // ELNK_CA; #else // // Setup the Reset vector First // NdisWriteRegisterUshort(&Adapter->Scp->SysBus, 0); NdisWriteRegisterUshort(&Adapter->Scp->IscpBase, 0); NdisWriteRegisterUshort( &Adapter->Scp->IscpOffset, OFFSET_ISCP ); // // Setup the ISCP // NdisWriteRegisterUlong(&Adapter->Iscp->ScbBaseAddress, 0); NdisWriteRegisterUshort( &Adapter->Iscp->ScbOffset, OFFSET_SCB ); NdisWriteRegisterUshort(&Adapter->Iscp->Busy, 0x01); // // Put the Scb in a known state // NdisWriteRegisterUshort(&Adapter->Scb->Status, CB_STATUS_FREE); NdisWriteRegisterUshort(&Adapter->Scb->Command, 0); NdisWriteRegisterUshort(&Adapter->Scb->CommandListOffset, ELNK_NULL); NdisWriteRegisterUshort(&Adapter->Scb->RFAOffset, ELNK_NULL); NdisWriteRegisterUshort(&Adapter->Scb->CrcErrors, 0); NdisWriteRegisterUshort(&Adapter->Scb->AlignmentErrors, 0); NdisWriteRegisterUshort(&Adapter->Scb->ResourceErrors, 0); NdisWriteRegisterUshort(&Adapter->Scb->OverrunErrors, 0); // // Reset Chip // ELNK_WRITE_UCHAR( Adapter, ELNK_CSR, CSR_RESET ); NdisStallExecution(1000); ELNK_WRITE_UCHAR( Adapter, ELNK_CSR, CSR_INTEN ); // // Do a Channel Attention to wake up card. When card wakes up, it will // be very hungry and will try to get its food from the reset vector at // location offset + 3FF6 for the address of the ISCP // ELNK_WRITE_UCHAR( Adapter, ELNK_CSR, CSR_DEFAULT ); // // Do Channel Attention // ELNK_CA; #endif return(TRUE); } STATIC VOID DoResetIndications( IN PELNK_ADAPTER Adapter, IN NDIS_STATUS Status ) /*++ Routine Description: This routine is called by SetConfigurationBlockAndInit to perform any indications which need to be done after a reset. Note that this routine will be called after either a successful reset or a failed reset. Arguments: Adapter - The adapter whose hardware has been initialized. Status - The status of the reset to send to the protocol(s). Return Value: None. --*/ { // // This will point (possibly null) to the open that // initiated the reset. // PELNK_OPEN ResettingOpen; // // We save off the open that caused this reset incase // we get *another* reset while we're indicating the // last reset is done. // ResettingOpen = Adapter->ResettingOpen; // // We need to signal every open binding that the // reset is complete. We increment the reference // count on the open binding while we're doing indications // so that the open can't be deleted out from under // us while we're indicating (recall that we can't own // the lock during the indication). // { PELNK_OPEN Open; PLIST_ENTRY CurrentLink; CurrentLink = Adapter->OpenBindings.Flink; while (CurrentLink != &Adapter->OpenBindings) { Open = CONTAINING_RECORD( CurrentLink, ELNK_OPEN, OpenList ); Open->References++; if (Status != NDIS_STATUS_SUCCESS) { // // Reset failed. Notify of death // NdisIndicateStatus( Open->NdisBindingContext, NDIS_STATUS_CLOSED, NULL, 0 ); } NdisIndicateStatus( Open->NdisBindingContext, NDIS_STATUS_RESET_END, &Status, sizeof(Status) ); NdisIndicateStatusComplete(Open->NdisBindingContext); Open->References--; CurrentLink = CurrentLink->Flink; } // // Look to see which open initiated the reset. // // If the reset was initiated for some obscure hardware // reason that can't be associated with a particular // open (e.g. memory error on receiving a packet) then // we won't have an initiating request so we can't // indicate. (The ResettingOpen pointer will be // NULL in this case.) // if (ResettingOpen) { NdisCompleteReset( ResettingOpen->NdisBindingContext, Status ); ResettingOpen->References--; } } Adapter->ResetInProgress = FALSE; if (Status == NDIS_STATUS_SUCCESS) { // // Process any Opens that may have been queued in the meantime // ElnkStartChip(Adapter, &Adapter->ReceiveInfo[Adapter->ReceiveHead]); ElnkProcessRequestQueue(Adapter); } else { // // Abort everything // ElnkAbortPendingQueue(Adapter, TRUE); } } STATIC VOID ElnkAbortPendingQueue( IN PELNK_ADAPTER Adapter, IN BOOLEAN AbortOpens ) /*++ Routine Description: This routine aborts all stuff in the pending queue. NOTE: This routine must be called with the lock acquired. Arguments: Adapter - The adapter whose hardware is to be initialized. AbortOpens - Should Open requests be aborted. Return Value: None. --*/ { PNDIS_REQUEST CurrentRequest; PNDIS_REQUEST * CurrentNextLocation; PELNK_OPEN TmpOpen; PELNK_REQUEST_RESERVED Reserved; // // If there is a close at the top of the queue, then // it may be in two states: // // 1- Has interrupted, and the InterruptDpc got the // interrupt out of Adapter->IsrValue before we zeroed it. // // 2- Has interrupted, but we zeroed Adapter->IsrValue // before it read it, OR has not yet interrupted. // // In case 1, the interrupt will be processed and the // close will complete without our intervention. In // case 2, the open will not complete. In that case // the CAM will have been updated for that open, so // all that remains is for us to dereference the open // as would have been done in the interrupt handler. // // Closes that are not at the top of the queue we // leave in place; when we restart the queue after // the reset, they will get processed. // CurrentRequest = Adapter->FirstRequest; if (CurrentRequest) { Reserved = PELNK_RESERVED_FROM_REQUEST(CurrentRequest); // // If the first request is a close, take it off the // queue, and "complete" it. // if (CurrentRequest->RequestType == NdisRequestClose) { Adapter->FirstRequest = Reserved->Next; --(Reserved->OpenBlock)->References; CurrentRequest = Adapter->FirstRequest; } CurrentNextLocation = &(Adapter->FirstRequest); while (CurrentRequest) { Reserved = PELNK_RESERVED_FROM_REQUEST(CurrentRequest); if (CurrentRequest->RequestType == NdisRequestClose) { CurrentNextLocation = &(Reserved->Next); } else if (CurrentRequest->RequestType == NdisRequestOpen) { if (AbortOpens) { // // Complete the open // TmpOpen = Reserved->OpenBlock; NdisReleaseSpinLock(&Adapter->Lock); NdisCompleteOpenAdapter( TmpOpen->NdisBindingContext, NDIS_STATUS_FAILURE, 0); ELNK_FREE_PHYS(TmpOpen); NdisAcquireSpinLock(&Adapter->Lock); // // Remove it from the list // *CurrentNextLocation = Reserved->Next; } else { // // Skip the open // CurrentNextLocation = &(Reserved->Next); } } else { // // Not a close, remove it from the list and // fail it. // *CurrentNextLocation = Reserved->Next; TmpOpen = Reserved->OpenBlock; NdisReleaseSpinLock(&Adapter->Lock); NdisCompleteRequest( TmpOpen->NdisBindingContext, CurrentRequest, NDIS_STATUS_REQUEST_ABORTED ); NdisAcquireSpinLock(&Adapter->Lock); TmpOpen->References--; } CurrentRequest = *CurrentNextLocation; } } } STATIC VOID SetupForReset( IN PELNK_ADAPTER Adapter, IN PELNK_OPEN Open ) /*++ Routine Description: This routine is used to fill in the who and why a reset is being set up as well as setting the appropriate fields in the adapter. NOTE: This routine must be called with the lock acquired. Arguments: Adapter - The adapter whose hardware is to be initialized. Open - A (possibly NULL) pointer to an Elnk open structure. The reason it could be null is if the adapter is initiating the reset on its own. Return Value: None. --*/ { BOOLEAN Cancelled; // // Stop our deadman timer // NdisCancelTimer(&Adapter->DeadmanTimer, &Cancelled); // // Shut down the chip. We won't be doing any more work until // the reset is complete. // ElnkStopChip(Adapter); // // We need to signal every open binding that the // reset has started. We increment the reference // count on the open binding while we're doing indications // so that the open can't be deleted out from under // us while we're indicating (recall that we can't own // the lock during the indication). // Adapter->CurrentCsr = CSR_DEFAULT; { PELNK_OPEN Open; PLIST_ENTRY CurrentLink; CurrentLink = Adapter->OpenBindings.Flink; while (CurrentLink != &Adapter->OpenBindings) { Open = CONTAINING_RECORD( CurrentLink, ELNK_OPEN, OpenList ); Open->References++; NdisReleaseSpinLock(&Adapter->Lock); NdisIndicateStatus( Open->NdisBindingContext, NDIS_STATUS_RESET_START, NULL, 0 ); NdisIndicateStatusComplete(Open->NdisBindingContext); NdisAcquireSpinLock(&Adapter->Lock); Open->References--; CurrentLink = CurrentLink->Flink; } } Adapter->ResetInProgress = TRUE; // // Shut down all of the transmit queues so that the // transmit portion of the chip will eventually calm down. // Adapter->StageOpen = FALSE; ElnkAbortPendingQueue(Adapter, FALSE); Adapter->ResettingOpen = Open; // // If there is a valid open we should up the reference count // so that the open can't be deleted before we indicate that // their request is finished. // if (Open) { Open->References++; } } VOID ElnkGetStationAddress( IN PELNK_ADAPTER Adapter ) /*++ Routine Description: This routine gets the network address from the hardware. Arguments: Adapter - Where to store the network address. Return Value: None. --*/ { #if !ELNKMC // // Select card address // ELNK_WRITE_UCHAR( Adapter, ELNK_CSR, 0x01 ); #endif ELNK_READ_UCHAR( Adapter, ELNK_STATION_ID, &Adapter->NetworkAddress[0] ); ELNK_READ_UCHAR( Adapter, ELNK_STATION_ID + 1, &Adapter->NetworkAddress[1] ); ELNK_READ_UCHAR( Adapter, ELNK_STATION_ID + 2 , &Adapter->NetworkAddress[2] ); ELNK_READ_UCHAR( Adapter, ELNK_STATION_ID + 3, &Adapter->NetworkAddress[3] ); ELNK_READ_UCHAR( Adapter, ELNK_STATION_ID + 4, &Adapter->NetworkAddress[4] ); ELNK_READ_UCHAR( Adapter, ELNK_STATION_ID + 5, &Adapter->NetworkAddress[5] ); if ELNKDEBUG { UINT i; for (i=0;i<6;i++) { DPrint2("%x-",(UCHAR)Adapter->NetworkAddress[i]); } DPrint1("\n"); } // // if no new address is specified, use the BIA // if (!Adapter->AddressChanged) { ETH_COPY_NETWORK_ADDRESS( Adapter->CurrentAddress, Adapter->NetworkAddress ); } } #pragma NDIS_INIT_FUNCTION(ElnkInitialInit) BOOLEAN ElnkInitialInit( IN PELNK_ADAPTER Adapter, IN UINT ElnkInterruptVector ) /*++ Routine Description: This routine sets up the initial init of the driver. Arguments: Adapter - The adapter for the hardware. ElnkInterruptVector - Interrupt number used by the card. Return Value: None. --*/ { NDIS_STATUS Status; // // stop the chip // ElnkStopChip(Adapter); NdisInitializeInterrupt( &Status, &Adapter->Interrupt, Adapter->NdisAdapterHandle, ElnkIsr, Adapter, ElnkStandardInterruptDpc, ElnkInterruptVector, ElnkInterruptVector, FALSE, #if ELNKMC (NdisInterruptLevelSensitive) #else (NdisInterruptLatched) #endif ); if (Status == NDIS_STATUS_SUCCESS) { if (!SetConfigurationBlockAndInit(Adapter)) { if ELNKDEBUG DPrint1("Error configurating block and initializing...\n"); ElnkLogError( Adapter, initialInit, NDIS_ERROR_CODE_HARDWARE_FAILURE, 0 ); NdisRemoveInterrupt(&Adapter->Interrupt); return FALSE; } } else { if ELNKDEBUG DPrint1("Elnk: Unsuccessful connect to interrupt\n"); ElnkLogError( Adapter, initialInit, NDIS_ERROR_CODE_INTERRUPT_CONNECT, (ULONG) ElnkInterruptVector ); return(FALSE); } return(TRUE); } STATIC VOID SetupSharedMemory( IN PELNK_ADAPTER Adapter ) /*++ Routine Description: This routine initializes and organizes the - Command List - Receive Frame Area Arguments: Adapter - The adapter to allocate memory for. Return Value: None. --*/ { // // Pointer to a Receive Entry. Used while initializing // the Receive Queue. // PRECEIVE_FRAME_DESCRIPTOR CurrentReceiveEntry; // // Pointer to a Command Block. Used while initializing // the Command Queue. // PTRANSMIT_CB CurrentCommandBlock; // // for loop variables // UINT i; if ELNKDEBUG DPrint1("Allocating Command Blocks\n"); // // Put the Command Blocks into a known state. // for( i = 0, CurrentCommandBlock = Adapter->TransmitQueue; i < Adapter->NumberOfTransmitBuffers; i++ ) { NdisZeroMappedMemory( (PUCHAR)CurrentCommandBlock, sizeof(TRANSMIT_CB) ); Adapter->TransmitInfo[i].NextCommand = ELNK_EMPTY; Adapter->TransmitInfo[i].OwningPacket = NULL; Adapter->TransmitInfo[i].OwningOpenBinding = NULL; Adapter->TransmitInfo[i].CommandBlock = CurrentCommandBlock; Adapter->TransmitInfo[i].CbOffset = ELNK_GET_CARD_ADDRESS( Adapter, CurrentCommandBlock ); Adapter->TransmitInfo[i].Buffer = CurrentCommandBlock + 1; Adapter->TransmitInfo[i].BufferOffset = ELNK_GET_CARD_ADDRESS( Adapter, Adapter->TransmitInfo[i].Buffer ); ASSERT(Adapter->TransmitInfo[i].BufferOffset == (Adapter->TransmitInfo[i].CbOffset + sizeof(TRANSMIT_CB)) ); NdisWriteRegisterUshort( &CurrentCommandBlock->TbdOffset, ELNK_GET_CARD_ADDRESS(Adapter, &CurrentCommandBlock->Tbd) ); // // ELNK_NULL is non-zero // NdisWriteRegisterUshort( &CurrentCommandBlock->NextCbOffset, ELNK_NULL ); NdisWriteRegisterUshort( &CurrentCommandBlock->Tbd.NextTbdOffset, ELNK_NULL ); NdisWriteRegisterUlong( &CurrentCommandBlock->Tbd.BufferOffset, Adapter->TransmitInfo[i].BufferOffset ); if ELNKDEBUG DPrint4("cb address = %x offset = %x buff = %x\n", CurrentCommandBlock, Adapter->TransmitInfo[i].CbOffset, Adapter->TransmitInfo[i].BufferOffset ); CurrentCommandBlock = (PTRANSMIT_CB)((PUCHAR)Adapter->TransmitInfo[i].Buffer + ELNK_OFFSET_TO_NEXT_BUFFER); } // // The multicast transmitinfo is the nth + 1 where n is the number // of transmit buffers. // // // Fill in the multicast Block // NdisZeroMappedMemory( (PUCHAR)Adapter->MulticastBlock, sizeof(NON_TRANSMIT_CB) ); Adapter->TransmitInfo[Adapter->NumberOfTransmitBuffers].NextCommand = ELNK_EMPTY; Adapter->TransmitInfo[Adapter->NumberOfTransmitBuffers].OwningPacket = NULL; Adapter->TransmitInfo[Adapter->NumberOfTransmitBuffers].OwningOpenBinding = NULL; Adapter->TransmitInfo[Adapter->NumberOfTransmitBuffers].CommandBlock = (PTRANSMIT_CB) Adapter->MulticastBlock; Adapter->TransmitInfo[Adapter->NumberOfTransmitBuffers].CbOffset = OFFSET_MULTICAST; Adapter->TransmitInfo[Adapter->NumberOfTransmitBuffers].Buffer = NULL; Adapter->TransmitInfo[Adapter->NumberOfTransmitBuffers].BufferOffset = ELNK_NULL; NdisWriteRegisterUshort( &Adapter->MulticastBlock->NextCbOffset, ELNK_NULL ); if ELNKDEBUG DPrint1("Allocating receive buffers\n"); // // Allocate the receive buffers and attach them to the Receive // Queue entries. // for( i = 0, CurrentReceiveEntry = (PRECEIVE_FRAME_DESCRIPTOR) Adapter->ReceiveQueue; i < Adapter->NumberOfReceiveBuffers; i++ ) { NdisZeroMemory( &Adapter->ReceiveInfo[i], sizeof(ELNK_RECEIVE_INFO) ); NdisZeroMappedMemory( (PUCHAR)CurrentReceiveEntry, sizeof(RECEIVE_FRAME_DESCRIPTOR) ); if ELNKDEBUG DPrint2("Rfd = %x",CurrentReceiveEntry); Adapter->ReceiveInfo[i].Rfd = CurrentReceiveEntry; Adapter->ReceiveInfo[i].RfdOffset = ELNK_GET_CARD_ADDRESS( Adapter, CurrentReceiveEntry ); if ELNKDEBUG DPrint2(" Offset = %x",Adapter->ReceiveInfo[i].RfdOffset); NdisWriteRegisterUshort( &Adapter->ReceiveInfo[i].Rfd->RbdOffset, ELNK_GET_CARD_ADDRESS(Adapter,&CurrentReceiveEntry->Rbd) ); Adapter->ReceiveInfo[i].NextRfdIndex = (i+1) % Adapter->NumberOfReceiveBuffers; Adapter->ReceiveInfo[i].Buffer = CurrentReceiveEntry + 1; Adapter->ReceiveInfo[i].BufferOffset = ELNK_GET_CARD_ADDRESS( Adapter, Adapter->ReceiveInfo[i].Buffer ); if ELNKDEBUG DPrint2(" Buffer Offset = %x\n",Adapter->ReceiveInfo[i].BufferOffset); CurrentReceiveEntry = (PRECEIVE_FRAME_DESCRIPTOR) ((PUCHAR) Adapter->ReceiveInfo[i].Buffer + ELNK_OFFSET_TO_NEXT_BUFFER); } if ELNKDEBUG DPrint1("initializing links\n"); for( i = 0; i < Adapter->NumberOfReceiveBuffers; i++ ) { UINT Next = Adapter->ReceiveInfo[i].NextRfdIndex; // // Fill the Receive Buffer Descriptor // CurrentReceiveEntry = Adapter->ReceiveInfo[i].Rfd; NdisWriteRegisterUlong( &CurrentReceiveEntry->Rbd.BufferOffset, Adapter->ReceiveInfo[i].BufferOffset ); NdisWriteRegisterUshort( &CurrentReceiveEntry->Rbd.Size, (USHORT) (MAXIMUM_ETHERNET_PACKET_SIZE | RBD_END_OF_LIST) ); NdisWriteRegisterUshort( &CurrentReceiveEntry->Rbd.Status, (USHORT) 0 ); #if 0 NdisWriteRegisterUshort( &CurrentReceiveEntry->Rbd.NextRbdOffset, Adapter->ReceiveInfo[Next].Rfd->RbdOffset ); #endif NdisWriteRegisterUshort( &CurrentReceiveEntry->Rbd.NextRbdOffset, ELNK_NULL ); // // Fill the Receive Frame Descriptor // NdisWriteRegisterUshort( &CurrentReceiveEntry->NextRfdOffset, Adapter->ReceiveInfo[Next].RfdOffset ); } // // initialize the last descriptor // NdisWriteRegisterUshort( &CurrentReceiveEntry->Command, RFD_COMMAND_END_OF_LIST | RFD_COMMAND_SUSPEND ); #if 0 NdisWriteRegisterUshort( &CurrentReceiveEntry->Rbd.Size, (USHORT) MAXIMUM_ETHERNET_PACKET_SIZE | RBD_END_OF_LIST ); #endif // // reset pointers // Adapter->ReceiveHead = 0; Adapter->ReceiveTail = Adapter->NumberOfReceiveBuffers - 1; } #if !ELNKMC BOOLEAN AlreadyGeneratedPattern = FALSE; #pragma NDIS_INIT_FUNCTION(Elnk16ConfigureAdapter) BOOLEAN Elnk16ConfigureAdapter( IN PELNK_ADAPTER Adapter, IN BOOLEAN IsExternal, IN BOOLEAN ZwsEnabled ) /*++ Routine Description: This routine is used to setup the card registers for the correct configuration Arguments: Adapter - adapter to configure. IsExternal - are we using External transceiver? ZwsEnabled - should zero wait state be enabled? Return Value: Returns true if configuration was done. --*/ { if (!AlreadyGeneratedPattern) { // // Initialize State // NdisWritePortUchar( Adapter->NdisAdapterHandle, ELNK16_ID_PORT, 0x00 ); Elnk16GenerateIdPattern(Adapter); // // Go to run state // NdisWritePortUchar( Adapter->NdisAdapterHandle, ELNK16_ID_PORT, 0x00 ); AlreadyGeneratedPattern = TRUE; } #if 0 // // Go to reset state // Elnk16GenerateIdPattern(Adapter); // // Go to IoLoad state // Elnk16GenerateIdPattern(Adapter); // // Set I/O base address // { USHORT IdPort; IdPort = (USHORT) (Adapter->IoBase - 0x200); if (IdPort > 0) { IdPort = IdPort >> 4 ; } NdisWritePortUchar( Adapter->NdisAdapterHandle, ELNK16_ID_PORT, (UCHAR)IdPort ); } #endif // // Now in the configuration state // // // Check if we have a card present... // { UCHAR Port1; UCHAR Port2; UCHAR Port3; UCHAR Port4; UCHAR Port5; UCHAR Port6; // // Select 3Com signature. We should get *3COM* in ascii // ELNK_WRITE_UCHAR(Adapter, ELNK_CSR, 0x00); ELNK_READ_UCHAR(Adapter, ELNK16_3COM, &Port1); ELNK_READ_UCHAR(Adapter, ELNK16_3COM + 1, &Port2); ELNK_READ_UCHAR(Adapter, ELNK16_3COM + 2, &Port3); ELNK_READ_UCHAR(Adapter, ELNK16_3COM + 3, &Port4); ELNK_READ_UCHAR(Adapter, ELNK16_3COM + 4, &Port5); ELNK_READ_UCHAR(Adapter, ELNK16_3COM + 5, &Port6); if (!((Port1 == '*') && (Port2 == '3') && (Port3 == 'C') && (Port4 == 'O') && (Port5 == 'M') && (Port6 == '*'))) { return(FALSE); } } #if NDIS_NT switch (Adapter->SharedRamSize) { case 16: Adapter->CardOffset = 0xC000; Adapter->NumberOfTransmitBuffers = ELNK16_16K_TRANSMITS; Adapter->NumberOfReceiveBuffers = ELNK16_16K_RECEIVES; break; case 32: Adapter->CardOffset = 0x8000; Adapter->NumberOfTransmitBuffers = ELNK16_32K_TRANSMITS; Adapter->NumberOfReceiveBuffers = ELNK16_32K_RECEIVES; break; case 48: Adapter->CardOffset = 0x4000; Adapter->NumberOfTransmitBuffers = ELNK16_48K_TRANSMITS; Adapter->NumberOfReceiveBuffers = ELNK16_48K_RECEIVES; break; case 64: Adapter->CardOffset = 0; Adapter->NumberOfTransmitBuffers = ELNK16_64K_TRANSMITS; Adapter->NumberOfReceiveBuffers = ELNK16_64K_RECEIVES; break; } // // Save transceiver type // Adapter->IsExternal = IsExternal; #if 0 // // Set Transceiver type // { UCHAR PortValue; if (IsExternal) { PortValue = 0x00; } else { PortValue = 0x80; } ELNK_WRITE_UCHAR(Adapter, ELNK16_ROM_CONFIG, PortValue); } // // Set window base address // { UCHAR PortValue; switch (Adapter->SharedRamPhys) { case 0xC0000: PortValue = 0; break; case 0xC8000: PortValue = 0x08; break; case 0xD0000: PortValue = 0x10; break; case 0xD8000: PortValue = 0x18; break; } // // Set ZWS value // if (ZwsEnabled) { PortValue |= 0x80; } ELNK_WRITE_UCHAR(Adapter, ELNK16_RAM_CONFIG, PortValue); } // // Set interrupt number // ELNK_WRITE_UCHAR(Adapter, ELNK16_ICR, (UCHAR)Adapter->InterruptVector); // // Go to the run state // Elnk16GenerateIdPattern(Adapter); #endif #endif NDIS_NT #if NDIS_WIN { UCHAR Temp; // Transceiver type ELNK_READ_UCHAR(Adapter, ELNK16_ROM_CONFIG, &Temp); Adapter->IsExternal = (Temp & ROMCR_BNC) ? 0 : 1; DPrint2("Temp = %x ",Temp); DPrint2("Adapter->IsExternal = %x\n",Adapter->IsExternal); // Interrupt number ELNK_READ_UCHAR(Adapter, ELNK16_ICR, &Temp); Adapter->InterruptVector = (UINT)(Temp & 0x0F); DPrint2("Temp = %x ",Temp); DPrint2("Adapter->InterruptVector = %x\n",Adapter->InterruptVector); // MM Base & Size ELNK_READ_UCHAR(Adapter, ELNK16_RAM_CONFIG, &Temp); if (Temp & 0x20) { Adapter->SharedRamSize = 64; switch (Temp & 0x0F) { case 0x00: Adapter->SharedRamPhys = 0xF00000; break; case 0x01: Adapter->SharedRamPhys = 0xF20000; break; case 0x02: Adapter->SharedRamPhys = 0xF40000; break; case 0x03: Adapter->SharedRamPhys = 0xF60000; break; default: Adapter->SharedRamPhys = 0xF80000; break; } } else { switch (Temp & 0x03) { case 0x00: Adapter->SharedRamSize = 16; break; case 0x01: Adapter->SharedRamSize = 32; break; case 0x02: Adapter->SharedRamSize = 48; break; default: Adapter->SharedRamSize = 64; break; } switch (Temp & 0x18) { case 0x00: Adapter->SharedRamPhys = 0x0C0000; break; case 0x08: Adapter->SharedRamPhys = 0x0C8000; break; case 0x10: Adapter->SharedRamPhys = 0x0D0000; break; default: Adapter->SharedRamPhys = 0x0D8000; break; } } switch (Adapter->SharedRamSize) { case 16: Adapter->CardOffset = 0xC000; Adapter->NumberOfTransmitBuffers = ELNK16_16K_TRANSMITS; Adapter->NumberOfReceiveBuffers = ELNK16_16K_RECEIVES; break; case 32: Adapter->CardOffset = 0x8000; Adapter->NumberOfTransmitBuffers = ELNK16_32K_TRANSMITS; Adapter->NumberOfReceiveBuffers = ELNK16_32K_RECEIVES; break; case 48: Adapter->CardOffset = 0x4000; Adapter->NumberOfTransmitBuffers = ELNK16_48K_TRANSMITS; Adapter->NumberOfReceiveBuffers = ELNK16_48K_RECEIVES; break; case 64: Adapter->CardOffset = 0; Adapter->NumberOfTransmitBuffers = ELNK16_64K_TRANSMITS; Adapter->NumberOfReceiveBuffers = ELNK16_64K_RECEIVES; break; } DPrint2("Temp = %x ",Temp); DPrint2("Adapter->SharedRamSize = %x\n",Adapter->SharedRamSize); DPrint2("Adapter->SharedRamPhys = %x\n",Adapter->SharedRamPhys); DPrint2("Adapter->CardOffset = %x\n",Adapter->CardOffset); DPrint2("Adapter->NumberOfTransmitBuffers = %x\n",Adapter->NumberOfTransmitBuffers); DPrint2("Adapter->NumberOfReceiveBuffers = %x\n",Adapter->NumberOfReceiveBuffers); // ZWS not needed } #endif // NDIS_WIN return(TRUE); } VOID Elnk16GenerateIdPattern( IN PELNK_ADAPTER Adapter ) /*++ Routine Description: This routine will write the ID pattern to port 0x100h. Arguments: Adapter - Context of the adapter Return Value: None. --*/ { UCHAR Value; UINT i; Value = 0xff; Adapter; for (i = 0 ; i < 255 ; i++) { NdisWritePortUchar( Adapter->NdisAdapterHandle, ELNK16_ID_PORT, Value ); if (Value & 0x80) { Value = (UCHAR) (Value << 1); Value ^= 0xe7; } else { Value = (UCHAR) (Value << 1); } } return; } #endif // !ELNKMC