https://git.reactos.org/?p=reactos.git;a=commitdiff;h=9c79a7982b1627151045b1...
commit 9c79a7982b1627151045b130627f63963fc88d63 Author: Obaid51 16-se-51@students.uettaxila.edu.pk AuthorDate: Tue Jan 4 12:16:01 2022 +0500 Commit: Victor Perevertkin victor.perevertkin@reactos.org CommitDate: Wed Feb 16 01:31:28 2022 +0300
[AC97] Import the AC97 driver sample
The source code is licensed under MIT license, taken from "MSDN Code Gallery Microsoft Samples" repository (https://github.com/microsoftarchive/msdn-code-gallery-microsoft)
The original license was MS-PL, but the driver was later relicensed as MIT.
Adopted to ReactOS code base by Michael Stamper.
Co-authored-by: Michael Stamper michaelstamper1@gmail.com --- drivers/wdm/audio/drivers/CMakeLists.txt | 1 + drivers/wdm/audio/drivers/ac97/CMakeLists.txt | 30 + drivers/wdm/audio/drivers/ac97/ac97.inf | 482 ++++ drivers/wdm/audio/drivers/ac97/ac97.rc | 24 + drivers/wdm/audio/drivers/ac97/ac97reg.h | 170 ++ drivers/wdm/audio/drivers/ac97/adapter.cpp | 583 +++++ drivers/wdm/audio/drivers/ac97/adapter.h | 71 + drivers/wdm/audio/drivers/ac97/common.cpp | 2309 ++++++++++++++++++++ drivers/wdm/audio/drivers/ac97/common.h | 387 ++++ drivers/wdm/audio/drivers/ac97/debug.h | 93 + drivers/wdm/audio/drivers/ac97/guids.h | 188 ++ drivers/wdm/audio/drivers/ac97/ichreg.h | 107 + drivers/wdm/audio/drivers/ac97/license.txt | 21 + drivers/wdm/audio/drivers/ac97/miniport.cpp | 1462 +++++++++++++ drivers/wdm/audio/drivers/ac97/miniport.h | 145 ++ drivers/wdm/audio/drivers/ac97/mintopo.cpp | 2129 ++++++++++++++++++ drivers/wdm/audio/drivers/ac97/mintopo.h | 339 +++ drivers/wdm/audio/drivers/ac97/prophnd.cpp | 1824 ++++++++++++++++ drivers/wdm/audio/drivers/ac97/rtminiport.cpp | 252 +++ drivers/wdm/audio/drivers/ac97/rtminiport.h | 155 ++ drivers/wdm/audio/drivers/ac97/rtstream.cpp | 425 ++++ drivers/wdm/audio/drivers/ac97/rtstream.h | 118 + drivers/wdm/audio/drivers/ac97/shared.h | 504 +++++ drivers/wdm/audio/drivers/ac97/stream.cpp | 634 ++++++ drivers/wdm/audio/drivers/ac97/stream.h | 200 ++ drivers/wdm/audio/drivers/ac97/stream2.cpp | 159 ++ .../wdm/audio/drivers/ac97/wavecyclicminiport.cpp | 182 ++ .../wdm/audio/drivers/ac97/wavecyclicminiport.h | 77 + .../wdm/audio/drivers/ac97/wavecyclicstream.cpp | 230 ++ drivers/wdm/audio/drivers/ac97/wavecyclicstream.h | 116 + drivers/wdm/audio/drivers/ac97/wavepciminiport.cpp | 214 ++ drivers/wdm/audio/drivers/ac97/wavepciminiport.h | 79 + drivers/wdm/audio/drivers/ac97/wavepcistream.cpp | 885 ++++++++ drivers/wdm/audio/drivers/ac97/wavepcistream.h | 138 ++ media/doc/3rd Party Files.txt | 6 + 35 files changed, 14739 insertions(+)
diff --git a/drivers/wdm/audio/drivers/CMakeLists.txt b/drivers/wdm/audio/drivers/CMakeLists.txt index 2f2f8289a9d..009a279a4a4 100644 --- a/drivers/wdm/audio/drivers/CMakeLists.txt +++ b/drivers/wdm/audio/drivers/CMakeLists.txt @@ -1,2 +1,3 @@
+add_subdirectory(ac97) add_subdirectory(CMIDriver) diff --git a/drivers/wdm/audio/drivers/ac97/CMakeLists.txt b/drivers/wdm/audio/drivers/ac97/CMakeLists.txt new file mode 100644 index 00000000000..ef182eee79e --- /dev/null +++ b/drivers/wdm/audio/drivers/ac97/CMakeLists.txt @@ -0,0 +1,30 @@ + +list(APPEND SOURCE + adapter.cpp + common.cpp + mintopo.cpp + prophnd.cpp + miniport.cpp + stream.cpp + stream2.cpp + rtminiport.cpp + rtstream.cpp + wavepciminiport.cpp + wavepcistream.cpp + wavecyclicminiport.cpp + wavecyclicstream.cpp) + +add_library(ac97 MODULE + ${SOURCE} + ac97.rc) + +target_link_libraries(ac97 stdunk libcntpr uuid) +set_module_type(ac97 wdmdriver) +add_importlibs(ac97 portcls hal ntoskrnl) + +if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang") + target_compile_options(ac97 PRIVATE -Wno-write-strings -Wno-switch) +endif() + +add_cd_file(TARGET ac97 DESTINATION reactos/system32/drivers FOR all) +add_driver_inf(ac97 ac97.inf) diff --git a/drivers/wdm/audio/drivers/ac97/ac97.inf b/drivers/wdm/audio/drivers/ac97/ac97.inf new file mode 100644 index 00000000000..abf1920b71c --- /dev/null +++ b/drivers/wdm/audio/drivers/ac97/ac97.inf @@ -0,0 +1,482 @@ +;Copyright (c) 1998-2000 Microsoft Corporation All rights Reserved +; +;Module Name: +; ac97.INF +; +;Abstract: +; INF file for installing AC97 WDM Driver +; +; +; During upgrade from Win98SE or Win ME, the default upgrade behavior won't upgrade the drivers +; to Win2k or Windows XP. The inf has implemented what's mentioned in +; WINDDK..\src\setup\devupgrd\devupgrd.doc. +; If your driver would never be installed under Win98SE or Win ME, you don't need this new migrate.dll +; stuff. + + +[Version] +Signature="$CHICAGO$" +Class=MEDIA +ClassGUID={4d36e96c-e325-11ce-bfc1-08002be10318} +provider=%ProviderName% +;;The following line is used only when the INF comes with the Windows system +;;IHV needs to comment out the following line for their OEM redistributed disk. +;;LayoutFile=layout.inf, layout1.inf, layout2.inf +DriverVer=02/22/2007,6.00.6000.1 +CatalogFile=ac97.cat + +;You must specify which platform is supported by each SourceDisksNames section +;Valid platform identifiers include .x86, .ia64, .alpha, .axp64 +[SourceDisksNames] +222=%DiskDescription%,,, + +;You must also specify which platform is supported by each SourceDisksFiles section +;Valid platform identifiers include .x86, .ia64, .alpha, .axp64 +[SourceDisksFiles] +ac97.sys=222 +;ac97prop.dll=222 +;the entries below are used by the device migration dll, on Win95/98 to Win2K upgrades +;Migrate.dll=222 +ac97.inf=222 + +[Manufacturer] +%MfgName%=Intel,NTAMD64,NTIA64,NTARM + +;; Excluding drivers from the "Add New Hardware" list. +[ControlFlags] +ExcludeFromSelect = * + +[Intel] +%ac97_AA.DeviceDesc%=ac97, PCI\VEN_8086&DEV_2415 +%ac97_AB.DeviceDesc%=ac97, PCI\VEN_8086&DEV_2425 +%ac97_BA.DeviceDesc%=ac97, PCI\VEN_8086&DEV_2445 + +;; This section enables installing on x64 systems + +[Intel.NTAMD64] +%ac97_AA.DeviceDesc%=ac97, PCI\VEN_8086&DEV_2415 +%ac97_AB.DeviceDesc%=ac97, PCI\VEN_8086&DEV_2425 +%ac97_BA.DeviceDesc%=ac97, PCI\VEN_8086&DEV_2445 + +;; This section enables installing on Itanium systems + +[Intel.NTIA64] +%ac97_AA.DeviceDesc%=ac97, PCI\VEN_8086&DEV_2415 +%ac97_AB.DeviceDesc%=ac97, PCI\VEN_8086&DEV_2425 +%ac97_BA.DeviceDesc%=ac97, PCI\VEN_8086&DEV_2445 + +;; This section enables installing on ARM systems + +[Intel.NTARM] +%ac97_AA.DeviceDesc%=ac97, PCI\VEN_8086&DEV_2415 +%ac97_AB.DeviceDesc%=ac97, PCI\VEN_8086&DEV_2425 +%ac97_BA.DeviceDesc%=ac97, PCI\VEN_8086&DEV_2445 + +[DestinationDirs] +ac97.CopyList=10,system32\drivers +;AC97PROP.CopyList=10,system32 + +DevUpgrd_Files = 10, win9xmig\DevUpgrd +WDMDriver_Files = 10, win9xmig\DevUpgrd\AC97WDM ; Replace "AC97WDM" with your favorite vendor related name + +[ac97] +;;This inf is intended for use by IHV. So, it's going to be used as from OEM distributed disk +;;This is why AlsoInstall got used instead if Needs & Include +AlsoInstall=KS.Registration(ks.inf), WDMAUDIO.Registration(wdmaudio.inf) +CopyFiles=ac97.CopyList, DevUpgrd_Files, WDMDriver_Files +AddReg=ac97.AddReg,ac97_NAMES.AddReg,ac97_OEM.AddReg,DevUpgrd_AddReg +KnownRegEntries=AC97.KnownRegEntries +;;Exclude driver installation for those PnP ID's. +;;This PnP ID is an example of a machine where the driver won't work correctly +ExcludeId=PCI\VEN_8086&DEV_2415&SUBSYS_536011D4&REV_00 + +[AC97.KnownRegEntries] +IsWin98Gold=keep + +[IsWin98Gold] +1=HKLM,Software\Microsoft\Windows\CurrentVersion,VersionNumber,0,4.10.1998 + +[ac97.CopyList] +ac97.sys + +[DevUpgrd_Files] +;;This migrate.dll can be found in the DDK (beta2 or after) under src\setup\devupgrd +;Migrate.dll + +[WDMDriver_Files] +ac97.inf ; Name of your INF goes here +ac97.sys ; Name of your driver file(s) goes here +;ac97prop.dll + + + +[ac97.Interfaces] +AddInterface=%KSCATEGORY_AUDIO%,%KSNAME_Wave%,ac97.Interface.Wave +AddInterface=%KSCATEGORY_RENDER%,%KSNAME_Wave%,ac97.Interface.Wave +AddInterface=%KSCATEGORY_CAPTURE%,%KSNAME_Wave%,ac97.Interface.Wave +AddInterface=%KSCATEGORY_REALTIME%,%KSNAME_Wave%,ac97.Interface.Wave +AddInterface=%KSCATEGORY_AUDIO%,%KSNAME_Topology%,ac97.Interface.Topology + +[ac97.Interface.Wave] +AddReg=ac97.I.Wave.AddReg + +[ac97.I.Wave.AddReg] +HKR,,CLSID,,%Proxy.CLSID% +HKR,,FriendlyName,,%ac97.Wave.szPname% + +[ac97.Interface.Topology] +AddReg=ac97.I.Topo.AddReg + +[ac97.I.Topo.AddReg] +HKR,,CLSID,,%Proxy.CLSID% +HKR,,FriendlyName,,%ac97.Topology.szPname% + +[ac97.AddReg] +HKR,,AssociatedFilters,,"wdmaud,swmidi,redbook" +HKR,,Driver,,ac97.sys +HKR,,NTMPDriver,,"ac97.sys,sbemul.sys" + +HKR,Drivers,SubClasses,,"wave,midi,mixer" + +HKR,Drivers\wave\wdmaud.drv,Driver,,wdmaud.drv +HKR,Drivers\midi\wdmaud.drv,Driver,,wdmaud.drv +HKR,Drivers\mixer\wdmaud.drv,Driver,,wdmaud.drv + +HKR,Drivers\wave\wdmaud.drv,Description,,%ac97.DeviceDesc% +HKR,Drivers\midi\wdmaud.drv, Description,,%ac97.DeviceDesc% +HKR,Drivers\mixer\wdmaud.drv,Description,,%ac97.DeviceDesc% + + +[DevUpgrd_AddReg] +HKLM,"Software\Microsoft\Windows\CurrentVersion\Setup\Migration DLLs","Microsoft Device Upgrade Pack",,%10%\win9xmig\DevUpgrd +HKLM,"Software\Microsoft\Windows\CurrentVersion\Setup\UpgradeDrivers","PCI\VEN_8086&DEV_2415",,%10%\win9xmig\DevUpgrd\AC97WDM\ac97.inf +HKLM,"Software\Microsoft\Windows\CurrentVersion\Setup\UpgradeDrivers","PCI\VEN_8086&DEV_2425",,%10%\win9xmig\DevUpgrd\AC97WDM\ac97.inf +HKLM,"Software\Microsoft\Windows\CurrentVersion\Setup\UpgradeDrivers","PCI\VEN_8086&DEV_2445",,%10%\win9xmig\DevUpgrd\AC97WDM\ac97.inf + + +[ac97.Services] +AddService = ac97, 0x00000002, ac97_Service_Inst + +[ac97_Service_Inst] +DisplayName = %ac97.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %10%\system32\drivers\ac97.sys + +[ac97_NAMES.AddReg] +;; Nodes +HKLM,%MediaCategories%%ICHGUID.PhoneVolume%,Name,,%ICHNode.PhoneVolume% +HKLM,%MediaCategories%%ICHGUID.PhoneVolume%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.PhoneMute%,Name,,%ICHNode.PhoneMute% +HKLM,%MediaCategories%%ICHGUID.PhoneMute%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.LineInMute%,Name,,%ICHNode.LineInMute% +HKLM,%MediaCategories%%ICHGUID.LineInMute%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.MainMix%,Name,,%ICHNode.MainMix% +HKLM,%MediaCategories%%ICHGUID.MainMix%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.3DBypass%,Name,,%ICHNode.3DBypass% +HKLM,%MediaCategories%%ICHGUID.3DBypass%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.3DEnable%,Name,,%ICHNode.3DEnable% +HKLM,%MediaCategories%%ICHGUID.3DEnable%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.BeepMix%,Name,,%ICHNode.BeepMix% +HKLM,%MediaCategories%%ICHGUID.BeepMix%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.HPVolume%,Name,,%ICHNode.HPVolume% +HKLM,%MediaCategories%%ICHGUID.HPVolume%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.HPMute%,Name,,%ICHNode.HPMute% +HKLM,%MediaCategories%%ICHGUID.HPMute%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.MonoOutSelect%,Name,,%ICHNode.MonoOutSelect% +HKLM,%MediaCategories%%ICHGUID.MonoOutSelect%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.WaveInSelect%,Name,,%ICHNode.WaveInSelect% +HKLM,%MediaCategories%%ICHGUID.WaveInSelect%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.MasterInVolume%,Name,,%ICHNode.MasterInVolume% +HKLM,%MediaCategories%%ICHGUID.MasterInVolume%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.MasterInMute%,Name,,%ICHNode.MasterInMute% +HKLM,%MediaCategories%%ICHGUID.MasterInMute%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.MicInVolume%,Name,,%ICHNode.MicInVolume% +HKLM,%MediaCategories%%ICHGUID.MicInVolume%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.MicInMute%,Name,,%ICHNode.MicInMute% +HKLM,%MediaCategories%%ICHGUID.MicInMute%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.SimulStereo%,Name,,%ICHNode.SimulStereo% +HKLM,%MediaCategories%%ICHGUID.SimulStereo%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.SurroundVolume%,Name,,%ICHNode.SurroundVolume% +HKLM,%MediaCategories%%ICHGUID.SurroundVolume%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.SurroundMute%,Name,,%ICHNode.SurroundMute% +HKLM,%MediaCategories%%ICHGUID.SurroundMute%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.CenterVolume%,Name,,%ICHNode.CenterVolume% +HKLM,%MediaCategories%%ICHGUID.CenterVolume%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.CenterMute%,Name,,%ICHNode.CenterMute% +HKLM,%MediaCategories%%ICHGUID.CenterMute%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.LFEVolume%,Name,,%ICHNode.LFEVolume% +HKLM,%MediaCategories%%ICHGUID.LFEVolume%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.LFEMute%,Name,,%ICHNode.LFEMute% +HKLM,%MediaCategories%%ICHGUID.LFEMute%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.FrontVolume%,Name,,%ICHNode.FrontVolume% +HKLM,%MediaCategories%%ICHGUID.FrontVolume%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.FrontMute%,Name,,%ICHNode.FrontMute% +HKLM,%MediaCategories%%ICHGUID.FrontMute%,Display,1,00,00,00,00 +;; Pins +HKLM,%MediaCategories%%ICHGUID.Surround%,Name,,%ICHPin.Surround% +HKLM,%MediaCategories%%ICHGUID.Surround%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.Center%,Name,,%ICHPin.Center% +HKLM,%MediaCategories%%ICHGUID.Center%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.LFE%,Name,,%ICHPin.LFE% +HKLM,%MediaCategories%%ICHGUID.LFE%,Display,1,00,00,00,00 +HKLM,%MediaCategories%%ICHGUID.Front%,Name,,%ICHPin.Front% +HKLM,%MediaCategories%%ICHGUID.Front%,Display,1,00,00,00,00 + + +[ac97_OEM.AddReg] +;;Default register value at driver startup +;;Uncomment some one of the lines if you want to overwrite the default setting +;;The value in these outcommented lines is the driver default. You can change +;;the register values (and uncomment the line) to overwrite the driver default. +;;low byte comes first, values are hexadezimal. +;HKR,Settings,MasterVolume,1,0,0 ;0dB +;HKR,Settings,HeadphoneVolume,1,0,0 ;0dB +;HKR,Settings,MonooutVolume,1,0,0 ;0dB +;HKR,Settings,ToneControls,1,F,F ;bypass +;HKR,Settings,BeepVolume,1,0,0 ;0dB +;HKR,Settings,PhoneVolume,1,8,80 ;muted +;HKR,Settings,MicVolume,1,8,80 ;muted +;HKR,Settings,LineInVolume,1,8,8 ;0dB +;HKR,Settings,CDVolume,1,8,8 ;0dB +;HKR,Settings,VideoVolume,1,8,8 ;0dB +;HKR,Settings,AUXVolume,1,8,8 ;0dB +;HKR,Settings,WaveOutVolume,1,8,8 ;0dB. This register is never touched by the system. +;HKR,Settings,RecordSelect,1,4,4 ;select LiniIn +;HKR,Settings,RecordGain,1,0,0 ;0dB +;HKR,Settings,RecordGainMic,1,0,0 ;0dB +;HKR,Settings,GeneralPurpose,1,0,0 ;pre 3D, 3D off, loudness off, mono=mix, mic1 +;HKR,Settings,3DControl,1,0,0 ;0% +;HKR,Settings,PowerDown,1,0,0 ;no power down +;HKR,Settings,ExtAudioCtrl,1,01,40 ;VRA, DACs on, MicIn off +;HKR,Settings,CenterLFEVolume,1,0,0 ;0dB +;HKR,Settings,SurroundVolume,1,0,0 ;0dB + +;;Configuration +;;You can disable some of the input lines by outcommenting some of the lines +;;below. This could be necessary if you have a AC97 codec on board that for +;;example supports Video input, but you don't have the Video input accessable +;;for the user (no plug in). +;HKR,Settings,DisablePCBeep,1,1 +;HKR,Settings,DisablePhone,1,1 +;HKR,Settings,DisableMic2,1,1 +;HKR,Settings,DisableVideo,1,1 +;HKR,Settings,DisableAUX,1,1 +;HKR,Settings,DisableHeadphone,1,1 +;HKR,Settings,DisableMonoOut,1,1 +HKR,Settings,DisableMicIn,1,1 +;HKR,Settings,DisableMic,1,1 ;disables all MIC lines, including MIC record. +;HKR,Settings,DisableLineIn,1,1 +;HKR,Settings,DisableCD,1,1 +;HKR,Settings,DisableSurround,1,1 +HKR,Settings,DisableCenterLFE,1,1 ;6ch playback is not supported. +;HKR,Settings,ChannelConfig,1,3,0,0,0 ;ChannelConfig set to stereo speakers. + +;;================= Windows NT ==================== +[ac97.NTX86] +Include=ks.inf,wdmaudio.inf +Needs=KS.Registration,WDMAUDIO.Registration +CopyFiles=ac97.CopyList +;,AC97PROP.CopyList +AddReg=ac97.AddReg,ac97_NAMES.AddReg,ac97_OEM.AddReg +;,AC97PROP.AddReg +;;Exclude driver installation for those PnP ID's. +;;This PnP ID is an example of a machine where the driver won't work correctly +ExcludeId=PCI\VEN_8086&DEV_2415&SUBSYS_536011D4&REV_00 + +[ac97.NTX86.Interfaces] +AddInterface=%KSCATEGORY_AUDIO%,%KSNAME_Wave%,ac97.Interface.Wave +AddInterface=%KSCATEGORY_RENDER%,%KSNAME_Wave%,ac97.Interface.Wave +AddInterface=%KSCATEGORY_CAPTURE%,%KSNAME_Wave%,ac97.Interface.Wave +AddInterface=%KSCATEGORY_AUDIO%,%KSNAME_Topology%,ac97.Interface.Topology + +[ac97.NTX86.Services] +AddService = ac97, 0x00000002, ac97_Service_Inst + +;[AC97PROP.CopyList] +;ac97prop.dll + +;[AC97PROP.AddReg] +;HKR,,EnumPropPages32,,"ac97prop.dll,AC97PropPageProvider" + + +[Strings] +ProviderName="FooProviderName" +MfgName="Intel" +DiskDescription="AC'97 WDM Driver Disk" + +ac97_AA.DeviceDesc="Intel 82801AA AC'97 Audio Controller" +ac97_AB.DeviceDesc="Intel 82801AB AC'97 Audio Controller" +ac97_BA.DeviceDesc="Intel 82801BA/BAM AC'97 Audio Controller" +ac97.DeviceDesc="Intel 82801 AC'97 Audio Controller" + +ac97.Wave.szPname="AC'97 Sound Card" +ac97.Topology.szPname="AC'97 Mixer" + +MediaCategories="SYSTEM\CurrentControlSet\Control\MediaCategories" + +Proxy.CLSID="{17CCA71B-ECD7-11D0-B908-00A0C9223196}" +KSCATEGORY_AUDIO="{6994AD04-93EF-11D0-A3CC-00A0C9223196}" +KSCATEGORY_RENDER="{65E8773E-8F56-11D0-A3B9-00A0C9223196}" +KSCATEGORY_CAPTURE="{65E8773D-8F56-11D0-A3B9-00A0C9223196}" +KSCATEGORY_REALTIME="{EB115FFC-10C8-4964-831D-6DCB02E6F23F}" +KSNAME_Wave="Wave" +KSNAME_Topology="Topology" + +ac97.SvcDesc = "Service for AC'97 Driver (WDM)" + +;; Nodes (non-localizeable) +ICHGUID.PhoneVolume ="{0A8C1A87-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.PhoneMute ="{0A8C1A88-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.LineInMute ="{0A8C1A91-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.MainMix ="{0A8C1A9B-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.3DBypass ="{0A8C1A9E-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.3DEnable ="{766DB5A4-6E94-11D2-9ADE-00C04F8EFB68}" +ICHGUID.BeepMix ="{0A8C1A9F-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.HPVolume ="{0A8C1AA5-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.HPMute ="{0A8C1AA6-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.MonoOutSelect ="{0A8C1AA9-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.WaveInSelect ="{0A8C1AAE-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.MasterInVolume ="{0A8C1AAF-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.MasterInMute ="{0A8C1AB0-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.MicInVolume ="{0A8C1AB2-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.MicInMute ="{0A8C1AB3-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.SimulStereo ="{B3AD50B5-3849-4983-ADD7-25E6268F912D}" +ICHGUID.SurroundVolume ="{A4B68BA4-6958-4ab4-BB01-E23C6F027C88}" +ICHGUID.SurroundMute ="{22654FBC-AC8F-4224-B19F-D858D2E10BDD}" +ICHGUID.CenterVolume ="{9B0F1946-ABD2-47a8-A778-BB86CDE1A167}" +ICHGUID.CenterMute ="{BEEF51ED-1041-43f8-9B96-5863D0A9342D}" +ICHGUID.LFEVolume ="{455FA6F2-21EC-4df4-B1E4-3155209797F3}" +ICHGUID.LFEMute ="{4A4D9210-C780-4768-BFD2-525FDBF4FCB4}" +ICHGUID.FrontVolume ="{9F4801BD-F746-4c7a-8A9D-F6E99004CC98}" +ICHGUID.FrontMute ="{C8E03B2A-EBD9-4554-A750-8E4472750A5B}" + +;; Pins (non-localizeable) +ICHGUID.Surround ="{81FBB14B-1BEE-4bf5-92EE-FFC4F75F326D}" +ICHGUID.Center ="{2D97372F-9CF6-4fd6-9E56-C68BACDF360D}" +ICHGUID.LFE ="{B60C4274-3BFD-430b-8364-D947E7D304B1}" +ICHGUID.Front ="{070395E2-BE7C-4b4d-B529-40CB9BFCF995}" + + +;; Nodes (localizeable) +ICHNode.PhoneVolume ="Phone Volume" +ICHNode.PhoneMute ="Phone Mute" +ICHNode.LineInMute ="Line In Mute" +ICHNode.MainMix ="Main Mix" +ICHNode.3DBypass ="3D Bypass" +ICHNode.3DEnable ="3D Enable" +ICHNode.BeepMix ="Beep Mix" +ICHNode.HPVolume ="Headphone Volume" +ICHNode.HPMute ="Headphone Mute" +ICHNode.MonoOutSelect ="Mono Out Select" +ICHNode.WaveInSelect ="Wave In Select" +ICHNode.MasterInVolume ="Wave In Volume" +ICHNode.MasterInMute ="Wave In Mute" +ICHNode.MicInVolume ="Mic In Volume" +ICHNode.MicInMute ="Mic In Mute" +ICHNode.SimulStereo ="Simulated Stereo" +ICHNode.SurroundVolume ="Rear Speaker Volume" +ICHNode.SurroundMute ="Rear Speaker Mute" +ICHNode.CenterVolume ="Center Volume" +ICHNode.CenterMute ="Center Mute" +ICHNode.LFEVolume ="Subwoofer Volume" +ICHNode.LFEMute ="Subwoofer Mute" +ICHNode.FrontVolume ="Front Volume" +ICHNode.FrontMute ="Front Mute" + +;; Pins +ICHPin.Surround ="Rear Speaker" +ICHPin.Center ="Center" +ICHPin.LFE ="Subwoofer" +ICHPin.Front ="Front Speaker" + +[Strings.0407] +ProviderName="FooProviderName" +MfgName="Intel" + +DiskDescription="Diskette für AC'97 WDM Treiberbeispiel" +ac97_AA.DeviceDesc="Intel 82801AA AC'97 Audiocontroller" +ac97_AB.DeviceDesc="Intel 82801AB AC'97 Audiocontroller" +ac97_BA.DeviceDesc="Intel 82801BA/BAM AC'97 Audiocontroller" +ac97.DeviceDesc="Intel 82801 AC'97 Audiocontroller" + +ac97.Wave.szPname="AC'97 Musikkarte" +ac97.Topology.szPname="AC'97 Mixer" + +MediaCategories="SYSTEM\CurrentControlSet\Control\MediaCategories" + +Proxy.CLSID="{17CCA71B-ECD7-11D0-B908-00A0C9223196}" +KSCATEGORY_AUDIO="{6994AD04-93EF-11D0-A3CC-00A0C9223196}" +KSCATEGORY_RENDER="{65E8773E-8F56-11D0-A3B9-00A0C9223196}" +KSCATEGORY_CAPTURE="{65E8773D-8F56-11D0-A3B9-00A0C9223196}" +KSCATEGORY_REALTIME="{EB115FFC-10C8-4964-831D-6DCB02E6F23F}" +KSNAME_Wave="Wave" +KSNAME_Topology="Topology" + +ac97.SvcDesc = "Installationshilfe für AC'97 Treiberbeispiel (WDM)" + + +;; Nodes (non-localizeable) +ICHGUID.PhoneVolume ="{0A8C1A87-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.PhoneMute ="{0A8C1A88-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.LineInMute ="{0A8C1A91-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.MainMix ="{0A8C1A9B-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.3DBypass ="{0A8C1A9E-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.3DEnable ="{766DB5A4-6E94-11D2-9ADE-00C04F8EFB68}" +ICHGUID.BeepMix ="{0A8C1A9F-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.HPVolume ="{0A8C1AA5-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.HPMute ="{0A8C1AA6-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.MonoOutSelect ="{0A8C1AA9-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.WaveInSelect ="{0A8C1AAE-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.MasterInVolume ="{0A8C1AAF-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.MasterInMute ="{0A8C1AB0-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.MicInVolume ="{0A8C1AB2-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.MicInMute ="{0A8C1AB3-42B0-11D2-95D2-00C04FB925D3}" +ICHGUID.SimulStereo ="{B3AD50B5-3849-4983-ADD7-25E6268F912D}" +ICHGUID.SurroundVolume ="{A4B68BA4-6958-4ab4-BB01-E23C6F027C88}" +ICHGUID.SurroundMute ="{22654FBC-AC8F-4224-B19F-D858D2E10BDD}" +ICHGUID.CenterVolume ="{9B0F1946-ABD2-47a8-A778-BB86CDE1A167}" +ICHGUID.CenterMute ="{BEEF51ED-1041-43f8-9B96-5863D0A9342D}" +ICHGUID.LFEVolume ="{455FA6F2-21EC-4df4-B1E4-3155209797F3}" +ICHGUID.LFEMute ="{4A4D9210-C780-4768-BFD2-525FDBF4FCB4}" +ICHGUID.FrontVolume ="{9F4801BD-F746-4c7a-8A9D-F6E99004CC98}" +ICHGUID.FrontMute ="{C8E03B2A-EBD9-4554-A750-8E4472750A5B}" + +;; Pins (non-localizeable) +ICHGUID.Surround ="{81FBB14B-1BEE-4bf5-92EE-FFC4F75F326D}" +ICHGUID.Center ="{2D97372F-9CF6-4fd6-9E56-C68BACDF360D}" +ICHGUID.LFE ="{B60C4274-3BFD-430b-8364-D947E7D304B1}" +ICHGUID.Front ="{070395E2-BE7C-4b4d-B529-40CB9BFCF995}" + +;; Nodes (localizeable) + +ICHNode.PhoneVolume ="Phone Lautstärke" +ICHNode.PhoneMute ="Phone Aus" +ICHNode.LineInMute ="Line In Aus" +ICHNode.MainMix ="Hauptmixer" +ICHNode.3DBypass ="3D umgehen" +ICHNode.3DEnable ="3D Aktivieren" +ICHNode.BeepMix ="Piepmixer" +ICHNode.MonoOutSelect ="Mono Ausgang Selektor" +ICHNode.WaveInSelect ="Aufnahme Selektor" +ICHNode.MasterInVolume ="Aufnahme Lautstärke" +ICHNode.MasterInMute ="Aufnahme unterdrücken" +ICHNode.MicInVolume ="Mic In Lautstärke" +ICHNode.MicInMute ="Mic In Aus" +ICHNode.SimulStereo ="Simuliertes Stereo" +ICHNode.SurroundVolume ="Lautsprecherlautstärke Hinten" +ICHNode.SurroundMute ="Lautsprecher Hinten Aus" +ICHNode.CenterVolume ="Lautsprecherlautstärke Mitte" +ICHNode.CenterMute ="Lautsprecher Mitte Aus" +ICHNode.LFEVolume ="Bass Lautsprecherlautstärke" +ICHNode.LFEMute ="Bass Lautsprecher Aus" +ICHNode.FrontVolume ="Lautsprecherlautstärke Vorne" +ICHNode.HPVolume ="Kopfh�rer Lautst�rke" +ICHNode.HPMute ="Kopfh�rer Aus" + +;; Pins +ICHPin.Surround ="Lautsprecher Hinten" +ICHPin.Center ="Lautsprecher Mitte" +ICHPin.LFE ="Bass Lautsprecher" +ICHPin.Front ="Lautsprecher Vorne" diff --git a/drivers/wdm/audio/drivers/ac97/ac97.rc b/drivers/wdm/audio/drivers/ac97/ac97.rc new file mode 100644 index 00000000000..87c817afc40 --- /dev/null +++ b/drivers/wdm/audio/drivers/ac97/ac97.rc @@ -0,0 +1,24 @@ +/******************************************************************************** +** Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved. +** +** Portions Copyright (c) 1998-1999 Intel Corporation +** +********************************************************************************/ + +/* The file ac97smpl.rc was reviewed by LCA in June 2011 and is acceptable for use by Microsoft. */ + +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SOUND +#define VER_FILEDESCRIPTION_STR "Integrated Controller Hub Audio Driver (WDM)" +#define VER_INTERNALNAME_STR "ac97smpl.sys" +#define VER_ORIGINALFILENAME_STR "ac97smpl.sys" + +#define VER_LEGALCOPYRIGHT_YEARS "1998-2003" +#define VER_LEGALCOPYRIGHT_STR "Copyright (c) Microsoft Corp." VER_LEGALCOPYRIGHT_YEARS + +#include "common.ver" + diff --git a/drivers/wdm/audio/drivers/ac97/ac97reg.h b/drivers/wdm/audio/drivers/ac97/ac97reg.h new file mode 100644 index 00000000000..8361ef5fb88 --- /dev/null +++ b/drivers/wdm/audio/drivers/ac97/ac97reg.h @@ -0,0 +1,170 @@ +/******************************************************************************** +** Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved. +** +** Portions Copyright (c) 1998-1999 Intel Corporation +** +********************************************************************************/ + +/* The file ac97reg.h was reviewed by LCA in June 2011 and is acceptable for use by Microsoft. */ + +#ifndef _AC97REG_H_ +#define _AC97REG_H_ + +// We use enum types cause the compiler can check variable passing if it is +// an enum (otherwise you could pass any value). That doesn't save us from +// doing reasonable run time checks in that range. +enum AC97Register +{ + AC97REG_RESET = 0, + AC97REG_MASTER_VOLUME, + AC97REG_HPHONE_VOLUME, + AC97REG_MMONO_VOLUME, + AC97REG_MASTER_TONE, + AC97REG_BEEP_VOLUME, + AC97REG_PHONE_VOLUME, + AC97REG_MIC_VOLUME, + AC97REG_LINE_IN_VOLUME, + AC97REG_CD_VOLUME, + AC97REG_VIDEO_VOLUME, + AC97REG_AUX_VOLUME, + AC97REG_PCM_OUT_VOLUME, + AC97REG_RECORD_SELECT, + AC97REG_RECORD_GAIN, + AC97REG_RECORD_GAIN_MIC, + AC97REG_GENERAL, + AC97REG_3D_CONTROL, + AC97REG_RESERVED, + AC97REG_POWERDOWN, + + // AC97-2.0 registers + AC97REG_EXT_AUDIO_ID, + AC97REG_EXT_AUDIO_CTRL, + AC97REG_FRONT_SAMPLERATE, + AC97REG_SURROUND_SAMPLERATE, + AC97REG_LFE_SAMPLERATE, + AC97REG_RECORD_SAMPLERATE, + AC97REG_MIC_SAMPLERATE, + AC97REG_CENTER_LFE_VOLUME, + AC97REG_SURROUND_VOLUME, + AC97REG_RESERVED2, + + // Modem registers from 0x3C to 0x58 (next 15 enums) + // Vendor Reserved = 0x5A-0x7A (next 16 enums) + + // Vendor IDs + AC97REG_VENDOR_ID1 = 0x3E, // thats register address 0x7C + AC97REG_VENDOR_ID2, + + // Defines an invalid register. Likewise, this is the highest + // possible value that can be used. + AC97REG_INVALID +}; + +#if (DBG) +// Note: This array only has the first 29 registers defined. +// There are many more. +const PCHAR RegStrings[] = +{ + "REG_RESET", + "REG_MASTER_VOLUME", + "REG_HPHONE_VOLUME", + "REG_MMONO_VOLUME", + "REG_MASTER_TONE", + "REG_BEEP_VOLUME", + "REG_PHONE_VOLUME", + "REG_MIC_VOLUME", + "REG_LINEIN_VOLUME", + "REG_CD_VOLUME", + "REG_VIDEO_VOLUME", + "REG_AUX_VOLUME", + "REG_PCMOUT_VOLUME", + "REG_RECORD_SELECT", + "REG_RECORD_GAIN", + "REG_RECORD_GAIN_MIC", + "REG_GENERAL", + "REG_3D_CONTROL", + "REG_RESERVED", + "REG_POWERDOWN", + "REG_EXT_AUDIO_ID", + "REG_EXT_AUDIO_CTRL", + "REG_FRONT_SAMPLERATE", + "REG_SURROUND_SAMPLERATE", + "REG_LFE_SAMPLERATE", + "REG_RECORD_SAMPLERATE", + "REG_MIC_SAMPLERATE", + "REG_CENTER_LFE_VOLUME", + "REG_SURROUND_VOLUME", + "REG_RESERVED2" +}; +#endif + +// This array maps the node controls to the AC97 registers. +// E.g. if you mute the master volume control you should modify AC97 +// register AC97REG_MASTER_VOLUME +typedef struct { + AC97Register reg; // we would only need one byte, but enums are int + WORD mask; // registers are 16 bit. +} tMapNodeToReg; + +const tMapNodeToReg stMapNodeToReg[] = +{ + // TODO: loopback + {AC97REG_PCM_OUT_VOLUME, 0x1F1F}, // NODE_WAVEOUT_VOLUME + {AC97REG_PCM_OUT_VOLUME, 0x8000}, // NODE_WAVEOUT_MUTE + {AC97REG_GENERAL, 0x8000}, // NODE_VIRT_WAVEOUT_3D_BYPASS + {AC97REG_BEEP_VOLUME, 0x001E}, // NODE_PCBEEP_VOLUME + {AC97REG_BEEP_VOLUME, 0x8000}, // NODE_PCBEEP_MUTE + {AC97REG_PHONE_VOLUME, 0x001F}, // NODE_PHONE_VOLUME + {AC97REG_PHONE_VOLUME, 0x8000}, // NODE_PHONE_MUTE + {AC97REG_GENERAL, 0x0100}, // NODE_MIC_SELECT + {AC97REG_MIC_VOLUME, 0x0040}, // NODE_MIC_BOOST + {AC97REG_MIC_VOLUME, 0x001F}, // NODE_MIC_VOLUME + {AC97REG_MIC_VOLUME, 0x8000}, // NODE_MIC_MUTE + {AC97REG_LINE_IN_VOLUME, 0x1F1F}, // NODE_LINEIN_VOLUME + {AC97REG_LINE_IN_VOLUME, 0x8000}, // NODE_LINEIN_MUTE + {AC97REG_CD_VOLUME, 0x1F1F}, // NODE_CD_VOLUME + {AC97REG_CD_VOLUME, 0x8000}, // NODE_CD_MUTE + {AC97REG_VIDEO_VOLUME, 0x1F1F}, // NODE_VIDEO_VOLUME + {AC97REG_VIDEO_VOLUME, 0x8000}, // NODE_VIDEO_MUTE + {AC97REG_AUX_VOLUME, 0x1F1F}, // NODE_AUX_VOLUME + {AC97REG_AUX_VOLUME, 0x8000}, // NODE_AUX_MUTE + {AC97REG_INVALID, 0x0000}, // NODE_MAIN_MIX doesn't has controls + {AC97REG_3D_CONTROL, 0x0F00}, // NODE_VIRT_3D_CENTER + {AC97REG_3D_CONTROL, 0x000F}, // NODE_VIRT_3D_DEPTH + {AC97REG_GENERAL, 0x2000}, // NODE_VIRT_3D_ENABLE + {AC97REG_INVALID, 0x0000}, // NODE_BEEP_MIX doesn't has controls + {AC97REG_MASTER_TONE, 0x0F00}, // NODE_BASS + {AC97REG_MASTER_TONE, 0x000F}, // NODE_TREBLE + {AC97REG_GENERAL, 0x1000}, // NODE_LOUDNESS + {AC97REG_GENERAL, 0x4000}, // NODE_SIMUL_STEREO + {AC97REG_MASTER_VOLUME, 0x3F3F}, // NODE_MASTEROUT_VOLUME + {AC97REG_MASTER_VOLUME, 0x8000}, // NODE_MASTEROUT_MUTE + {AC97REG_HPHONE_VOLUME, 0x3F3F}, // NODE_HPHONE_VOLUME + {AC97REG_HPHONE_VOLUME, 0x8000}, // NODE_HPHONE_MUTE + {AC97REG_GENERAL, 0x0200}, // NODE_MONOOUT_SELECT + {AC97REG_MMONO_VOLUME, 0x803F}, // NODE_VIRT_MONOOUT_VOLUME1 + {AC97REG_MMONO_VOLUME, 0x803F}, // NODE_VIRT_MONOOUT_VOLUME2 + {AC97REG_RECORD_SELECT, 0x0707}, // NODE_WAVEIN_SELECT + {AC97REG_RECORD_GAIN, 0x0F0F}, // NODE_VIRT_MASTER_INPUT_VOLUME1 + {AC97REG_RECORD_GAIN, 0x0F0F}, // NODE_VIRT_MASTER_INPUT_VOLUME2 + {AC97REG_RECORD_GAIN, 0x0F0F}, // NODE_VIRT_MASTER_INPUT_VOLUME3 + {AC97REG_RECORD_GAIN, 0x0F0F}, // NODE_VIRT_MASTER_INPUT_VOLUME4 + {AC97REG_RECORD_GAIN, 0x0F0F}, // NODE_VIRT_MASTER_INPUT_VOLUME5 + {AC97REG_RECORD_GAIN, 0x0F0F}, // NODE_VIRT_MASTER_INPUT_VOLUME6 + {AC97REG_RECORD_GAIN, 0x0F0F}, // NODE_VIRT_MASTER_INPUT_VOLUME7 + {AC97REG_RECORD_GAIN, 0x0F0F}, // NODE_VIRT_MASTER_INPUT_VOLUME8 + {AC97REG_RECORD_GAIN_MIC, 0x000F}, // NODE_MICIN_VOLUME + {AC97REG_RECORD_GAIN_MIC, 0x8000}, // NODE_MICIN_MUTE + {AC97REG_SURROUND_VOLUME, 0x3F3F}, // NODE_SURROUND_VOLUME + {AC97REG_SURROUND_VOLUME, 0x8080}, // NODE_SURROUND_MUTE + {AC97REG_CENTER_LFE_VOLUME, 0x3F00},// NODE_CENTER_VOLUME + {AC97REG_CENTER_LFE_VOLUME, 0x8000},// NODE_CENTER_MUTE + {AC97REG_CENTER_LFE_VOLUME, 0x003F},// NODE_LFE_VOLUME + {AC97REG_CENTER_LFE_VOLUME, 0x0080},// NODE_LFE_MUTE + {AC97REG_MASTER_VOLUME, 0x3F3F}, // NODE_FRONT_VOLUME + {AC97REG_MASTER_VOLUME, 0x8000}, // NODE_FRONT_MUTE + {AC97REG_INVALID, 0x0000}, // NODE_VIRT_MASTERMONO_VOLUME doesn't have controls + {AC97REG_INVALID, 0x0000} // NODE_VIRT_MASTERMONO_MUTE doesn't have controls +}; + +#endif //_AC97REG_H_ diff --git a/drivers/wdm/audio/drivers/ac97/adapter.cpp b/drivers/wdm/audio/drivers/ac97/adapter.cpp new file mode 100644 index 00000000000..341abf7ea13 --- /dev/null +++ b/drivers/wdm/audio/drivers/ac97/adapter.cpp @@ -0,0 +1,583 @@ +/******************************************************************************** +** Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved. +** +** Portions Copyright (c) 1998-1999 Intel Corporation +** +********************************************************************************/ + +/* The file adapter.cpp was reviewed by LCA in June 2011 and is acceptable for use by Microsoft. */ + +// +// The name that is printed in debug output messages +// +#define STR_MODULENAME "AC97 Adapter: " + +// +// All the GUIDs from portcls and your own defined GUIDs end up in this object. +// +#define PUT_GUIDS_HERE + +// +// We want the global debug variables here. +// +#define DEFINE_DEBUG_VARS + +#include "adapter.h" + + +/***************************************************************************** + * Referenced forward + */ +DRIVER_ADD_DEVICE AddDevice; + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif +/***************************************************************************** + * 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. + * This function is internally used and validates no parameters. + */ +NTSTATUS InstallSubdevice +( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_ PWSTR Name, + _In_ REFGUID PortClassId, + _In_ REFGUID MiniportClassId, + _In_opt_ PFNCREATEMINIPORT MiniportCreate, + _In_opt_ PUNKNOWN UnknownAdapter, + _In_opt_ PRESOURCELIST ResourceList, + _In_opt_ REFGUID PortInterfaceId, + _Out_opt_ PMINIPORT * OutMiniport, + _Out_opt_ PUNKNOWN * OutPortUnknown +) +{ + PAGED_CODE (); + + NTSTATUS ntStatus; + PPORT port; + PMINIPORT miniport; + + DOUT (DBG_PRINT, ("[InstallSubdevice]")); + +#ifndef __REACTOS__ + UNREFERENCED_PARAMETER(PortInterfaceId); +#endif + + // + // Create the port driver object + // + ntStatus = PcNewPort (&port,PortClassId); + + // + // return immediately in case of an error + // + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // + // Create the miniport object + // + if (MiniportCreate) + { + ntStatus = MiniportCreate ((PUNKNOWN*)&miniport, MiniportClassId, + NULL, NonPagedPool); + } + else + { + ntStatus = PcNewMiniport (&miniport,MiniportClassId); + } + + // + // return immediately in case of an error + // + if (!NT_SUCCESS (ntStatus)) + { + port->Release (); + return ntStatus; + } + + // + // Init the port driver and miniport in one go. + // +#ifdef _MSC_VER +#pragma warning(push) +#endif + // IPort::Init's annotation on ResourceList requires it to be non-NULL. However, + // for dynamic devices, we may no longer have the resource list and this should + // still succeed. + // +#ifdef _MSC_VER +#pragma warning(disable:6387) +#endif + ntStatus = port->Init (DeviceObject, Irp, miniport, UnknownAdapter, + ResourceList); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + if (NT_SUCCESS (ntStatus)) + { + // + // Register the subdevice (port/miniport combination). + // + ntStatus = PcRegisterSubdevice (DeviceObject, Name, port); + + // + // Deposit the port as an unknown if it's needed. + // + if (OutPortUnknown && NT_SUCCESS (ntStatus)) + { + ntStatus = port->QueryInterface (IID_IUnknown, + (PVOID *)OutPortUnknown); + } + + // + // Deposit the miniport as an IMiniport if it's needed. + // + if ( OutMiniport && NT_SUCCESS (ntStatus) ) + { + ntStatus = miniport->QueryInterface (IID_IMiniport, + (PVOID *)OutMiniport); + } + } + + // + // Release the reference for the port and miniport. This is the right + // thing to do, regardless of the outcome. + // + miniport->Release (); + port->Release (); + + + return ntStatus; +} + + +/***************************************************************************** + * ValidateResources + ***************************************************************************** + * This function validates the list of resources for the various functions on + * the card. This code is specific to the adapter. + * This function doesn't check the ResourceList parameter and returns + * STATUS_SUCCESS when the resources are valid. + */ +NTSTATUS ValidateResources +( + IN PRESOURCELIST ResourceList // All resources. +) +{ + PAGED_CODE (); + + DOUT (DBG_PRINT, ("[ValidateResources]")); + + // + // Get counts for the types of resources. + // + ULONG countIO = ResourceList->NumberOfPorts (); + ULONG countIRQ = ResourceList->NumberOfInterrupts (); + ULONG countDMA = ResourceList->NumberOfDmas (); + + // validate resources + if ((countIO != 2) || (countIRQ != 1) || (countDMA != 0)) + { + DOUT (DBG_ERROR, ("Unknown configuration:\n" + " IO count: %d\n" + " IRQ count: %d\n" + " DMA count: %d", + countIO, countIRQ, countDMA)); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + return STATUS_SUCCESS; +} + +/***************************************************************************** + * 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 GZCALL 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); + + NTSTATUS ntStatus; + + DOUT (DBG_PRINT, ("[StartDevice]")); + + // + // Determine which version of the OS we are running under. We don't want + // to run under Win98G. + // + + // create a wave cyclic port + PPORT pPort = 0; + ntStatus = PcNewPort (&pPort,CLSID_PortWaveCyclic); + + // check error code + if (NT_SUCCESS (ntStatus)) + { + // query for the event interface which is not supported in Win98 gold. + PPORTEVENTS pPortEvents = 0; + ntStatus = pPort->QueryInterface (IID_IPortEvents, + (PVOID *)&pPortEvents); + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("This driver is not for Win98 Gold!")); + ntStatus = STATUS_UNSUCCESSFUL; // change error code. + } + else + { + pPortEvents->Release (); + } + pPort->Release (); + } + + // now return in case it was Win98 Gold. + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // + // Validate the resources. + // We don't have to split the resources into several resource lists cause + // the topology miniport doesn't need a resource list, the wave pci miniport + // needs all resources like the adapter common object. + // + ntStatus = ValidateResources (ResourceList); + + // + // return immediately in case of an error + // + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // + // If the adapter has the right resources... + // + PADAPTERCOMMON pAdapterCommon = NULL; + PUNKNOWN pUnknownCommon; + + // create a new adapter common object + ntStatus = NewAdapterCommon (&pUnknownCommon, IID_IAC97AdapterCommon, + NULL, NonPagedPool); + + if (NT_SUCCESS (ntStatus)) + { + // query for the IAC97AdapterCommon interface + ntStatus = pUnknownCommon->QueryInterface (IID_IAC97AdapterCommon, + (PVOID *)&pAdapterCommon); + if (NT_SUCCESS (ntStatus)) + { + // Initialize the object + ntStatus = pAdapterCommon->Init (ResourceList, DeviceObject); + + if (NT_SUCCESS (ntStatus)) + { + // register with PortCls for power-management services + ntStatus = PcRegisterAdapterPowerManagement ((PUNKNOWN)pAdapterCommon, + DeviceObject); + } + } + + // release the IID_IAC97AdapterCommon on adapter common + pUnknownCommon->Release (); + } + + // print error message. + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("Could not create or query AdapterCommon.")); + } + + // + // These are the port driver pointers we are keeping around for registering + // physical connections. + // + PMINIPORT miniTopology = NULL; + PUNKNOWN unknownWave = NULL; + PUNKNOWN unknownTopology = NULL; + PAC97MINIPORTTOPOLOGY pMiniportTopology = NULL; + + // + // Start the topology miniport. + // + if (NT_SUCCESS (ntStatus)) + { + ntStatus = InstallSubdevice (DeviceObject, + Irp, + L"Topology", + CLSID_PortTopology, + CLSID_PortTopology, // not used + CreateAC97MiniportTopology, + pAdapterCommon, + NULL, + GUID_NULL, + &miniTopology, + &unknownTopology); + + if (NT_SUCCESS (ntStatus)) + { + // query for the IAC97MiniportTopology interface + ntStatus = miniTopology->QueryInterface (IID_IAC97MiniportTopology, + (PVOID *)&pMiniportTopology); + miniTopology->Release (); + miniTopology = NULL; + } + + // print error message. + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("Could not create or query TopologyICH")); + } + } + + // + // Start the wave miniport. + // + if (NT_SUCCESS (ntStatus)) + { +#if (NTDDI_VERSION >= NTDDI_VISTA) + ntStatus = InstallSubdevice (DeviceObject, + Irp, + L"Wave", + CLSID_PortWaveRT, + CLSID_PortWaveRT, // not used + CreateAC97MiniportWaveRT, + pAdapterCommon, + ResourceList, + IID_IPortWaveRT, + NULL, + &unknownWave); + + if (!NT_SUCCESS (ntStatus)) + { +#endif + // + // If creation of the RT port failed we can fall back to the WavePCI + // or WaveCyc port of portcls. In this case, we try the WavePCI port. + // +#if 1 + ntStatus = InstallSubdevice (DeviceObject, + Irp, + L"Wave", + CLSID_PortWaveCyclic, + CLSID_PortWaveCyclic, // not used + CreateAC97MiniportWaveCyclic, + pAdapterCommon, + ResourceList, + IID_IPortWaveCyclic, + NULL, + &unknownWave); +#else + ntStatus = InstallSubdevice (DeviceObject, + Irp, + L"Wave", + CLSID_PortWavePci, + CLSID_PortWavePci, // not used + CreateAC97MiniportWavePCI, + pAdapterCommon, + ResourceList, + IID_IPortWavePci, + NULL, + &unknownWave); +#endif + + +#if (NTDDI_VERSION >= NTDDI_VISTA) + } +#endif + // print error message. + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("WaveRT and WavePCI miniport installation failed!")); + } + } + + // + // Establish physical connections between filters as shown. + // + // +------+ +------+ + // | Wave | | Topo | + // Capture <---|2 3|<===|x |<--- CD + // | | | | + // Render --->|0 1|===>|y |<--- Line In + // | | | | + // Mic <---|4 5|<===|z |<--- Mic + // +------+ | | + // | |---> Line Out + // +------+ + // + // Note that the pin numbers for the nodes to be connected + // vary depending on the hardware/codec configuration. + // Also, the mic input may or may not be present. + // + // So, + // Do a QI on unknownTopology to get an interface to call + // a method on to get the topology miniport pin IDs. + + if (NT_SUCCESS (ntStatus)) + { + ULONG ulWaveOut, ulWaveIn, ulMicIn; + + // get the pin numbers. + DOUT (DBG_PRINT, ("Connecting topo and wave.")); + ntStatus = pMiniportTopology->GetPhysicalConnectionPins (&ulWaveOut, + &ulWaveIn, &ulMicIn); + + // register wave render connection + if (NT_SUCCESS (ntStatus)) + { + ntStatus = PcRegisterPhysicalConnection (DeviceObject, + unknownWave, + PIN_WAVEOUT_BRIDGE, + unknownTopology, + ulWaveOut); + // print error message. + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("Cannot connect topology and wave miniport" + " (render)!")); + } + } + + + if (NT_SUCCESS (ntStatus)) + { + // register wave capture connection + ntStatus = PcRegisterPhysicalConnection (DeviceObject, + unknownTopology, + ulWaveIn, + unknownWave, + PIN_WAVEIN_BRIDGE); + // print error message. + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("Cannot connect topology and wave miniport" + " (capture)!")); + } + } + + if (NT_SUCCESS (ntStatus)) + { + // register mic capture connection + if (pAdapterCommon->GetPinConfig (PINC_MICIN_PRESENT)) + { + ntStatus = PcRegisterPhysicalConnection (DeviceObject, + unknownTopology, + ulMicIn, + unknownWave, + PIN_MICIN_BRIDGE); + // print error message. + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("Cannot connect topology and wave miniport" + " (MIC)!")); + } + } + } + } + + // + // 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 (); + + // and the AC97 miniport. + if (pMiniportTopology) + pMiniportTopology->Release (); + + + return ntStatus; // whatever this is ... +} + +/***************************************************************************** + * AddDevice + ***************************************************************************** + * This function is called by the operating system when the device is added. + * All adapter drivers can use this code without change. + */ +// disable prefast warning 28152 because +// DO_DEVICE_INITIALIZING is cleared in PcAddAdapterDevice +#ifdef _MSC_VER +#pragma warning(disable:28152) +#endif +NTSTATUS GZCALL AddDevice +( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject +) +{ + PAGED_CODE (); + + DOUT (DBG_PRINT, ("[AddDevice]")); + + + // + // Tell portcls (the class driver) to add the device. + // + return PcAddAdapterDevice (DriverObject, + PhysicalDeviceObject, + (PCPFNSTARTDEVICE)StartDevice, + MAX_MINIPORTS, + 0); +} + +/***************************************************************************** + * 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" DRIVER_INITIALIZE DriverEntry; +extern "C" NTSTATUS GZCALL DriverEntry +( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPathName +) +{ + PAGED_CODE (); + + DOUT (DBG_PRINT, ("[DriverEntry]")); + + // + // Tell the class driver to initialize the driver. + // + NTSTATUS RetValue = PcInitializeAdapterDriver (DriverObject, + RegistryPathName, + (PDRIVER_ADD_DEVICE)AddDevice); + + + return RetValue; +} + + + diff --git a/drivers/wdm/audio/drivers/ac97/adapter.h b/drivers/wdm/audio/drivers/ac97/adapter.h new file mode 100644 index 00000000000..f5d5d4c2892 --- /dev/null +++ b/drivers/wdm/audio/drivers/ac97/adapter.h @@ -0,0 +1,71 @@ +/******************************************************************************** +** Copyright (c) 1998-1999 Microsoft Corporation. All Rights Reserved. +** +** Portions Copyright (c) 1998-1999 Intel Corporation +** +********************************************************************************/ + +/* The file adapter.h was reviewed by LCA in June 2011 and is acceptable for use by Microsoft. */ + +#ifndef _ADAPTER_H_ +#define _ADAPTER_H_ + +#include "shared.h" + +/***************************************************************************** + * Defines + ***************************************************************************** + */ +const ULONG MAX_MINIPORTS = 2; + +/***************************************************************************** + * Functions + ***************************************************************************** + */ +// +// both wave & topology miniport create function prototypes have this form: +// +typedef HRESULT (*PFNCREATEMINIPORT)( + OUT PUNKNOWN * Unknown, + IN REFCLSID ClassId, + IN PUNKNOWN OuterUnknown OPTIONAL, + IN POOL_TYPE PoolType +); + +/***************************************************************************** + * Externals + ***************************************************************************** + */ +extern NTSTATUS CreateAC97MiniportWaveRT +( + OUT PUNKNOWN * Unknown, + IN REFCLSID, + IN PUNKNOWN UnknownOuter OPTIONAL, + IN POOL_TYPE PoolType +); + +extern NTSTATUS CreateAC97MiniportWavePCI +( + OUT PUNKNOWN * Unknown, + IN REFCLSID, + IN PUNKNOWN UnknownOuter OPTIONAL, + IN POOL_TYPE PoolType +); + +extern NTSTATUS CreateAC97MiniportWaveCyclic +( + OUT PUNKNOWN * Unknown, + IN REFCLSID, + IN PUNKNOWN UnknownOuter OPTIONAL, + IN POOL_TYPE PoolType +); + +extern NTSTATUS CreateAC97MiniportTopology +( + OUT PUNKNOWN * Unknown, + IN REFCLSID, + IN PUNKNOWN UnknownOuter OPTIONAL, + IN POOL_TYPE PoolType +); + +#endif //_ADAPTER_H_ diff --git a/drivers/wdm/audio/drivers/ac97/common.cpp b/drivers/wdm/audio/drivers/ac97/common.cpp new file mode 100644 index 00000000000..fd55d166d8c --- /dev/null +++ b/drivers/wdm/audio/drivers/ac97/common.cpp @@ -0,0 +1,2309 @@ +/******************************************************************************** +** Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved. +** +** Portions Copyright (c) 1998-1999 Intel Corporation +** +********************************************************************************/ + +/* The file common.cpp was reviewed by LCA in June 2011 and is acceptable for use by Microsoft. */ + +// Every debug output has "Modulname text" +#define STR_MODULENAME "AC97 Common: " + +#include "common.h" + + +/***************************************************************************** + * Static Members + ***************************************************************************** + */ + +// +// This is the register cache including registry names and default values. The +// first WORD contains the register value and the second WORD contains a flag. +// Currently, we only set SHREG_INVALID if we have to read the register at +// startup (that's true when there is no constant default value for the +// register). Note that we cache the registers only to prevent read access to +// the AC97 CoDec during runtime, because this is slow (40us). +// We only set SHREG_INIT if we want to set the register to default at driver +// startup. If needed, the third field contains the registry name and the +// forth field contains a default value that is used when there is no registry +// entry. +// The flag SHREG_NOCACHE is used when we don't want to cache the register +// at all. This is neccessary for status registers and sample rate registers. +// +tAC97Registers CAC97AdapterCommon::m_stAC97Registers[] = +{ +{0x0000, SHREG_INVALID, NULL, 0}, // AC97REG_RESET +{0x8000, SHREG_INIT, L"MasterVolume", 0x0000}, // AC97REG_MASTER_VOLUME +{0x8000, SHREG_INIT, L"HeadphoneVolume", 0x0000}, // AC97REG_HPHONE_VOLUME +{0x8000, SHREG_INIT, L"MonooutVolume", 0x0000}, // AC97REG_MMONO_VOLUME +{0x0F0F, SHREG_INIT, L"ToneControls", 0x0F0F}, // AC97REG_MASTER_TONE +{0x0000, SHREG_INVALID | + SHREG_INIT, L"BeepVolume", 0x0000}, // AC97REG_BEEP_VOLUME +{0x8008, SHREG_INIT, L"PhoneVolume", 0x8008}, // AC97REG_PHONE_VOLUME +{0x8008, SHREG_INIT, L"MicVolume", 0x8008}, // AC97REG_MIC_VOLUME +{0x8808, SHREG_INIT, L"LineInVolume", 0x0808}, // AC97REG_LINE_IN_VOLUME +{0x8808, SHREG_INIT, L"CDVolume", 0x0808}, // AC97REG_CD_VOLUME +{0x8808, SHREG_INIT, L"VideoVolume", 0x0808}, // AC97REG_VIDEO_VOLUME +{0x8808, SHREG_INIT, L"AUXVolume", 0x0808}, // AC97REG_AUX_VOLUME +{0x8808, SHREG_INIT, L"WaveOutVolume", 0x0808}, // AC97REG_PCM_OUT_VOLUME +{0x0000, SHREG_INIT, L"RecordSelect", 0x0404}, // AC97REG_RECORD_SELECT +{0x8000, SHREG_INIT, L"RecordGain", 0x0000}, // AC97REG_RECORD_GAIN +{0x8000, SHREG_INIT, L"RecordGainMic", 0x0000}, // AC97REG_RECORD_GAIN_MIC +{0x0000, SHREG_INIT, L"GeneralPurpose", 0x0000}, // AC97REG_GENERAL +{0x0000, SHREG_INIT, L"3DControl", 0x0000}, // AC97REG_3D_CONTROL +{0x0000, SHREG_NOCACHE, NULL, 0}, // AC97REG_RESERVED +{0x0000, SHREG_NOCACHE | + SHREG_INIT, L"PowerDown", 0}, // AC97REG_POWERDOWN +// AC97-2.0 registers +{0x0000, SHREG_INVALID, NULL, 0}, // AC97REG_EXT_AUDIO_ID +{0x0000, SHREG_NOCACHE | + SHREG_INIT, L"ExtAudioCtrl", 0x4001}, // AC97REG_EXT_AUDIO_CTRL +{0xBB80, SHREG_NOCACHE, NULL, 0}, // AC97REG_FRONT_SAMPLERATE +{0xBB80, SHREG_NOCACHE, NULL, 0}, // AC97REG_SURROUND_SAMPLERATE +{0xBB80, SHREG_NOCACHE, NULL, 0}, // AC97REG_LFE_SAMPLERATE +{0xBB80, SHREG_NOCACHE, NULL, 0}, // AC97REG_RECORD_SAMPLERATE +{0xBB80, SHREG_NOCACHE, NULL, 0}, // AC97REG_MIC_SAMPLERATE +{0x8080, SHREG_INIT, L"CenterLFEVolume", 0x0000}, // AC97REG_CENTER_LFE_VOLUME +{0x8080, SHREG_INIT, L"SurroundVolume", 0x0000}, // AC97REG_SURROUND_VOLUME +{0x0000, SHREG_NOCACHE, NULL, 0} // AC97REG_RESERVED2 + +// We leave the other values blank. There would be a huge gap with 31 +// elements that are currently unused, and then there would be 2 other +// (used) values, the vendor IDs. We just force a read from the vendor +// IDs in the end of ProbeHWConfig to fill the cache. +}; + + +// +// This is the hardware configuration information. The first struct is for +// nodes, which we default to FALSE. The second struct is for Pins, which +// contains the configuration (FALSE) and the registry string which is the +// reason for making a static struct so we can just fill in the name. +// +tHardwareConfig CAC97AdapterCommon::m_stHardwareConfig = +{ + // Nodes + {{FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, + {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, + {FALSE}}, + // Pins + {{FALSE, L"DisablePCBeep"}, // PINC_PCBEEP_PRESENT + {FALSE, L"DisablePhone"}, // PINC_PHONE_PRESENT + {FALSE, L"DisableMic2"}, // PINC_MIC2_PRESENT + {FALSE, L"DisableVideo"}, // PINC_VIDEO_PRESENT + {FALSE, L"DisableAUX"}, // PINC_AUX_PRESENT + {FALSE, L"DisableHeadphone"}, // PINC_HPOUT_PRESENT + {FALSE, L"DisableMonoOut"}, // PINC_MONOOUT_PRESENT + {FALSE, L"DisableMicIn"}, // PINC_MICIN_PRESENT + {FALSE, L"DisableMic"}, // PINC_MIC_PRESENT + {FALSE, L"DisableLineIn"}, // PINC_LINEIN_PRESENT + {FALSE, L"DisableCD"}, // PINC_CD_PRESENT + {FALSE, L"DisableSurround"}, // PINC_SURROUND_PRESENT + {FALSE, L"DisableCenterLFE"}} // PINC_CENTER_LFE_PRESENT +}; + + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif +/***************************************************************************** + * NewAdapterCommon + ***************************************************************************** + * Create a new adapter common object. + */ +NTSTATUS NewAdapterCommon +( + OUT PUNKNOWN *Unknown, + IN REFCLSID, + IN PUNKNOWN UnknownOuter OPTIONAL, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + IN POOL_TYPE PoolType +) +{ + PAGED_CODE (); + + ASSERT (Unknown); + + DOUT (DBG_PRINT, ("[NewAdapterCommon]")); + + STD_CREATE_BODY_WITH_TAG_(CAC97AdapterCommon,Unknown,UnknownOuter,PoolType, + PoolTag, PADAPTERCOMMON); +} + + +/***************************************************************************** + * CAC97AdapterCommon::Init + ***************************************************************************** + * Initialize the adapter common object -> initialize and probe HW. + * Pass only checked resources. + */ +STDMETHODIMP_(NTSTATUS) CAC97AdapterCommon::Init +( + IN PRESOURCELIST ResourceList, + IN PDEVICE_OBJECT DeviceObject +) +{ + PAGED_CODE (); + + ASSERT (ResourceList); + ASSERT (DeviceObject); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::Init]")); + + // + // Set the topology pointer to NULL. + // + m_Topology = NULL; + + // + // Save the device object + // + m_pDeviceObject = DeviceObject; + + // + // Get the base address for the AC97 codec and bus master. + // + ASSERT (ResourceList->FindTranslatedPort (0)); + m_pCodecBase = (PUSHORT)ResourceList->FindTranslatedPort (0)-> + u.Port.Start.QuadPart; + + ASSERT (ResourceList->FindTranslatedPort (1)); + m_pBusMasterBase = (PUCHAR)ResourceList->FindTranslatedPort (1)-> + u.Port.Start.QuadPart; + + DOUT (DBG_SYSINFO, ("Configuration:\n" + " Bus Master = 0x%p\n" + " Codec = 0x%p", + m_pBusMasterBase, m_pCodecBase)); + + // + // Set m_bDirectRead to TRUE so that all AC97 register read and + // writes are going directly to the HW + // + m_bDirectRead = TRUE; + + // + // Initialize the hardware. + // + ntStatus = InitAC97 (); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // + // Probe hardware configuration + // + ntStatus = ProbeHWConfig (); + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("Probing of hardware configuration failed!")); + return ntStatus; + } + + // + // Now, every AC97 read access goes to the cache. + // + m_bDirectRead = FALSE; + + // + // Restore the AC97 registers now. + // +#if (DBG) + DumpConfig (); +#endif + ntStatus = SetAC97Default (); + + // + // Initialize the device state. + // + m_PowerState = PowerDeviceD0; + + return ntStatus; +} + + +/***************************************************************************** + * CAC97AdapterCommon::~CAC97AdapterCommon + ***************************************************************************** + * Destructor. + */ +CAC97AdapterCommon::~CAC97AdapterCommon () +{ + PAGED_CODE (); + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::~CAC97AdapterCommon]")); +} + + +#if (DBG) +/***************************************************************************** + * CAC97AdapterCommon::DumpConfig + ***************************************************************************** + * Dumps the HW configuration for the AC97 codec. + */ +void CAC97AdapterCommon::DumpConfig (void) +{ + PAGED_CODE (); + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::DumpConfig]")); + + // + // Print debug output for MICIN. + // + if (GetPinConfig (PINC_MICIN_PRESENT)) + { + DOUT (DBG_PROBE, ("MICIN found")); + } + else + { + DOUT (DBG_PROBE, ("No MICIN found")); + } + + // + // Print debug output for tone controls. + // + if (GetNodeConfig (NODEC_TONE_PRESENT)) + { + DOUT (DBG_PROBE, ("Tone controls found")); + } + else + { + DOUT (DBG_PROBE, ("No tone controls found")); + } + + // + // Print debug output for mono out. + // + if (!GetPinConfig (PINC_MONOOUT_PRESENT)) + { + DOUT (DBG_PROBE, ("No mono out found")); + } + + // + // Print debug output for headphones. + // + if (!GetPinConfig (PINC_HPOUT_PRESENT)) + { + DOUT (DBG_PROBE, ("No headphone out found")); + } + + // + // Print debug output for loudness. + // + if (GetNodeConfig (NODEC_LOUDNESS_PRESENT)) + { + DOUT (DBG_PROBE, ("Loudness found")); + } + else + { + DOUT (DBG_PROBE, ("No Loudness found")); + } + + // + // Print debug output for 3D. + // + if (GetNodeConfig (NODEC_3D_PRESENT)) + { + DOUT (DBG_PROBE, ("3D controls found")); + } + else + { + DOUT (DBG_PROBE, ("No 3D controls found")); + } + + // + // Print debug output for pc beep. + // + if (GetPinConfig (PINC_PCBEEP_PRESENT)) + { + DOUT (DBG_PROBE, ("PC beep found")); + } + else + { + DOUT (DBG_PROBE, ("No PC beep found")); + } + + // + // Print debug output for phone line (or mono line input). + // + if (GetPinConfig (PINC_PHONE_PRESENT)) + { + DOUT (DBG_PROBE, ("Phone found")); + } + else + { + DOUT (DBG_PROBE, ("No Phone found")); + } + + // + // Print debug output for video. + // + if (GetPinConfig (PINC_VIDEO_PRESENT)) + { + DOUT (DBG_PROBE, ("Video in found")); + } + else + { + DOUT (DBG_PROBE, ("No Video in found")); + } + + // + // Print debug output for AUX. + // + if (GetPinConfig (PINC_AUX_PRESENT)) + { + DOUT (DBG_PROBE, ("AUX in found")); + } + else + { + DOUT (DBG_PROBE, ("No AUX in found")); + } + + // + // Print debug output for second miorophone. + // + if (GetPinConfig (PINC_MIC2_PRESENT)) + { + DOUT (DBG_PROBE, ("MIC2 found")); + } + else + { + DOUT (DBG_PROBE, ("No MIC2 found")); + } + + // + // Print debug output for 3D stuff. + // + if (GetNodeConfig (NODEC_3D_PRESENT)) + { + if (GetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE)) + { + DOUT (DBG_PROBE, ("Adjustable 3D center control found")); + } + if (GetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE)) + { + DOUT (DBG_PROBE, ("Nonadjustable 3D depth control found")); + } + } + + // + // Print debug output for quality of master volume. + // + if (GetNodeConfig (NODEC_6BIT_MASTER_VOLUME)) + { + DOUT (DBG_PROBE, ("6bit master out found")); + } + else + { + DOUT (DBG_PROBE, ("5bit master out found")); + } + + // + // Print debug output for quality of headphones volume. + // + if (GetPinConfig (PINC_HPOUT_PRESENT)) + { + if (GetNodeConfig (NODEC_6BIT_HPOUT_VOLUME)) + { + DOUT (DBG_PROBE, ("6bit headphone out found")); + } + else + { + DOUT (DBG_PROBE, ("5bit headphone out found")); + } + } + + // + // Print debug output for quality of mono out volume. + // + if (GetPinConfig (PINC_MONOOUT_PRESENT)) + { + if (GetNodeConfig (NODEC_6BIT_MONOOUT_VOLUME)) + { + DOUT (DBG_PROBE, ("6bit mono out found")); + } + else + { + DOUT (DBG_PROBE, ("5bit mono out found")); + } + } + + // + // Print sample rate information. + // + if (GetNodeConfig (NODEC_PCM_VARIABLERATE_SUPPORTED)) + { + DOUT (DBG_PROBE, ("PCM variable sample rate supported")); + } + else + { + DOUT (DBG_PROBE, ("only 48KHz PCM supported")); + } + + // + // Print double rate information. + // + if (GetNodeConfig (NODEC_PCM_DOUBLERATE_SUPPORTED)) + { + DOUT (DBG_PROBE, ("PCM double sample rate supported")); + } + + // + // Print mic rate information. + // + if (GetNodeConfig (NODEC_MIC_VARIABLERATE_SUPPORTED)) + { + DOUT (DBG_PROBE, ("MIC variable sample rate supported")); + } + else + { + DOUT (DBG_PROBE, ("only 48KHz MIC supported")); + } + + // print DAC information + if (GetNodeConfig (NODEC_CENTER_DAC_PRESENT)) + { + DOUT (DBG_PROBE, ("center DAC found")); + } + if (GetNodeConfig (NODEC_SURROUND_DAC_PRESENT)) + { + DOUT (DBG_PROBE, ("surround DAC found")); + } + if (GetNodeConfig (NODEC_LFE_DAC_PRESENT)) + { + DOUT (DBG_PROBE, ("LFE DAC found")); + } +} +#endif + +/***************************************************************************** + * CAC97AdapterCommon::NonDelegatingQueryInterface + ***************************************************************************** + * Obtains an interface. This function works just like a COM QueryInterface + * call and is used if the object is not being aggregated. + * We basically just check any GUID we know and return this object in case we + * know it. + */ +STDMETHODIMP_(NTSTATUS) CAC97AdapterCommon::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +{ + PAGED_CODE (); + + ASSERT (Object); + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::NonDelegatingQueryInterface]")); + + // Is it IID_IUnknown? + if (IsEqualGUIDAligned (Interface, IID_IUnknown)) + { + *Object = (PVOID)(PUNKNOWN)(PADAPTERCOMMON)this; + } + else + // or IID_IAC97AdapterCommon ... + if (IsEqualGUIDAligned (Interface, IID_IAC97AdapterCommon)) + { + *Object = (PVOID)(PADAPTERCOMMON)this; + } + else + // or IID_IAdapterPowerManagement ... + if (IsEqualGUIDAligned (Interface, IID_IAdapterPowerManagement)) + { + *Object = (PVOID)(PADAPTERPOWERMANAGEMENT)this; + } + else + { + // nothing found, must be an unknown interface. + *Object = NULL; + return STATUS_INVALID_PARAMETER; + } + + // + // We reference the interface for the caller. + // + ((PUNKNOWN)*Object)->AddRef (); + return STATUS_SUCCESS; +} + +/***************************************************************************** + * CAC97AdapterCommon::InitAC97 + ***************************************************************************** + * Initialize the AC97 (without hosing the modem if it got installed first). + */ +NTSTATUS CAC97AdapterCommon::InitAC97 (void) +{ + PAGED_CODE (); + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::InitAC97]")); + + // + // First check if there is an AC link to the primary CoDec. + // + NTSTATUS ntStatus = PrimaryCodecReady (); + if (NT_SUCCESS (ntStatus)) + { + // + // Second, reset this primary CoDec; If this is a AMC97 CoDec, only + // the audio registers are reset. If this is a MC97 CoDec, the CoDec + // should ignore the reset (according to the spec). + // + WriteCodecRegister (AC97REG_RESET, 0x00, 0xFFFF); + + ntStatus = PowerUpCodec (); + } + else + { + DOUT (DBG_ERROR, ("Initialization of AC97 CoDec failed.")); + } + + return ntStatus; +} + +/***************************************************************************** + * CAC97AdapterCommon::Check6thBitSupport + ***************************************************************************** + * Probes for 6th bit volume control support. + * The passed parameters are the AC97 register that has the volume control and + * the node config that should be set in this case. + */ +NTSTATUS CAC97AdapterCommon::Check6thBitSupport +( + IN AC97Register AC97Reg, + IN TopoNodeConfig Config +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus; + WORD wCodecReg; + WORD wOriginal; + + // Read the current value. + ntStatus = ReadCodecRegister (AC97Reg, &wOriginal); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // Write the 6th bit; for mono controls we write 0x20, for stereo + // controls 0x2020. + ntStatus = WriteCodecRegister (AC97Reg, + (AC97Reg == AC97REG_MMONO_VOLUME) ? 0x0020 : 0x2020, 0xFFFF); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // And read back. + ntStatus = ReadCodecRegister (AC97Reg, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // Check return. For mono 0x20 and for stereo 0x2020. + if (((wCodecReg & 0x0020) && (AC97Reg == AC97REG_MMONO_VOLUME)) || + (wCodecReg & 0x2020)) + { + SetNodeConfig (Config, TRUE); + } + else + { + SetNodeConfig (Config, FALSE); + } + + // Restore original value. + WriteCodecRegister (AC97Reg, wOriginal, 0xFFFF); + + return ntStatus; +} + +/***************************************************************************** + * CAC97AdapterCommon::ProbeHWConfig + ***************************************************************************** + * Probes the hardware configuration. + * If this function returns with an error, then the configuration is not + * complete! Probing the registers is done by reading them (and comparing with + * the HW default value) or when the default is unknown, writing to them and + * reading back + restoring. + * Additionally, we read the registry so that a HW vendor can overwrite (means + * disable) found registers in case the adapter (e.g. video) is not visible to + * the user (he can't plug in a video audio there). + * + * This is a very long function with all of the error checking! + */ +NTSTATUS CAC97AdapterCommon::ProbeHWConfig (void) +{ + PAGED_CODE (); + + NTSTATUS ntStatus = STATUS_SUCCESS; + DWORD dwGlobalStatus; + WORD wCodecID; + WORD wCodecReg; + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::ProbeHWConfig]")); + + // + // Wait for the whatever 97 to complete reset and establish a link. + // + ntStatus = PrimaryCodecReady (); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // + // Master volume is one of the supported registers on an AC97 + // + ntStatus = ReadCodecRegister (AC97REG_MASTER_VOLUME, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // Default is x8000. + if (wCodecReg != 0x8000) + return STATUS_NO_SUCH_DEVICE; + + // + // This gives us information about the AC97 CoDec + // + ntStatus = ReadCodecRegister (AC97REG_RESET, &wCodecID); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // + // Fill out the configuration stuff. + // + + SetPinConfig (PINC_MICIN_PRESENT, wCodecID & 0x0001); + + // Check if OEM wants to disable MIC record line. + if (DisableAC97Pin (PINC_MICIN_PRESENT)) + SetPinConfig (PINC_MICIN_PRESENT, FALSE); + + // If we still have MIC record line, enable the DAC in ext. audio register. + if (GetPinConfig (PINC_MICIN_PRESENT)) + // Enable ADC MIC. + WriteCodecRegister (AC97REG_EXT_AUDIO_CTRL, 0, 0x4000); + else + // Disable ADC MIC. + WriteCodecRegister (AC97REG_EXT_AUDIO_CTRL, 0x4000, 0x4000); + + // + // Continue setting configuration information. + // + + SetNodeConfig (NODEC_TONE_PRESENT, wCodecID & 0x0004); + SetNodeConfig (NODEC_SIMUL_STEREO_PRESENT, wCodecID & 0x0008); + SetPinConfig (PINC_HPOUT_PRESENT, wCodecID & 0x0010); + + // Check if OEM wants to disable headphone output. + if (DisableAC97Pin (PINC_HPOUT_PRESENT)) + SetPinConfig (PINC_HPOUT_PRESENT, FALSE); + + SetNodeConfig (NODEC_LOUDNESS_PRESENT, wCodecID & 0x0020); + SetNodeConfig (NODEC_3D_PRESENT, wCodecID & 0x7C00); + + // + // Test for the input pins that are always there but could be disabled + // by the HW vender + // + + // Check if OEM wants to disable mic input. + SetPinConfig (PINC_MIC_PRESENT, !DisableAC97Pin (PINC_MIC_PRESENT)); + + // Check if OEM wants to disable line input. + SetPinConfig (PINC_LINEIN_PRESENT, !DisableAC97Pin (PINC_LINEIN_PRESENT)); + + // Check if OEM wants to disable CD input. + SetPinConfig (PINC_CD_PRESENT, !DisableAC97Pin (PINC_CD_PRESENT)); + + + // + // For the rest, we have to probe the registers. + // + + // + // Test for Mono out. + // + ntStatus = ReadCodecRegister (AC97REG_MMONO_VOLUME, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // Default is x8000. + SetPinConfig (PINC_MONOOUT_PRESENT, (wCodecReg == 0x8000)); + + // Check if OEM wants to disable mono output. + if (DisableAC97Pin (PINC_MONOOUT_PRESENT)) + SetPinConfig (PINC_MONOOUT_PRESENT, FALSE); + + // + // Test for PC beeper support. + // + ntStatus = ReadCodecRegister (AC97REG_BEEP_VOLUME, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // default is x0 or x8000. If it's 0x8000 then we know for sure that the + // CoDec has a PcBeep, otherwise we have to check the register + if (wCodecReg == 0x8000) + SetPinConfig (PINC_PCBEEP_PRESENT, TRUE); + else if (!wCodecReg) + { + // mute the pc beeper. + ntStatus = WriteCodecRegister (AC97REG_BEEP_VOLUME, 0x8000, 0xFFFF); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // read back + ntStatus = ReadCodecRegister (AC97REG_BEEP_VOLUME, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + if (wCodecReg == 0x8000) + { + // yep, we have support. + SetPinConfig (PINC_PCBEEP_PRESENT, TRUE); + // reset to default value. + WriteCodecRegister (AC97REG_BEEP_VOLUME, 0x0, 0xFFFF); + } + else + // nope, not present + SetPinConfig (PINC_PCBEEP_PRESENT, FALSE); + } + else + // any other value then 0x0 and 0x8000. + SetPinConfig (PINC_PCBEEP_PRESENT, FALSE); + + // Check if OEM wants to disable beeper support. + if (DisableAC97Pin (PINC_PCBEEP_PRESENT)) + SetPinConfig (PINC_PCBEEP_PRESENT, FALSE); + + // + // Test for phone support. + // + ntStatus = ReadCodecRegister (AC97REG_PHONE_VOLUME, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // Default is x8008. + SetPinConfig (PINC_PHONE_PRESENT, (wCodecReg == 0x8008)); + + // Check if OEM wants to disable phone input. + if (DisableAC97Pin (PINC_PHONE_PRESENT)) + SetPinConfig (PINC_PHONE_PRESENT, FALSE); + + // + // Test for video support. + // + ntStatus = ReadCodecRegister (AC97REG_VIDEO_VOLUME, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // Default is x8808. + SetPinConfig (PINC_VIDEO_PRESENT, (wCodecReg == 0x8808)); + + // Check if OEM wants to disable video input. + if (DisableAC97Pin (PINC_VIDEO_PRESENT)) + SetPinConfig (PINC_VIDEO_PRESENT, FALSE); + + // + // Test for Aux support. + // + ntStatus = ReadCodecRegister (AC97REG_AUX_VOLUME, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // Default is 0x8808. + SetPinConfig (PINC_AUX_PRESENT, (wCodecReg == 0x8808)); + + // Check if OEM wants to disable aux input. + if (DisableAC97Pin (PINC_AUX_PRESENT)) + SetPinConfig (PINC_AUX_PRESENT, FALSE); + + // + // Test for Mic2 source. + // + ntStatus = ReadCodecRegister (AC97REG_GENERAL, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // Test for Mic2 select bit. + if (wCodecReg & 0x0100) + SetPinConfig (PINC_MIC2_PRESENT, TRUE); + else + { + // Select Mic2 as source. + ntStatus = WriteCodecRegister (AC97REG_GENERAL, 0x0100, 0x0100); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // Read back. + ntStatus = ReadCodecRegister (AC97REG_GENERAL, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + if (wCodecReg & 0x0100) + { + // Yep, we have support so set it to the default value. + SetPinConfig (PINC_MIC2_PRESENT, TRUE); + // reset to default value. + WriteCodecRegister (AC97REG_GENERAL, 0, 0x0100); + } + else + SetPinConfig (PINC_MIC2_PRESENT, FALSE); + } + + // Check if OEM wants to disable mic2 input. + if (DisableAC97Pin (PINC_MIC2_PRESENT)) + SetPinConfig (PINC_MIC2_PRESENT, FALSE); + + // + // Test the 3D controls. + // + if (GetNodeConfig (NODEC_3D_PRESENT)) + { + // + // First test for fixed 3D controls. Write default value ... + // + ntStatus = WriteCodecRegister (AC97REG_3D_CONTROL, 0, 0xFFFF); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // Read 3D register. Default is 0 when adjustable, otherwise it is + // a fixed value. + ntStatus = ReadCodecRegister (AC97REG_3D_CONTROL, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // + // Check center and depth separately. + // + + // For center + SetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE, !(wCodecReg & 0x0F00)); + + // For depth + SetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE, !(wCodecReg & 0x000F)); + + // + // Test for adjustable controls. + // + WriteCodecRegister (AC97REG_3D_CONTROL, 0x0A0A, 0xFFFF); + + // Read 3D register. Now it should be 0x0A0A for adjustable controls, + // otherwise it is a fixed control or simply not there. + ReadCodecRegister (AC97REG_3D_CONTROL, &wCodecReg); + + // Restore the default value + WriteCodecRegister (AC97REG_3D_CONTROL, 0, 0xFFFF); + + // Check the center control for beeing adjustable + if (GetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE) && + (wCodecReg & 0x0F00) != 0x0A00) + { + SetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE, FALSE); + } + + // Check the depth control for beeing adjustable + if (GetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE) && + (wCodecReg & 0x000F) != 0x000A) + { + SetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE, FALSE); + } + } + + // + // Check for 6th bit support in volume controls. To check the 6th bit, + // we first have to write a value (with 6th bit set) and then read it + // back. After that, we should restore the register to its default value. + // + + // + // Start with the master volume. + // + Check6thBitSupport (AC97REG_MASTER_VOLUME, NODEC_6BIT_MASTER_VOLUME); + + // + // Check for a headphone volume control. + // + if (GetPinConfig (PINC_HPOUT_PRESENT)) + { + Check6thBitSupport (AC97REG_HPHONE_VOLUME, NODEC_6BIT_HPOUT_VOLUME); + } + + // + // Mono out there? + // + if (GetPinConfig (PINC_MONOOUT_PRESENT)) + { + Check6thBitSupport (AC97REG_MMONO_VOLUME, NODEC_6BIT_MONOOUT_VOLUME); + } + + // + // Get extended AC97 V2.0 information + // + ntStatus = ReadCodecRegister (AC97REG_EXT_AUDIO_ID, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // + // Store the information + // + SetNodeConfig (NODEC_PCM_VARIABLERATE_SUPPORTED, wCodecReg & 0x0001); + SetNodeConfig (NODEC_PCM_DOUBLERATE_SUPPORTED, wCodecReg & 0x0002); + SetNodeConfig (NODEC_MIC_VARIABLERATE_SUPPORTED, wCodecReg & 0x0008); + SetNodeConfig (NODEC_CENTER_DAC_PRESENT, wCodecReg & 0x0040); + SetNodeConfig (NODEC_SURROUND_DAC_PRESENT, wCodecReg & 0x0080); + SetNodeConfig (NODEC_LFE_DAC_PRESENT, wCodecReg & 0x0100); + + // + // In case we have some features get some more information and program + // the codec. + // + if (wCodecReg) + { + // + // Enable variable sample rate in the control register and disable + // double rate. Also enable all DACs. + // + WriteCodecRegister (AC97REG_EXT_AUDIO_CTRL, wCodecReg & 0x0009, 0x380B); + + // + // Check for codecs that have only one sample rate converter. These + // codecs will stick registers AC97REG_FRONT_SAMPLERATE and + // AC97REG_RECORD_SAMPLERATE together. + // + if (GetNodeConfig (NODEC_PCM_VARIABLERATE_SUPPORTED)) + { + // The default of the sample rate registers should be 0xBB80. + WriteCodecRegister (AC97REG_FRONT_SAMPLERATE, 0xBB80, 0xFFFF); + + // Write 44.1KHz into record VSR, then check playback again. + WriteCodecRegister (AC97REG_RECORD_SAMPLERATE, 0xAC44, 0xFFFF); + ntStatus = ReadCodecRegister (AC97REG_FRONT_SAMPLERATE, &wCodecReg); + WriteCodecRegister (AC97REG_RECORD_SAMPLERATE, 0xBB80, 0xFFFF); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // + // Set the flag accordingly + // + SetNodeConfig (NODEC_PCM_VSR_INDEPENDENT_RATES, (wCodecReg == 0xBB80)); + } + + // + // Check multichanel support on the AC97. + // + if (GetNodeConfig (NODEC_SURROUND_DAC_PRESENT)) + { + dwGlobalStatus = ReadBMControlRegister32 (GLOB_STA); + + // + // Codec supports >2 chanel, does AC97 too? + // + if ((GetNodeConfig (NODEC_CENTER_DAC_PRESENT) || + GetNodeConfig (NODEC_LFE_DAC_PRESENT)) && + (dwGlobalStatus & GLOB_STA_MC6)) + { + SetPinConfig (PINC_CENTER_LFE_PRESENT, TRUE); + } + else + { + SetPinConfig (PINC_CENTER_LFE_PRESENT, FALSE); + } + + // + // Do we support at least 4 channels? + // + SetPinConfig (PINC_SURROUND_PRESENT, (dwGlobalStatus & GLOB_STA_MC4)); + } + else + { + // + // Only 2 channel (stereo) support. + // + SetPinConfig (PINC_CENTER_LFE_PRESENT, FALSE); + SetPinConfig (PINC_SURROUND_PRESENT, FALSE); + } + } + + // Check if OEM wants to disable surround output. + if (DisableAC97Pin (PINC_SURROUND_PRESENT)) + SetPinConfig (PINC_SURROUND_PRESENT, FALSE); + + // Check if OEM wants to disable center and LFE output. + if (DisableAC97Pin (PINC_CENTER_LFE_PRESENT)) + SetPinConfig (PINC_CENTER_LFE_PRESENT, FALSE); + + // + // Check the 6th bit support for the additional channels. + // + if (GetPinConfig (PINC_SURROUND_PRESENT)) + Check6thBitSupport (AC97REG_SURROUND_VOLUME, NODEC_6BIT_SURROUND_VOLUME); + + if (GetPinConfig (PINC_CENTER_LFE_PRESENT)) + Check6thBitSupport (AC97REG_CENTER_LFE_VOLUME, NODEC_6BIT_CENTER_LFE_VOLUME); + + // + // We read these registers because they are dependent on the codec. + // + ReadCodecRegister (AC97REG_VENDOR_ID1, &wCodecReg); + ReadCodecRegister (AC97REG_VENDOR_ID2, &wCodecReg); + + return STATUS_SUCCESS; +} + + +/***************************************************************************** + * CAC97AdapterCommon::AcquireCodecSemiphore + ***************************************************************************** + * Acquires the AC97 semiphore. This can not be called at dispatch level + * because it can timeout if a lower IRQL thread has the semaphore. + */ +NTSTATUS CAC97AdapterCommon::AcquireCodecSemiphore () +{ + PAGED_CODE (); + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::AcquireCodecSemiphore]")); + + ULONG ulCount = 0; + while (READ_PORT_UCHAR (m_pBusMasterBase + CAS) & CAS_CAS) + { + // + // Do we want to give up?? + // + if (ulCount++ > 100) + { + DOUT (DBG_ERROR, ("Cannot acquire semaphore.")); + return STATUS_IO_TIMEOUT; + } + + // + // Let's wait a little, 40us and then try again. + // + KeStallExecutionProcessor (40L); + } + + return STATUS_SUCCESS; +} + + +/***************************************************************************** + * CAC97AdapterCommon::ReadCodecRegister + ***************************************************************************** + * Reads a AC97 register. Don't call at PASSIVE_LEVEL. + */ +STDMETHODIMP_(NTSTATUS) CAC97AdapterCommon::ReadCodecRegister +( + _In_range_(0, AC97REG_INVALID) AC97Register reg, + _Out_ PWORD wData +) +{ + PAGED_CODE (); + + ASSERT (wData); + ASSERT (reg < AC97REG_INVALID); // audio can only be in the primary codec + _Analysis_assume_(reg < AC97REG_INVALID); + + NTSTATUS ntStatus; + ULONG Status; + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::ReadCodecRegister]")); + + // + // Check if we have to access the HW directly. + // + if (m_bDirectRead || (m_stAC97Registers[reg].wFlags & SHREG_INVALID) || + (m_stAC97Registers[reg].wFlags & SHREG_NOCACHE)) + { + // + // Grab the codec access semiphore. + // + ntStatus = AcquireCodecSemiphore (); + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("ReadCodecRegister couldn't acquire the semiphore" + " for reg. %s", reg <= AC97REG_RESERVED2 ? RegStrings[reg] : + reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" : + reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : + "REG_INVALID")); + return ntStatus; + } + + // + // Read the data. + // + *wData = READ_PORT_USHORT (m_pCodecBase + reg); + + // + // Check to see if the read was successful. + // + Status = READ_PORT_ULONG ((PULONG)(m_pBusMasterBase + GLOB_STA)); + if (Status & GLOB_STA_RCS) + { + // + // clear the timeout bit + // + WRITE_PORT_ULONG ((PULONG)(m_pBusMasterBase + GLOB_STA), Status); + *wData = 0; + DOUT (DBG_ERROR, ("ReadCodecRegister timed out for register %s", + reg <= AC97REG_RESERVED2 ? RegStrings[reg] : + reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" : + reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : + "REG_INVALID")); + return STATUS_IO_TIMEOUT; + } + + // + // Clear invalid flag + // + m_stAC97Registers[reg].wCache = *wData; + m_stAC97Registers[reg].wFlags &= ~SHREG_INVALID; + + DOUT (DBG_REGS, ("AC97READ: %s = 0x%04x (HW)", + reg <= AC97REG_RESERVED2 ? RegStrings[reg] : + reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" : + reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : + "REG_INVALID", *wData)); + } + else + { + // + // Otherwise, use the value in the cache. + // + *wData = m_stAC97Registers[reg].wCache; + DOUT (DBG_REGS, ("AC97READ: %s = 0x%04x (C)", + reg <= AC97REG_RESERVED2 ? RegStrings[reg] : + reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" : + reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : + "REG_INVALID", *wData)); + } + + return STATUS_SUCCESS; +} + + +/***************************************************************************** + * CAC97AdapterCommon::WriteCodecRegister + ***************************************************************************** + * Writes to a AC97 register. This can only be done at passive level because + * the AcquireCodecSemiphore call could fail! + */ +STDMETHODIMP_(NTSTATUS) CAC97AdapterCommon::WriteCodecRegister +( + _In_range_(0, AC97REG_INVALID) AC97Register reg, + _In_ WORD wData, + _In_ WORD wMask +) +{ + PAGED_CODE (); + + ASSERT (reg < AC97REG_INVALID); // audio can only be in the primary codec + + WORD TempData = 0; + NTSTATUS ntStatus = STATUS_SUCCESS; + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::WriteCodecRegister]")); + + // + // No mask? Could happen when you try to prg. left channel of a + // mono volume. + // + if (!wMask) + return STATUS_SUCCESS; + + // + // Check to see if we are only writing specific bits. If so, we want + // to leave some bits in the register alone. + // + if (wMask != 0xffff) + { + // + // Read the current register contents. + // + ntStatus = ReadCodecRegister (reg, &TempData); + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("WriteCodecRegiser read for mask failed")); + return ntStatus; + } + + // + // Do the masking. + // + TempData &= ~wMask; + TempData |= (wMask & wData); + } + else + { + TempData = wData; + } + + + // + // Grab the codec access semiphore. + // + ntStatus = AcquireCodecSemiphore (); + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("WriteCodecRegister failed for register %s", + reg <= AC97REG_RESERVED2 ? RegStrings[reg] : + reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" : + reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : "REG_INVALID")); + return ntStatus; + } + + // + // Write the data. + // + WRITE_PORT_USHORT (m_pCodecBase + reg, TempData); + + // + // Update cache. + // + _Analysis_assume_(reg < AC97REG_INVALID); + m_stAC97Registers[reg].wCache = TempData; + + DOUT (DBG_REGS, ("AC97WRITE: %s -> 0x%04x", + reg <= AC97REG_RESERVED2 ? RegStrings[reg] : + reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" : + reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : + "REG_INVALID", TempData)); + + + return STATUS_SUCCESS; +} + + +/***************************************************************************** + * CAC97AdapterCommon::PrimaryCodecReady + ***************************************************************************** + * Checks whether the primary codec is present and ready. This may take + * awhile if we are bringing it up from a cold reset so give it a second + * before giving up. + */ +NTSTATUS CAC97AdapterCommon::PrimaryCodecReady (void) +{ + PAGED_CODE (); + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::PrimaryCodecReady]")); + + + // + // Enable the AC link and raise the reset line. + // + DWORD dwRegValue = ReadBMControlRegister32 (GLOB_CNT); + + // If someone enabled GPI Interrupt Enable, then he hopefully handles that + // too. + dwRegValue = (dwRegValue | GLOB_CNT_COLD) & ~(GLOB_CNT_ACLOFF | GLOB_CNT_PRIE); + WriteBMControlRegister (GLOB_CNT, dwRegValue); + + // + // Wait for the Codec to be ready. + // + ULONG WaitCycles = 200; + LARGE_INTEGER WaitTime; + + WaitTime.QuadPart = (-50000); // wait 5000us (5ms) relative + + do + { + if (READ_PORT_ULONG ((PULONG)(m_pBusMasterBase + GLOB_STA)) & + GLOB_STA_PCR) + { + return STATUS_SUCCESS; + } + + KeDelayExecutionThread (KernelMode, FALSE, &WaitTime); + } while (WaitCycles--); + + DOUT (DBG_ERROR, ("PrimaryCodecReady timed out!")); + return STATUS_IO_TIMEOUT; +} + + +/***************************************************************************** + * CAC97AdapterCommon::PowerUpCodec + ***************************************************************************** + * Sets the Codec to the highest power state and waits until the Codec reports + * that the power state is reached. + */ +NTSTATUS CAC97AdapterCommon::PowerUpCodec (void) +{ + PAGED_CODE (); + + WORD wCodecReg; + NTSTATUS ntStatus; + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::PowerUpCodec]")); + + // + // Power up the Codec. + // + WriteCodecRegister (AC97REG_POWERDOWN, 0x00, 0xFFFF); + + // + // Wait for the Codec to be powered up. + // + ULONG WaitCycles = 200; + LARGE_INTEGER WaitTime; + + WaitTime.QuadPart = (-50000); // wait 5000us (5ms) relative + + do + { + // + // Read the power management register. + // + ntStatus = ReadCodecRegister (AC97REG_POWERDOWN, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + { + wCodecReg = 0; // Will cause an error. + break; + } + + // + // Check the power state. Should be ready. + // + if ((wCodecReg & 0x0f) == 0x0f) + break; + + // + // Let's wait a little, 5ms and then try again. + // + KeDelayExecutionThread (KernelMode, FALSE, &WaitTime); + } while (WaitCycles--); + + // Check if we timed out. + if ((wCodecReg & 0x0f) != 0x0f) + { + DOUT (DBG_ERROR, ("PowerUpCodec timed out. CoDec not powered up.")); + ntStatus = STATUS_DEVICE_NOT_READY; + } + + return ntStatus; +} + + +/***************************************************************************** + * CAC97AdapterCommon::ProgramSampleRate + ***************************************************************************** + * Programs the sample rate. If the rate cannot be programmed, the routine + * restores the register and returns STATUS_UNSUCCESSFUL. + * We don't handle double rate sample rates here, because the Intel AC97 con- + * troller cannot serve CoDecs with double rate or surround sound. If you want + * to modify this driver for another AC97 controller, then you might want to + * change this function too. + */ +STDMETHODIMP_(NTSTATUS) CAC97AdapterCommon::ProgramSampleRate +( + IN AC97Register Register, + IN DWORD dwSampleRate +) +{ + PAGED_CODE (); + + WORD wOldRateReg, wCodecReg; + NTSTATUS ntStatus; + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::ProgramSampleRate]")); + + // + // Check if we support variable sample rate. + // + switch(Register) + { + case AC97REG_MIC_SAMPLERATE: + // + // Variable sample rate supported? + // + if (GetNodeConfig (NODEC_MIC_VARIABLERATE_SUPPORTED)) + { + // Range supported? + if (dwSampleRate > 48000ul) + { + // Not possible. + DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate)); + return STATUS_NOT_SUPPORTED; + } + } + else + { + // Only 48000KHz possible. + if (dwSampleRate != 48000ul) + { + DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate)); + return STATUS_NOT_SUPPORTED; + } + + return STATUS_SUCCESS; + } + break; + + case AC97REG_FRONT_SAMPLERATE: + case AC97REG_SURROUND_SAMPLERATE: + case AC97REG_LFE_SAMPLERATE: + case AC97REG_RECORD_SAMPLERATE: + // + // Variable sample rate supported? + // + if (GetNodeConfig (NODEC_PCM_VARIABLERATE_SUPPORTED)) + { + // + // Check range supported + // + if (dwSampleRate > 48000ul) + { + DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate)); + return STATUS_NOT_SUPPORTED; + } + } + else + { + // Only 48KHz possible. + if (dwSampleRate != 48000ul) + { + DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate)); + return STATUS_NOT_SUPPORTED; + } + + return STATUS_SUCCESS; + } + break; + + default: + DOUT (DBG_ERROR, ("Invalid sample rate register!")); + return STATUS_UNSUCCESSFUL; + } + + + // + // Save the old sample rate register. + // + ntStatus = ReadCodecRegister (Register, &wOldRateReg); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // + // program the rate. + // + ntStatus = WriteCodecRegister (Register, (WORD)dwSampleRate, 0xFFFF); + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("Cannot program sample rate.")); + return ntStatus; + } + + // + // Read it back. + // + ntStatus = ReadCodecRegister (Register, &wCodecReg); + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("Cannot read sample rate.")); + return ntStatus; + } + + // + // Validate. + // + if (wCodecReg != dwSampleRate) + { + // + // restore sample rate and ctrl register. + // + WriteCodecRegister (Register, wOldRateReg, 0xFFFF); + + DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate)); + return STATUS_NOT_SUPPORTED; + } + + DOUT (DBG_VSR, ("Samplerate changed to %d.", dwSampleRate)); + return STATUS_SUCCESS; +} + + +/***************************************************************************** + * CAC97AdapterCommon::PowerChangeState + ***************************************************************************** + * Change power state for the device. We handle the codec, PowerChangeNotify + * in the wave miniport handles the DMA registers. + */ +STDMETHODIMP_(void) CAC97AdapterCommon::PowerChangeState +( + _In_ POWER_STATE NewState +) +{ + PAGED_CODE (); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::PowerChangeNotify]")); + + // + // Check to see if this is the current power state. + // + if (NewState.DeviceState == m_PowerState) + { + DOUT (DBG_POWER, ("New device state equals old state.")); + return; + } + + // + // Check the new device state. + // + if ((NewState.DeviceState < PowerDeviceD0) || + (NewState.DeviceState > PowerDeviceD3)) + { + DOUT (DBG_ERROR, ("Unknown device state: D%d.", + (ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0)); + return; + } + + DOUT (DBG_POWER, ("Changing state to D%d.", (ULONG)NewState.DeviceState - + (ULONG)PowerDeviceD0)); + + // + // Switch on new state. + // + switch (NewState.DeviceState) + { + case PowerDeviceD0: + // + // If we are coming from D2 or D3 we have to restore the registers cause + // there might have been a power loss. + // + if ((m_PowerState == PowerDeviceD3) || (m_PowerState == PowerDeviceD2)) + { + // + // Reset AD3 to indicate that we are now awake. + // Because the system has only one power irp at a time, we are sure + // that the modem driver doesn't get called while we are restoring + // power. + // + WriteBMControlRegister (GLOB_STA, + ReadBMControlRegister32 (GLOB_STA) & ~GLOB_STA_AD3); + + // + // Restore codec registers. + // + ntStatus = RestoreCodecRegisters (); + } + else // We are coming from power state D1 + { + ntStatus = PowerUpCodec (); + } + + // Print error code. + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("PowerChangeState failed to restore the codec.")); + } + break; + + case PowerDeviceD1: + // + // This sleep state is the lowest latency sleep state with respect + // to the latency time required to return to D0. If the + // driver is not being used an inactivity timer in portcls will + // place the driver in this state after a timeout period + // controllable via the registry. + // + + // Let's power down the DAC/ADC's and analog mixer. + WriteCodecRegister (AC97REG_POWERDOWN, 0x0700, 0xFFFF); + break; + + case PowerDeviceD2: + case PowerDeviceD3: + // + // This is a full hibernation state and is the longest latency sleep + // state. In this modes the power could be removed or reduced that + // much that the AC97 controller looses information, so we save + // whatever we have to save. + // + + // + // Powerdown ADC, DAC, Mixer, Vref, HP amp, and Exernal Amp but not + // AC-link and Clk + // + WriteCodecRegister (AC97REG_POWERDOWN, 0xCF00, 0xFFFF); + + // + // Only in D3 mode we set the AD3 bit and evtl. shut off the AC link. + // + if (NewState.DeviceState == PowerDeviceD3) + { + // + // Set the AD3 bit. + // + ULONG ulReg = ReadBMControlRegister32 (GLOB_STA); + WriteBMControlRegister (GLOB_STA, ulReg | GLOB_STA_AD3); + + // + // We check if the modem is sleeping. If it is, we can shut off the + // AC link also. We shut off the AC link also if the modem is not + // there. + // + if ((ulReg & GLOB_STA_MD3) || !(ulReg & GLOB_STA_SCR)) + { + // Set Codec to super sleep + WriteCodecRegister (AC97REG_POWERDOWN, 0xFF00, 0xFFFF); + + // Disable the AC-link signals + ulReg = ReadBMControlRegister32 (GLOB_CNT); + WriteBMControlRegister (GLOB_CNT, (ulReg | GLOB_CNT_ACLOFF) & ~GLOB_CNT_COLD); + } + } + break; + } + + // + // Save the new state. This local value is used to determine when to + // cache property accesses and when to permit the driver from accessing + // the hardware. + // + m_PowerState = NewState.DeviceState; + DOUT (DBG_POWER, ("Entering D%d", (ULONG)m_PowerState - + (ULONG)PowerDeviceD0)); +} + + +/***************************************************************************** + * CAC97AdapterCommon::QueryPowerChangeState + ***************************************************************************** + * Query to see if the device can change to this power state + */ +STDMETHODIMP_(NTSTATUS) CAC97AdapterCommon::QueryPowerChangeState +( + _In_ POWER_STATE NewState +) +{ + PAGED_CODE (); + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::QueryPowerChangeState]")); + + // Check here to see of a legitimate state is being requested + // based on the device state and fail the call if the device/driver + // cannot support the change requested. Otherwise, return STATUS_SUCCESS. + // Note: A QueryPowerChangeState() call is not guaranteed to always preceed + // a PowerChangeState() call. + + // check the new state being requested + switch (NewState.DeviceState) + { + case PowerDeviceD0: + case PowerDeviceD1: + case PowerDeviceD2: + case PowerDeviceD3: + return STATUS_SUCCESS; + + default: + DOUT (DBG_ERROR, ("Unknown device state: D%d.", + (ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0)); + return STATUS_NOT_IMPLEMENTED; + } +} + + +/***************************************************************************** + * CAC97AdapterCommon::QueryDeviceCapabilities + ***************************************************************************** + * Called at startup to get the caps for the device. This structure provides + * the system with the mappings between system power state and device power + * state. This typically will not need modification by the driver. + * If the driver modifies these mappings then the driver is not allowed to + * change the mapping to a weaker power state (e.g. from S1->D3 to S1->D1). + * + */ +_Use_decl_annotations_ +STDMETHODIMP_(NTSTATUS) CAC97AdapterCommon::QueryDeviceCapabilities +( + PDEVICE_CAPABILITIES PowerDeviceCaps +) +{ + PAGED_CODE (); + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::QueryDeviceCapabilities]")); + + UNREFERENCED_PARAMETER(PowerDeviceCaps); + + return STATUS_SUCCESS; +} + + +/***************************************************************************** + * CAC97AdapterCommon::RestoreAC97Registers + ***************************************************************************** + * Preset the AC97 registers with default values. The routine first checks if + * There are registry entries for the default values. If not, we have hard + * coded values too ;) + */ +NTSTATUS CAC97AdapterCommon::SetAC97Default (void) +{ + PAGED_CODE (); + + PREGISTRYKEY DriverKey; + PREGISTRYKEY SettingsKey; + UNICODE_STRING sKeyName; + ULONG ulDisposition; + ULONG ulResultLength; + PVOID KeyInfo = NULL; + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::SetAC97Default]")); + + // open the driver registry key + NTSTATUS ntStatus = PcNewRegistryKey (&DriverKey, // IRegistryKey + NULL, // OuterUnknown + DriverRegistryKey, // Registry key type + KEY_READ, // Access flags + m_pDeviceObject, // Device object + NULL, // Subdevice + NULL, // ObjectAttributes + 0, // Create options + NULL); // Disposition + if (NT_SUCCESS (ntStatus)) + { + // make a unicode string for the subkey name + RtlInitUnicodeString (&sKeyName, L"Settings"); + + // open the settings subkey + ntStatus = DriverKey->NewSubKey (&SettingsKey, // Subkey + NULL, // OuterUnknown + KEY_READ, // Access flags + &sKeyName, // Subkey name + REG_OPTION_NON_VOLATILE, // Create options + &ulDisposition); + + if (NT_SUCCESS (ntStatus)) + { + // allocate data to hold key info + KeyInfo = ExAllocatePoolWithTag (PagedPool, + sizeof(KEY_VALUE_PARTIAL_INFORMATION) + + sizeof(WORD), PoolTag); + if (NULL != KeyInfo) + { + // loop through all mixer settings + for (AC97Register i = AC97REG_RESET; i <= AC97REG_RESERVED2; + i = (AC97Register)(i + 1)) + { + if (m_stAC97Registers[i].wFlags & SHREG_INIT) + { + // init key name + RtlInitUnicodeString (&sKeyName, + m_stAC97Registers[i].sRegistryName); + + // query the value key + ntStatus = SettingsKey->QueryValueKey (&sKeyName, + KeyValuePartialInformation, + KeyInfo, + sizeof(KEY_VALUE_PARTIAL_INFORMATION) + + sizeof(WORD), + &ulResultLength); + if (NT_SUCCESS (ntStatus)) + { + PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = + (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo; + + if (PartialInfo->DataLength == sizeof(WORD)) + { + // set mixer register to registry value + WriteCodecRegister + (i, *(PWORD)PartialInfo->Data, 0xFFFF); + } + else // write the hard coded default + { + // if key access failed, set to default + WriteCodecRegister + (i, m_stAC97Registers[i].wWantedDefault, 0xFFFF); + } + } + else // write the hard coded default + { + // if key access failed, set to default + WriteCodecRegister + (i, m_stAC97Registers[i].wWantedDefault, 0xFFFF); + } + } + } + + // we want to return status success even if the last QueryValueKey + // failed. + ntStatus = STATUS_SUCCESS; + + // free the key info + ExFreePoolWithTag (KeyInfo,PoolTag); + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + // release the settings key + SettingsKey->Release (); + } + + // release the driver key + DriverKey->Release (); + } + + + // in case we did not query the registry (cause of lack of resources) + // restore default values and return insufficient resources. + if (!NT_SUCCESS (ntStatus)) + { + // copy hard coded default settings + for (AC97Register i = AC97REG_RESET; i < AC97REG_RESERVED2; + i = (AC97Register)(i + 1)) + { + if (m_stAC97Registers[i].wFlags & SHREG_INIT) + { + WriteCodecRegister (i, m_stAC97Registers[i].wWantedDefault, 0xFFFF); + } + } + } + + return ntStatus; +} + + +/***************************************************************************** + * CAC97AdapterCommon::DisableAC97Pin + ***************************************************************************** + * Returns TRUE when the HW vendor wants to disable the pin. A disabled pin is + * not shown to the user (means it is not included in the topology). The + * reason for doing this could be that some of the input lines like Aux or + * Video are not available to the user (to plug in something) but the codec + * can handle those lines. + */ +BOOL CAC97AdapterCommon::DisableAC97Pin +( + IN TopoPinConfig pin +) +{ + PAGED_CODE (); + + PREGISTRYKEY DriverKey; + PREGISTRYKEY SettingsKey; + UNICODE_STRING sKeyName; + ULONG ulDisposition; + ULONG ulResultLength; + PVOID KeyInfo = NULL; + BOOL bDisable = FALSE; + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::DisableAC97Pin]")); + + // open the driver registry key + NTSTATUS ntStatus = PcNewRegistryKey (&DriverKey, // IRegistryKey + NULL, // OuterUnknown + DriverRegistryKey, // Registry key type + KEY_READ, // Access flags + m_pDeviceObject, // Device object + NULL, // Subdevice + NULL, // ObjectAttributes + 0, // Create options + NULL); // Disposition + if (NT_SUCCESS (ntStatus)) + { + // make a unicode string for the subkey name + RtlInitUnicodeString (&sKeyName, L"Settings"); + + // open the settings subkey + ntStatus = DriverKey->NewSubKey (&SettingsKey, // Subkey + NULL, // OuterUnknown + KEY_READ, // Access flags + &sKeyName, // Subkey name + REG_OPTION_NON_VOLATILE, // Create options + &ulDisposition); + + if (NT_SUCCESS (ntStatus)) + { + // allocate data to hold key info + KeyInfo = ExAllocatePoolWithTag (PagedPool, + sizeof(KEY_VALUE_PARTIAL_INFORMATION) + + sizeof(BYTE), PoolTag); + if (NULL != KeyInfo) + { + // init key name + RtlInitUnicodeString (&sKeyName, m_stHardwareConfig. + Pins[pin].sRegistryName); + + // query the value key + ntStatus = SettingsKey->QueryValueKey (&sKeyName, + KeyValuePartialInformation, + KeyInfo, + sizeof(KEY_VALUE_PARTIAL_INFORMATION) + + sizeof(BYTE), + &ulResultLength ); + if (NT_SUCCESS (ntStatus)) + { + PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = + (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo; + + if (PartialInfo->DataLength == sizeof(BYTE)) + { + // store the value + if (*(PBYTE)PartialInfo->Data) + bDisable = TRUE; + else + bDisable = FALSE; + } + } + + // free the key info + ExFreePoolWithTag (KeyInfo,PoolTag); + } + + // release the settings key + SettingsKey->Release (); + } + + // release the driver key + DriverKey->Release (); + } + + // if one of the stuff above fails we return the default, which is FALSE. + return bDisable; +} + + +/***************************************************************************** + * CAC97AdapterCommon::RestoreCodecRegisters + ***************************************************************************** + * write back cached mixer values to codec registers + */ +NTSTATUS CAC97AdapterCommon::RestoreCodecRegisters (void) +{ + PAGED_CODE (); + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::RestoreCodecRegisters]")); + + // + // Initialize the AC97 codec. + // + NTSTATUS ntStatus = InitAC97 (); + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + // + // Restore all codec registers. Failure is not critical. + // + for (AC97Register i = AC97REG_MASTER_VOLUME; i < AC97REG_RESERVED2; + i = (AC97Register)(i + 1)) + { + WriteCodecRegister (i, m_stAC97Registers[i].wCache, 0xFFFF); + } + + return STATUS_SUCCESS; +} + +/***************************************************************************** + * CAC97AdapterCommon::ReadChannelConfigDefault + ***************************************************************************** + * This function reads the default channel config from the registry. The + * registry entry "ChannelConfig" is set every every time we get a + * KSPROPERTY_AUDIO_CHANNEL_CONFIG for the DAC node. + * In case the key doesn't exist we assume a channel config of stereo speakers, + * cause that is the default of DSOUND. + */ +STDMETHODIMP_(void) CAC97AdapterCommon::ReadChannelConfigDefault +( + PDWORD pdwChannelConfig, + PWORD pwChannels +) +{ + PAGED_CODE (); + + PREGISTRYKEY DriverKey; + PREGISTRYKEY SettingsKey; + UNICODE_STRING sKeyName; + ULONG ulDisposition; + ULONG ulResultLength; + PVOID KeyInfo = NULL; + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::ReadChannelConfigDefault]")); + + // This is the default: 2 speakers, stereo. + *pdwChannelConfig = KSAUDIO_SPEAKER_STEREO; + *pwChannels = 2; + + // open the driver registry key + NTSTATUS ntStatus = PcNewRegistryKey (&DriverKey, // IRegistryKey + NULL, // OuterUnknown + DriverRegistryKey, // Registry key type + KEY_READ, // Access flags + m_pDeviceObject, // Device object + NULL, // Subdevice + NULL, // ObjectAttributes + 0, // Create options + NULL); // Disposition + if (NT_SUCCESS (ntStatus)) + { + // make a unicode string for the subkey name + RtlInitUnicodeString (&sKeyName, L"Settings"); + + // open the settings subkey + ntStatus = DriverKey->NewSubKey (&SettingsKey, // Subkey + NULL, // OuterUnknown + KEY_READ, // Access flags + &sKeyName, // Subkey name + REG_OPTION_NON_VOLATILE, // Create options + &ulDisposition); + + if (NT_SUCCESS (ntStatus)) + { + // allocate data to hold key info + KeyInfo = ExAllocatePoolWithTag (PagedPool, + sizeof(KEY_VALUE_PARTIAL_INFORMATION) + + sizeof(DWORD), PoolTag); + if (NULL != KeyInfo) + { + // init key name + RtlInitUnicodeString (&sKeyName, L"ChannelConfig"); + + // query the value key + ntStatus = SettingsKey->QueryValueKey (&sKeyName, + KeyValuePartialInformation, + KeyInfo, + sizeof(KEY_VALUE_PARTIAL_INFORMATION) + + sizeof(DWORD), + &ulResultLength ); + if (NT_SUCCESS (ntStatus)) + { + PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = + (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo; + + if (PartialInfo->DataLength == sizeof(DWORD)) + { + switch (*(PLONG)PartialInfo->Data) + { + case KSAUDIO_SPEAKER_QUAD: + case KSAUDIO_SPEAKER_SURROUND: + if (GetPinConfig (PINC_SURROUND_PRESENT)) + { + *pdwChannelConfig = *(PDWORD)PartialInfo->Data; + *pwChannels = 4; + } + break; + + case KSAUDIO_SPEAKER_5POINT1: + if (GetPinConfig (PINC_SURROUND_PRESENT) && + GetPinConfig (PINC_CENTER_LFE_PRESENT)) + { + *pdwChannelConfig = *(PDWORD)PartialInfo->Data; + *pwChannels = 6; + } + break; + } + } + } + + // free the key info + ExFreePoolWithTag (KeyInfo,PoolTag); + } + + // release the settings key + SettingsKey->Release (); + } + + // release the driver key + DriverKey->Release (); + } +} + +/***************************************************************************** + * CAC97AdapterCommon::WriteChannelConfigDefault + ***************************************************************************** + * This function writes the default channel config to the registry. The + * registry entry "ChannelConfig" is set every every time we get a + * KSPROPERTY_AUDIO_CHANNEL_CONFIG for the DAC node. + */ +STDMETHODIMP_(void) CAC97AdapterCommon::WriteChannelConfigDefault (DWORD dwChannelConfig) +{ + PAGED_CODE (); + + PREGISTRYKEY DriverKey; + PREGISTRYKEY SettingsKey; + UNICODE_STRING sKeyName; + ULONG ulDisposition; + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::WriteChannelConfigDefault]")); + + // open the driver registry key + NTSTATUS ntStatus = PcNewRegistryKey (&DriverKey, // IRegistryKey + NULL, // OuterUnknown + DriverRegistryKey, // Registry key type + KEY_WRITE, // Access flags + m_pDeviceObject, // Device object + NULL, // Subdevice + NULL, // ObjectAttributes + 0, // Create options + NULL); // Disposition + if (NT_SUCCESS (ntStatus)) + { + // make a unicode string for the subkey name + RtlInitUnicodeString (&sKeyName, L"Settings"); + + // open the settings subkey + ntStatus = DriverKey->NewSubKey (&SettingsKey, // Subkey + NULL, // OuterUnknown + KEY_WRITE, // Access flags + &sKeyName, // Subkey name + REG_OPTION_NON_VOLATILE, // Create options + &ulDisposition); + + if (NT_SUCCESS (ntStatus)) + { + // init key name + RtlInitUnicodeString (&sKeyName, L"ChannelConfig"); + + // query the value key + ntStatus = SettingsKey->SetValueKey (&sKeyName, + REG_DWORD, + &dwChannelConfig, + sizeof (DWORD)); + if (!NT_SUCCESS (ntStatus)) + { + DOUT (DBG_ERROR, ("Could not write the ChannelConfig to registry.")); + } + + // release the settings key + SettingsKey->Release (); + } + + // release the driver key + DriverKey->Release (); + } +} + +/***************************************************************************** + * Non paged code begins here + ***************************************************************************** + */ + +#ifdef _MSC_VER +#pragma code_seg() +#endif +/***************************************************************************** + * CAC97AdapterCommon::WriteBMControlRegister + ***************************************************************************** + * Writes a byte (UCHAR) to BusMaster Control register. + */ +STDMETHODIMP_(void) CAC97AdapterCommon::WriteBMControlRegister +( + IN ULONG ulOffset, + IN UCHAR ucValue +) +{ + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::WriteBMControlRegister] (UCHAR)")); + + WRITE_PORT_UCHAR ((PUCHAR)(m_pBusMasterBase + ulOffset), ucValue); + + DOUT (DBG_REGS, ("WriteBMControlRegister wrote 0x%2x to 0x%4p.", + ucValue, m_pBusMasterBase + ulOffset)); +} + +/***************************************************************************** + * CAC97AdapterCommon::WriteBMControlRegister + ***************************************************************************** + * Writes a word (USHORT) to BusMaster Control register. + */ +STDMETHODIMP_(void) CAC97AdapterCommon::WriteBMControlRegister +( + IN ULONG ulOffset, + IN USHORT usValue +) +{ + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::WriteBMControlRegister (USHORT)]")); + + WRITE_PORT_USHORT ((PUSHORT)(m_pBusMasterBase + ulOffset), usValue); + + DOUT (DBG_REGS, ("WriteBMControlRegister wrote 0x%4x to 0x%4p", + usValue, m_pBusMasterBase + ulOffset)); +} + +/***************************************************************************** + * CAC97AdapterCommon::WriteBMControlRegister + ***************************************************************************** + * Writes a DWORD (ULONG) to BusMaster Control register. + */ +STDMETHODIMP_(void) CAC97AdapterCommon::WriteBMControlRegister +( + IN ULONG ulOffset, + IN ULONG ulValue +) +{ + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::WriteBMControlRegister (ULONG)]")); + + WRITE_PORT_ULONG ((PULONG)(m_pBusMasterBase + ulOffset), ulValue); + + DOUT (DBG_REGS, ("WriteBMControlRegister wrote 0x%8x to 0x%4p.", + ulValue, m_pBusMasterBase + ulOffset)); +} + +/***************************************************************************** + * CAC97AdapterCommon::ReadBMControlRegister8 + ***************************************************************************** + * Read a byte (UCHAR) from BusMaster Control register. + */ +STDMETHODIMP_(UCHAR) CAC97AdapterCommon::ReadBMControlRegister8 +( + IN ULONG ulOffset +) +{ + UCHAR ucValue = UCHAR(-1); + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::ReadBMControlRegister8]")); + + ucValue = READ_PORT_UCHAR ((PUCHAR)(m_pBusMasterBase + ulOffset)); + + DOUT (DBG_REGS, ("ReadBMControlRegister read 0x%2x from 0x%4p.", ucValue, + m_pBusMasterBase + ulOffset)); + + return ucValue; +} + +/***************************************************************************** + * CAC97AdapterCommon::ReadBMControlRegister16 + ***************************************************************************** + * Read a word (USHORT) from BusMaster Control register. + */ +STDMETHODIMP_(USHORT) CAC97AdapterCommon::ReadBMControlRegister16 +( + IN ULONG ulOffset +) +{ + USHORT usValue = USHORT(-1); + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::ReadBMControlRegister16]")); + + usValue = READ_PORT_USHORT ((PUSHORT)(m_pBusMasterBase + ulOffset)); + + DOUT (DBG_REGS, ("ReadBMControlRegister read 0x%4x = 0x%4p", usValue, + m_pBusMasterBase + ulOffset)); + + return usValue; +} + +/***************************************************************************** + * CAC97AdapterCommon::ReadBMControlRegister32 + ***************************************************************************** + * Read a dword (ULONG) from BusMaster Control register. + */ +STDMETHODIMP_(ULONG) CAC97AdapterCommon::ReadBMControlRegister32 +( + IN ULONG ulOffset +) +{ + ULONG ulValue = ULONG(-1); + + DOUT (DBG_PRINT, ("[CAC97AdapterCommon::ReadBMControlRegister32]")); + + ulValue = READ_PORT_ULONG ((PULONG)(m_pBusMasterBase + ulOffset)); + + DOUT (DBG_REGS, ("ReadBMControlRegister read 0x%8x = 0x%4p", ulValue, + m_pBusMasterBase + ulOffset)); + + return ulValue; +} + diff --git a/drivers/wdm/audio/drivers/ac97/common.h b/drivers/wdm/audio/drivers/ac97/common.h new file mode 100644 index 00000000000..8b5162ddffa --- /dev/null +++ b/drivers/wdm/audio/drivers/ac97/common.h @@ -0,0 +1,387 @@ +/******************************************************************************** +** Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved. +** +** Portions Copyright (c) 1998-1999 Intel Corporation +** +********************************************************************************/ + +/* The file common.h was reviewed by LCA in June 2011 and is acceptable for use by Microsoft. */ + +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#include "shared.h" + +/***************************************************************************** + * Structs + ***************************************************************************** + */ + +// +// Contains pin and node configuration of the AC97 codec. +// +typedef struct +{ + // For nodes. + struct + { + BOOL bNodeConfig; + } Nodes[NODEC_TOP_ELEMENT]; + + // For pins. + struct + { + BOOL bPinConfig; + PWCHAR sRegistryName; + } Pins[PINC_TOP_ELEMENT]; +} tHardwareConfig; + +// +// We cache the AC97 registers. Additionally, we want some default values +// when the driver comes up first that are different from the HW default +// values. The string in the structure is the name of the registry entry +// that can be used instead of the hard coded default value. +// +typedef struct +{ + WORD wCache; + WORD wFlags; + PWCHAR sRegistryName; + WORD wWantedDefault; +} tAC97Registers; + + +/***************************************************************************** + * Constants + ***************************************************************************** + */ + +// +// This means shadow register are to be read at least once to initialize. +// +const WORD SHREG_INVALID = 0x0001; + +// +// This means shadow register should be overwritten with default value at +// driver init. +// +const WORD SHREG_INIT = 0x0002; + +// +// This constant is used to prevent register caching. +// +const WORD SHREG_NOCACHE = 0x0004; + + +/***************************************************************************** + * Classes + ***************************************************************************** + */ + +/***************************************************************************** + * CAC97AdapterCommon + ***************************************************************************** + * This is the common adapter object shared by all miniports to access the + * hardware. + */ +class CAC97AdapterCommon : public IAC97AdapterCommon, + public IAdapterPowerManagement, + public CUnknown +{ +private: + static tAC97Registers m_stAC97Registers[64]; // The shadow registers. + static tHardwareConfig m_stHardwareConfig; // The hardware configuration. + PDEVICE_OBJECT m_pDeviceObject; // Device object used for registry access. + PWORD m_pCodecBase; // The AC97 I/O port address. + PUCHAR m_pBusMasterBase; // The Bus Master base address. + BOOL m_bDirectRead; // Used during init time. + DEVICE_POWER_STATE m_PowerState; // Current power state of the device. + PAC97MINIPORTTOPOLOGY m_Topology; // Miniport Topology pointer. + + + /************************************************************************* + * CAC97AdapterCommon methods + ************************************************************************* + */ + + // + // Resets AC97 audio registers. + // + NTSTATUS InitAC97 (void); + + // + // Checks for existance of registers. + // + NTSTATUS ProbeHWConfig (void); + + // + // Checks for 6th bit support in the volume control. + // + NTSTATUS Check6thBitSupport (IN AC97Register, IN TopoNodeConfig); + + // + // Returns true if you should disable the input or output pin. + // + BOOL DisableAC97Pin (IN TopoPinConfig); + +#if (DBG) + // + // Dumps the probed configuration. + // + void DumpConfig (void); +#endif + + // + // Sets AC97 registers to default. + // + NTSTATUS SetAC97Default (void); + + // + // Aquires the semaphore for AC97 register access. + // + NTSTATUS AcquireCodecSemiphore (void); + + // + // Checks if there is a AC97 link between AC97 and codec. + // + NTSTATUS PrimaryCodecReady (void); + + // + // Powers up the Codec. + // + NTSTATUS PowerUpCodec (void); + + // + // Saves native audio bus master control registers values to be used + // upon suspend. + // + NTSTATUS ReadNABMCtrlRegs (void); + + // + // Writes back native audio bus master control resgister to be used upon + // resume. + // + NTSTATUS RestoreNABMCtrlRegs (void); + +public: + DECLARE_STD_UNKNOWN(); + DEFINE_STD_CONSTRUCTOR(CAC97AdapterCommon); + ~CAC97AdapterCommon(); + + /************************************************************************* + * IAdapterPowerManagement methods + ************************************************************************* + */ + IMP_IAdapterPowerManagement; + + /************************************************************************* + * IAC97AdapterCommon methods + ************************************************************************* + */ + + // + // Initialize the adapter common object -> initialize and probe HW. + // + STDMETHODIMP_(NTSTATUS) Init + ( + IN PRESOURCELIST ResourceList, + IN PDEVICE_OBJECT DeviceObject + ); + + // + // Returns if pin exists. + // + STDMETHODIMP_(BOOL) GetPinConfig + ( + IN TopoPinConfig pin + ) + { + return m_stHardwareConfig.Pins[pin].bPinConfig; + }; + + // + // Sets the pin configuration (exist/not exist). + // + STDMETHODIMP_(void) SetPinConfig + ( + IN TopoPinConfig pin, + IN BOOL config + ) + { + m_stHardwareConfig.Pins[pin].bPinConfig = config; + }; + + // + // Return if node exists. + // + STDMETHODIMP_(BOOL) GetNodeConfig + ( + IN TopoNodeConfig node + ) + { + return m_stHardwareConfig.Nodes[node].bNodeConfig; + }; + + // + // Sets the node configuration (exist/not exist). + // + STDMETHODIMP_(void) SetNodeConfig + ( + IN TopoNodeConfig node, + IN BOOL config + ) + { + m_stHardwareConfig.Nodes[node].bNodeConfig = config; + }; + + // + // Returns the AC97 register that is assosiated with the node. + // + STDMETHODIMP_(AC97Register) GetNodeReg + ( IN TopoNodes node + ) + { + return stMapNodeToReg[node].reg; + }; + + // + // Returns the AC97 register mask that is assosiated with the node. + // + STDMETHODIMP_(WORD) GetNodeMask + ( + IN TopoNodes node + ) + { + return stMapNodeToReg[node].mask; + }; + + // + // Reads a AC97 register. + // + STDMETHODIMP_(NTSTATUS) ReadCodecRegister + ( + _In_range_(0, AC97REG_INVALID) AC97Register Register, + _Out_ PWORD wData + ); + + // + // Writes a AC97 register. + // + STDMETHODIMP_(NTSTATUS) WriteCodecRegister + ( + _In_range_(0, AC97REG_INVALID) AC97Register Register, + _In_ WORD wData, + _In_ WORD wMask + ); + + // + // Reads a 8 bit AC97 bus master register. + // + STDMETHODIMP_(UCHAR) ReadBMControlRegister8 + ( + IN ULONG ulOffset + ); + + // + // Reads a 16 bit AC97 bus master register. + // + STDMETHODIMP_(USHORT) ReadBMControlRegister16 + ( + IN ULONG ulOffset + ); + + // + // Reads a 32 bit AC97 bus master register. + // + STDMETHODIMP_(ULONG) ReadBMControlRegister32 + ( + IN ULONG ulOffset + ); + + // + // Writes a 8 bit AC97 bus master register. + // + STDMETHODIMP_(void) WriteBMControlRegister + ( + IN ULONG ulOffset, + IN UCHAR Value + ); + + // + // writes a 16 bit AC97 bus master register. + // + STDMETHODIMP_(void) WriteBMControlRegister + ( + IN ULONG ulOffset, + IN USHORT Value + ); + + // writes a 32 bit AC97 bus master register. + STDMETHODIMP_(void) WriteBMControlRegister + ( + IN ULONG ulOffset, + IN ULONG Value + ); + + // + // Write back cached mixer values to codec registers. + // + STDMETHODIMP_(NTSTATUS) RestoreCodecRegisters(); + + // + // Programs a sample rate. + // + STDMETHODIMP_(NTSTATUS) ProgramSampleRate + ( + IN AC97Register Register, + IN DWORD dwSampleRate + ); + + // + // Stores the topology pointer. Used for DRM only. + // + STDMETHODIMP_(void) SetMiniportTopology (PAC97MINIPORTTOPOLOGY topo) + { + m_Topology = topo; + }; + + // + // Returns the topology pointer. Used for DRM only. + // + STDMETHODIMP_(PAC97MINIPORTTOPOLOGY) GetMiniportTopology (void) + { + return m_Topology; + }; + + // + // This function reads the default channel config and is called only by the + // wave miniport. + // + STDMETHODIMP_(void) ReadChannelConfigDefault + ( + PDWORD pdwChannelConfig, + PWORD pwChannels + ); + + // + // This function writes the default channel config and is called only by the + // wave miniport. + // + STDMETHODIMP_(void) WriteChannelConfigDefault + ( + DWORD dwChannelConfig + ); + + /************************************************************************* + * Friends + ************************************************************************* + */ + + friend NTSTATUS NewAdapterCommon + ( + OUT PADAPTERCOMMON *OutAdapterCommon, + IN PRESOURCELIST ResourceList + ); +}; + +#endif //_COMMON_H_ diff --git a/drivers/wdm/audio/drivers/ac97/debug.h b/drivers/wdm/audio/drivers/ac97/debug.h new file mode 100644 index 00000000000..78c5335c16d --- /dev/null +++ b/drivers/wdm/audio/drivers/ac97/debug.h @@ -0,0 +1,93 @@ +/******************************************************************************** +** Copyright (c) 1998-1999 Microsoft Corporation. All Rights Reserved. +** +** Portions Copyright (c) 1998-1999 Intel Corporation +** +********************************************************************************/ + +/* The file debug.h was reviewed by LCA in June 2011 and is acceptable for use by Microsoft. */ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +// +// Modified version of ksdebug.h to support runtime debug level changes. +// +const int DBG_NONE = 0x00000000; +const int DBG_PRINT = 0x00000001; // Blabla. Function entries for example +const int DBG_WARNING = 0x00000002; // warning level +const int DBG_ERROR = 0x00000004; // this doesn't generate a breakpoint + +// specific debug output; you don't have to enable DBG_PRINT for this. +const int DBG_STREAM = 0x00000010; // Enables stream output. +const int DBG_POWER = 0x00000020; // Enables power management output. +const int DBG_DMA = 0x00000040; // Enables DMA engine output. +const int DBG_REGS = 0x00000080; // Enables register outout. +const int DBG_PROBE = 0x00000100; // Enables hardware probing output. +const int DBG_SYSINFO = 0x00000200; // Enables system info output. +const int DBG_VSR = 0x00000400; // Enables variable sample rate output. +const int DBG_PROPERTY = 0x00000800; // Enables property handler output +const int DBG_POSITION = 0x00001000; // Enables printing of position on GetPosition +const int DBG_PINS = 0x10000000; // Enables dump of created pins in topology +const int DBG_NODES = 0x20000000; // Enables dump of created nodes in topology +const int DBG_CONNS = 0x40000000; // Enables dump of the connections in topology + +const int DBG_ALL = 0xFFFFFFFF; + +// +// The default statements that will print are warnings (DBG_WARNING) and +// errors (DBG_ERROR). +// +const int DBG_DEFAULT = 0x00000004; // Errors only. + + +// +// Define global debug variable. +// +#ifdef DEFINE_DEBUG_VARS +#if (DBG) +unsigned long ulDebugOut = DBG_DEFAULT; +#endif + +#else // !DEFINED_DEBUG_VARS +#if (DBG) +extern unsigned long ulDebugOut; +#endif +#endif + + +// +// Define the print statement. +// +#if defined(__cplusplus) +extern "C" { +#endif // #if defined(__cplusplus) + +// +// DBG is 1 in checked builds +// +#if (DBG) +#define DOUT(lvl, strings) \ + if ((lvl) & ulDebugOut) \ + { \ + DbgPrint(STR_MODULENAME); \ + DbgPrint strings; \ + DbgPrint("\n"); \ + } + +#define BREAK() \ + DbgBreakPoint() + +#else // if (!DBG) +#define DOUT(lvl, strings) +#define BREAK() +#endif // !DBG + + +#if defined(__cplusplus) +} +#endif // #if defined(__cplusplus) + + + +#endif diff --git a/drivers/wdm/audio/drivers/ac97/guids.h b/drivers/wdm/audio/drivers/ac97/guids.h new file mode 100644 index 00000000000..8230403da81 --- /dev/null +++ b/drivers/wdm/audio/drivers/ac97/guids.h @@ -0,0 +1,188 @@ +/******************************************************************************** +** Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved. +** +** Portions Copyright (c) 1998-1999 Intel Corporation +** +********************************************************************************/ + +/* The file guids.h was reviewed by LCA in June 2011 and is acceptable for use by Microsoft. */ + +#ifndef _GUIDS_H_ +#define _GUIDS_H_ + + +/***************************************************************************** + * GUIDs + ***************************************************************************** + * GUIDs for friendly names. + */ + +// PHONE Volume Name +#define STATIC_MYKSNAME_PHONE_VOLUME\ + 0x0A8C1A87, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1A87-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_PHONE_VOLUME); +#define MYKSNAME_PHONE_VOLUME DEFINE_GUIDNAMED(MYKSNAME_PHONE_VOLUME) + +// PHONE Mute Name +#define STATIC_MYKSNAME_PHONE_MUTE\ + 0x0A8C1A88, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1A88-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_PHONE_MUTE); +#define MYKSNAME_PHONE_MUTE DEFINE_GUIDNAMED(MYKSNAME_PHONE_MUTE) + +// LINEIN Mute Name +#define STATIC_MYKSNAME_LINEIN_MUTE\ + 0x0A8C1A91, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1A91-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_LINEIN_MUTE); +#define MYKSNAME_LINEIN_MUTE DEFINE_GUIDNAMED(MYKSNAME_LINEIN_MUTE) + +// Main Mix Name +#define STATIC_MYKSNAME_MAIN_MIX\ + 0x0A8C1A9B, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1A9B-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_MAIN_MIX); +#define MYKSNAME_MAIN_MIX DEFINE_GUIDNAMED(MYKSNAME_MAIN_MIX) + +// 3D Bypass Name +#define STATIC_MYKSNAME_WAVEOUT_3D_BYPASS\ + 0x0A8C1A9E, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1A9E-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_WAVEOUT_3D_BYPASS); +#define MYKSNAME_WAVEOUT_3D_BYPASS DEFINE_GUIDNAMED(MYKSNAME_WAVEOUT_3D_BYPASS) + +// 3D Enable Name +#define STATIC_MYKSNAME_3D_ENABLE\ + 0x766db5a4, 0x6e94, 0x11d2, 0x9a, 0xde, 0x0, 0xc0, 0x4f, 0x8e, 0xfb, 0x68 +DEFINE_GUIDSTRUCT("766DB5A4-6E94-11d2-9ADE-00C04F8EFB68", MYKSNAME_3D_ENABLE); +#define MYKSNAME_3D_ENABLE DEFINE_GUIDNAMED(MYKSNAME_3D_ENABLE) + +// Beep Mix Name +#define STATIC_MYKSNAME_BEEP_MIX\ + 0x0A8C1A9F, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1A9F-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_BEEP_MIX); +#define MYKSNAME_BEEP_MIX DEFINE_GUIDNAMED(MYKSNAME_BEEP_MIX) + +// HPOUT Volume Name +#define STATIC_MYKSNAME_HPOUT_VOLUME\ + 0x0A8C1AA5, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1AA5-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_HPOUT_VOLUME); +#define MYKSNAME_HPOUT_VOLUME DEFINE_GUIDNAMED(MYKSNAME_HPOUT_VOLUME) + +// HPOUT Mute Name +#define STATIC_MYKSNAME_HPOUT_MUTE\ + 0x0A8C1AA6, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1AA6-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_HPOUT_MUTE); +#define MYKSNAME_HPOUT_MUTE DEFINE_GUIDNAMED(MYKSNAME_HPOUT_MUTE) + +// MONOOUT Select Name +#define STATIC_MYKSNAME_MONOOUT_SELECT\ + 0x0A8C1AA9, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1AA9-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_MONOOUT_SELECT); +#define MYKSNAME_MONOOUT_SELECT DEFINE_GUIDNAMED(MYKSNAME_MONOOUT_SELECT) + +// WAVEIN Select Name +#define STATIC_MYKSNAME_WAVEIN_SELECT\ + 0x0A8C1AAE, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1AAE-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_WAVEIN_SELECT); +#define MYKSNAME_WAVEIN_SELECT DEFINE_GUIDNAMED(MYKSNAME_WAVEIN_SELECT) + +// MASTER INPUT Volume Name +#define STATIC_MYKSNAME_MASTER_INPUT_VOLUME\ + 0x0A8C1AAF, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1AAF-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_MASTER_INPUT_VOLUME); +#define MYKSNAME_MASTER_INPUT_VOLUME DEFINE_GUIDNAMED(MYKSNAME_MASTER_INPUT_VOLUME) + +// MASTER INPUT Mute Name +#define STATIC_MYKSNAME_MASTER_INPUT_MUTE\ + 0x0A8C1AB0, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1AB0-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_MASTER_INPUT_MUTE); +#define MYKSNAME_MASTER_INPUT_MUTE DEFINE_GUIDNAMED(MYKSNAME_MASTER_INPUT_MUTE) + +// MICIN Volume Name +#define STATIC_MYKSNAME_MICIN_VOLUME\ + 0x0A8C1AB2, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1AB2-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_MICIN_VOLUME); +#define MYKSNAME_MICIN_VOLUME DEFINE_GUIDNAMED(MYKSNAME_MICIN_VOLUME) + +// MICIN Mute Name +#define STATIC_MYKSNAME_MICIN_MUTE\ + 0x0A8C1AB3, 0x42B0, 0x11D2, 0x95, 0xD2, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3 +DEFINE_GUIDSTRUCT("0A8C1AB3-42B0-11D2-95D2-00C04FB925D3", MYKSNAME_MICIN_MUTE); +#define MYKSNAME_MICIN_MUTE DEFINE_GUIDNAMED(MYKSNAME_MICIN_MUTE) + +// Simulated Stereo Name +#define STATIC_MYKSNAME_SIMUL_STEREO\ + 0xB3AD50B5, 0x3849, 0x4983, 0xAD, 0xD7, 0x25, 0xE6, 0x26, 0x8F, 0x91, 0x2D +DEFINE_GUIDSTRUCT("B3AD50B5-3849-4983-ADD7-25E6268F912D", MYKSNAME_SIMUL_STEREO); +#define MYKSNAME_SIMUL_STEREO DEFINE_GUIDNAMED(MYKSNAME_SIMUL_STEREO) + +// Surround Volume Name +#define STATIC_MYKSNAME_SURROUND_VOLUME\ + 0xa4b68ba4, 0x6958, 0x4ab4, 0xbb, 0x1, 0xe2, 0x3c, 0x6f, 0x2, 0x7c, 0x88 +DEFINE_GUIDSTRUCT("A4B68BA4-6958-4ab4-BB01-E23C6F027C88", MYKSNAME_SURROUND_VOLUME); +#define MYKSNAME_SURROUND_VOLUME DEFINE_GUIDNAMED(MYKSNAME_SURROUND_VOLUME) + +// Surround Mute Name +#define STATIC_MYKSNAME_SURROUND_MUTE\ + 0x22654fbc, 0xac8f, 0x4224, 0xb1, 0x9f, 0xd8, 0x58, 0xd2, 0xe1, 0xb, 0xdd +DEFINE_GUIDSTRUCT("22654FBC-AC8F-4224-B19F-D858D2E10BDD", MYKSNAME_SURROUND_MUTE); +#define MYKSNAME_SURROUND_MUTE DEFINE_GUIDNAMED(MYKSNAME_SURROUND_MUTE) + +// Center Volume Name +#define STATIC_MYKSNAME_CENTER_VOLUME\ + 0x9b0f1946, 0xabd2, 0x47a8, 0xa7, 0x78, 0xbb, 0x86, 0xcd, 0xe1, 0xa1, 0x67 +DEFINE_GUIDSTRUCT("9B0F1946-ABD2-47a8-A778-BB86CDE1A167", MYKSNAME_CENTER_VOLUME); +#define MYKSNAME_CENTER_VOLUME DEFINE_GUIDNAMED(MYKSNAME_CENTER_VOLUME) + +// Center Mute Name +#define STATIC_MYKSNAME_CENTER_MUTE\ + 0xbeef51ed, 0x1041, 0x43f8, 0x9b, 0x96, 0x58, 0x63, 0xd0, 0xa9, 0x34, 0x2d +DEFINE_GUIDSTRUCT("BEEF51ED-1041-43f8-9B96-5863D0A9342D", MYKSNAME_CENTER_MUTE); +#define MYKSNAME_CENTER_MUTE DEFINE_GUIDNAMED(MYKSNAME_CENTER_MUTE) + +// LFE Volume Name +#define STATIC_MYKSNAME_LFE_VOLUME\ + 0x455fa6f2, 0x21ec, 0x4df4, 0xb1, 0xe4, 0x31, 0x55, 0x20, 0x97, 0x97, 0xf3 +DEFINE_GUIDSTRUCT("455FA6F2-21EC-4df4-B1E4-3155209797F3", MYKSNAME_LFE_VOLUME); +#define MYKSNAME_LFE_VOLUME DEFINE_GUIDNAMED(MYKSNAME_LFE_VOLUME) + +// LFE Mute Name +#define STATIC_MYKSNAME_LFE_MUTE\ + 0x4a4d9210, 0xc780, 0x4768, 0xbf, 0xd2, 0x52, 0x5f, 0xdb, 0xf4, 0xfc, 0xb4 +DEFINE_GUIDSTRUCT("4A4D9210-C780-4768-BFD2-525FDBF4FCB4", MYKSNAME_LFE_MUTE); +#define MYKSNAME_LFE_MUTE DEFINE_GUIDNAMED(MYKSNAME_LFE_MUTE) + +// Front Volume Name +#define STATIC_MYKSNAME_FRONT_VOLUME\ + 0x9f4801bd, 0xf746, 0x4c7a, 0x8a, 0x9d, 0xf6, 0xe9, 0x90, 0x4, 0xcc, 0x98 +DEFINE_GUIDSTRUCT("9F4801BD-F746-4c7a-8A9D-F6E99004CC98", MYKSNAME_FRONT_VOLUME); +#define MYKSNAME_FRONT_VOLUME DEFINE_GUIDNAMED(MYKSNAME_FRONT_VOLUME) + +// Front Mute Name +#define STATIC_MYKSNAME_FRONT_MUTE\ + 0xc8e03b2a, 0xebd9, 0x4554, 0xa7, 0x50, 0x8e, 0x44, 0x72, 0x75, 0xa, 0x5b +DEFINE_GUIDSTRUCT("C8E03B2A-EBD9-4554-A750-8E4472750A5B", MYKSNAME_FRONT_MUTE); +#define MYKSNAME_FRONT_MUTE DEFINE_GUIDNAMED(MYKSNAME_FRONT_MUTE) + +// Surround Pin Name +#define STATIC_MYKSNAME_SURROUND\ + 0x81fbb14b, 0x1bee, 0x4bf5, 0x92, 0xee, 0xff, 0xc4, 0xf7, 0x5f, 0x32, 0x6d +DEFINE_GUIDSTRUCT("81FBB14B-1BEE-4bf5-92EE-FFC4F75F326D", MYKSNAME_SURROUND); +#define MYKSNAME_SURROUND DEFINE_GUIDNAMED(MYKSNAME_SURROUND) + +// Center Pin Name +#define STATIC_MYKSNAME_CENTER\ + 0x2d97372f, 0x9cf6, 0x4fd6, 0x9e, 0x56, 0xc6, 0x8b, 0xac, 0xdf, 0x36, 0xd +DEFINE_GUIDSTRUCT("2D97372F-9CF6-4fd6-9E56-C68BACDF360D", MYKSNAME_CENTER); +#define MYKSNAME_CENTER DEFINE_GUIDNAMED(MYKSNAME_CENTER) + +// LFE Pin Name +#define STATIC_MYKSNAME_LFE\ + 0xb60c4274, 0x3bfd, 0x430b, 0x83, 0x64, 0xd9, 0x47, 0xe7, 0xd3, 0x4, 0xb1 +DEFINE_GUIDSTRUCT("B60C4274-3BFD-430b-8364-D947E7D304B1", MYKSNAME_LFE); +#define MYKSNAME_LFE DEFINE_GUIDNAMED(MYKSNAME_LFE) + +// Front Pin Name +#define STATIC_MYKSNAME_FRONT\ + 0x70395e2, 0xbe7c, 0x4b4d, 0xb5, 0x29, 0x40, 0xcb, 0x9b, 0xfc, 0xf9, 0x95 +DEFINE_GUIDSTRUCT("070395E2-BE7C-4b4d-B529-40CB9BFCF995", MYKSNAME_FRONT); +#define MYKSNAME_FRONT DEFINE_GUIDNAMED(MYKSNAME_FRONT) + +#endif // _GUIDS_H_ diff --git a/drivers/wdm/audio/drivers/ac97/ichreg.h b/drivers/wdm/audio/drivers/ac97/ichreg.h new file mode 100644 index 00000000000..504a2260c5a --- /dev/null +++ b/drivers/wdm/audio/drivers/ac97/ichreg.h @@ -0,0 +1,107 @@ +/******************************************************************************** +** Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved. +** +** Portions Copyright (c) 1998-1999 Intel Corporation +** +********************************************************************************/ + +/* The file ichreg.h was reviewed by LCA in June 2011 and is acceptable for use by Microsoft. */ + +#ifndef _ICHREG_H_ +#define _ICHREG_H_ + +// We define the offsets like PI_BDBAR as ULONG (instead of UCHAR) for run +// time efficiency. + +// CoDec AC97 register space offsets +const ULONG PRIMARY_CODEC = 0x00; +const ULONG SECONDARY_CODEC = 0x80; + +// Native audio bus master control registers (offsets) +const ULONG PI_BDBAR = 0x00; // PCM In Buffer Descriptor Base Address Register +const ULONG PI_CIV = 0x04; // PCM In Current Index Value +const ULONG PI_LVI = 0x05; // PCM In Last Valid Index +const ULONG PI_SR = 0x06; // PCM In Status Register +const ULONG PI_PICB = 0x08; // PCM In Position In Current Buffer +const ULONG PI_PIV = 0x0A; // PCM In Prefetch Index Value +const ULONG PI_CR = 0x0B; // PCM In Control Register +const ULONG PO_BDBAR = 0x10; // PCM Out Buffer Descriptor Base Address Register +const ULONG PO_CIV = 0x14; // PCM Out Current Index Value +const ULONG PO_LVI = 0x15; // PCM Out Last Valid Index +const ULONG PO_SR = 0x16; // PCM Out Status Register +const ULONG PO_PICB = 0x18; // PCM Out Position In Current Buffer +const ULONG PO_PIV = 0x1A; // PCM Out Prefetch Index Value +const ULONG PO_CR = 0x1B; // PCM Out Control Register +const ULONG MC_BDBAR = 0x20; // Mic In Buffer Descriptor Base Address Register +const ULONG MC_CIV = 0x24; // Mic In Current Index Value +const ULONG MC_LVI = 0x25; // Mic In Last Valid Index +const ULONG MC_SR = 0x26; // Mic In Status Register +const ULONG MC_PICB = 0x28; // Mic In Position In Current Buffer +const ULONG MC_PIV = 0x2A; // Mic In Prefetch Index Value +const ULONG MC_CR = 0x2B; // Mic In Control Register +const ULONG GLOB_CNT = 0x2C; // Global Control +const ULONG GLOB_STA = 0x30; // Global Status +const ULONG CAS = 0x34; // Codec Access Semiphore + +// Defines for relative accesses (offsets) +const ULONG X_PI_BASE = 0x00; // PCM In Base +const ULONG X_PO_BASE = 0x10; // PCM Out Base +const ULONG X_MC_BASE = 0x20; // Mic In Base +const ULONG X_BDBAR = 0x00; // Buffer Descriptor Base Address Register +const ULONG X_CIV = 0x04; // Current Index Value +const ULONG X_LVI = 0x05; // Last Valid Index +const ULONG X_SR = 0x06; // Status Register +const ULONG X_PICB = 0x08; // Position In Current Buffer +const ULONG X_PIV = 0x0A; // Prefetch Index Value +const ULONG X_CR = 0x0B; // Control Register + +// Bits defined in satatus register (*_SR) +const USHORT SR_FIFOE = 0x0010; // FIFO error +const USHORT SR_BCIS = 0x0008; // Buffer Completeion Interrupt Status +const USHORT SR_LVBCI = 0x0004; // Last Valid Buffer Completion Interrupt +const USHORT SR_CELV = 0x0002; // Last Valid Buffer Completion Interrupt + +// Global Control bit defines (GLOB_CNT) +const ULONG GLOB_CNT_PCM6 = 0x00200000; // 6 Channel Mode bit +const ULONG GLOB_CNT_PCM4 = 0x00100000; // 4 Channel Mode bit +const ULONG GLOB_CNT_SRIE = 0x00000020; // Secondary Resume Interrupt Enable +const ULONG GLOB_CNT_PRIE = 0x00000010; // Primary Resume Interrupt Enable +const ULONG GLOB_CNT_ACLOFF = 0x00000008; // ACLINK Off +const ULONG GLOB_CNT_WARM = 0x00000004; // AC97 Warm Reset +const ULONG GLOB_CNT_COLD = 0x00000002; // AC97 Cold Reset +const ULONG GLOB_CNT_GIE = 0x00000001; // GPI Interrupt Enable + +// Global Status bit defines (GLOB_STA) +const ULONG GLOB_STA_MC6 = 0x00200000; // Multichannel Capability 6 channel +const ULONG GLOB_STA_MC4 = 0x00100000; // Multichannel Capability 4 channel +const ULONG GLOB_STA_MD3 = 0x00020000; // Modem Power Down Semiphore +const ULONG GLOB_STA_AD3 = 0x00010000; // Audio Power Down Semiphore +const ULONG GLOB_STA_RCS = 0x00008000; // Read Completion Status +const ULONG GLOB_STA_B3S12 = 0x00004000; // Bit 3 Slot 12 +const ULONG GLOB_STA_B2S12 = 0x00002000; // Bit 2 Slot 12 +const ULONG GLOB_STA_B1S12 = 0x00001000; // Bit 1 Slot 12 +const ULONG GLOB_STA_SRI = 0x00000800; // Secondary Resume Interrupt +const ULONG GLOB_STA_PRI = 0x00000400; // Primary Resume Interrupt +const ULONG GLOB_STA_SCR = 0x00000200; // Secondary Codec Ready +const ULONG GLOB_STA_PCR = 0x00000100; // Primary Codec Ready +const ULONG GLOB_STA_MINT = 0x00000080; // Mic In Interrupt +const ULONG GLOB_STA_POINT = 0x00000040; // PCM Out Interrupt +const ULONG GLOB_STA_PIINT = 0x00000020; // PCM In Interrupt +const ULONG GLOB_STA_MOINT = 0x00000004; // Modem Out Interrupt + +// CoDec Access Semiphore bit defines (CAS) +const UCHAR CAS_CAS = 0x01; // Codec Access Semiphore Bit + +// DMA Engine Control Register (*_CR) bit defines +const UCHAR CR_IOCE = 0x10; // Interrupt On Completion Enable +const UCHAR CR_FEIE = 0x08; // FIFO Error Interrupt Enable +const UCHAR CR_LVBIE = 0x04; // Last Valid Buffer Interrupt Enable +const UCHAR CR_RPBM = 0x01; // Run/Pause Bus Master +const UCHAR CR_RR = 0x02; // Reset Registers (RR) + +// BDL policy bits +const USHORT IOC_ENABLE = 0x8000; +const USHORT BUP_SET = 0x4000; + +#endif //_ICHREG_H_ + diff --git a/drivers/wdm/audio/drivers/ac97/license.txt b/drivers/wdm/audio/drivers/ac97/license.txt new file mode 100644 index 00000000000..9e841e7a26e --- /dev/null +++ b/drivers/wdm/audio/drivers/ac97/license.txt @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/drivers/wdm/audio/drivers/ac97/miniport.cpp b/drivers/wdm/audio/drivers/ac97/miniport.cpp new file mode 100644 index 00000000000..83bb53d9d3c --- /dev/null +++ b/drivers/wdm/audio/drivers/ac97/miniport.cpp @@ -0,0 +1,1462 @@ +// Every debug output has "Modulname text" +#define STR_MODULENAME "AC97 Miniport: " + +//#include "shared.h" +//#include "miniport.h" +#include "wavepciminiport.h" + +/***************************************************************************** + * PinDataRangesPCMStream + ***************************************************************************** + * The next 3 arrays contain information about the data ranges of the pin for + * wave capture, wave render and mic capture. + * These arrays are filled dynamically by BuildDataRangeInformation(). + */ + +static KSDATARANGE_AUDIO PinDataRangesPCMStreamRender[WAVE_SAMPLERATES_TESTED]; +static KSDATARANGE_AUDIO PinDataRangesPCMStreamCapture[WAVE_SAMPLERATES_TESTED]; +static KSDATARANGE_AUDIO PinDataRangesMicStream[MIC_SAMPLERATES_TESTED]; + +static KSDATARANGE PinDataRangesAnalogBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +/***************************************************************************** + * PinDataRangesPointersPCMStream + ***************************************************************************** + * The next 3 arrays contain the pointers to the data range information of + * the pin for wave capture, wave render and mic capture. + * These arrays are filled dynamically by BuildDataRangeInformation(). + */ +static PKSDATARANGE PinDataRangePointersPCMStreamRender[WAVE_SAMPLERATES_TESTED]; +static PKSDATARANGE PinDataRangePointersPCMStreamCapture[WAVE_SAMPLERATES_TESTED]; +static PKSDATARANGE PinDataRangePointersMicStream[MIC_SAMPLERATES_TESTED]; + +/***************************************************************************** + * PinDataRangePointerAnalogStream + ***************************************************************************** + * This structure pointers to the data range structures for the wave pins. + */ +static PKSDATARANGE PinDataRangePointersAnalogBridge[] = +{ + (PKSDATARANGE) PinDataRangesAnalogBridge +}; + + +/***************************************************************************** + * Wave Miniport Topology + *======================== + * + * +-----------+ + * | | + * Capture (PIN_WAVEIN) <---|2 --ADC-- 3|<=== (PIN_WAVEIN_BRIDGE) + * | | + * Render (PIN_WAVEOUT) --->|0 --DAC-- 1|===> (PIN_WAVEOUT_BRIDGE) + * | | + * Mic (PIN_MICIN) <---|4 --ADC-- 5|<=== (PIN_MICIN_BRIDGE) + * +-----------+ + * + * Note that the exposed pins (left side) have to be a multiple of 2 + * since there are some dependencies in the stream object. + */ + +/***************************************************************************** + * MiniportPins + ***************************************************************************** + * This structure describes pin (stream) types provided by this miniport. + * The field that sets the number of data range entries (SIZEOF_ARRAY) is + * overwritten by BuildDataRangeInformation(). + */ +static PCPIN_DESCRIPTOR MiniportPins[] = +{ + // PIN_WAVEOUT + { + 1,1,0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(PinDataRangePointersPCMStreamRender), // DataRangesCount + PinDataRangePointersPCMStreamRender, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_SINK, // Communication + (GUID *) &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + + // PIN_WAVEOUT_BRIDGE + { + 0,0,0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(PinDataRangePointersAnalogBridge), // DataRangesCount + PinDataRangePointersAnalogBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + (GUID *) &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + + // PIN_WAVEIN + { + 1,1,0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(PinDataRangePointersPCMStreamCapture), // DataRangesCount + PinDataRangePointersPCMStreamCapture, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_SINK, // Communication + (GUID *) &PINNAME_CAPTURE, // Category + &KSAUDFNAME_RECORDING_CONTROL, // Name + 0 // Reserved + } + }, + + // PIN_WAVEIN_BRIDGE + { + 0,0,0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(PinDataRangePointersAnalogBridge), // DataRangesCount + PinDataRangePointersAnalogBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + (GUID *) &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + + // + // The Microphone pins are not used if PINC_MICIN_PRESENT is not set. + // To remove them, Init() will reduce the "PinCount" in the + // MiniportFilterDescriptor. + // + // PIN_MICIN + { + 1,1,0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(PinDataRangePointersMicStream),// DataRangesCount + PinDataRangePointersMicStream, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_SINK, // Communication + (GUID *) &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + + // PIN_MICIN_BRIDGE + { + 0,0,0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(PinDataRangePointersAnalogBridge), // DataRangesCount + PinDataRangePointersAnalogBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + (GUID *) &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +/***************************************************************************** + * PropertiesDAC + ***************************************************************************** + * Properties for the DAC node. + */ +static PCPROPERTY_ITEM PropertiesDAC[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CHANNEL_CONFIG, + KSPROPERTY_TYPE_SET, + CMiniport::PropertyChannelConfig + } +}; + +/***************************************************************************** + * AutomationVolume + ***************************************************************************** + * Automation table for volume controls. + */ +DEFINE_PCAUTOMATION_TABLE_PROP (AutomationDAC, PropertiesDAC); + +/***************************************************************************** + * TopologyNodes + ***************************************************************************** + * List of nodes. + */ +static PCNODE_DESCRIPTOR MiniportNodes[] = +{ + // NODE_WAVEOUT_DAC + { + 0, // Flags + &AutomationDAC, // AutomationTable + &KSNODETYPE_DAC, // Type + NULL // Name + }, + // NODE_WAVEIN_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + }, + // + // The Microphone node is not used if PINC_MICIN_PRESENT is not set. + // To remove them, Init() will reduce the "NodeCount" in the + // MiniportFilterDescriptor. + // + // NODE_MICIN_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +/***************************************************************************** + * MiniportConnections + ***************************************************************************** + * This structure identifies the connections between filter pins and + * node pins. + */ +static PCCONNECTION_DESCRIPTOR MiniportConnections[] = +{ + //from_node from_pin to_node to_pin + { PCFILTER_NODE, PIN_WAVEOUT, NODE_WAVEOUT_DAC, 1}, + { NODE_WAVEOUT_DAC, 0, PCFILTER_NODE, PIN_WAVEOUT_BRIDGE}, + { PCFILTER_NODE, PIN_WAVEIN_BRIDGE, NODE_WAVEIN_ADC, 1}, + { NODE_WAVEIN_ADC, 0, PCFILTER_NODE, PIN_WAVEIN}, + // + // The Microphone connection is not used if PINC_MICIN_PRESENT is not set. + // To remove them, Init() will reduce the "ConnectionCount" in the + // MiniportFilterDescriptor. + // + { PCFILTER_NODE, PIN_MICIN_BRIDGE, NODE_MICIN_ADC, 1}, + { NODE_MICIN_ADC, 0, PCFILTER_NODE, PIN_MICIN} +}; + +/***************************************************************************** + * MiniportFilterDescriptor + ***************************************************************************** + * Complete miniport description. + * Init() modifies the pin count, node count and connection count in absence + * of the MicIn recording line. + */ +static PCFILTER_DESCRIPTOR MiniportFilterDescriptor = +{ + 0, // Version + NULL, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MiniportPins), // PinCount + MiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MiniportNodes), // NodeCount + MiniportNodes, // Nodes + SIZEOF_ARRAY(MiniportConnections), // ConnectionCount + MiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories: NULL->use defaults (audio, render, capture) +}; + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif +/***************************************************************************** + * CMiniport::PropertyChannelConfig + ***************************************************************************** + * This is the property handler for KSPROPERTY_AUDIO_CHANNEL_CONFIG of the + * DAC node. It sets the channel configuration (how many channels, how user + * was setting up the speakers). + */ +NTSTATUS CMiniport::PropertyChannelConfig +( + IN PPCPROPERTY_REQUEST PropertyRequest +) +{ + PAGED_CODE (); + + ASSERT (PropertyRequest); + + DOUT (DBG_PRINT, ("[CMiniport::PropertyChannelConfig]")); + + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + // The major target is the object pointer to the wave miniport. + // HACK ALERT - unsafe pointer cast - HACK ALERT + CMiniport *that = (CMiniport *)(CMiniportWaveICH*) + (PMINIPORTWAVEPCI)PropertyRequest->MajorTarget; + + ASSERT (that); + + // We only have a set defined. + if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + // validate buffer size. + if (PropertyRequest->ValueSize < sizeof(LONG)) + return ntStatus; + + // The "Value" is the input buffer with the channel config. + if (PropertyRequest->Value) + { + // We can accept different channel configurations, depending + // on the number of channels we can play. + if (that->AdapterCommon->GetPinConfig (PINC_SURROUND_PRESENT)) + { + if (that->AdapterCommon->GetPinConfig (PINC_CENTER_LFE_PRESENT)) + { + // we accept 5.1 + if (*(PLONG)PropertyRequest->Value == KSAUDIO_SPEAKER_5POINT1) + { + that->m_dwChannelMask = *(PLONG)PropertyRequest->Value; + that->m_wChannels = 6; + that->AdapterCommon->WriteChannelConfigDefault (that->m_dwChannelMask); + ntStatus = STATUS_SUCCESS; + } + } + + // accept also surround or quad. + if ((*(PLONG)PropertyRequest->Value == KSAUDIO_SPEAKER_QUAD) || + (*(PLONG)PropertyRequest->Value == KSAUDIO_SPEAKER_SURROUND)) + { + that->m_dwChannelMask = *(PLONG)PropertyRequest->Value; + that->m_wChannels = 4; + that->AdapterCommon->WriteChannelConfigDefault (that->m_dwChannelMask); + ntStatus = STATUS_SUCCESS; + } + } + + // accept also stereo speakers. + if (*(PLONG)PropertyRequest->Value == KSAUDIO_SPEAKER_STEREO) + { + that->m_dwChannelMask = *(PLONG)PropertyRequest->Value; + that->m_wChannels = 2; + that->AdapterCommon->WriteChannelConfigDefault (that->m_dwChannelMask); + ntStatus = STATUS_SUCCESS; + } + } + } + + return ntStatus; +} + + +/***************************************************************************** + * CMiniport::BuildDataRangeInformation + ***************************************************************************** + * This function dynamically build the data range information for the pins. + * It also connects the static arrays with the data range information + * structure. + * If this function returns with an error the miniport should be destroyed. + * + * To build the data range information, we test the most popular sample rates, ... 10011 lines suppressed ...