NTSTATUS NICStartSend( __in PFDO_DATA FdoData, __in PMP_TCB pMpTcb ) /*++ Routine Description: Issue a send command to the NIC Assumption: Send spinlock has been acquired Arguments: FdoData Pointer to our FdoData pMpTcb Pointer to MP_TCB Return Value: NTSTATUS code --*/ { NTSTATUS status; DebugPrint(TRACE, DBG_WRITE, "--> NICStartSend\n"); // // If the transmit unit is idle (very first transmit) then we must // setup the general pointer and issue a full CU-start // if (FdoData->TransmitIdle) { DebugPrint(TRACE, DBG_WRITE, "CU is idle -- First TCB added to Active List\n"); // // Wait for the SCB to clear before we set the general pointer // if (!WaitScb(FdoData)) { DebugPrint(ERROR, DBG_WRITE, "NICStartSend -- WaitScb returned error\n"); status = STATUS_DEVICE_DATA_ERROR; goto exit; } // // Don't try to start the transmitter if the command unit is not // idle ((not idle) == (Cu-Suspended or Cu-Active)). // if ((FdoData->CSRAddress->ScbStatus & SCB_CUS_MASK) != SCB_CUS_IDLE) { DebugPrint(ERROR, DBG_WRITE, "FdoData = %p, CU Not IDLE\n", FdoData); MP_SET_HARDWARE_ERROR(FdoData); KeStallExecutionProcessor(25); } FdoData->CSRAddress->ScbGeneralPointer = pMpTcb->HwTcbPhys; status = D100IssueScbCommand(FdoData, SCB_CUC_START, FALSE); FdoData->TransmitIdle = FALSE; FdoData->ResumeWait = TRUE; } else { // // If the command unit has already been started, then append this // TCB onto the end of the transmit chain, and issue a CU-Resume. // DebugPrint(LOUD, DBG_WRITE, "adding TCB to Active chain\n"); // // Clear the suspend bit on the previous packet. // pMpTcb->PrevHwTcb->TxCbHeader.CbCommand &= ~CB_S_BIT; // // Issue a CU-Resume command to the device. We only need to do a // WaitScb if the last command was NOT a RESUME. // status = D100IssueScbCommand(FdoData, SCB_CUC_RESUME, FdoData->ResumeWait); } exit: DebugPrint(TRACE, DBG_WRITE, "<-- NICStartSend\n"); return status; }
NTSTATUS NICSetMulticastList( PFDO_DATA FdoData ) /*++ Routine Description: This routine will set up the FdoData for a specified multicast address list Arguments: FdoData Pointer to our FdoData Return Value: --*/ { NTSTATUS status; PUCHAR McAddress; UINT i, j; BOOLEAN bResult; DebugPrint(TRACE, DBG_IOCTLS, "--> NICSetMulticastList\n"); // // Setup the command block for the multicast command. // for (i = 0; i < FdoData->MCAddressCount; i++) { DebugPrint(TRACE, DBG_IOCTLS, "MC(%d) = %02x-%02x-%02x-%02x-%02x-%02x\n", i, FdoData->MCList[i][0], FdoData->MCList[i][1], FdoData->MCList[i][2], FdoData->MCList[i][3], FdoData->MCList[i][4], FdoData->MCList[i][5]); McAddress = &FdoData->NonTxCmdBlock->NonTxCb.Multicast.McAddress[i*ETHERNET_ADDRESS_LENGTH]; for (j = 0; j < ETH_LENGTH_OF_ADDRESS; j++) *(McAddress++) = FdoData->MCList[i][j]; } FdoData->NonTxCmdBlock->NonTxCb.Multicast.McCount = (USHORT)(FdoData->MCAddressCount * ETH_LENGTH_OF_ADDRESS); ((PCB_HEADER_STRUC)FdoData->NonTxCmdBlock)->CbStatus = 0; ((PCB_HEADER_STRUC)FdoData->NonTxCmdBlock)->CbCommand = CB_MULTICAST; // // Wait for the SCB to clear before we check the CU status. // if (!WaitScb(FdoData)) { status = STATUS_DEVICE_DATA_ERROR; MP_EXIT; } // // If we have issued any transmits, then the CU will either be active, or // in the suspended state. If the CU is active, then we wait for it to be // suspended. // if (FdoData->TransmitIdle == FALSE) { // // Wait for suspended state // MP_STALL_AND_WAIT((FdoData->CSRAddress->ScbStatus & SCB_CUS_MASK) != SCB_CUS_ACTIVE, 5000, bResult); if (!bResult) { MP_SET_HARDWARE_ERROR(FdoData); status = STATUS_DEVICE_DATA_ERROR; } // // Restore the transmit software flags. After the multicast command is // issued, the command unit will be idle, because the EL bit will be // set in the multicast commmand block. // FdoData->TransmitIdle = TRUE; FdoData->ResumeWait = TRUE; } // // Update the command list pointer. // FdoData->CSRAddress->ScbGeneralPointer = FdoData->NonTxCmdBlockPhys; // // Submit the multicast command to the FdoData and wait for it to complete. // status = D100SubmitCommandBlockAndWait(FdoData); if (status != STATUS_SUCCESS) { status = STATUS_DEVICE_NOT_READY; } exit: DebugPrint(TRACE, DBG_IOCTLS, "<-- NICSetMulticastList, status=%x\n", status); return(status); }
NTSTATUS NICSetPacketFilter( __in PFDO_DATA FdoData, __in ULONG PacketFilter ) /*++ Routine Description: This routine will set up the FdoData so that it accepts packets that match the specified packet filter. The only filter bits that can truly be toggled are for broadcast and promiscuous Arguments: FdoData Pointer to our FdoData PacketFilter The new packet filter Return Value: --*/ { NTSTATUS status = STATUS_SUCCESS; UCHAR NewParameterField; UINT i; BOOLEAN bResult; DebugPrint(TRACE, DBG_IOCTLS, "--> NICSetPacketFilter, PacketFilter=%08x\n", PacketFilter); // // Need to enable or disable broadcast and promiscuous support depending // on the new filter // NewParameterField = CB_557_CFIG_DEFAULT_PARM15; if (PacketFilter & NDIS_PACKET_TYPE_BROADCAST) { NewParameterField &= ~CB_CFIG_BROADCAST_DIS; } else { NewParameterField |= CB_CFIG_BROADCAST_DIS; } if (PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) { NewParameterField |= CB_CFIG_PROMISCUOUS; } else { NewParameterField &= ~CB_CFIG_PROMISCUOUS; } do { if ((FdoData->OldParameterField == NewParameterField ) && !(PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST)) { break; } // // Only need to do something to the HW if the filter bits have changed. // FdoData->OldParameterField = NewParameterField; ((PCB_HEADER_STRUC)FdoData->NonTxCmdBlock)->CbCommand = CB_CONFIGURE; ((PCB_HEADER_STRUC)FdoData->NonTxCmdBlock)->CbStatus = 0; ((PCB_HEADER_STRUC)FdoData->NonTxCmdBlock)->CbLinkPointer = DRIVER_NULL; // // First fill in the static (end user can't change) config bytes // FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[0] = CB_557_CFIG_DEFAULT_PARM0; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[2] = CB_557_CFIG_DEFAULT_PARM2; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[3] = CB_557_CFIG_DEFAULT_PARM3; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[6] = CB_557_CFIG_DEFAULT_PARM6; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[9] = CB_557_CFIG_DEFAULT_PARM9; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[10] = CB_557_CFIG_DEFAULT_PARM10; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[11] = CB_557_CFIG_DEFAULT_PARM11; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[12] = CB_557_CFIG_DEFAULT_PARM12; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[13] = CB_557_CFIG_DEFAULT_PARM13; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[14] = CB_557_CFIG_DEFAULT_PARM14; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[16] = CB_557_CFIG_DEFAULT_PARM16; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[17] = CB_557_CFIG_DEFAULT_PARM17; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[18] = CB_557_CFIG_DEFAULT_PARM18; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[20] = CB_557_CFIG_DEFAULT_PARM20; // // Set the Tx underrun retries // FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[7] = (UCHAR) (CB_557_CFIG_DEFAULT_PARM7 | (FdoData->AiUnderrunRetry << 1)); // // Set the Tx and Rx Fifo limits // FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[1] = (UCHAR) ((FdoData->AiTxFifo << 4) | FdoData->AiRxFifo); // // set the MWI enable bit if needed // if (FdoData->MWIEnable) FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[3] |= CB_CFIG_B3_MWI_ENABLE; // // Set the Tx and Rx DMA maximum byte count fields. // if ((FdoData->AiRxDmaCount) || (FdoData->AiTxDmaCount)) { FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[4] = FdoData->AiRxDmaCount; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[5] = (UCHAR) (FdoData->AiTxDmaCount | CB_CFIG_DMBC_EN); } else { FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[4] = CB_557_CFIG_DEFAULT_PARM4; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[5] = CB_557_CFIG_DEFAULT_PARM5; } // // Setup for MII or 503 operation. The CRS+CDT bit should only be // set when operating in 503 mode. // if (FdoData->PhyAddress == 32) { FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[8] = (CB_557_CFIG_DEFAULT_PARM8 & (~CB_CFIG_503_MII)); FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[15] = (UCHAR) (NewParameterField | CB_CFIG_CRS_OR_CDT); } else { FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[8] = (CB_557_CFIG_DEFAULT_PARM8 | CB_CFIG_503_MII); FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[15] = (UCHAR) (NewParameterField & (~CB_CFIG_CRS_OR_CDT)); } // // Setup Full duplex stuff // // // If forced to half duplex // if (FdoData->AiForceDpx == 1) { FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[19] = (CB_557_CFIG_DEFAULT_PARM19 & (~(CB_CFIG_FORCE_FDX| CB_CFIG_FDX_ENABLE))); } // // If forced to full duplex // else if (FdoData->AiForceDpx == 2) { FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[19] = (CB_557_CFIG_DEFAULT_PARM19 | CB_CFIG_FORCE_FDX); } // // If auto-duplex // else { FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[19] = CB_557_CFIG_DEFAULT_PARM19; } // // if multicast all is being turned on, set the bit // if (PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST) { FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[21] = (CB_557_CFIG_DEFAULT_PARM21 | CB_CFIG_MULTICAST_ALL); } else { FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[21] = CB_557_CFIG_DEFAULT_PARM21; } // // Wait for the SCB to clear before we check the CU status. // if (!WaitScb(FdoData)) { status = STATUS_DEVICE_DATA_ERROR; break; } // // If we have issued any transmits, then the CU will either be active, // or in the suspended state. If the CU is active, then we wait for // it to be suspended. // if (FdoData->TransmitIdle == FALSE) { // // Wait for suspended state // MP_STALL_AND_WAIT((FdoData->CSRAddress->ScbStatus & SCB_CUS_MASK) != SCB_CUS_ACTIVE, 5000, bResult); if (!bResult) { MP_SET_HARDWARE_ERROR(FdoData); status = STATUS_DEVICE_DATA_ERROR; break; } // // Check the current status of the receive unit // if ((FdoData->CSRAddress->ScbStatus & SCB_RUS_MASK) != SCB_RUS_IDLE) { // Issue an RU abort. Since an interrupt will be issued, the // RU will be started by the DPC. status = D100IssueScbCommand(FdoData, SCB_RUC_ABORT, TRUE); if (status != STATUS_SUCCESS) { break; } } if (!WaitScb(FdoData)) { status = STATUS_DEVICE_DATA_ERROR; break; } // // Restore the transmit software flags. After the multicast // command is issued, the command unit will be idle, because the // EL bit will be set in the multicast commmand block. // FdoData->TransmitIdle = TRUE; FdoData->ResumeWait = TRUE; } // // Display config info // DebugPrint(TRACE, DBG_IOCTLS, "Re-Issuing Configure command for filter change\n"); DebugPrint(TRACE, DBG_IOCTLS, "Config Block at virt addr %p, phys address %x\n", &((PCB_HEADER_STRUC)FdoData->NonTxCmdBlock)->CbStatus, FdoData->NonTxCmdBlockPhys); for (i = 0; i < CB_CFIG_BYTE_COUNT; i++) DebugPrint(LOUD, DBG_IOCTLS, " Config byte %x = %.2x\n", i, FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[i]); // // Submit the configure command to the chip, and wait for it to complete. // FdoData->CSRAddress->ScbGeneralPointer = FdoData->NonTxCmdBlockPhys; status = D100SubmitCommandBlockAndWait(FdoData); if (status != STATUS_SUCCESS) { status = STATUS_DEVICE_NOT_READY; } } while (FALSE); DebugPrint(TRACE, DBG_IOCTLS, "<-- NICSetPacketFilter, Status=%x\n", status); return(status); }
NTSTATUS NICLinkDetection( PFDO_DATA FdoData ) /*++ Routine Description: Timer function for postponed link negotiation. Called from the NICWatchDogEvtTimerFunc. After the link detection is over we will complete any pending ioctl or send IRPs. Arguments: FdoData Pointer to our FdoData Return Value: NT status --*/ { NTSTATUS status = STATUS_SUCCESS; MEDIA_STATE CurrMediaState; PNDISPROT_QUERY_OID pQuery = NULL; PNDISPROT_SET_OID pSet = NULL; PVOID DataBuffer; ULONG BytesWritten; NDIS_OID Oid; PVOID InformationBuffer; size_t bufSize; WDFREQUEST request; // // Handle the link negotiation. // if (FdoData->bLinkDetectionWait) { status = ScanAndSetupPhy(FdoData); } else { status = PhyDetect(FdoData); } if (status == STATUS_PENDING) { return status; } // // Reset some variables for link detection // FdoData->bLinkDetectionWait = FALSE; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "NICLinkDetection - negotiation done\n"); WdfSpinLockAcquire(FdoData->Lock); MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_LINK_DETECTION); WdfSpinLockRelease(FdoData->Lock); // // Any OID query request pending? // status = NICGetIoctlRequest(FdoData->PendingIoctlQueue, IOCTL_NDISPROT_QUERY_OID_VALUE, &request); if(NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(request, sizeof(NDISPROT_QUERY_OID), &DataBuffer, &bufSize); if(NT_SUCCESS(status)) { pQuery = (PNDISPROT_QUERY_OID)DataBuffer; Oid = pQuery->Oid; InformationBuffer = &pQuery->Data[0]; switch(Oid) { case OID_GEN_LINK_SPEED: *((PULONG)InformationBuffer) = FdoData->usLinkSpeed * 10000; BytesWritten = sizeof(ULONG); break; case OID_GEN_MEDIA_CONNECT_STATUS: default: ASSERT(Oid == OID_GEN_MEDIA_CONNECT_STATUS); CurrMediaState = NICIndicateMediaState(FdoData); RtlMoveMemory(InformationBuffer, &CurrMediaState, sizeof(NDIS_MEDIA_STATE)); BytesWritten = sizeof(NDIS_MEDIA_STATE); } WdfRequestCompleteWithInformation(request, status, BytesWritten); } } // // Any OID set request pending? // status = NICGetIoctlRequest(FdoData->PendingIoctlQueue, IOCTL_NDISPROT_SET_OID_VALUE, &request); if(NT_SUCCESS(status)) { ULONG PacketFilter; status = WdfRequestRetrieveOutputBuffer(request, sizeof(NDISPROT_SET_OID), &DataBuffer, &bufSize); if(NT_SUCCESS(status)) { pSet = (PNDISPROT_SET_OID)DataBuffer; Oid = pSet->Oid; InformationBuffer = &pSet->Data[0]; if (Oid == OID_GEN_CURRENT_PACKET_FILTER) { RtlMoveMemory(&PacketFilter, InformationBuffer, sizeof(ULONG)); WdfSpinLockAcquire(FdoData->Lock); status = NICSetPacketFilter( FdoData, PacketFilter); WdfSpinLockRelease(FdoData->Lock); if (status == STATUS_SUCCESS) { FdoData->PacketFilter = PacketFilter; } WdfRequestCompleteWithInformation(request, status, 0); } } } // // Any read pending? // WdfSpinLockAcquire(FdoData->RcvLock); // // Start the NIC receive unit // status = NICStartRecv(FdoData); if (status != STATUS_SUCCESS) { MP_SET_HARDWARE_ERROR(FdoData); } WdfSpinLockRelease(FdoData->RcvLock); // // Send packets which have been queued while link detection was going on. // NICCheckForQueuedSends(FdoData); return status; }