Sample Code
Windows Driver Samples/ PCIDRV - WDF Driver for PCI Device/ C++/ kmdf/ HW/ nic_init.c/
/*++ Copyright (c) Microsoft Corporation. All rights reserved. THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. Module Name: NIC_INIT.c Abstract: Contains rotuines to do resource allocation and hardware initialization & shutdown. Environment: Kernel mode --*/ #include "precomp.h" #if defined(EVENT_TRACING) #include "nic_init.tmh" #endif #ifdef ALLOC_PRAGMA #pragma alloc_text (PAGE, NICAllocateSoftwareResources) #pragma alloc_text (PAGE, NICFreeSoftwareResources) #pragma alloc_text (PAGE, NICMapHWResources) #pragma alloc_text (PAGE, NICUnmapHWResources) #pragma alloc_text (PAGE, NICGetDeviceInformation) #pragma alloc_text (PAGE, NICReadAdapterInfo) #pragma alloc_text (PAGE, NICAllocAdapterMemory) #pragma alloc_text (PAGE, NICFreeAdapterMemory) #pragma alloc_text (PAGE, NICInitRecvBuffers) #pragma alloc_text (PAGE, NICSelfTest) #pragma alloc_text (PAGE, HwClearAllCounters) #pragma alloc_text (PAGE, NICAllocRfd) #pragma alloc_text (PAGE, NICFreeRfd) #pragma alloc_text (PAGE, NICFreeRfdWorkItem) #endif NTSTATUS NICAllocateSoftwareResources( IN OUT PFDO_DATA FdoData ) /*++ Routine Description: This routine creates two parallel queues and 3 manual queues to hold Read, Write and IOCTL requests. It also creates the interrupt object and DMA object, and performs some additional initialization. Arguments: FdoData Pointer to our FdoData Return Value: None --*/ { NTSTATUS status; WDF_IO_QUEUE_CONFIG ioQueueConfig; WDF_DMA_ENABLER_CONFIG dmaConfig; ULONG maximumLength, maxLengthSupported; WDF_OBJECT_ATTRIBUTES attributes; ULONG maxMapRegistersRequired, miniMapRegisters; ULONG mapRegistersAllocated; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "-->NICAllocateSoftwareResources\n"); PAGED_CODE(); // // Initialize all the static data first to make sure we don't touch // uninitialized list in the ContextCleanup callback if the // AddDevice fails for any reason. // InitializeListHead(&FdoData->PoMgmt.PatternList); InitializeListHead(&FdoData->RecvList); // // This a global lock, to synchonize access to device context. // WDF_OBJECT_ATTRIBUTES_INIT(&attributes); attributes.ParentObject = FdoData->WdfDevice; status = WdfSpinLockCreate(&attributes,&FdoData->Lock); if(!NT_SUCCESS(status)){ return status; } // // Get the BUS_INTERFACE_STANDARD for our device so that we can // read & write to PCI config space. // status = WdfFdoQueryForInterface(FdoData->WdfDevice, &GUID_BUS_INTERFACE_STANDARD, (PINTERFACE) &FdoData->BusInterface, sizeof(BUS_INTERFACE_STANDARD), 1, // Version NULL); //InterfaceSpecificData if (!NT_SUCCESS (status)){ return status; } // // First make sure this is our device before doing whole lot // of other things. // status = NICGetDeviceInformation(FdoData); if (!NT_SUCCESS (status)){ return status; } NICGetDeviceInfSettings(FdoData); // // We will create and configure a queue for receiving // write requests. If these requests have to be pended for any // reason, they will be forwarded to a manual queue created after this one. // Framework automatically takes the responsibility of handling // cancellation when the requests are waiting in the queue. This is // a managed queue. So the framework will take care of queueing // incoming requests when the pnp/power state transition takes place. // Since we have configured the queue to dispatch all the specific requests // we care about, we don't need a default queue. A default queue is // used to receive requests that are not preconfigured to go to // a specific queue. // WDF_IO_QUEUE_CONFIG_INIT( &ioQueueConfig, WdfIoQueueDispatchParallel ); ioQueueConfig.EvtIoWrite = PciDrvEvtIoWrite; status = WdfIoQueueCreate( FdoData->WdfDevice, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &FdoData->WriteQueue // queue handle ); if (!NT_SUCCESS (status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfIoQueueCreate failed 0x%x\n", status); return status; } status = WdfDeviceConfigureRequestDispatching( FdoData->WdfDevice, FdoData->WriteQueue, WdfRequestTypeWrite); if(!NT_SUCCESS (status)){ ASSERT(NT_SUCCESS(status)); TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Error in config'ing write Queue 0x%x\n", status); return status; } // // Manual internal queue for write reqeusts. This will be used to queue // the write requests presented to us from the parallel default queue // when we are low in TCB resources or when the device // is busy doing link detection. // Requests can be canceled while waiting in the queue without any // notification to the driver. // WDF_IO_QUEUE_CONFIG_INIT( &ioQueueConfig, WdfIoQueueDispatchManual ); status = WdfIoQueueCreate ( FdoData->WdfDevice, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &FdoData->PendingWriteQueue ); if(!NT_SUCCESS (status)){ TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Error Creating manual write Queue 0x%x\n", status); return status; } // // Manual queue for read requests (WdfRequestTypeRead). We will configure the queue // so that incoming read requests are directly dispatched to this queue. We will // manually remove the requests from the queue and service them in our recv // interrupt handler. WDF_IO_QUEUE_CONFIG_INIT initializes queues to be // auto managed by default. // WDF_IO_QUEUE_CONFIG_INIT( &ioQueueConfig, WdfIoQueueDispatchManual ); status = WdfIoQueueCreate ( FdoData->WdfDevice, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &FdoData->PendingReadQueue ); if(!NT_SUCCESS (status)){ TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Error Creating read Queue 0x%x\n", status); return status; } status = WdfDeviceConfigureRequestDispatching( FdoData->WdfDevice, FdoData->PendingReadQueue, WdfRequestTypeRead); if(!NT_SUCCESS (status)){ ASSERT(NT_SUCCESS(status)); TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Error in config'ing read Queue 0x%x\n", status); return status; } // // Parallel queue for device I/O control (WdfRequestTypeDeviceControl) requests. // We will configure the queue so that all the incoming ioctl requests // go directly to this queue. We will try to service the requests immediately. // If we can't, we will forward the request to a manual queue created below // and try to service it from a DPC when the appropriate event happens. // WDF_IO_QUEUE_CONFIG_INIT( &ioQueueConfig, WdfIoQueueDispatchParallel ); ioQueueConfig.EvtIoDeviceControl = PciDrvEvtIoDeviceControl; status = WdfIoQueueCreate ( FdoData->WdfDevice, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &FdoData->IoctlQueue ); if(!NT_SUCCESS (status)){ TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Error Creating ioctl Queue 0x%x\n", status); return status; } status = WdfDeviceConfigureRequestDispatching( FdoData->WdfDevice, FdoData->IoctlQueue, WdfRequestTypeDeviceControl); if(!NT_SUCCESS (status)){ ASSERT(NT_SUCCESS(status)); TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Error in config'ing ioctl Queue 0x%x\n", status); return status; } // // Manual internal queue for device I/O control requests. This will be used to // queue the ioctl requests presented to us from the parallel ioctl queue // when we cannot handle them immediately. This is a managed queue. // Requests be get canceled while waiting in the queue without any // notification to the driver. // WDF_IO_QUEUE_CONFIG_INIT( &ioQueueConfig, WdfIoQueueDispatchManual ); status = WdfIoQueueCreate ( FdoData->WdfDevice, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &FdoData->PendingIoctlQueue ); if(!NT_SUCCESS (status)){ TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Error Creating manual Ioctl Queue 0x%x\n", status); return status; } #ifndef PCIDRV_CREATE_INTERRUPT_IN_PREPARE_HARDWARE { WDF_INTERRUPT_CONFIG interruptConfig; // // Create WDFINTERRUPT object. // WDF_INTERRUPT_CONFIG_INIT(&interruptConfig, NICEvtInterruptIsr, NICEvtInterruptDpc); // // These first two callbacks will be called at DIRQL. Their job is to // enable and disable interrupts. // interruptConfig.EvtInterruptEnable = NICEvtInterruptEnable; interruptConfig.EvtInterruptDisable = NICEvtInterruptDisable; status = WdfInterruptCreate(FdoData->WdfDevice, &interruptConfig, WDF_NO_OBJECT_ATTRIBUTES, &FdoData->WdfInterrupt); if (!NT_SUCCESS (status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfInterruptCreate failed: %!STATUS!\n", status); return status; } } #endif // // Alignment requirement must be 16-byte for this device. This alignment // value will be inherits by the DMA enabler and used when you allocate // common buffers. // WdfDeviceSetAlignmentRequirement( FdoData->WdfDevice, FILE_OCTA_ALIGNMENT); // // Bare minimum number of map registers required to do // a single NIC_MAX_PACKET_SIZE transfer. // miniMapRegisters = BYTES_TO_PAGES(NIC_MAX_PACKET_SIZE) + 1; // // Maximum map registers required to do simultaneous transfer // of all TCBs assuming each packet spanning NIC_MAX_PHYS_BUF_COUNT // Buffer can span multiple MDLs. // maxMapRegistersRequired = FdoData->NumTcb * NIC_MAX_PHYS_BUF_COUNT; // // The maximum length of buffer for maxMapRegistersRequired number of // map registers would be. // maximumLength = (maxMapRegistersRequired-1) << PAGE_SHIFT; // // Create a new DMA Object for Scatter/Gather DMA mode. // WDF_DMA_ENABLER_CONFIG_INIT( &dmaConfig, WdfDmaProfileScatterGather, maximumLength ); status = WdfDmaEnablerCreate( FdoData->WdfDevice, &dmaConfig, WDF_NO_OBJECT_ATTRIBUTES, &FdoData->WdfDmaEnabler ); if (!NT_SUCCESS (status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfDmaEnblerCreate failed: %08X\n", status); return status; } maxLengthSupported = (ULONG) WdfDmaEnablerGetFragmentLength(FdoData->WdfDmaEnabler, WdfDmaDirectionReadFromDevice); mapRegistersAllocated = BYTES_TO_PAGES(maxLengthSupported) + 1; if(mapRegistersAllocated < miniMapRegisters) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Not enough map registers: Allocated %d, Required %d\n", mapRegistersAllocated, miniMapRegisters); status = STATUS_INSUFFICIENT_RESOURCES; return status; } // // Adjust our TCB count based on the MapRegisters we got. We will // take the best case scenario where the packet is going to span // no more than 2 pages. // FdoData->NumTcb = mapRegistersAllocated/miniMapRegisters; // // Make sure it doesn't exceed NIC_MAX_TCBS. // FdoData->NumTcb = min(FdoData->NumTcb, NIC_MAX_TCBS); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "MapRegisters Allocated %d\n", mapRegistersAllocated); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Adjusted TCB count is %d\n", FdoData->NumTcb); // // Set the maximum allowable DMA Scatter/Gather list fragmentation size. // WdfDmaEnablerSetMaximumScatterGatherElements( FdoData->WdfDmaEnabler, NIC_MAX_PHYS_BUF_COUNT ); // // Create a lock to protect all the write-related buffer lists. // WDF_OBJECT_ATTRIBUTES_INIT(&attributes); attributes.ParentObject = FdoData->WdfDevice; status = WdfSpinLockCreate(&attributes,&FdoData->SendLock); if(!NT_SUCCESS(status)){ return status; } // // Create a lock to protect all the read-related buffer lists // WDF_OBJECT_ATTRIBUTES_INIT(&attributes); attributes.ParentObject = FdoData->WdfDevice; status = WdfSpinLockCreate(&attributes,&FdoData->RcvLock); if(!NT_SUCCESS(status)){ return status; } status = NICAllocAdapterMemory(FdoData); if (NT_SUCCESS(status)) { // // This sets up send buffers. It doesn't actually touch hardware. // NICInitSendBuffers(FdoData); // // This sets up receive buffers. It doesn't actually touch hardware. // status = NICInitRecvBuffers(FdoData); } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- NICAllocateSoftwareResources\n"); return status; } NTSTATUS NICFreeSoftwareResources( IN OUT PFDO_DATA FdoData ) /*++ Routine Description: Free all the software resources. We shouldn't touch the hardware. This functions is called in the context of EvtDeviceContextCleanup. Most of the resources created in NICAllocateResources such as queues, DMA enabler, SpinLocks, CommonBuffer, are already freed by framework because they are associated with the WDFDEVICE directly or indirectly as child objects. Arguments: FdoData Pointer to our FdoData Return Value: None --*/ { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "-->NICFreeSoftwareResources\n"); PAGED_CODE(); NICFreeAdapterMemory(FdoData); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<--NICFreeSoftwareResources\n"); return STATUS_SUCCESS; } NTSTATUS NICMapHWResources( IN OUT PFDO_DATA FdoData, IN WDFCMRESLIST ResourcesRaw, IN WDFCMRESLIST ResourcesTranslated ) /*++ Routine Description: Gets the HW resources assigned by the bus driver and: 1) Maps them to system address space. 2) If PCIDRV_CREATE_INTERRUPT_IN_PREPARE_HARDWARE is defined, it creates a WDFINTERRUPT object. Called during EvtDevicePrepareHardware callback. Three base address registers are supported by the 8255x: 1) CSR Memory Mapped Base Address Register (BAR 0 at offset 10) 2) CSR I/O Mapped Base Address Register (BAR 1 at offset 14) 3) Flash Memory Mapped Base Address Register (BAR 2 at offset 18) The 8255x requires one BAR for I/O mapping and one BAR for memory mapping of these registers anywhere within the 32-bit memory address space. The driver determines which BAR (I/O or Memory) is used to access the Control/Status Registers. Just for illustration, this driver maps both memory and I/O registers and shows how to use READ_PORT_xxx or READ_REGISTER_xxx functions to perform I/O in a platform independent basis. On some platforms, the I/O registers can get mapped into memory space and your driver should be able to handle this transparently. One BAR is also required to map the accesses to an optional Flash memory. The 82557 implements this register regardless of the presence or absence of a Flash chip on the adapter. The 82558 and 82559 implement this register only if a bit is set in the EEPROM. The size of the space requested by this register is 1Mbyte, and it is always mapped anywhere in the 32-bit memory address space. Note: Although the 82558 only supports up to 64 Kbytes of Flash memory and the 82559 only supports 128 Kbytes of Flash memory, the driver requests 1 Mbyte of address space. Software should not access Flash addresses above 64 Kbytes for the 82558 or 128 Kbytes for the 82559 because Flash accesses above the limits are aliased to lower addresses. Arguments: FdoData Pointer to our FdoData ResourcesRaw - Pointer to list of raw resources passed to EvtDevicePrepareHardware callback ResourcesTranslated - Pointer to list of translated resources passed to EvtDevicePrepareHardware callback Return Value: NTSTATUS --*/ { PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor; ULONG i; NTSTATUS status = STATUS_SUCCESS; BOOLEAN bResPort = FALSE; BOOLEAN bResInterrupt = FALSE; BOOLEAN bResMemory = FALSE; ULONG numberOfBARs = 0; UNREFERENCED_PARAMETER(ResourcesRaw); PAGED_CODE(); for (i=0; i<WdfCmResourceListGetCount(ResourcesTranslated); i++) { descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, i); if(!descriptor){ TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfResourceCmGetDescriptor"); return STATUS_DEVICE_CONFIGURATION_ERROR; } switch (descriptor->Type) { case CmResourceTypePort: // // We will increment the BAR count only for valid resources. We will // not count the private device types added by the PCI bus driver. // numberOfBARs++; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "I/O mapped CSR: (%x) Length: (%d)\n", descriptor->u.Port.Start.LowPart, descriptor->u.Port.Length); // // The resources are listed in the same order the as // BARs in the config space, so this should be the second one. // if(numberOfBARs != 2) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "I/O mapped CSR is not in the right order\n"); status = STATUS_DEVICE_CONFIGURATION_ERROR; return status; } // // The port is in I/O space on this machine. // We should use READ_PORT_Xxx, and WRITE_PORT_Xxx routines // to read or write to the port. // FdoData->IoBaseAddress = ULongToPtr(descriptor->u.Port.Start.LowPart); FdoData->IoRange = descriptor->u.Port.Length; // // Since all our accesses are USHORT wide, we will create an accessor // table just for these two functions. // FdoData->ReadPort = NICReadPortUShort; FdoData->WritePort = NICWritePortUShort; bResPort = TRUE; FdoData->MappedPorts = FALSE; break; case CmResourceTypeMemory: numberOfBARs++; if(numberOfBARs == 1) { TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Memory mapped CSR:(%x:%x) Length:(%d)\n", descriptor->u.Memory.Start.LowPart, descriptor->u.Memory.Start.HighPart, descriptor->u.Memory.Length); // // Our CSR memory space should be 0x1000 in size. // ASSERT(descriptor->u.Memory.Length == 0x1000); FdoData->MemPhysAddress = descriptor->u.Memory.Start; FdoData->CSRAddress = MmMapIoSpace( descriptor->u.Memory.Start, NIC_MAP_IOSPACE_LENGTH, MmNonCached); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "CSRAddress=%p\n", FdoData->CSRAddress); bResMemory = TRUE; } else if(numberOfBARs == 2){ TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "I/O mapped CSR in Memory Space: (%x) Length: (%d)\n", descriptor->u.Memory.Start.LowPart, descriptor->u.Memory.Length); // // The port is in memory space on this machine. // We should call MmMapIoSpace to map the physical to virtual // address, and also use the READ/WRITE_REGISTER_xxx function // to read or write to the port. // FdoData->IoBaseAddress = MmMapIoSpace( descriptor->u.Memory.Start, descriptor->u.Memory.Length, MmNonCached); FdoData->ReadPort = NICReadRegisterUShort; FdoData->WritePort = NICWriteRegisterUShort; FdoData->MappedPorts = TRUE; bResPort = TRUE; } else if(numberOfBARs == 3){ TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Flash memory:(%x:%x) Length:(%d)\n", descriptor->u.Memory.Start.LowPart, descriptor->u.Memory.Start.HighPart, descriptor->u.Memory.Length); // // Our flash memory should be 1MB in size. Since we don't // access the memory, let us not bother mapping it. // //ASSERT(descriptor->u.Memory.Length == 0x100000); } else { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Memory Resources are not in the right order\n"); status = STATUS_DEVICE_CONFIGURATION_ERROR; return status; } break; case CmResourceTypeInterrupt: ASSERT(!bResInterrupt); #ifdef PCIDRV_CREATE_INTERRUPT_IN_PREPARE_HARDWARE { WDF_INTERRUPT_CONFIG interruptConfig; // // Create WDFINTERRUPT object. // WDF_INTERRUPT_CONFIG_INIT(&interruptConfig, NICEvtInterruptIsr, NICEvtInterruptDpc); // // These first two callbacks will be called at DIRQL. Their job is to // enable and disable interrupts. // interruptConfig.EvtInterruptEnable = NICEvtInterruptEnable; interruptConfig.EvtInterruptDisable = NICEvtInterruptDisable; interruptConfig.InterruptTranslated = descriptor; interruptConfig.InterruptRaw = WdfCmResourceListGetDescriptor(ResourcesRaw, i); status = WdfInterruptCreate(FdoData->WdfDevice, &interruptConfig, WDF_NO_OBJECT_ATTRIBUTES, &FdoData->WdfInterrupt); if (!NT_SUCCESS (status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfInterruptCreate failed: %!STATUS!\n", status); return status; } } #endif bResInterrupt = TRUE; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Interrupt level: 0x%0x, Vector: 0x%0x\n", descriptor->u.Interrupt.Level, descriptor->u.Interrupt.Vector); break; default: // // This could be device-private type added by the PCI bus driver. We // shouldn't filter this or change the information contained in it. // TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Unhandled resource type (0x%x)\n", descriptor->Type); break; } } // // Make sure we got all the 3 resources to work with. // if (!(bResPort && bResInterrupt && bResMemory)) { status = STATUS_DEVICE_CONFIGURATION_ERROR; return status; } // // Read additional info from NIC such as MAC address // status = NICReadAdapterInfo(FdoData); if (status != STATUS_SUCCESS) { return status; } // // Test our adapter hardware // status = NICSelfTest(FdoData); if (status != STATUS_SUCCESS) { return status; } return status; } NTSTATUS NICUnmapHWResources( IN OUT PFDO_DATA FdoData ) /*++ Routine Description: Disconnect the interrupt and unmap all the memory and I/O resources. Arguments: FdoData Pointer to our FdoData Return Value: None --*/ { PAGED_CODE(); // // Free hardware resources // if (FdoData->CSRAddress) { MmUnmapIoSpace(FdoData->CSRAddress, NIC_MAP_IOSPACE_LENGTH); FdoData->CSRAddress = NULL; } if(FdoData->MappedPorts){ MmUnmapIoSpace(FdoData->IoBaseAddress, FdoData->IoRange); FdoData->IoBaseAddress = NULL; } return STATUS_SUCCESS; } NTSTATUS NICGetDeviceInformation( IN PFDO_DATA FdoData ) /*++ Routine Description: This function reads the PCI config space and make sure that it's our device and stores the device IDs and power information in the device extension. Should be done in the StartDevice. Arguments: FdoData Pointer to our FdoData Return Value: None --*/ { NTSTATUS status = STATUS_SUCCESS; DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) UCHAR buffer[NIC_PCI_E100_HDR_LENGTH ]; PPCI_COMMON_CONFIG pPciConfig = (PPCI_COMMON_CONFIG) buffer; USHORT usPciCommand; ULONG bytesRead =0; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "---> NICGetDeviceInformation\n"); PAGED_CODE(); RtlZeroMemory(buffer, sizeof(buffer)); bytesRead = FdoData->BusInterface.GetBusData( FdoData->BusInterface.Context, PCI_WHICHSPACE_CONFIG, //READ buffer, FIELD_OFFSET(PCI_COMMON_CONFIG, VendorID), NIC_PCI_E100_HDR_LENGTH); if (bytesRead != NIC_PCI_E100_HDR_LENGTH) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "GetBusData (NIC_PCI_E100_HDR_LENGTH) failed =%d\n", bytesRead); return STATUS_INVALID_DEVICE_REQUEST; } // // Is this our device? // if (pPciConfig->VendorID != NIC_PCI_VENDOR_ID || pPciConfig->DeviceID != NIC_PCI_DEVICE_ID) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "VendorID/DeviceID don't match - %x/%x\n", pPciConfig->VendorID, pPciConfig->DeviceID); //return STATUS_DEVICE_DOES_NOT_EXIST; } // // save TRACE_LEVEL_INFORMATION from config space // FdoData->RevsionID = pPciConfig->RevisionID; FdoData->SubVendorID = pPciConfig->u.type0.SubVendorID; FdoData->SubSystemID = pPciConfig->u.type0.SubSystemID; NICExtractPMInfoFromPciSpace (FdoData, (PUCHAR)pPciConfig); usPciCommand = pPciConfig->Command; if ((usPciCommand & PCI_ENABLE_WRITE_AND_INVALIDATE) && (FdoData->MWIEnable)){ FdoData->MWIEnable = TRUE; } else { FdoData->MWIEnable = FALSE; } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICGetDeviceInformation\n"); return status; } NTSTATUS NICReadAdapterInfo( IN PFDO_DATA FdoData ) /*++ Routine Description: Read the mac addresss from the adapter Arguments: FdoData Pointer to our device context Return Value: NTSTATUS code --*/ { NTSTATUS status = STATUS_SUCCESS; USHORT usValue; int i; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICReadAdapterInfo\n"); PAGED_CODE(); FdoData->EepromAddressSize = GetEEpromAddressSize( GetEEpromSize(FdoData, (PUCHAR)FdoData->IoBaseAddress)); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "EepromAddressSize = %d\n", FdoData->EepromAddressSize); // // Read node address from the EEPROM // for (i=0; i< ETH_LENGTH_OF_ADDRESS; i += 2) { usValue = ReadEEprom(FdoData, (PUCHAR)FdoData->IoBaseAddress, (USHORT)(EEPROM_NODE_ADDRESS_BYTE_0 + (i/2)), FdoData->EepromAddressSize); *((PUSHORT)(&FdoData->PermanentAddress[i])) = usValue; } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Permanent Address = %02x-%02x-%02x-%02x-%02x-%02x\n", FdoData->PermanentAddress[0], FdoData->PermanentAddress[1], FdoData->PermanentAddress[2], FdoData->PermanentAddress[3], FdoData->PermanentAddress[4], FdoData->PermanentAddress[5]); if (ETH_IS_MULTICAST(FdoData->PermanentAddress) || ETH_IS_BROADCAST(FdoData->PermanentAddress)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Permanent address is invalid\n"); status = STATUS_INVALID_ADDRESS; } else { if (!FdoData->bOverrideAddress) { ETH_COPY_NETWORK_ADDRESS(FdoData->CurrentAddress, FdoData->PermanentAddress); } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Current Address = %02x-%02x-%02x-%02x-%02x-%02x\n", FdoData->CurrentAddress[0], FdoData->CurrentAddress[1], FdoData->CurrentAddress[2], FdoData->CurrentAddress[3], FdoData->CurrentAddress[4], FdoData->CurrentAddress[5]); } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICReadAdapterInfo, status=%x\n", status); return status; } NTSTATUS NICAllocAdapterMemory( IN PFDO_DATA FdoData ) /*++ Routine Description: Allocate all the memory blocks for send, receive and others Arguments: FdoData Pointer to our adapter Return Value: --*/ { NTSTATUS status = STATUS_SUCCESS; PUCHAR pMem; ULONG MemPhys; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICAllocAdapterMemory\n"); PAGED_CODE(); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "NumTcb=%d\n", FdoData->NumTcb); do { // // Send + Misc // // // Allocate MP_TCB's // status = RtlULongMult(FdoData->NumTcb, sizeof(MP_TCB), &FdoData->MpTcbMemSize); if(!NT_SUCCESS(status)){ TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "RtlUlongMult failed 0x%x\n", status); break; } pMem = ExAllocatePoolWithTag(NonPagedPool, FdoData->MpTcbMemSize, PCIDRV_POOL_TAG); if (NULL == pMem ) { status = STATUS_INSUFFICIENT_RESOURCES; TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Failed to allocate MP_TCB's\n"); break; } RtlZeroMemory(pMem, FdoData->MpTcbMemSize); FdoData->MpTcbMem = pMem; // HW_START // // Allocate shared memory for send // FdoData->HwSendMemAllocSize = FdoData->NumTcb * (sizeof(TXCB_STRUC) + NIC_MAX_PHYS_BUF_COUNT * sizeof(TBD_STRUC)); _Analysis_assume_(FdoData->HwSendMemAllocSize > 0); status = WdfCommonBufferCreate( FdoData->WdfDmaEnabler, FdoData->HwSendMemAllocSize, WDF_NO_OBJECT_ATTRIBUTES, &FdoData->WdfSendCommonBuffer ); if (status != STATUS_SUCCESS) { FdoData->HwSendMemAllocSize = 0; TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfCommonBufferCreate(Send) " "failed %08X\n", status ); break; } FdoData->HwSendMemAllocVa = WdfCommonBufferGetAlignedVirtualAddress( FdoData->WdfSendCommonBuffer); FdoData->HwSendMemAllocLa = WdfCommonBufferGetAlignedLogicalAddress( FdoData->WdfSendCommonBuffer); RtlZeroMemory(FdoData->HwSendMemAllocVa, FdoData->HwSendMemAllocSize); // // Allocate shared memory for other uses // // FIXME-NOTE: The WdfCommonBufferGetAlignedVirtualAddress functions // return device-specified aligned pointers...use them. // FdoData->HwMiscMemAllocSize = sizeof(SELF_TEST_STRUC) + ALIGN_16 + sizeof(DUMP_AREA_STRUC) + ALIGN_16 + sizeof(NON_TRANSMIT_CB) + ALIGN_16 + sizeof(ERR_COUNT_STRUC) + ALIGN_16; // // Allocate the shared memory for the command block data structures. // status = WdfCommonBufferCreate( FdoData->WdfDmaEnabler, FdoData->HwMiscMemAllocSize, WDF_NO_OBJECT_ATTRIBUTES, &FdoData->WdfMiscCommonBuffer ); if (status != STATUS_SUCCESS) { FdoData->HwMiscMemAllocSize = 0; TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfCommonBufferCreate(Misc) " "failed %08X\n", status ); break; } FdoData->HwMiscMemAllocVa = WdfCommonBufferGetAlignedVirtualAddress( FdoData->WdfMiscCommonBuffer); FdoData->HwMiscMemAllocLa = WdfCommonBufferGetAlignedLogicalAddress( FdoData->WdfMiscCommonBuffer); RtlZeroMemory(FdoData->HwMiscMemAllocVa, FdoData->HwMiscMemAllocSize); pMem = FdoData->HwMiscMemAllocVa; MemPhys = FdoData->HwMiscMemAllocLa.LowPart ; FdoData->SelfTest = (PSELF_TEST_STRUC)MP_ALIGNMEM(pMem, ALIGN_16); FdoData->SelfTestPhys = MP_ALIGNMEM_PHYS(MemPhys, ALIGN_16); pMem = (PUCHAR)FdoData->SelfTest + sizeof(SELF_TEST_STRUC); MemPhys = FdoData->SelfTestPhys + sizeof(SELF_TEST_STRUC); FdoData->NonTxCmdBlock = (PNON_TRANSMIT_CB)MP_ALIGNMEM(pMem, ALIGN_16); FdoData->NonTxCmdBlockPhys = MP_ALIGNMEM_PHYS(MemPhys, ALIGN_16); pMem = (PUCHAR)FdoData->NonTxCmdBlock + sizeof(NON_TRANSMIT_CB); MemPhys = FdoData->NonTxCmdBlockPhys + sizeof(NON_TRANSMIT_CB); FdoData->DumpSpace = (PDUMP_AREA_STRUC)MP_ALIGNMEM(pMem, ALIGN_16); FdoData->DumpSpacePhys = MP_ALIGNMEM_PHYS(MemPhys, ALIGN_16); pMem = (PUCHAR)FdoData->DumpSpace + sizeof(DUMP_AREA_STRUC); MemPhys = FdoData->DumpSpacePhys + sizeof(DUMP_AREA_STRUC); FdoData->StatsCounters = (PERR_COUNT_STRUC)MP_ALIGNMEM(pMem, ALIGN_16); FdoData->StatsCounterPhys = MP_ALIGNMEM_PHYS(MemPhys, ALIGN_16); // HW_END // // Recv // // set the max number of RFDs // disable the RFD grow/shrink scheme if user specifies a NumRfd value // larger than NIC_MAX_GROW_RFDS FdoData->MaxNumRfd = max(FdoData->NumRfd, NIC_MAX_GROW_RFDS); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "NumRfd = %d\n", FdoData->NumRfd); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "MaxNumRfd = %d\n", FdoData->MaxNumRfd); // // The driver should allocate more data than sizeof(RFD_STRUC) to allow the // driver to align the data(after ethernet header) at 8 byte boundary // FdoData->HwRfdSize = sizeof(RFD_STRUC) + MORE_DATA_FOR_ALIGN; status = STATUS_SUCCESS; } WHILE( FALSE ); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICAllocAdapterMemory, status=%x\n", status); return status; } VOID NICFreeAdapterMemory( IN PFDO_DATA FdoData ) /*++ Routine Description: Free all the resources and MP_ADAPTER data block Arguments: FdoData Pointer to our adapter Return Value: None --*/ { PMP_RFD pMpRfd; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICFreeAdapterMemory\n"); PAGED_CODE(); // No active and waiting sends ASSERT(FdoData->nBusySend == 0); ASSERT(FdoData->nWaitSend == 0); ASSERT(FdoData->nReadyRecv == FdoData->CurrNumRfd); while (!IsListEmpty(&FdoData->RecvList)) { pMpRfd = (PMP_RFD)RemoveHeadList(&FdoData->RecvList); pMpRfd->DeleteCommonBuffer = FALSE; NICFreeRfd(FdoData, pMpRfd); } FdoData->WdfSendCommonBuffer = NULL; FdoData->HwSendMemAllocVa = NULL; FdoData->WdfMiscCommonBuffer = NULL; FdoData->HwMiscMemAllocVa = NULL; FdoData->SelfTest = NULL; FdoData->StatsCounters = NULL; FdoData->NonTxCmdBlock = NULL; FdoData->DumpSpace = NULL; // Free the memory for MP_TCB structures if (FdoData->MpTcbMem) { ExFreePoolWithTag(FdoData->MpTcbMem, PCIDRV_POOL_TAG); FdoData->MpTcbMem = NULL; } //Free all the wake up patterns on this adapter NICRemoveAllWakeUpPatterns(FdoData); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICFreeAdapterMemory\n"); } VOID NICInitSendBuffers( IN PFDO_DATA FdoData ) /*++ Routine Description: Initialize send data structures. Can be called at DISPATCH_LEVEL. Arguments: FdoData - Pointer to our adapter context Return Value: None --*/ { PMP_TCB pMpTcb; PHW_TCB pHwTcb; ULONG HwTcbPhys; ULONG TcbCount; PTBD_STRUC pHwTbd; ULONG HwTbdPhys; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICInitSendBuffers\n"); FdoData->TransmitIdle = TRUE; FdoData->ResumeWait = TRUE; // Setup the initial pointers to the SW and HW TCB data space pMpTcb = (PMP_TCB) FdoData->MpTcbMem; pHwTcb = (PHW_TCB) FdoData->HwSendMemAllocVa; HwTcbPhys = FdoData->HwSendMemAllocLa.LowPart; // Setup the initial pointers to the TBD data space. // TBDs are located immediately following the TCBs pHwTbd = (PTBD_STRUC) (FdoData->HwSendMemAllocVa + (sizeof(TXCB_STRUC) * FdoData->NumTcb)); HwTbdPhys = HwTcbPhys + (sizeof(TXCB_STRUC) * FdoData->NumTcb); _Analysis_assume_(FdoData->HwSendMemAllocSize >= (FdoData->NumTcb * sizeof(HW_TCB))); _Analysis_assume_(FdoData->MpTcbMemSize >= (FdoData->NumTcb * sizeof(MP_TCB))); // Go through and set up each TCB for (TcbCount = 0; TcbCount < FdoData->NumTcb; TcbCount++) { pMpTcb->HwTcb = pHwTcb; // save ptr to HW TCB pMpTcb->HwTcbPhys = HwTcbPhys; // save HW TCB physical address pMpTcb->HwTbd = pHwTbd; // save ptr to TBD array pMpTcb->HwTbdPhys = HwTbdPhys; // save TBD array physical address if (TcbCount){ pMpTcb->PrevHwTcb = pHwTcb - 1; } else { pMpTcb->PrevHwTcb = (PHW_TCB)((PUCHAR)FdoData->HwSendMemAllocVa + ((FdoData->NumTcb - 1) * sizeof(HW_TCB))); } pHwTcb->TxCbHeader.CbStatus = 0; // clear the status pHwTcb->TxCbHeader.CbCommand = CB_EL_BIT | CB_TX_SF_BIT | CB_TRANSMIT; // Set the link pointer in HW TCB to the next TCB in the chain. // If this is the last TCB in the chain, then set it to the first TCB. if (TcbCount < FdoData->NumTcb - 1) { pMpTcb->Next = pMpTcb + 1; pHwTcb->TxCbHeader.CbLinkPointer = HwTcbPhys + sizeof(HW_TCB); } else { pMpTcb->Next = (PMP_TCB) FdoData->MpTcbMem; pHwTcb->TxCbHeader.CbLinkPointer = FdoData->HwSendMemAllocLa.LowPart; } pHwTcb->TxCbThreshold = (UCHAR) FdoData->AiThreshold; pHwTcb->TxCbTbdPointer = HwTbdPhys; pMpTcb++; pHwTcb++; HwTcbPhys += sizeof(TXCB_STRUC); pHwTbd = (PTBD_STRUC)((PUCHAR)pHwTbd + sizeof(TBD_STRUC) * NIC_MAX_PHYS_BUF_COUNT); HwTbdPhys += sizeof(TBD_STRUC) * NIC_MAX_PHYS_BUF_COUNT; } // set the TCB head/tail indexes // head is the olded one to free, tail is the next one to use FdoData->CurrSendHead = (PMP_TCB) FdoData->MpTcbMem; FdoData->CurrSendTail = (PMP_TCB) FdoData->MpTcbMem; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICInitSendBuffers\n"); } NTSTATUS NICInitRecvBuffers( IN PFDO_DATA FdoData ) /*++ Routine Description: Initialize receive data structures Arguments: FdoData - Pointer to our adapter context Return Value: --*/ { NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES; PMP_RFD pMpRfd; ULONG RfdCount; WDFMEMORY memoryHdl; PDRIVER_CONTEXT driverContext = GetDriverContext(WdfGetDriver()); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICInitRecvBuffers\n"); PAGED_CODE(); // Setup each RFD for (RfdCount = 0; RfdCount < FdoData->NumRfd; RfdCount++) { status = WdfMemoryCreateFromLookaside( driverContext->RecvLookaside, &memoryHdl ); if(!NT_SUCCESS(status)){ TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Failed to get lookaside buffer\n"); continue; } pMpRfd = WdfMemoryGetBuffer(memoryHdl, NULL); if (!pMpRfd) { //ErrorValue = ERRLOG_OUT_OF_LOOKASIDE_MEMORY; continue; } pMpRfd->LookasideMemoryHdl = memoryHdl; // // Allocate the shared memory for this RFD. // _Analysis_assume_(FdoData->HwRfdSize > 0); status = WdfCommonBufferCreate( FdoData->WdfDmaEnabler, FdoData->HwRfdSize, WDF_NO_OBJECT_ATTRIBUTES, &pMpRfd->WdfCommonBuffer ); if (status != STATUS_SUCCESS) { pMpRfd->WdfCommonBuffer = NULL; WdfObjectDelete(pMpRfd->LookasideMemoryHdl); continue; } pMpRfd->OriginalHwRfd = WdfCommonBufferGetAlignedVirtualAddress(pMpRfd->WdfCommonBuffer); pMpRfd->OriginalHwRfdLa = WdfCommonBufferGetAlignedLogicalAddress(pMpRfd->WdfCommonBuffer); // // Get a 8-byts aligned memory from the original HwRfd // pMpRfd->HwRfd = (PHW_RFD)DATA_ALIGN(pMpRfd->OriginalHwRfd); // // Now HwRfd is already 8-bytes aligned, and the size of HwPfd // header(not data part) is a multiple of 8, // If we shift HwRfd 0xA bytes up, the Ethernet header size // is 14 bytes long, then the data will be at // 8 byte boundary. // pMpRfd->HwRfd = (PHW_RFD)((PUCHAR)(pMpRfd->HwRfd) + HWRFD_SHIFT_OFFSET); // // Update physical address accordingly // pMpRfd->HwRfdLa.QuadPart = pMpRfd->OriginalHwRfdLa.QuadPart + BYTES_SHIFT(pMpRfd->HwRfd, pMpRfd->OriginalHwRfd); status = NICAllocRfd(FdoData, pMpRfd); if (!NT_SUCCESS(status)) { WdfObjectDelete(pMpRfd->LookasideMemoryHdl); continue; } // // Add this RFD to the RecvList // FdoData->CurrNumRfd++; NICReturnRFD(FdoData, pMpRfd); } if (FdoData->CurrNumRfd > NIC_MIN_RFDS) { status = STATUS_SUCCESS; } // // FdoData->CurrNumRfd < NIC_MIN_RFDs // if (status != STATUS_SUCCESS) { // TODO: Log an entry into the eventlog } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICInitRecvBuffers, status=%x\n", status); return status; } NTSTATUS NICAllocRfd( IN PFDO_DATA FdoData, IN PMP_RFD pMpRfd ) /*++ Routine Description: Allocate NDIS_PACKET and NDIS_BUFFER associated with a RFD. Can be called at DISPATCH_LEVEL. Arguments: FdoData Pointer to our adapter pMpRfd pointer to a RFD Return Value: --*/ { NTSTATUS status = STATUS_SUCCESS; PHW_RFD pHwRfd; UNREFERENCED_PARAMETER(FdoData); PAGED_CODE(); do{ pHwRfd = pMpRfd->HwRfd; pMpRfd->HwRfdPhys = pMpRfd->HwRfdLa.LowPart; pMpRfd->Flags = 0; pMpRfd->Mdl = IoAllocateMdl((PVOID)&pHwRfd->RfdBuffer.RxMacHeader, NIC_MAX_PACKET_SIZE, FALSE, FALSE, NULL); if (!pMpRfd->Mdl) { status = STATUS_INSUFFICIENT_RESOURCES; break; } MmBuildMdlForNonPagedPool(pMpRfd->Mdl); pMpRfd->Buffer = &pHwRfd->RfdBuffer.RxMacHeader; // Init each RFD header pHwRfd->RfdRbdPointer = DRIVER_NULL; pHwRfd->RfdSize = NIC_MAX_PACKET_SIZE; } WHILE (FALSE); if (!NT_SUCCESS(status)) { if (pMpRfd->WdfCommonBuffer) { // // Free HwRfd, we need to free the original memory // pointed by OriginalHwRfd. // WdfObjectDelete( pMpRfd->WdfCommonBuffer ); pMpRfd->WdfCommonBuffer = NULL; pMpRfd->HwRfd = NULL; pMpRfd->OriginalHwRfd = NULL; pMpRfd->DeleteCommonBuffer = TRUE; } } return status; } VOID NICFreeRfd( IN PFDO_DATA FdoData, IN PMP_RFD pMpRfd ) /*++ Routine Description: Free a RFD. Can be called at DISPATCH_LEVEL. Arguments: FdoData Pointer to our adapter pMpRfd Pointer to a RFD Return Value: None --*/ { UNREFERENCED_PARAMETER(FdoData); PAGED_CODE(); ASSERT(pMpRfd->HwRfd); ASSERT(pMpRfd->Mdl); IoFreeMdl(pMpRfd->Mdl); // // Free HwRfd, we need to free the original memory pointed // by OriginalHwRfd. // if (pMpRfd->DeleteCommonBuffer == TRUE) { WdfObjectDelete( pMpRfd->WdfCommonBuffer ); } pMpRfd->WdfCommonBuffer = NULL; pMpRfd->HwRfd = NULL; pMpRfd->OriginalHwRfd = NULL; WdfObjectDelete(pMpRfd->LookasideMemoryHdl); } VOID NICAllocRfdWorkItem( IN WDFWORKITEM WorkItem ) /*++ Routine Description: Worker routine to allocate memory for RFD at PASSIVE_LEVEL. Arguments: WorkItem - Handle to framework item object. Return Value: VOID --*/ { PFDO_DATA FdoData; //KIRQL oldIrql; PMP_RFD TempMpRfd; NTSTATUS status; PWORKER_ITEM_CONTEXT item; WDFMEMORY tempMpRfdMemHdl; PDRIVER_CONTEXT driverContext = GetDriverContext(WdfGetDriver()); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "---> NICAllocRfdWorkItem\n"); item = GetWorkItemContext(WorkItem); FdoData = item->FdoData; status = WdfMemoryCreateFromLookaside(driverContext->RecvLookaside, &tempMpRfdMemHdl); if(!NT_SUCCESS(status)){ TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Failed to get lookaside buffer\n"); return ; } TempMpRfd = WdfMemoryGetBuffer(tempMpRfdMemHdl, NULL); if (TempMpRfd) { TempMpRfd->LookasideMemoryHdl = tempMpRfdMemHdl; // // Allocate the shared memory for this RFD. // _Analysis_assume_(FdoData->HwRfdSize > 0); status = WdfCommonBufferCreate(FdoData->WdfDmaEnabler, FdoData->HwRfdSize, WDF_NO_OBJECT_ATTRIBUTES, &TempMpRfd->WdfCommonBuffer); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "WdfCommonBufferCreate failed %X\n", status); WdfObjectDelete(TempMpRfd->LookasideMemoryHdl); goto Exit; } TempMpRfd->OriginalHwRfd = WdfCommonBufferGetAlignedVirtualAddress( TempMpRfd->WdfCommonBuffer); TempMpRfd->OriginalHwRfdLa = WdfCommonBufferGetAlignedLogicalAddress( TempMpRfd->WdfCommonBuffer); // // First get a HwRfd at 8 byte boundary from OriginalHwRfd // TempMpRfd->HwRfd = (PHW_RFD)DATA_ALIGN(TempMpRfd->OriginalHwRfd); // // Then shift HwRfd so that the data(after ethernet header) is at 8 bytes boundary // TempMpRfd->HwRfd = (PHW_RFD)((PUCHAR)TempMpRfd->HwRfd + HWRFD_SHIFT_OFFSET); // // Update physical address as well // TempMpRfd->HwRfdLa.QuadPart = TempMpRfd->OriginalHwRfdLa.QuadPart + BYTES_SHIFT(TempMpRfd->HwRfd, TempMpRfd->OriginalHwRfd); status = NICAllocRfd(FdoData, TempMpRfd); if (!NT_SUCCESS(status)) { // // NICAllocRfd frees common buffer when it returns an TRACE_LEVEL_ERROR. // So, let us not worry about freeing that here. // WdfObjectDelete(TempMpRfd->LookasideMemoryHdl); TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "Recv: NICAllocRfd failed %x\n", status); goto Exit; } WdfSpinLockAcquire(FdoData->RcvLock); // // Add this RFD to the RecvList // FdoData->CurrNumRfd++; NICReturnRFD(FdoData, TempMpRfd); WdfSpinLockRelease(FdoData->RcvLock); ASSERT(FdoData->CurrNumRfd <= FdoData->MaxNumRfd); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "CurrNumRfd=%d\n", FdoData->CurrNumRfd); } Exit: FdoData->AllocNewRfd = FALSE; WdfObjectDelete(WorkItem); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<--- NICAllocRfdWorkItem\n"); return; } VOID NICFreeRfdWorkItem( IN WDFWORKITEM WorkItem ) /*++ Routine Description: Worker routine to RFD memory at PASSIVE_LEVEL. Arguments: WorkItem - Handle to framework item object. Return Value: VOID --*/ { PFDO_DATA fdoData; PMP_RFD pMpRfd; PWORKER_ITEM_CONTEXT item; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "---> NICFreeRfdWorkItem\n"); PAGED_CODE(); item = GetWorkItemContext(WorkItem); fdoData = item->FdoData; pMpRfd = (PMP_RFD)item->Argument1; NICFreeRfd(fdoData, pMpRfd); WdfObjectDelete(WorkItem); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<--- NICFreeRfdWorkItem\n"); return; } NTSTATUS NICSelfTest( IN PFDO_DATA FdoData ) /*++ Routine Description: Perform a NIC self-test Arguments: FdoData Pointer to our adapter Return Value: NT status code --*/ { NTSTATUS status = STATUS_SUCCESS; ULONG SelfTestCommandCode; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICSelfTest\n"); PAGED_CODE(); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "SelfTest=%p, SelfTestPhys=%x\n", FdoData->SelfTest, FdoData->SelfTestPhys); // // Issue a software reset to the adapter // HwSoftwareReset(FdoData); // // Execute The PORT Self Test Command On The 82558. // ASSERT(FdoData->SelfTestPhys != 0); SelfTestCommandCode = FdoData->SelfTestPhys; // // Setup SELF TEST Command Code in D3 - D0 // SelfTestCommandCode |= PORT_SELFTEST; // // Initialize the self-test signature and results DWORDS // FdoData->SelfTest->StSignature = 0; FdoData->SelfTest->StResults = 0xffffffff; // // Do the port command // FdoData->CSRAddress->Port = SelfTestCommandCode; MP_STALL_EXECUTION(NIC_DELAY_POST_SELF_TEST_MS); // // if The First Self Test DWORD Still Zero, We've timed out. If the second // DWORD is not zero then we have an TRACE_LEVEL_ERROR. // if ((FdoData->SelfTest->StSignature == 0) || (FdoData->SelfTest->StResults != 0)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "StSignature=%x, StResults=%x\n", FdoData->SelfTest->StSignature, FdoData->SelfTest->StResults); status = STATUS_DEVICE_CONFIGURATION_ERROR; } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICSelfTest, status=%x\n", status); return status; } NTSTATUS NICInitializeAdapter( IN PFDO_DATA FdoData ) /*++ Routine Description: Initialize the adapter and set up everything Arguments: FdoData Pointer to our adapter Return Value: NT Status Code --*/ { NTSTATUS status; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICInitializeAdapter\n"); do { // set up our link indication variable // it doesn't matter what this is right now because it will be // set correctly if link fails FdoData->MediaState = Connected; // Issue a software reset to the D100 HwSoftwareReset(FdoData); // Load the CU BASE (set to 0, because we use linear mode) FdoData->CSRAddress->ScbGeneralPointer = 0; status = D100IssueScbCommand(FdoData, SCB_CUC_LOAD_BASE, FALSE); if (status != STATUS_SUCCESS) { break; } // Wait for the SCB command word to clear before we set the general pointer if (!WaitScb(FdoData)) { status = STATUS_DEVICE_DATA_ERROR; break; } // Load the RU BASE (set to 0, because we use linear mode) FdoData->CSRAddress->ScbGeneralPointer = 0; status = D100IssueScbCommand(FdoData, SCB_RUC_LOAD_BASE, FALSE); if (status != STATUS_SUCCESS) { break; } // Configure the adapter status = HwConfigure(FdoData); if (status != STATUS_SUCCESS) { break; } status = HwSetupIAAddress(FdoData); if (status != STATUS_SUCCESS) { break; } // Clear the internal counters HwClearAllCounters(FdoData); } WHILE (FALSE); if (status != STATUS_SUCCESS) { // TODO: Log an entry into the eventlog } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICInitializeAdapter, Status=%x\n", status); return status; } VOID NICShutdown( IN PFDO_DATA FdoData) /*++ Routine Description: Shutdown the device Arguments: FdoData - Pointer to our adapter Return Value: None --*/ { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "---> NICShutdown\n"); if(FdoData->CSRAddress) { // // Disable interrupt and issue a full reset // NICDisableInterrupt(FdoData); NICIssueFullReset(FdoData); // // Reset the PHY chip. We do this so that after a warm boot, the PHY will // be in a known state, with auto-negotiation enabled. // ResetPhy(FdoData); } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<--- NICShutdown\n"); } VOID HwSoftwareReset( IN PFDO_DATA FdoData ) /*++ Routine Description: Issue a software reset to the hardware Arguments: FdoData Pointer to our adapter Return Value: None --*/ { TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> HwSoftwareReset\n"); // Issue a PORT command with a data word of 0 FdoData->CSRAddress->Port = PORT_SOFTWARE_RESET; // wait after the port reset command KeStallExecutionProcessor(NIC_DELAY_POST_RESET); // Mask off our interrupt line -- its unmasked after reset NICDisableInterrupt(FdoData); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "<-- HwSoftwareReset\n"); } NTSTATUS HwConfigure( IN PFDO_DATA FdoData ) /*++ Routine Description: Configure the hardware Arguments: FdoData Pointer to our adapter Return Value: NT Status --*/ { NTSTATUS status; PCB_HEADER_STRUC NonTxCmdBlockHdr = (PCB_HEADER_STRUC)FdoData->NonTxCmdBlock; UINT i; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> HwConfigure\n"); // // Init the packet filter to nothing. // FdoData->OldPacketFilter = FdoData->PacketFilter; FdoData->PacketFilter = 0; // // Store the current setting for BROADCAST/PROMISCUOS modes FdoData->OldParameterField = CB_557_CFIG_DEFAULT_PARM15; // Setup the non-transmit command block header for the configure command. NonTxCmdBlockHdr->CbStatus = 0; NonTxCmdBlockHdr->CbCommand = CB_CONFIGURE; NonTxCmdBlockHdr->CbLinkPointer = DRIVER_NULL; // Fill in the configure command data. // 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; FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[21] = CB_557_CFIG_DEFAULT_PARM21; // Now fill in the rest of the configuration bytes (the bytes that contain // user configurable parameters). // Set the Tx and Rx Fifo limits FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[1] = (UCHAR) ((FdoData->AiTxFifo << 4) | FdoData->AiRxFifo); 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; } FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[7] = (UCHAR) ((CB_557_CFIG_DEFAULT_PARM7 & (~CB_CFIG_URUN_RETRY)) | (FdoData->AiUnderrunRetry << 1) ); // 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] = (CB_557_CFIG_DEFAULT_PARM15 | 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] = ((CB_557_CFIG_DEFAULT_PARM15 & (~CB_CFIG_CRS_OR_CDT)) | CB_CFIG_BROADCAST_DIS); } // 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 { // We must force full duplex on if we are using PHY 0, and we are // supposed to run in FDX mode. We do this because the D100 has only // one FDX# input pin, and that pin will be connected to PHY 1. if ((FdoData->PhyAddress == 0) && (FdoData->usDuplexMode == 2)) FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[19] = (CB_557_CFIG_DEFAULT_PARM19 | CB_CFIG_FORCE_FDX); else FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[19] = CB_557_CFIG_DEFAULT_PARM19; } // display the config TRACE_LEVEL_INFORMATION to the debugger TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, " Issuing Configure command\n"); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, " Config Block at virt addr %p phys address %x\n", &NonTxCmdBlockHdr->CbStatus, FdoData->NonTxCmdBlockPhys); for (i=0; i < CB_CFIG_BYTE_COUNT; i++) TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, " Config byte %x = %.2x\n", i, FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[i]); // Wait for the SCB command word to clear before we set the general pointer if (!WaitScb(FdoData)) { status = STATUS_DEVICE_DATA_ERROR; } else { ASSERT(FdoData->CSRAddress->ScbCommandLow == 0); FdoData->CSRAddress->ScbGeneralPointer = FdoData->NonTxCmdBlockPhys; // Submit the configure command to the chip, and wait for it to complete. status = D100SubmitCommandBlockAndWait(FdoData); } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "<-- HwConfigure, Status=%x\n", status); return status; } NTSTATUS HwSetupIAAddress( IN PFDO_DATA FdoData ) /*++ Routine Description: Set up the individual MAC address Arguments: FdoData Pointer to our adapter Return Value: NT Status code --*/ { NTSTATUS status; UINT i; PCB_HEADER_STRUC NonTxCmdBlockHdr = (PCB_HEADER_STRUC)FdoData->NonTxCmdBlock; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> HwSetupIAAddress\n"); // Individual Address Setup NonTxCmdBlockHdr->CbStatus = 0; NonTxCmdBlockHdr->CbCommand = CB_IA_ADDRESS; NonTxCmdBlockHdr->CbLinkPointer = DRIVER_NULL; // Copy in the station's individual address for (i = 0; i < ETH_LENGTH_OF_ADDRESS; i++) FdoData->NonTxCmdBlock->NonTxCb.Setup.IaAddress[i] = FdoData->CurrentAddress[i]; // Update the command list pointer. We don't need to do a WaitSCB here // because this command is either issued immediately after a reset, or // after another command that runs in polled mode. This guarantees that // the low byte of the SCB command word will be clear. The only commands // that don't run in polled mode are transmit and RU-start commands. ASSERT(FdoData->CSRAddress->ScbCommandLow == 0); FdoData->CSRAddress->ScbGeneralPointer = FdoData->NonTxCmdBlockPhys; // Submit the IA configure command to the chip, and wait for it to complete. status = D100SubmitCommandBlockAndWait(FdoData); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "<-- HwSetupIAAddress, Status=%x\n", status); return status; } NTSTATUS HwClearAllCounters( IN PFDO_DATA FdoData ) /*++ Routine Description: This routine will clear the hardware TRACE_LEVEL_ERROR statistic counters Arguments: FdoData Pointer to our adapter Return Value: NT Status code --*/ { NTSTATUS status; BOOLEAN bResult; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> HwClearAllCounters\n"); PAGED_CODE(); do { // Load the dump counters pointer. Since this command is generated only // after the IA setup has complete, we don't need to wait for the SCB // command word to clear ASSERT(FdoData->CSRAddress->ScbCommandLow == 0); FdoData->CSRAddress->ScbGeneralPointer = FdoData->StatsCounterPhys; // Issue the load dump counters address command status = D100IssueScbCommand(FdoData, SCB_CUC_DUMP_ADDR, FALSE); if (status != STATUS_SUCCESS) break; // Now dump and reset all of the statistics status = D100IssueScbCommand(FdoData, SCB_CUC_DUMP_RST_STAT, TRUE); if (status != STATUS_SUCCESS) break; // Now wait for the dump/reset to complete, timeout value 2 secs MP_STALL_AND_WAIT(FdoData->StatsCounters->CommandComplete == 0xA007, 2000, bResult); if (!bResult) { MP_SET_HARDWARE_ERROR(FdoData); status = STATUS_DEVICE_DATA_ERROR; break; } // init packet counts FdoData->GoodTransmits = 0; FdoData->GoodReceives = 0; // init transmit error counts FdoData->TxAbortExcessCollisions = 0; FdoData->TxLateCollisions = 0; FdoData->TxDmaUnderrun = 0; FdoData->TxLostCRS = 0; FdoData->TxOKButDeferred = 0; FdoData->OneRetry = 0; FdoData->MoreThanOneRetry = 0; FdoData->TotalRetries = 0; // init receive error counts FdoData->RcvCrcErrors = 0; FdoData->RcvAlignmentErrors = 0; FdoData->RcvResourceErrors = 0; FdoData->RcvDmaOverrunErrors = 0; FdoData->RcvCdtFrames = 0; FdoData->RcvRuntErrors = 0; } WHILE (FALSE); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "<-- HwClearAllCounters, Status=%x\n", status); return status; } VOID NICGetDeviceInfSettings( IN OUT PFDO_DATA FdoData ) { // // Number of ReceiveFrameDescriptors // if(!PciDrvReadRegistryValue(FdoData, L"NumRfd", &FdoData->NumRfd)){ FdoData->NumRfd = 32; } FdoData->NumRfd = min(FdoData->NumRfd, NIC_MAX_RFDS); FdoData->NumRfd = max(FdoData->NumRfd, 1); // // Number of Transmit Control Blocks // if(!PciDrvReadRegistryValue(FdoData, L"NumTcb", &FdoData->NumTcb)){ FdoData->NumTcb = NIC_DEF_TCBS; } FdoData->NumTcb = min(FdoData->NumTcb, NIC_MAX_TCBS); FdoData->NumTcb = max(FdoData->NumTcb, 1); // // Max number of buffers required for coalescing fragmented packet. // Not implemented in this sample // if(!PciDrvReadRegistryValue(FdoData, L"NumCoalesce", &FdoData->NumBuffers)){ FdoData->NumBuffers = 8; } FdoData->NumBuffers = min(FdoData->NumBuffers, 32); FdoData->NumBuffers = max(FdoData->NumBuffers, 1); // // Get the Link Speed & Duplex. // if(!PciDrvReadRegistryValue(FdoData, L"SpeedDuplex", &FdoData->SpeedDuplex)){ FdoData->SpeedDuplex = 0; } FdoData->SpeedDuplex = min(FdoData->SpeedDuplex, 4); FdoData->SpeedDuplex = max(FdoData->SpeedDuplex, 0); // // Decode SpeedDuplex // Value 0 means Auto detect // Value 1 means 10Mb-Half-Duplex // Value 2 means 10Mb-Full-Duplex // Value 3 means 100Mb-Half-Duplex // Value 4 means 100Mb-Full-Duplex // switch(FdoData->SpeedDuplex) { case 1: FdoData->AiTempSpeed = 10; FdoData->AiForceDpx = 1; break; case 2: FdoData->AiTempSpeed = 10; FdoData->AiForceDpx = 2; break; case 3: FdoData->AiTempSpeed = 100; FdoData->AiForceDpx = 1; break; case 4: FdoData->AiTempSpeed = 100; FdoData->AiForceDpx = 2; break; } // // Rest of these values are currently not configured thru INF. // FdoData->PhyAddress = 0xFF; FdoData->Connector = 0; FdoData->AiTxFifo = DEFAULT_TX_FIFO_LIMIT; FdoData->AiRxFifo = DEFAULT_RX_FIFO_LIMIT; FdoData->AiTxDmaCount = 0; FdoData->AiRxDmaCount = 0; FdoData->AiUnderrunRetry = DEFAULT_UNDERRUN_RETRY; FdoData->AiThreshold = 200; FdoData->MWIEnable = 1; FdoData->Congest = 0; return; }
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials