/***************************************************************************** * adapter.cpp - SB16 adapter driver implementation. ***************************************************************************** * Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved. * * This files does setup and resource allocation/verification for the SB16 * card. It controls which miniports are started and which resources are * given to each miniport. It also deals with interrupt sharing between * miniports by hooking the interrupt and calling the correct DPC. */ // // All the GUIDS for all the miniports end up in this object. // #define PUT_GUIDS_HERE #define STR_MODULENAME "sb16Adapter: " #include "common.h" /***************************************************************************** * Defines */ #define MAX_MINIPORTS 5 #if (DBG) #define SUCCEEDS(s) ASSERT(NT_SUCCESS(s)) #else #define SUCCEEDS(s) (s) #endif /***************************************************************************** * Externals */ NTSTATUS CreateMiniportWaveCyclicSB16 ( OUT PUNKNOWN * Unknown, IN REFCLSID, IN PUNKNOWN UnknownOuter OPTIONAL, IN POOL_TYPE PoolType ); NTSTATUS CreateMiniportTopologySB16 ( OUT PUNKNOWN * Unknown, IN REFCLSID, IN PUNKNOWN UnknownOuter OPTIONAL, IN POOL_TYPE PoolType ); /***************************************************************************** * Referenced forward */ extern "C" NTSTATUS AddDevice ( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ); NTSTATUS StartDevice ( IN PDEVICE_OBJECT DeviceObject, // Device object. IN PIRP Irp, // IO request packet. IN PRESOURCELIST ResourceList // List of hardware resources. ); NTSTATUS AssignResources ( IN PRESOURCELIST ResourceList, // All resources. OUT PRESOURCELIST * ResourceListWave, // Wave resources. OUT PRESOURCELIST * ResourceListWaveTable, // Wave table resources. OUT PRESOURCELIST * ResourceListFmSynth, // FM synth resources. OUT PRESOURCELIST * ResourceListUart, // UART resources. OUT PRESOURCELIST * ResourceListAdapter // a copy needed by the adapter ); #ifdef DO_RESOURCE_FILTERING extern "C" NTSTATUS AdapterDispatchPnp ( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ); #endif DWORD DeterminePlatform(PPORTTOPOLOGY Port); #pragma code_seg("INIT") /***************************************************************************** * DriverEntry() ***************************************************************************** * This function is called by the operating system when the driver is loaded. * All adapter drivers can use this code without change. */ extern "C" NTSTATUS DriverEntry ( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPathName ) { PAGED_CODE(); // // Tell the class driver to initialize the driver. // NTSTATUS ntStatus = PcInitializeAdapterDriver( DriverObject, RegistryPathName, AddDevice ); #ifdef DO_RESOURCE_FILTERING // // We want to do resource filtering, so we'll install our own PnP IRP handler. // if(NT_SUCCESS(ntStatus)) { DriverObject->MajorFunction[IRP_MJ_PNP] = AdapterDispatchPnp; } #endif return ntStatus; } #pragma code_seg("PAGE") /***************************************************************************** * AddDevice() ***************************************************************************** * This function is called by the operating system when the device is added. * All adapter drivers can use this code without change. */ extern "C" NTSTATUS AddDevice ( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ) { PAGED_CODE(); // // Tell the class driver to add the device. // return PcAddAdapterDevice( DriverObject, PhysicalDeviceObject, PCPFNSTARTDEVICE( StartDevice ), MAX_MINIPORTS, 0 ); } /***************************************************************************** * InstallSubdevice() ***************************************************************************** * This function creates and registers a subdevice consisting of a port * driver, a minport driver and a set of resources bound together. It will * also optionally place a pointer to an interface on the port driver in a * specified location before initializing the port driver. This is done so * that a common ISR can have access to the port driver during initialization, * when the ISR might fire. */ NTSTATUS InstallSubdevice ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PWCHAR Name, IN REFGUID PortClassId, IN REFGUID MiniportClassId, IN PFNCREATEINSTANCE MiniportCreate OPTIONAL, IN PUNKNOWN UnknownAdapter OPTIONAL, IN PRESOURCELIST ResourceList, IN REFGUID PortInterfaceId, OUT PUNKNOWN * OutPortInterface OPTIONAL, OUT PUNKNOWN * OutPortUnknown OPTIONAL ) { PAGED_CODE(); _DbgPrintF(DEBUGLVL_VERBOSE, ("InstallSubdevice")); ASSERT(DeviceObject); ASSERT(Irp); ASSERT(Name); // // Create the port driver object // PPORT port; NTSTATUS ntStatus = PcNewPort(&port,PortClassId); if (NT_SUCCESS(ntStatus)) { // // Deposit the port somewhere if it's needed. // if (OutPortInterface) { // // Failure here doesn't cause the entire routine to fail. // (void) port->QueryInterface ( PortInterfaceId, (PVOID *) OutPortInterface ); } PUNKNOWN miniport; // // Create the miniport object // if (MiniportCreate) { ntStatus = MiniportCreate ( &miniport, MiniportClassId, NULL, NonPagedPool ); } else { ntStatus = PcNewMiniport((PMINIPORT*) &miniport,MiniportClassId); } if (NT_SUCCESS(ntStatus)) { // // Init the port driver and miniport in one go. // ntStatus = port->Init( DeviceObject, Irp, miniport, UnknownAdapter, ResourceList ); if (NT_SUCCESS(ntStatus)) { // // Register the subdevice (port/miniport combination). // ntStatus = PcRegisterSubdevice( DeviceObject, Name, port ); if (!(NT_SUCCESS(ntStatus))) { _DbgPrintF(DEBUGLVL_TERSE, ("StartDevice: PcRegisterSubdevice failed")); } } else { _DbgPrintF(DEBUGLVL_TERSE, ("InstallSubdevice: port->Init failed")); } // // We don't need the miniport any more. Either the port has it, // or we've failed, and it should be deleted. // miniport->Release(); } else { _DbgPrintF(DEBUGLVL_TERSE, ("InstallSubdevice: PcNewMiniport failed")); } if (NT_SUCCESS(ntStatus)) { // // Deposit the port as an unknown if it's needed. // if (OutPortUnknown) { // // Failure here doesn't cause the entire routine to fail. // (void) port->QueryInterface ( IID_IUnknown, (PVOID *) OutPortUnknown ); } } else { // // Retract previously delivered port interface. // if (OutPortInterface && (*OutPortInterface)) { (*OutPortInterface)->Release(); *OutPortInterface = NULL; } } // // Release the reference which existed when PcNewPort() gave us the // pointer in the first place. This is the right thing to do // regardless of the outcome. // port->Release(); } else { _DbgPrintF(DEBUGLVL_TERSE, ("InstallSubdevice: PcNewPort failed")); } return ntStatus; } /***************************************************************************** * StartDevice() ***************************************************************************** * This function is called by the operating system when the device is started. * It is responsible for starting the miniports. This code is specific to * the adapter because it calls out miniports for functions that are specific * to the adapter. */ NTSTATUS StartDevice ( IN PDEVICE_OBJECT DeviceObject, // Device object. IN PIRP Irp, // IO request packet. IN PRESOURCELIST ResourceList // List of hardware resources. ) { PAGED_CODE(); ASSERT(DeviceObject); ASSERT(Irp); ASSERT(ResourceList); // // These are the sub-lists of resources that will be handed to the // miniports. // PRESOURCELIST resourceListWave = NULL; PRESOURCELIST resourceListWaveTable = NULL; PRESOURCELIST resourceListFmSynth = NULL; PRESOURCELIST resourceListUart = NULL; PRESOURCELIST resourceListAdapter = NULL; // // These are the port driver pointers we are keeping around for registering // physical connections. // PUNKNOWN unknownTopology = NULL; PUNKNOWN unknownWave = NULL; PUNKNOWN unknownWaveTable = NULL; PUNKNOWN unknownFmSynth = NULL; // // Assign resources to individual miniports. Each sub-list is a copy // of the resources from the master list. Each sublist must be released. // NTSTATUS ntStatus = AssignResources( ResourceList, &resourceListWave, &resourceListWaveTable, &resourceListFmSynth, &resourceListUart, &resourceListAdapter ); // // if AssignResources succeeded... // if(NT_SUCCESS(ntStatus)) { // // If the adapter has resources... // PADAPTERCOMMON pAdapterCommon = NULL; if (resourceListAdapter) { PUNKNOWN pUnknownCommon; // create a new adapter common object ntStatus = NewAdapterCommon( &pUnknownCommon, IID_IAdapterCommon, NULL, NonPagedPool ); if (NT_SUCCESS(ntStatus)) { ASSERT( pUnknownCommon ); // query for the IAdapterCommon interface ntStatus = pUnknownCommon->QueryInterface( IID_IAdapterCommon, (PVOID *)&pAdapterCommon ); if (NT_SUCCESS(ntStatus)) { // Initialize the object ntStatus = pAdapterCommon->Init( resourceListAdapter, DeviceObject ); if (NT_SUCCESS(ntStatus)) { // register with PortCls for power-management services ntStatus = PcRegisterAdapterPowerManagement( (PUNKNOWN)pAdapterCommon, DeviceObject ); } } // release the IUnknown on adapter common pUnknownCommon->Release(); } // release the adapter common resource list resourceListAdapter->Release(); } // // Start the topology miniport. // if (NT_SUCCESS(ntStatus)) { ntStatus = InstallSubdevice( DeviceObject, Irp, L"Topology", CLSID_PortTopology, CLSID_PortTopology, // not used CreateMiniportTopologySB16, pAdapterCommon, NULL, GUID_NULL, NULL, &unknownTopology ); } // // Start the SB wave miniport if it exists. // if (resourceListWave) { if (NT_SUCCESS(ntStatus)) { ntStatus = InstallSubdevice( DeviceObject, Irp, L"Wave", CLSID_PortWaveCyclic, CLSID_PortWaveCyclic, // not used CreateMiniportWaveCyclicSB16, pAdapterCommon, resourceListWave, IID_IPortWaveCyclic, NULL, &unknownWave ); } // release the wave resource list resourceListWave->Release(); } // Start the wave table miniport if it exists. if (resourceListWaveTable) { // // NOTE: The wavetable is not currently supported in this sample driver. // // release the wavetable resource list resourceListWaveTable->Release(); } // // Start the FM synth miniport if it exists. // if (resourceListFmSynth) { // // Synth not working yet. // if (NT_SUCCESS(ntStatus)) { // // Failure here is not fatal. // InstallSubdevice( DeviceObject, Irp, L"FMSynth", CLSID_PortMidi, CLSID_MiniportDriverFmSynth, NULL, pAdapterCommon, resourceListFmSynth, GUID_NULL, NULL, &unknownFmSynth ); } // release the FM synth resource list resourceListFmSynth->Release(); } // // Start the UART miniport if it exists. // if (resourceListUart) { if (NT_SUCCESS(ntStatus)) { // // Failure here is not fatal. // InstallSubdevice( DeviceObject, Irp, L"Uart", CLSID_PortDMus, CLSID_MiniportDriverDMusUART, NULL, pAdapterCommon->GetInterruptSync(), resourceListUart, IID_IPortDMus, NULL, // interface to port not needed NULL ); // not physically connected to anything } resourceListUart->Release(); } // // Establish physical connections between filters as shown. // // +------+ +------+ // | Wave | | Topo | // Capture <---|0 1|<===|6 2|<--- CD // | | | | // Render --->|2 3|===>|0 3|<--- Line In // +------+ | | // +------+ | 4|<--- Mic // | FM | | | // MIDI --->|0 1|===>|1 5|---> Line Out // +------+ +------+ // if (unknownTopology) { DWORD version = DeterminePlatform((PPORTTOPOLOGY)unknownTopology); _DbgPrintF(DEBUGLVL_VERBOSE,("Detected platform version 0x%02X",version)); if (unknownWave) { // register wave <=> topology connections PcRegisterPhysicalConnection( (PDEVICE_OBJECT)DeviceObject, unknownTopology, 6, unknownWave, 1 ); PcRegisterPhysicalConnection( (PDEVICE_OBJECT)DeviceObject, unknownWave, 3, unknownTopology, 0 ); } if (unknownFmSynth) { // register fmsynth <=> topology connection PcRegisterPhysicalConnection( (PDEVICE_OBJECT)DeviceObject, unknownFmSynth, 1, unknownTopology, 1 ); } } // // Release the adapter common object. It either has other references, // or we need to delete it anyway. // if (pAdapterCommon) { pAdapterCommon->Release(); } // // Release the unknowns. // if (unknownTopology) { unknownTopology->Release(); } if (unknownWave) { unknownWave->Release(); } if (unknownWaveTable) { unknownWaveTable->Release(); } if (unknownFmSynth) { unknownFmSynth->Release(); } } return ntStatus; } /***************************************************************************** * AssignResources() ***************************************************************************** * This function assigns the list of resources to the various functions on * the card. This code is specific to the adapter. All the non-NULL resource * lists handed back must be released by the caller. */ NTSTATUS AssignResources ( IN PRESOURCELIST ResourceList, // All resources. OUT PRESOURCELIST * ResourceListWave, // Wave resources. OUT PRESOURCELIST * ResourceListWaveTable, // Wave table resources. OUT PRESOURCELIST * ResourceListFmSynth, // FM synth resources. OUT PRESOURCELIST * ResourceListUart, // Uart resources. OUT PRESOURCELIST * ResourceListAdapter // For the adapter ) { PAGED_CODE(); BOOLEAN detectedWaveTable = FALSE; BOOLEAN detectedUart = FALSE; BOOLEAN detectedFmSynth = FALSE; // // Get counts for the types of resources. // ULONG countIO = ResourceList->NumberOfPorts(); ULONG countIRQ = ResourceList->NumberOfInterrupts(); ULONG countDMA = ResourceList->NumberOfDmas(); // // Determine the type of card based on port resources. // TODO: Detect wave table. // NTSTATUS ntStatus = STATUS_SUCCESS; switch (countIO) { case 1: // // No FM synth or UART. // if ( (ResourceList->FindTranslatedPort(0)->u.Port.Length < 16) || (countIRQ < 1) || (countDMA < 1) ) { ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR; } break; case 2: // // MPU-401 or FM synth, not both. // if ( (ResourceList->FindTranslatedPort(0)->u.Port.Length < 16) || (countIRQ < 1) || (countDMA < 1) ) { ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR; } else { // // Length of second port indicates which function. // switch (ResourceList->FindTranslatedPort(1)->u.Port.Length) { case 2: detectedUart = TRUE; break; case 4: detectedFmSynth = TRUE; break; default: ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR; break; } } break; case 3: // // Both MPU-401 and FM synth. // if ( (ResourceList->FindTranslatedPort(0)->u.Port.Length < 16) || (ResourceList->FindTranslatedPort(1)->u.Port.Length != 2) || (ResourceList->FindTranslatedPort(2)->u.Port.Length != 4) || (countIRQ < 1) || (countDMA < 1) ) { ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR; } else { detectedUart = TRUE; detectedFmSynth = TRUE; } break; default: ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR; break; } // // Build the resource list for the SB wave I/O. // *ResourceListWave = NULL; if (NT_SUCCESS(ntStatus)) { ntStatus = PcNewResourceSublist ( ResourceListWave, NULL, PagedPool, ResourceList, countDMA + countIRQ + 1 ); if (NT_SUCCESS(ntStatus)) { ULONG i; // // Add the base address // ntStatus = (*ResourceListWave)-> AddPortFromParent(ResourceList,0); // // Add the DMA channel(s). // if (NT_SUCCESS(ntStatus)) { for (i = 0; i < countDMA; i++) { ntStatus = (*ResourceListWave)-> AddDmaFromParent(ResourceList,i); } } // // Add the IRQ lines. // if (NT_SUCCESS(ntStatus)) { for (i = 0; i < countIRQ; i++) { SUCCEEDS((*ResourceListWave)-> AddInterruptFromParent(ResourceList,i)); } } } } // // Build list of resources for wave table. // *ResourceListWaveTable = NULL; if (NT_SUCCESS(ntStatus) && detectedWaveTable) { // // TODO: Assign wave table resources. // } // // Build list of resources for UART. // *ResourceListUart = NULL; if (NT_SUCCESS(ntStatus) && detectedUart) { ntStatus = PcNewResourceSublist ( ResourceListUart, NULL, PagedPool, ResourceList, 2 ); if (NT_SUCCESS(ntStatus)) { ntStatus = (*ResourceListUart)-> AddPortFromParent(ResourceList,1); if (NT_SUCCESS(ntStatus)) { ntStatus = (*ResourceListUart)-> AddInterruptFromParent(ResourceList,0); } } } // // Build list of resources for FM synth. // *ResourceListFmSynth = NULL; if (NT_SUCCESS(ntStatus) && detectedFmSynth) { ntStatus = PcNewResourceSublist ( ResourceListFmSynth, NULL, PagedPool, ResourceList, 1 ); if (NT_SUCCESS(ntStatus)) { ntStatus = (*ResourceListFmSynth)-> AddPortFromParent(ResourceList,detectedUart ? 2 : 1); } } // // Build list of resources for the adapter. // *ResourceListAdapter = NULL; if (NT_SUCCESS(ntStatus)) { ntStatus = PcNewResourceSublist ( ResourceListAdapter, NULL, PagedPool, ResourceList, 3 ); if (NT_SUCCESS(ntStatus)) { // // The interrupt to share. // ntStatus = (*ResourceListAdapter)-> AddInterruptFromParent(ResourceList,0); // // The base IO port (to tell who's interrupt it is) // if (NT_SUCCESS(ntStatus)) { ntStatus = (*ResourceListAdapter)-> AddPortFromParent(ResourceList,0); } if (detectedUart && NT_SUCCESS(ntStatus)) { // // The Uart port // ntStatus = (*ResourceListAdapter)-> AddPortFromParent(ResourceList,1); } } } // // Clean up if failure occurred. // if (! NT_SUCCESS(ntStatus)) { if (*ResourceListWave) { (*ResourceListWave)->Release(); *ResourceListWave = NULL; } if (*ResourceListWaveTable) { (*ResourceListWaveTable)->Release(); *ResourceListWaveTable = NULL; } if (*ResourceListUart) { (*ResourceListUart)->Release(); *ResourceListUart = NULL; } if (*ResourceListFmSynth) { (*ResourceListFmSynth)->Release(); *ResourceListFmSynth = NULL; } if(*ResourceListAdapter) { (*ResourceListAdapter)->Release(); *ResourceListAdapter = NULL; } } return ntStatus; } #ifdef DO_RESOURCE_FILTERING /***************************************************************************** * AdapterDispatchPnp() ***************************************************************************** * Supplying your PnP resource filtering needs. */ extern "C" NTSTATUS AdapterDispatchPnp ( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) { PAGED_CODE(); ASSERT(pDeviceObject); ASSERT(pIrp); NTSTATUS ntStatus = STATUS_SUCCESS; PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); if( pIrpStack->MinorFunction == IRP_MN_FILTER_RESOURCE_REQUIREMENTS ) { // // Do your resource requirements filtering here!! // _DbgPrintF(DEBUGLVL_VERBOSE,("[AdapterDispatchPnp] - IRP_MN_FILTER_RESOURCE_REQUIREMENTS")); // set the return status pIrp->IoStatus.Status = ntStatus; } // // Pass the IRPs on to PortCls // ntStatus = PcDispatchIrp( pDeviceObject, pIrp ); return ntStatus; } #endif /***************************************************************************** * DeterminePlatform() ***************************************************************************** * Figure out which WDM platform we are currently running on. * Note: the Port parameter could be WAVECYCLIC, WAVEPCI, DMUS or MIDI instead. * * TODO: Make this work on old DDK. * */ DWORD DeterminePlatform(PPORTTOPOLOGY Port) { PAGED_CODE(); ASSERT(Port); // // The generally accepted way of determining audio stack vintage: // PPORTCLSVERSION pPortClsVersion; PDRMPORT pDrmPort; PPORTEVENTS pPortEvents; DWORD dwVersion; (void) Port->QueryInterface( IID_IPortClsVersion, (PVOID *) &pPortClsVersion); (void) Port->QueryInterface( IID_IDrmPort, (PVOID *) &pDrmPort); (void) Port->QueryInterface( IID_IPortEvents, (PVOID *) &pPortEvents); // // Try for the exact release (Win98SE QFE3, WinME QFE, Win2KSP2, WinXP, or later). // if (pPortClsVersion) { dwVersion = pPortClsVersion->GetVersion(); pPortClsVersion->Release(); } // // Try for WinME // else if (pDrmPort) { dwVersion = kVersionWinME; ASSERT(IoIsWdmVersionAvailable(0x01,0x05)); // // TODO: Look for registry entries that denote WinME QFEs // HKLM\Software\Microsoft\Windows\CurrentVersion\Setup\Updates\..., etc. // } // // Try for Win2K family. // Note that SP1 contains no real audio stack changes, // while SP2 contains non-PCM support and other fixes. // else if (IoIsWdmVersionAvailable(0x01,0x10)) { dwVersion = kVersionWin2K; // // TODO: Detect whether SP1 or earlier. // } // // Must be Win98 or Win98SE. // IPortEvents was new in Win98SE. // else if (pPortEvents) { dwVersion = kVersionWin98SE; // or older QFEs // // TODO: Look for registry entries that denote older Win98SE QFEs // HKLM\Software\Microsoft\Windows\CurrentVersion\Setup\Updates\W98.SE\UPD\269601, etc. // } // // Process of elimination tells us it is Win98. // else { dwVersion = kVersionWin98; // // TODO: Look for registry entries that denote older Win98 QFEs // HKLM\Software\Microsoft\Windows\CurrentVersion\Setup\Updates\..., etc. // } // dwVersion contains enum with version of audio stack return dwVersion; } #pragma code_seg() /***************************************************************************** * _purecall() ***************************************************************************** * The C++ compiler loves me. * TODO: Figure out how to put this into portcls.sys */ int __cdecl _purecall( void ) { ASSERT( !"Pure virutal function called" ); return 0; }