├── nau8825 ├── stdint.h ├── resource.h ├── hidcommon.h ├── nau8825.rc ├── spb.h ├── trace.h ├── nau8825.inf ├── nau8825.vcxproj ├── nau8825.h ├── spb.c ├── registers.h └── nau8825.c ├── README.md ├── nau8825.sln ├── LICENSE.txt ├── .gitignore └── nau8825 Package └── nau8825 Package.vcxproj /nau8825/stdint.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define BIT(nr) (1UL << (nr)) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nau8825 2 | NAU8825 Headphone Codec Driver 3 | 4 | Tested on Acer C771 Chromebook. Should work for several Skylake Chromebooks. -------------------------------------------------------------------------------- /nau8825/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by nau8825.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /nau8825/hidcommon.h: -------------------------------------------------------------------------------- 1 | #if !defined(_NAU8825_COMMON_H_) 2 | #define _NAU8825_COMMON_H_ 3 | 4 | // 5 | //These are the device attributes returned by vmulti in response 6 | // to IOCTL_HID_GET_DEVICE_ATTRIBUTES. 7 | // 8 | 9 | #define NAU8825_PID 0x8825 10 | #define NAU8825_VID 0x1050 11 | #define NAU8825_VERSION 0x0001 12 | 13 | // 14 | // These are the report ids 15 | // 16 | 17 | #define REPORTID_MEDIA 0x01 18 | #define REPORTID_SPECKEYS 0x02 19 | 20 | #pragma pack(1) 21 | typedef struct _NAU8825_MEDIA_REPORT 22 | { 23 | 24 | BYTE ReportID; 25 | 26 | BYTE ControlCode; 27 | 28 | } Nau8825MediaReport; 29 | #pragma pack() 30 | 31 | #define CONTROL_CODE_JACK_TYPE 0x1 32 | 33 | #pragma pack(1) 34 | typedef struct _CSAUDIO_SPECKEY_REPORT 35 | { 36 | 37 | BYTE ReportID; 38 | 39 | BYTE ControlCode; 40 | 41 | BYTE ControlValue; 42 | 43 | } CsAudioSpecialKeyReport; 44 | 45 | #pragma pack() 46 | 47 | #pragma pack(1) 48 | typedef struct _CSAUDIO_SPECKEYREQ_REPORT 49 | { 50 | 51 | BYTE ReportID; 52 | 53 | BYTE AnyCode; 54 | 55 | } CsAudioSpecialKeyRequestReport; 56 | #pragma pack() 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /nau8825/nau8825.rc: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Microsoft Corporation All Rights Reserved 4 | 5 | Module Name: 6 | 7 | nau8825.rc 8 | 9 | Abstract: 10 | 11 | --*/ 12 | 13 | #include 14 | 15 | #define VER_FILETYPE VFT_DRV 16 | #define VER_FILESUBTYPE VFT2_DRV_SOUND 17 | #define VER_FILEDESCRIPTION_STR "Nuvoton Nau88L25 Codec" 18 | #define VER_INTERNALNAME_STR "nau8825.sys" 19 | #define VER_ORIGINALFILENAME_STR "nau8825.sys" 20 | 21 | #define VER_LEGALCOPYRIGHT_YEARS "2023" 22 | #define VER_LEGALCOPYRIGHT_STR "Copyright (C) " VER_LEGALCOPYRIGHT_YEARS " CoolStar." 23 | 24 | #define VER_FILEVERSION 1,0,0,0 25 | #define VER_PRODUCTVERSION_STR "1.0.0.0" 26 | #define VER_PRODUCTVERSION 1,0,0,0 27 | #define LVER_PRODUCTVERSION_STR L"1.0.0.0" 28 | 29 | #define VER_FILEFLAGSMASK (VS_FF_DEBUG | VS_FF_PRERELEASE) 30 | #ifdef DEBUG 31 | #define VER_FILEFLAGS (VS_FF_DEBUG) 32 | #else 33 | #define VER_FILEFLAGS (0) 34 | #endif 35 | 36 | #define VER_FILEOS VOS_NT_WINDOWS32 37 | 38 | #define VER_COMPANYNAME_STR "CoolStar" 39 | #define VER_PRODUCTNAME_STR "Nuvoton Nau88L25 Codec" 40 | 41 | #include "common.ver" -------------------------------------------------------------------------------- /nau8825/spb.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | Copyright (c) Microsoft Corporation. All Rights Reserved. 3 | Sample code. Dealpoint ID #843729. 4 | 5 | Module Name: 6 | 7 | spb.h 8 | 9 | Abstract: 10 | 11 | This module contains the touch driver I2C helper definitions. 12 | 13 | Environment: 14 | 15 | Kernel Mode 16 | 17 | Revision History: 18 | 19 | --*/ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | 26 | #define DEFAULT_SPB_BUFFER_SIZE 64 27 | #define RESHUB_USE_HELPER_ROUTINES 28 | 29 | // 30 | // SPB (I2C) context 31 | // 32 | 33 | typedef struct _SPB_CONTEXT 34 | { 35 | WDFIOTARGET SpbIoTarget; 36 | LARGE_INTEGER I2cResHubId; 37 | WDFMEMORY WriteMemory; 38 | WDFMEMORY ReadMemory; 39 | WDFWAITLOCK SpbLock; 40 | } SPB_CONTEXT; 41 | 42 | NTSTATUS 43 | SpbXferDataSynchronously( 44 | _In_ SPB_CONTEXT* SpbContext, 45 | _In_ PVOID SendData, 46 | _In_ ULONG SendLength, 47 | _In_reads_bytes_(Length) PVOID Data, 48 | _In_ ULONG Length 49 | ); 50 | 51 | VOID 52 | SpbTargetDeinitialize( 53 | IN WDFDEVICE FxDevice, 54 | IN SPB_CONTEXT* SpbContext 55 | ); 56 | 57 | NTSTATUS 58 | SpbTargetInitialize( 59 | IN WDFDEVICE FxDevice, 60 | IN SPB_CONTEXT* SpbContext 61 | ); 62 | 63 | NTSTATUS 64 | SpbWriteDataSynchronously( 65 | IN SPB_CONTEXT* SpbContext, 66 | IN PVOID Data, 67 | IN ULONG Length 68 | ); -------------------------------------------------------------------------------- /nau8825/trace.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef _TRACE_H_ 4 | #define _TRACE_H_ 5 | 6 | extern "C" 7 | { 8 | // 9 | // Tracing Definitions: 10 | // 11 | // Control GUID: 12 | // {73e3b785-f5fb-423e-94a9-56627fea9053} 13 | // 14 | 15 | #define WPP_CONTROL_GUIDS \ 16 | WPP_DEFINE_CONTROL_GUID( \ 17 | SpbTestToolTraceGuid, \ 18 | (73e3b785,f5fb,423e,94a9,56627fea9053), \ 19 | WPP_DEFINE_BIT(TRACE_FLAG_WDFLOADING) \ 20 | WPP_DEFINE_BIT(TRACE_FLAG_SPBAPI) \ 21 | WPP_DEFINE_BIT(TRACE_FLAG_OTHER) \ 22 | ) 23 | } 24 | 25 | #define WPP_LEVEL_FLAGS_LOGGER(level,flags) WPP_LEVEL_LOGGER(flags) 26 | #define WPP_LEVEL_FLAGS_ENABLED(level, flags) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= level) 27 | 28 | #define Trace CyapaPrint 29 | #define FuncEntry 30 | #define FuncExit 31 | #define WPP_INIT_TRACING 32 | #define WPP_CLEANUP 33 | #define TRACE_FLAG_SPBAPI 0 34 | #define TRACE_FLAG_WDFLOADING 0 35 | 36 | // begin_wpp config 37 | // FUNC FuncEntry{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS); 38 | // FUNC FuncExit{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS); 39 | // USEPREFIX(FuncEntry, "%!STDPREFIX! [%!FUNC!] --> entry"); 40 | // USEPREFIX(FuncExit, "%!STDPREFIX! [%!FUNC!] <--"); 41 | // end_wpp 42 | 43 | #endif _TRACE_H_ 44 | -------------------------------------------------------------------------------- /nau8825/nau8825.inf: -------------------------------------------------------------------------------- 1 | ;/*++ 2 | ; 3 | ;Copyright (c) CoolStar. All rights reserved. 4 | ; 5 | ;Module Name: 6 | ; coolstar.inf 7 | ; 8 | ;Abstract: 9 | ; INF file for installing the NAU8825 Driver 10 | ; 11 | ; 12 | ;--*/ 13 | 14 | [Version] 15 | Signature = "$WINDOWS NT$" 16 | Class = Media 17 | ClassGuid = {4d36e96c-e325-11ce-bfc1-08002be10318} 18 | Provider = CoolStar 19 | DriverVer = 2/25/2022,1.0.0 20 | CatalogFile = nau8825.cat 21 | PnpLockdown=1 22 | 23 | [DestinationDirs] 24 | DefaultDestDir = 12 25 | 26 | ; ================= Class section ===================== 27 | 28 | [SourceDisksNames] 29 | 1 = %DiskId1%,,,"" 30 | 31 | [SourceDisksFiles] 32 | nau8825.sys = 1,, 33 | 34 | ;***************************************** 35 | ; nau8825 Install Section 36 | ;***************************************** 37 | 38 | [Manufacturer] 39 | %StdMfg%=Standard,NTAMD64 40 | 41 | ; Decorated model section take precedence over undecorated 42 | ; ones on XP and later. 43 | [Standard.NTAMD64] 44 | %nau8825.DeviceDesc%=nau8825_Device, ACPI\10508825 45 | 46 | [nau8825_Device.NT] 47 | CopyFiles=Drivers_Dir 48 | 49 | [nau8825_Device.NT.HW] 50 | AddReg=nau8825_AddReg 51 | 52 | [Drivers_Dir] 53 | nau8825.sys 54 | 55 | [nau8825_AddReg] 56 | ; Set to 1 to connect the first interrupt resource found, 0 to leave disconnected 57 | HKR,Settings,"ConnectInterrupt",0x00010001,0 58 | HKR,,"UpperFilters",0x00010000,"mshidkmdf" 59 | 60 | ;-------------- Service installation 61 | [nau8825_Device.NT.Services] 62 | AddService = nau8825,%SPSVCINST_ASSOCSERVICE%, nau8825_Service_Inst 63 | 64 | ; -------------- nau8825 driver install sections 65 | [nau8825_Service_Inst] 66 | DisplayName = %nau8825.SVCDESC% 67 | ServiceType = 1 ; SERVICE_KERNEL_DRIVER 68 | StartType = 3 ; SERVICE_DEMAND_START 69 | ErrorControl = 1 ; SERVICE_ERROR_NORMAL 70 | ServiceBinary = %12%\nau8825.sys 71 | LoadOrderGroup = Base 72 | 73 | [Strings] 74 | SPSVCINST_ASSOCSERVICE= 0x00000002 75 | StdMfg = "CoolStar" 76 | DiskId1 = "nau8825 Installation Disk #1" 77 | nau8825.DeviceDesc = "Nuvoton Nau88L25 Codec" 78 | nau8825.SVCDESC = "nau8825 Service" 79 | -------------------------------------------------------------------------------- /nau8825.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31829.152 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nau8825", "nau8825\nau8825.vcxproj", "{36580C07-EDC3-4C2B-B45F-6AB017E01A5D}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nau8825 Package", "nau8825 Package\nau8825 Package.vcxproj", "{3DAE7ED3-003A-4495-8352-3D7B5B5D846F}" 9 | ProjectSection(ProjectDependencies) = postProject 10 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D} = {36580C07-EDC3-4C2B-B45F-6AB017E01A5D} 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Win32 = Debug|Win32 16 | Debug|x64 = Debug|x64 17 | Release|Win32 = Release|Win32 18 | Release|x64 = Release|x64 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D}.Debug|Win32.ActiveCfg = Debug|Win32 22 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D}.Debug|Win32.Build.0 = Debug|Win32 23 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D}.Debug|Win32.Deploy.0 = Debug|Win32 24 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D}.Debug|x64.ActiveCfg = Release|x64 25 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D}.Debug|x64.Build.0 = Release|x64 26 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D}.Debug|x64.Deploy.0 = Release|x64 27 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D}.Release|Win32.ActiveCfg = Release|Win32 28 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D}.Release|Win32.Build.0 = Release|Win32 29 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D}.Release|Win32.Deploy.0 = Release|Win32 30 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D}.Release|x64.ActiveCfg = Release|x64 31 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D}.Release|x64.Build.0 = Release|x64 32 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D}.Release|x64.Deploy.0 = Release|x64 33 | {3DAE7ED3-003A-4495-8352-3D7B5B5D846F}.Debug|Win32.ActiveCfg = Debug|Win32 34 | {3DAE7ED3-003A-4495-8352-3D7B5B5D846F}.Debug|Win32.Build.0 = Debug|Win32 35 | {3DAE7ED3-003A-4495-8352-3D7B5B5D846F}.Debug|Win32.Deploy.0 = Debug|Win32 36 | {3DAE7ED3-003A-4495-8352-3D7B5B5D846F}.Debug|x64.ActiveCfg = Release|x64 37 | {3DAE7ED3-003A-4495-8352-3D7B5B5D846F}.Debug|x64.Build.0 = Release|x64 38 | {3DAE7ED3-003A-4495-8352-3D7B5B5D846F}.Debug|x64.Deploy.0 = Release|x64 39 | {3DAE7ED3-003A-4495-8352-3D7B5B5D846F}.Release|Win32.ActiveCfg = Release|Win32 40 | {3DAE7ED3-003A-4495-8352-3D7B5B5D846F}.Release|Win32.Build.0 = Release|Win32 41 | {3DAE7ED3-003A-4495-8352-3D7B5B5D846F}.Release|Win32.Deploy.0 = Release|Win32 42 | {3DAE7ED3-003A-4495-8352-3D7B5B5D846F}.Release|x64.ActiveCfg = Release|x64 43 | {3DAE7ED3-003A-4495-8352-3D7B5B5D846F}.Release|x64.Build.0 = Release|x64 44 | {3DAE7ED3-003A-4495-8352-3D7B5B5D846F}.Release|x64.Deploy.0 = Release|x64 45 | EndGlobalSection 46 | GlobalSection(SolutionProperties) = preSolution 47 | HideSolutionNode = FALSE 48 | EndGlobalSection 49 | GlobalSection(ExtensibilityGlobals) = postSolution 50 | SolutionGuid = {9976A14A-3F14-4DE9-A9B7-4F14DEDB8D18} 51 | EndGlobalSection 52 | EndGlobal 53 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2021 CoolStar 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | 16 | ====================== Windows Driver Samples License ====================== 17 | 18 | The Microsoft Public License (MS-PL) 19 | Copyright (c) 2015 Microsoft 20 | 21 | This license governs use of the accompanying software. If you use the software, you 22 | accept this license. If you do not accept the license, do not use the software. 23 | 24 | 1. Definitions 25 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the 26 | same meaning here as under U.S. copyright law. 27 | A "contribution" is the original software, or any additions or changes to the software. 28 | A "contributor" is any person that distributes its contribution under this license. 29 | "Licensed patents" are a contributor's patent claims that read directly on its contribution. 30 | 31 | 2. Grant of Rights 32 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. 33 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 34 | 35 | 3. Conditions and Limitations 36 | (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. 37 | (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. 38 | (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. 39 | (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. 40 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. -------------------------------------------------------------------------------- /nau8825/nau8825.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {36580C07-EDC3-4C2B-B45F-6AB017E01A5D} 23 | {1bc93793-694f-48fe-9372-81e2b05556fd} 24 | v4.5 25 | 11.0 26 | Win8.1 Debug 27 | Win32 28 | nau8825 29 | $(LatestTargetPlatformVersion) 30 | 31 | 32 | 33 | 34 | 35 | true 36 | WindowsKernelModeDriver10.0 37 | Driver 38 | KMDF 39 | 40 | 41 | 42 | 43 | false 44 | WindowsKernelModeDriver10.0 45 | Driver 46 | KMDF 47 | 48 | 49 | 50 | 51 | true 52 | WindowsKernelModeDriver10.0 53 | Driver 54 | KMDF 55 | 56 | 57 | 58 | 59 | false 60 | WindowsKernelModeDriver10.0 61 | Driver 62 | KMDF 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | DbgengKernelDebugger 74 | 75 | 76 | DbgengKernelDebugger 77 | 78 | 79 | DbgengKernelDebugger 80 | 81 | 82 | DbgengKernelDebugger 83 | 84 | 85 | 86 | true 87 | trace.h 88 | true 89 | false 90 | 91 | 92 | 1.0.0 93 | 94 | 95 | 96 | 97 | true 98 | trace.h 99 | true 100 | false 101 | 102 | 103 | 1.0.0 104 | 105 | 106 | 107 | 108 | true 109 | trace.h 110 | true 111 | false 112 | 113 | 114 | 1.0.0 115 | 116 | 117 | 118 | 119 | true 120 | trace.h 121 | true 122 | false 123 | 124 | 125 | 1.0.0 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015/2017 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # Visual Studio 2017 auto generated files 34 | Generated\ Files/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | 44 | # Build Results of an ATL Project 45 | [Dd]ebugPS/ 46 | [Rr]eleasePS/ 47 | dlldata.c 48 | 49 | # Benchmark Results 50 | BenchmarkDotNet.Artifacts/ 51 | 52 | # .NET Core 53 | project.lock.json 54 | project.fragment.lock.json 55 | artifacts/ 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_h.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *_wpftmp.csproj 81 | *.log 82 | *.vspscc 83 | *.vssscc 84 | .builds 85 | *.pidb 86 | *.svclog 87 | *.scc 88 | 89 | # Chutzpah Test files 90 | _Chutzpah* 91 | 92 | # Visual C++ cache files 93 | ipch/ 94 | *.aps 95 | *.ncb 96 | *.opendb 97 | *.opensdf 98 | *.sdf 99 | *.cachefile 100 | *.VC.db 101 | *.VC.VC.opendb 102 | 103 | # Visual Studio profiler 104 | *.psess 105 | *.vsp 106 | *.vspx 107 | *.sap 108 | 109 | # Visual Studio Trace Files 110 | *.e2e 111 | 112 | # TFS 2012 Local Workspace 113 | $tf/ 114 | 115 | # Guidance Automation Toolkit 116 | *.gpState 117 | 118 | # ReSharper is a .NET coding add-in 119 | _ReSharper*/ 120 | *.[Rr]e[Ss]harper 121 | *.DotSettings.user 122 | 123 | # JustCode is a .NET coding add-in 124 | .JustCode 125 | 126 | # TeamCity is a build add-in 127 | _TeamCity* 128 | 129 | # DotCover is a Code Coverage Tool 130 | *.dotCover 131 | 132 | # AxoCover is a Code Coverage Tool 133 | .axoCover/* 134 | !.axoCover/settings.json 135 | 136 | # Visual Studio code coverage results 137 | *.coverage 138 | *.coveragexml 139 | 140 | # NCrunch 141 | _NCrunch_* 142 | .*crunch*.local.xml 143 | nCrunchTemp_* 144 | 145 | # MightyMoose 146 | *.mm.* 147 | AutoTest.Net/ 148 | 149 | # Web workbench (sass) 150 | .sass-cache/ 151 | 152 | # Installshield output folder 153 | [Ee]xpress/ 154 | 155 | # DocProject is a documentation generator add-in 156 | DocProject/buildhelp/ 157 | DocProject/Help/*.HxT 158 | DocProject/Help/*.HxC 159 | DocProject/Help/*.hhc 160 | DocProject/Help/*.hhk 161 | DocProject/Help/*.hhp 162 | DocProject/Help/Html2 163 | DocProject/Help/html 164 | 165 | # Click-Once directory 166 | publish/ 167 | 168 | # Publish Web Output 169 | *.[Pp]ublish.xml 170 | *.azurePubxml 171 | # Note: Comment the next line if you want to checkin your web deploy settings, 172 | # but database connection strings (with potential passwords) will be unencrypted 173 | *.pubxml 174 | *.publishproj 175 | 176 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 177 | # checkin your Azure Web App publish settings, but sensitive information contained 178 | # in these scripts will be unencrypted 179 | PublishScripts/ 180 | 181 | # NuGet Packages 182 | *.nupkg 183 | # The packages folder can be ignored because of Package Restore 184 | **/[Pp]ackages/* 185 | # except build/, which is used as an MSBuild target. 186 | !**/[Pp]ackages/build/ 187 | # Uncomment if necessary however generally it will be regenerated when needed 188 | #!**/[Pp]ackages/repositories.config 189 | # NuGet v3's project.json files produces more ignorable files 190 | *.nuget.props 191 | *.nuget.targets 192 | 193 | # Microsoft Azure Build Output 194 | csx/ 195 | *.build.csdef 196 | 197 | # Microsoft Azure Emulator 198 | ecf/ 199 | rcf/ 200 | 201 | # Windows Store app package directories and files 202 | AppPackages/ 203 | BundleArtifacts/ 204 | Package.StoreAssociation.xml 205 | _pkginfo.txt 206 | *.appx 207 | 208 | # Visual Studio cache files 209 | # files ending in .cache can be ignored 210 | *.[Cc]ache 211 | # but keep track of directories ending in .cache 212 | !*.[Cc]ache/ 213 | 214 | # Others 215 | ClientBin/ 216 | ~$* 217 | *~ 218 | *.dbmdl 219 | *.dbproj.schemaview 220 | *.jfm 221 | *.pfx 222 | *.publishsettings 223 | orleans.codegen.cs 224 | 225 | # Including strong name files can present a security risk 226 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 227 | #*.snk 228 | 229 | # Since there are multiple workflows, uncomment next line to ignore bower_components 230 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 231 | #bower_components/ 232 | 233 | # RIA/Silverlight projects 234 | Generated_Code/ 235 | 236 | # Backup & report files from converting an old project file 237 | # to a newer Visual Studio version. Backup files are not needed, 238 | # because we have git ;-) 239 | _UpgradeReport_Files/ 240 | Backup*/ 241 | UpgradeLog*.XML 242 | UpgradeLog*.htm 243 | ServiceFabricBackup/ 244 | *.rptproj.bak 245 | 246 | # SQL Server files 247 | *.mdf 248 | *.ldf 249 | *.ndf 250 | 251 | # Business Intelligence projects 252 | *.rdl.data 253 | *.bim.layout 254 | *.bim_*.settings 255 | *.rptproj.rsuser 256 | 257 | # Microsoft Fakes 258 | FakesAssemblies/ 259 | 260 | # GhostDoc plugin setting file 261 | *.GhostDoc.xml 262 | 263 | # Node.js Tools for Visual Studio 264 | .ntvs_analysis.dat 265 | node_modules/ 266 | 267 | # Visual Studio 6 build log 268 | *.plg 269 | 270 | # Visual Studio 6 workspace options file 271 | *.opt 272 | 273 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 274 | *.vbw 275 | 276 | # Visual Studio LightSwitch build output 277 | **/*.HTMLClient/GeneratedArtifacts 278 | **/*.DesktopClient/GeneratedArtifacts 279 | **/*.DesktopClient/ModelManifest.xml 280 | **/*.Server/GeneratedArtifacts 281 | **/*.Server/ModelManifest.xml 282 | _Pvt_Extensions 283 | 284 | # Paket dependency manager 285 | .paket/paket.exe 286 | paket-files/ 287 | 288 | # FAKE - F# Make 289 | .fake/ 290 | 291 | # JetBrains Rider 292 | .idea/ 293 | *.sln.iml 294 | 295 | # CodeRush personal settings 296 | .cr/personal 297 | 298 | # Python Tools for Visual Studio (PTVS) 299 | __pycache__/ 300 | *.pyc 301 | 302 | # Cake - Uncomment if you are using it 303 | # tools/** 304 | # !tools/packages.config 305 | 306 | # Tabs Studio 307 | *.tss 308 | 309 | # Telerik's JustMock configuration file 310 | *.jmconfig 311 | 312 | # BizTalk build output 313 | *.btp.cs 314 | *.btm.cs 315 | *.odx.cs 316 | *.xsd.cs 317 | 318 | # OpenCover UI analysis results 319 | OpenCover/ 320 | 321 | # Azure Stream Analytics local run output 322 | ASALocalRun/ 323 | 324 | # MSBuild Binary and Structured Log 325 | *.binlog 326 | 327 | # NVidia Nsight GPU debugger configuration file 328 | *.nvuser 329 | 330 | # MFractors (Xamarin productivity tool) working folder 331 | .mfractor/ 332 | 333 | # Local History for Visual Studio 334 | .localhistory/ 335 | -------------------------------------------------------------------------------- /nau8825 Package/nau8825 Package.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {3DAE7ED3-003A-4495-8352-3D7B5B5D846F} 23 | {4605da2c-74a5-4865-98e1-152ef136825f} 24 | v4.5 25 | 11.0 26 | Win8.1 Debug 27 | Win32 28 | nau8825_Package 29 | $(LatestTargetPlatformVersion) 30 | 31 | 32 | 33 | 34 | 35 | true 36 | WindowsKernelModeDriver10.0 37 | Utility 38 | Package 39 | true 40 | 41 | 42 | 43 | 44 | false 45 | WindowsKernelModeDriver10.0 46 | Utility 47 | Package 48 | true 49 | 50 | 51 | 52 | 53 | true 54 | WindowsKernelModeDriver10.0 55 | Utility 56 | Package 57 | true 58 | 59 | 60 | 61 | 62 | false 63 | WindowsKernelModeDriver10.0 64 | Utility 65 | Package 66 | true 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | DbgengKernelDebugger 78 | False 79 | True 80 | 81 | 82 | 83 | False 84 | False 85 | True 86 | 87 | 133563 88 | 89 | 90 | DbgengKernelDebugger 91 | False 92 | True 93 | 94 | 95 | 96 | False 97 | False 98 | True 99 | 100 | 133563 101 | 102 | 103 | DbgengKernelDebugger 104 | False 105 | True 106 | 107 | 108 | 109 | False 110 | False 111 | True 112 | 113 | 133563 114 | 115 | 116 | DbgengKernelDebugger 117 | False 118 | True 119 | 120 | 121 | 122 | False 123 | False 124 | True 125 | 126 | 133563 127 | 128 | 129 | 130 | SHA256 131 | 132 | 133 | 134 | 135 | SHA256 136 | 137 | 138 | 139 | 140 | SHA256 141 | 142 | 143 | 144 | 145 | SHA256 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | {36580c07-edc3-4c2b-b45f-6ab017e01a5d} 154 | 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /nau8825/nau8825.h: -------------------------------------------------------------------------------- 1 | #if !defined(_NAU8825_H_) 2 | #define _NAU8825_H_ 3 | 4 | #pragma warning(disable:4200) // suppress nameless struct/union warning 5 | #pragma warning(disable:4201) // suppress nameless struct/union warning 6 | #pragma warning(disable:4214) // suppress bit field types other than int warning 7 | #include 8 | #include 9 | 10 | #pragma warning(default:4200) 11 | #pragma warning(default:4201) 12 | #pragma warning(default:4214) 13 | #include 14 | 15 | #pragma warning(disable:4201) // suppress nameless struct/union warning 16 | #pragma warning(disable:4214) // suppress bit field types other than int warning 17 | #include 18 | 19 | #include 20 | 21 | #include "hidcommon.h" 22 | 23 | #include 24 | 25 | #include "spb.h" 26 | 27 | enum snd_jack_types { 28 | SND_JACK_HEADPHONE = 0x0001, 29 | SND_JACK_MICROPHONE = 0x0002, 30 | SND_JACK_HEADSET = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE, 31 | }; 32 | 33 | // 34 | // String definitions 35 | // 36 | 37 | #define DRIVERNAME "da7219.sys: " 38 | 39 | #define NAU8825_POOL_TAG (ULONG) '5288' 40 | 41 | #define true 1 42 | #define false 0 43 | 44 | typedef enum { 45 | CSAudioEndpointTypeDSP, 46 | CSAudioEndpointTypeSpeaker, 47 | CSAudioEndpointTypeHeadphone, 48 | CSAudioEndpointTypeMicArray, 49 | CSAudioEndpointTypeMicJack 50 | } CSAudioEndpointType; 51 | 52 | typedef enum { 53 | CSAudioEndpointRegister, 54 | CSAudioEndpointStart, 55 | CSAudioEndpointStop, 56 | CSAudioEndpointOverrideFormat, 57 | CSAudioEndpointI2SParameters 58 | } CSAudioEndpointRequest; 59 | 60 | typedef struct CSAUDIOFORMATOVERRIDE { 61 | UINT16 channels; 62 | UINT16 frequency; 63 | UINT16 bitsPerSample; 64 | UINT16 validBitsPerSample; 65 | BOOLEAN force32BitOutputContainer; 66 | } CsAudioFormatOverride; 67 | 68 | typedef struct CSAUDIOI2SPARAMS { 69 | UINT32 version; 70 | 71 | UINT32 mclk; 72 | UINT32 bclk_rate; 73 | UINT32 frequency; 74 | UINT32 tdm_slots; 75 | UINT32 tdm_slot_width; 76 | UINT32 rx_slots; 77 | UINT32 tx_slots; 78 | UINT32 valid_bits; //end of version 1 79 | } CsAudioI2SParameters; 80 | 81 | typedef struct CSAUDIOARG { 82 | UINT32 argSz; 83 | CSAudioEndpointType endpointType; 84 | CSAudioEndpointRequest endpointRequest; 85 | union { 86 | CsAudioFormatOverride formatOverride; 87 | CsAudioI2SParameters i2sParameters; 88 | }; 89 | } CsAudioArg, * PCsAudioArg; 90 | 91 | typedef UCHAR HID_REPORT_DESCRIPTOR, * PHID_REPORT_DESCRIPTOR; 92 | 93 | #ifdef DESCRIPTOR_DEF 94 | HID_REPORT_DESCRIPTOR DefaultReportDescriptor[] = { 95 | // 96 | // Consumer Control starts here 97 | // 98 | 0x05, 0x0C, /* Usage Page (Consumer Devices) */ 99 | 0x09, 0x01, /* Usage (Consumer Control) */ 100 | 0xA1, 0x01, /* Collection (Application) */ 101 | 0x85, REPORTID_MEDIA, /* Report ID=1 */ 102 | 0x05, 0x0C, /* Usage Page (Consumer Devices) */ 103 | 0x15, 0x00, /* Logical Minimum (0) */ 104 | 0x25, 0x01, /* Logical Maximum (1) */ 105 | 0x75, 0x01, /* Report Size (1) */ 106 | 0x95, 0x04, /* Report Count (4) */ 107 | 0x09, 0xCD, /* Usage (Play / Pause) */ 108 | 0x09, 0xCF, /* Usage (Voice Command) */ 109 | 0x09, 0xE9, /* Usage (Volume Up) */ 110 | 0x09, 0xEA, /* Usage (Volume Down) */ 111 | 0x81, 0x02, /* Input (Data, Variable, Absolute) */ 112 | 0x95, 0x04, /* Report Count (4) */ 113 | 0x81, 0x01, /* Input (Constant) */ 114 | 0xC0, /* End Collection */ 115 | 116 | 0x06, 0x00, 0xff, // USAGE_PAGE (Vendor Defined Page 1) 117 | 0x09, 0x04, // USAGE (Vendor Usage 4) 118 | 0xa1, 0x01, // COLLECTION (Application) 119 | 0x85, REPORTID_SPECKEYS, // REPORT_ID (Special Keys) 120 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 121 | 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (256) 122 | 0x75, 0x08, // REPORT_SIZE (8) - bits 123 | 0x95, 0x01, // REPORT_COUNT (1) - Bytes 124 | 0x09, 0x02, // USAGE (Vendor Usage 1) 125 | 0x81, 0x02, // INPUT (Data,Var,Abs) 126 | 0x09, 0x03, // USAGE (Vendor Usage 2) 127 | 0x81, 0x02, // INPUT (Data,Var,Abs) 128 | 0x09, 0x02, // USAGE (Vendor Usage 1) 129 | 0x91, 0x02, // OUTPUT (Data,Var,Abs) 130 | 0xc0, // END_COLLECTION 131 | }; 132 | 133 | 134 | // 135 | // This is the default HID descriptor returned by the mini driver 136 | // in response to IOCTL_HID_GET_DEVICE_DESCRIPTOR. The size 137 | // of report descriptor is currently the size of DefaultReportDescriptor. 138 | // 139 | 140 | CONST HID_DESCRIPTOR DefaultHidDescriptor = { 141 | 0x09, // length of HID descriptor 142 | 0x21, // descriptor type == HID 0x21 143 | 0x0100, // hid spec release 144 | 0x00, // country code == Not Specified 145 | 0x01, // number of HID class descriptors 146 | { 0x22, // descriptor type 147 | sizeof(DefaultReportDescriptor) } // total length of report descriptor 148 | }; 149 | #endif 150 | 151 | typedef struct _NAU8825_CONTEXT 152 | { 153 | 154 | WDFDEVICE FxDevice; 155 | 156 | WDFQUEUE ReportQueue; 157 | 158 | SPB_CONTEXT I2CContext; 159 | 160 | BOOLEAN DevicePoweredOn; 161 | 162 | WDFINTERRUPT Interrupt; 163 | 164 | INT JackType; 165 | 166 | PCALLBACK_OBJECT CSAudioAPICallback; 167 | PVOID CSAudioAPICallbackObj; 168 | 169 | BOOLEAN CSAudioManaged; 170 | 171 | BOOLEAN ReclockRequested; 172 | UINT32 bclkRate; 173 | UINT32 freq; 174 | UINT32 validBits; 175 | 176 | UINT8 jkdet_enable; 177 | UINT8 jkdet_pull_enable; 178 | UINT8 jkdet_pull_up; 179 | UINT8 jkdet_polarity; 180 | UINT8 vref_impedance; 181 | UINT8 micbias_voltage; 182 | UINT8 sar_threshold_num; 183 | UINT8 sar_threshold[8]; 184 | UINT8 sar_hysteresis; 185 | UINT8 sar_voltage; 186 | UINT8 sar_compare_time; 187 | UINT8 sar_sampling_time; 188 | UINT8 key_debounce; 189 | UINT8 jack_insert_debounce; 190 | UINT8 jack_eject_debounce; 191 | 192 | } NAU8825_CONTEXT, *PNAU8825_CONTEXT; 193 | 194 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(NAU8825_CONTEXT, GetDeviceContext) 195 | 196 | // 197 | // Function definitions 198 | // 199 | 200 | DRIVER_INITIALIZE DriverEntry; 201 | 202 | EVT_WDF_DRIVER_UNLOAD Nau8825DriverUnload; 203 | 204 | EVT_WDF_DRIVER_DEVICE_ADD Nau8825EvtDeviceAdd; 205 | 206 | EVT_WDFDEVICE_WDM_IRP_PREPROCESS Nau8825EvtWdmPreprocessMnQueryId; 207 | 208 | EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL Nau8825EvtInternalDeviceControl; 209 | 210 | NTSTATUS 211 | Nau8825GetHidDescriptor( 212 | IN WDFDEVICE Device, 213 | IN WDFREQUEST Request 214 | ); 215 | 216 | NTSTATUS 217 | Nau8825GetReportDescriptor( 218 | IN WDFDEVICE Device, 219 | IN WDFREQUEST Request 220 | ); 221 | 222 | NTSTATUS 223 | Nau8825GetDeviceAttributes( 224 | IN WDFREQUEST Request 225 | ); 226 | 227 | NTSTATUS 228 | Nau8825GetString( 229 | IN WDFREQUEST Request 230 | ); 231 | 232 | NTSTATUS 233 | Nau8825WriteReport( 234 | IN PNAU8825_CONTEXT DevContext, 235 | IN WDFREQUEST Request 236 | ); 237 | 238 | NTSTATUS 239 | Nau8825ProcessVendorReport( 240 | IN PNAU8825_CONTEXT DevContext, 241 | IN PVOID ReportBuffer, 242 | IN ULONG ReportBufferLen, 243 | OUT size_t* BytesWritten 244 | ); 245 | 246 | NTSTATUS 247 | Nau8825ReadReport( 248 | IN PNAU8825_CONTEXT DevContext, 249 | IN WDFREQUEST Request, 250 | OUT BOOLEAN* CompleteRequest 251 | ); 252 | 253 | NTSTATUS 254 | Nau8825SetFeature( 255 | IN PNAU8825_CONTEXT DevContext, 256 | IN WDFREQUEST Request, 257 | OUT BOOLEAN* CompleteRequest 258 | ); 259 | 260 | NTSTATUS 261 | Nau8825GetFeature( 262 | IN PNAU8825_CONTEXT DevContext, 263 | IN WDFREQUEST Request, 264 | OUT BOOLEAN* CompleteRequest 265 | ); 266 | 267 | PCHAR 268 | DbgHidInternalIoctlString( 269 | IN ULONG IoControlCode 270 | ); 271 | 272 | // 273 | // Helper macros 274 | // 275 | 276 | #define DEBUG_LEVEL_ERROR 1 277 | #define DEBUG_LEVEL_INFO 2 278 | #define DEBUG_LEVEL_VERBOSE 3 279 | 280 | #define DBG_INIT 1 281 | #define DBG_PNP 2 282 | #define DBG_IOCTL 4 283 | 284 | #if 0 285 | #define Nau8825Print(dbglevel, dbgcatagory, fmt, ...) { \ 286 | if (Nau8825DebugLevel >= dbglevel && \ 287 | (Nau8825DebugCatagories && dbgcatagory)) \ 288 | { \ 289 | DbgPrint(DRIVERNAME); \ 290 | DbgPrint(fmt, __VA_ARGS__); \ 291 | } \ 292 | } 293 | #else 294 | #define Nau8825Print(dbglevel, fmt, ...) { \ 295 | } 296 | #endif 297 | 298 | #endif -------------------------------------------------------------------------------- /nau8825/spb.c: -------------------------------------------------------------------------------- 1 | /*++ 2 | Copyright (c) Microsoft Corporation. All Rights Reserved. 3 | Sample code. Dealpoint ID #843729. 4 | 5 | Module Name: 6 | 7 | spb.c 8 | 9 | Abstract: 10 | 11 | Contains all I2C-specific functionality 12 | 13 | Environment: 14 | 15 | Kernel mode 16 | 17 | Revision History: 18 | 19 | --*/ 20 | 21 | #include "nau8825.h" 22 | #include "spb.h" 23 | #include 24 | 25 | static ULONG Nau8825DebugLevel = 100; 26 | static ULONG Nau8825DebugCatagories = DBG_INIT || DBG_PNP || DBG_IOCTL; 27 | 28 | NTSTATUS 29 | SpbDoWriteDataSynchronously( 30 | IN SPB_CONTEXT* SpbContext, 31 | IN PVOID Data, 32 | IN ULONG Length 33 | ) 34 | /*++ 35 | 36 | Routine Description: 37 | 38 | This helper routine abstracts creating and sending an I/O 39 | request (I2C Write) to the Spb I/O target. 40 | 41 | Arguments: 42 | 43 | SpbContext - Pointer to the current device context 44 | Address - The I2C register address to write to 45 | Data - A buffer to receive the data at at the above address 46 | Length - The amount of data to be read from the above address 47 | 48 | Return Value: 49 | 50 | NTSTATUS Status indicating success or failure 51 | 52 | --*/ 53 | { 54 | PUCHAR buffer; 55 | ULONG length; 56 | WDFMEMORY memory; 57 | WDF_MEMORY_DESCRIPTOR memoryDescriptor; 58 | NTSTATUS status; 59 | 60 | length = Length; 61 | memory = NULL; 62 | 63 | if (length > DEFAULT_SPB_BUFFER_SIZE) 64 | { 65 | status = WdfMemoryCreate( 66 | WDF_NO_OBJECT_ATTRIBUTES, 67 | NonPagedPool, 68 | NAU8825_POOL_TAG, 69 | length, 70 | &memory, 71 | (PVOID*)&buffer); 72 | 73 | if (!NT_SUCCESS(status)) 74 | { 75 | Nau8825Print( 76 | DEBUG_LEVEL_ERROR, 77 | DBG_IOCTL, 78 | "Error allocating memory for Spb write - %!STATUS!", 79 | status); 80 | goto exit; 81 | } 82 | 83 | WDF_MEMORY_DESCRIPTOR_INIT_HANDLE( 84 | &memoryDescriptor, 85 | memory, 86 | NULL); 87 | } 88 | else 89 | { 90 | buffer = (PUCHAR)WdfMemoryGetBuffer(SpbContext->WriteMemory, NULL); 91 | 92 | WDF_MEMORY_DESCRIPTOR_INIT_BUFFER( 93 | &memoryDescriptor, 94 | (PVOID)buffer, 95 | length); 96 | } 97 | 98 | RtlCopyMemory(buffer, Data, length); 99 | 100 | status = WdfIoTargetSendWriteSynchronously( 101 | SpbContext->SpbIoTarget, 102 | NULL, 103 | &memoryDescriptor, 104 | NULL, 105 | NULL, 106 | NULL); 107 | 108 | if (!NT_SUCCESS(status)) 109 | { 110 | Nau8825Print( 111 | DEBUG_LEVEL_ERROR, 112 | DBG_IOCTL, 113 | "Error writing to Spb - %!STATUS!", 114 | status); 115 | goto exit; 116 | } 117 | 118 | exit: 119 | 120 | if (NULL != memory) 121 | { 122 | WdfObjectDelete(memory); 123 | } 124 | 125 | return status; 126 | } 127 | 128 | NTSTATUS 129 | SpbWriteDataSynchronously( 130 | IN SPB_CONTEXT* SpbContext, 131 | IN PVOID Data, 132 | IN ULONG Length 133 | ) 134 | /*++ 135 | 136 | Routine Description: 137 | 138 | This routine abstracts creating and sending an I/O 139 | request (I2C Write) to the Spb I/O target and utilizes 140 | a helper routine to do work inside of locked code. 141 | 142 | Arguments: 143 | 144 | SpbContext - Pointer to the current device context 145 | Address - The I2C register address to write to 146 | Data - A buffer to receive the data at at the above address 147 | Length - The amount of data to be read from the above address 148 | 149 | Return Value: 150 | 151 | NTSTATUS Status indicating success or failure 152 | 153 | --*/ 154 | { 155 | NTSTATUS status; 156 | 157 | WdfWaitLockAcquire(SpbContext->SpbLock, NULL); 158 | 159 | status = SpbDoWriteDataSynchronously( 160 | SpbContext, 161 | Data, 162 | Length); 163 | 164 | WdfWaitLockRelease(SpbContext->SpbLock); 165 | 166 | return status; 167 | } 168 | 169 | NTSTATUS 170 | SpbXferDataSynchronously( 171 | _In_ SPB_CONTEXT* SpbContext, 172 | _In_ PVOID SendData, 173 | _In_ ULONG SendLength, 174 | _In_reads_bytes_(Length) PVOID Data, 175 | _In_ ULONG Length 176 | ) 177 | /*++ 178 | Routine Description: 179 | This helper routine abstracts creating and sending an I/O 180 | request (I2C Read) to the Spb I/O target. 181 | Arguments: 182 | SpbContext - Pointer to the current device context 183 | Address - The I2C register address to read from 184 | Data - A buffer to receive the data at at the above address 185 | Length - The amount of data to be read from the above address 186 | Return Value: 187 | NTSTATUS Status indicating success or failure 188 | --*/ 189 | { 190 | PUCHAR buffer; 191 | WDFMEMORY memory; 192 | WDF_MEMORY_DESCRIPTOR memoryDescriptor; 193 | NTSTATUS status; 194 | ULONG_PTR bytesRead; 195 | 196 | WdfWaitLockAcquire(SpbContext->SpbLock, NULL); 197 | 198 | memory = NULL; 199 | status = STATUS_INVALID_PARAMETER; 200 | bytesRead = 0; 201 | 202 | // 203 | // Xfer transactions start by writing an address pointer 204 | // 205 | status = SpbDoWriteDataSynchronously( 206 | SpbContext, 207 | SendData, 208 | SendLength); 209 | 210 | if (!NT_SUCCESS(status)) 211 | { 212 | Nau8825Print( 213 | DEBUG_LEVEL_ERROR, 214 | DBG_IOCTL, 215 | "Error setting address pointer for Spb read - %!STATUS!", 216 | status); 217 | goto exit; 218 | } 219 | 220 | if (Length > DEFAULT_SPB_BUFFER_SIZE) 221 | { 222 | status = WdfMemoryCreate( 223 | WDF_NO_OBJECT_ATTRIBUTES, 224 | NonPagedPool, 225 | NAU8825_POOL_TAG, 226 | Length, 227 | &memory, 228 | (PVOID*)&buffer); 229 | 230 | if (!NT_SUCCESS(status)) 231 | { 232 | Nau8825Print( 233 | DEBUG_LEVEL_ERROR, 234 | DBG_IOCTL, 235 | "Error allocating memory for Spb read - %!STATUS!", 236 | status); 237 | goto exit; 238 | } 239 | 240 | WDF_MEMORY_DESCRIPTOR_INIT_HANDLE( 241 | &memoryDescriptor, 242 | memory, 243 | NULL); 244 | } 245 | else 246 | { 247 | buffer = (PUCHAR)WdfMemoryGetBuffer(SpbContext->ReadMemory, NULL); 248 | 249 | WDF_MEMORY_DESCRIPTOR_INIT_BUFFER( 250 | &memoryDescriptor, 251 | (PVOID)buffer, 252 | Length); 253 | } 254 | 255 | 256 | status = WdfIoTargetSendReadSynchronously( 257 | SpbContext->SpbIoTarget, 258 | NULL, 259 | &memoryDescriptor, 260 | NULL, 261 | NULL, 262 | &bytesRead); 263 | 264 | if (!NT_SUCCESS(status) || 265 | bytesRead != Length) 266 | { 267 | Nau8825Print( 268 | DEBUG_LEVEL_ERROR, 269 | DBG_IOCTL, 270 | "Error reading from Spb - %!STATUS!", 271 | status); 272 | goto exit; 273 | } 274 | 275 | // 276 | // Copy back to the caller's buffer 277 | // 278 | RtlCopyMemory(Data, buffer, Length); 279 | 280 | exit: 281 | if (NULL != memory) 282 | { 283 | WdfObjectDelete(memory); 284 | } 285 | 286 | WdfWaitLockRelease(SpbContext->SpbLock); 287 | 288 | return status; 289 | } 290 | 291 | VOID 292 | SpbTargetDeinitialize( 293 | IN WDFDEVICE FxDevice, 294 | IN SPB_CONTEXT* SpbContext 295 | ) 296 | /*++ 297 | 298 | Routine Description: 299 | 300 | This helper routine is used to free any members added to the SPB_CONTEXT, 301 | note the SPB I/O target is parented to the device and will be 302 | closed and free'd when the device is removed. 303 | 304 | Arguments: 305 | 306 | FxDevice - Handle to the framework device object 307 | SpbContext - Pointer to the current device context 308 | 309 | Return Value: 310 | 311 | NTSTATUS Status indicating success or failure 312 | 313 | --*/ 314 | { 315 | UNREFERENCED_PARAMETER(FxDevice); 316 | UNREFERENCED_PARAMETER(SpbContext); 317 | 318 | // 319 | // Free any SPB_CONTEXT allocations here 320 | // 321 | if (SpbContext->SpbLock != NULL) 322 | { 323 | WdfObjectDelete(SpbContext->SpbLock); 324 | } 325 | 326 | if (SpbContext->ReadMemory != NULL) 327 | { 328 | WdfObjectDelete(SpbContext->ReadMemory); 329 | } 330 | 331 | if (SpbContext->WriteMemory != NULL) 332 | { 333 | WdfObjectDelete(SpbContext->WriteMemory); 334 | } 335 | } 336 | 337 | NTSTATUS 338 | SpbTargetInitialize( 339 | IN WDFDEVICE FxDevice, 340 | IN SPB_CONTEXT* SpbContext 341 | ) 342 | /*++ 343 | 344 | Routine Description: 345 | 346 | This helper routine opens the Spb I/O target and 347 | initializes a request object used for the lifetime 348 | of communication between this driver and Spb. 349 | 350 | Arguments: 351 | 352 | FxDevice - Handle to the framework device object 353 | SpbContext - Pointer to the current device context 354 | 355 | Return Value: 356 | 357 | NTSTATUS Status indicating success or failure 358 | 359 | --*/ 360 | { 361 | WDF_OBJECT_ATTRIBUTES objectAttributes; 362 | WDF_IO_TARGET_OPEN_PARAMS openParams; 363 | UNICODE_STRING spbDeviceName; 364 | WCHAR spbDeviceNameBuffer[RESOURCE_HUB_PATH_SIZE]; 365 | NTSTATUS status; 366 | 367 | WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); 368 | objectAttributes.ParentObject = FxDevice; 369 | 370 | status = WdfIoTargetCreate( 371 | FxDevice, 372 | &objectAttributes, 373 | &SpbContext->SpbIoTarget); 374 | 375 | if (!NT_SUCCESS(status)) 376 | { 377 | Nau8825Print( 378 | DEBUG_LEVEL_ERROR, 379 | DBG_IOCTL, 380 | "Error creating IoTarget object - %!STATUS!", 381 | status); 382 | 383 | WdfObjectDelete(SpbContext->SpbIoTarget); 384 | goto exit; 385 | } 386 | 387 | RtlInitEmptyUnicodeString( 388 | &spbDeviceName, 389 | spbDeviceNameBuffer, 390 | sizeof(spbDeviceNameBuffer)); 391 | 392 | status = RESOURCE_HUB_CREATE_PATH_FROM_ID( 393 | &spbDeviceName, 394 | SpbContext->I2cResHubId.LowPart, 395 | SpbContext->I2cResHubId.HighPart); 396 | 397 | if (!NT_SUCCESS(status)) 398 | { 399 | Nau8825Print( 400 | DEBUG_LEVEL_ERROR, 401 | DBG_IOCTL, 402 | "Error creating Spb resource hub path string - %!STATUS!", 403 | status); 404 | goto exit; 405 | } 406 | 407 | WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( 408 | &openParams, 409 | &spbDeviceName, 410 | (GENERIC_READ | GENERIC_WRITE)); 411 | 412 | openParams.ShareAccess = 0; 413 | openParams.CreateDisposition = FILE_OPEN; 414 | openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL; 415 | 416 | status = WdfIoTargetOpen(SpbContext->SpbIoTarget, &openParams); 417 | 418 | if (!NT_SUCCESS(status)) 419 | { 420 | Nau8825Print( 421 | DEBUG_LEVEL_ERROR, 422 | DBG_IOCTL, 423 | "Error opening Spb target for communication - %!STATUS!", 424 | status); 425 | goto exit; 426 | } 427 | 428 | // 429 | // Allocate some fixed-size buffers from NonPagedPool for typical 430 | // Spb transaction sizes to avoid pool fragmentation in most cases 431 | // 432 | status = WdfMemoryCreate( 433 | WDF_NO_OBJECT_ATTRIBUTES, 434 | NonPagedPool, 435 | NAU8825_POOL_TAG, 436 | DEFAULT_SPB_BUFFER_SIZE, 437 | &SpbContext->WriteMemory, 438 | NULL); 439 | 440 | if (!NT_SUCCESS(status)) 441 | { 442 | Nau8825Print( 443 | DEBUG_LEVEL_ERROR, 444 | DBG_IOCTL, 445 | "Error allocating default memory for Spb write - %!STATUS!", 446 | status); 447 | goto exit; 448 | } 449 | 450 | status = WdfMemoryCreate( 451 | WDF_NO_OBJECT_ATTRIBUTES, 452 | NonPagedPool, 453 | NAU8825_POOL_TAG, 454 | DEFAULT_SPB_BUFFER_SIZE, 455 | &SpbContext->ReadMemory, 456 | NULL); 457 | 458 | if (!NT_SUCCESS(status)) 459 | { 460 | Nau8825Print( 461 | DEBUG_LEVEL_ERROR, 462 | DBG_IOCTL, 463 | "Error allocating default memory for Spb read - %!STATUS!", 464 | status); 465 | goto exit; 466 | } 467 | 468 | // 469 | // Allocate a waitlock to guard access to the default buffers 470 | // 471 | status = WdfWaitLockCreate( 472 | WDF_NO_OBJECT_ATTRIBUTES, 473 | &SpbContext->SpbLock); 474 | 475 | if (!NT_SUCCESS(status)) 476 | { 477 | Nau8825Print( 478 | DEBUG_LEVEL_ERROR, 479 | DBG_IOCTL, 480 | "Error creating Spb Waitlock - %!STATUS!", 481 | status); 482 | goto exit; 483 | } 484 | 485 | exit: 486 | 487 | if (!NT_SUCCESS(status)) 488 | { 489 | SpbTargetDeinitialize(FxDevice, SpbContext); 490 | } 491 | 492 | return status; 493 | } -------------------------------------------------------------------------------- /nau8825/registers.h: -------------------------------------------------------------------------------- 1 | #include "stdint.h" 2 | 3 | #ifndef __NAU8825_H__ 4 | #define __NAU8825_H__ 5 | 6 | #define NAU8825_REG_RESET 0x00 7 | #define NAU8825_REG_ENA_CTRL 0x01 8 | #define NAU8825_REG_IIC_ADDR_SET 0x02 9 | #define NAU8825_REG_CLK_DIVIDER 0x03 10 | #define NAU8825_REG_FLL1 0x04 11 | #define NAU8825_REG_FLL2 0x05 12 | #define NAU8825_REG_FLL3 0x06 13 | #define NAU8825_REG_FLL4 0x07 14 | #define NAU8825_REG_FLL5 0x08 15 | #define NAU8825_REG_FLL6 0x09 16 | #define NAU8825_REG_FLL_VCO_RSV 0x0a 17 | #define NAU8825_REG_HSD_CTRL 0x0c 18 | #define NAU8825_REG_JACK_DET_CTRL 0x0d 19 | #define NAU8825_REG_INTERRUPT_MASK 0x0f 20 | #define NAU8825_REG_IRQ_STATUS 0x10 21 | #define NAU8825_REG_INT_CLR_KEY_STATUS 0x11 22 | #define NAU8825_REG_INTERRUPT_DIS_CTRL 0x12 23 | #define NAU8825_REG_SAR_CTRL 0x13 24 | #define NAU8825_REG_KEYDET_CTRL 0x14 25 | #define NAU8825_REG_VDET_THRESHOLD_1 0x15 26 | #define NAU8825_REG_VDET_THRESHOLD_2 0x16 27 | #define NAU8825_REG_VDET_THRESHOLD_3 0x17 28 | #define NAU8825_REG_VDET_THRESHOLD_4 0x18 29 | #define NAU8825_REG_GPIO34_CTRL 0x19 30 | #define NAU8825_REG_GPIO12_CTRL 0x1a 31 | #define NAU8825_REG_TDM_CTRL 0x1b 32 | #define NAU8825_REG_I2S_PCM_CTRL1 0x1c 33 | #define NAU8825_REG_I2S_PCM_CTRL2 0x1d 34 | #define NAU8825_REG_LEFT_TIME_SLOT 0x1e 35 | #define NAU8825_REG_RIGHT_TIME_SLOT 0x1f 36 | #define NAU8825_REG_BIQ_CTRL 0x20 37 | #define NAU8825_REG_BIQ_COF1 0x21 38 | #define NAU8825_REG_BIQ_COF2 0x22 39 | #define NAU8825_REG_BIQ_COF3 0x23 40 | #define NAU8825_REG_BIQ_COF4 0x24 41 | #define NAU8825_REG_BIQ_COF5 0x25 42 | #define NAU8825_REG_BIQ_COF6 0x26 43 | #define NAU8825_REG_BIQ_COF7 0x27 44 | #define NAU8825_REG_BIQ_COF8 0x28 45 | #define NAU8825_REG_BIQ_COF9 0x29 46 | #define NAU8825_REG_BIQ_COF10 0x2a 47 | #define NAU8825_REG_ADC_RATE 0x2b 48 | #define NAU8825_REG_DAC_CTRL1 0x2c 49 | #define NAU8825_REG_DAC_CTRL2 0x2d 50 | #define NAU8825_REG_DAC_DGAIN_CTRL 0x2f 51 | #define NAU8825_REG_ADC_DGAIN_CTRL 0x30 52 | #define NAU8825_REG_MUTE_CTRL 0x31 53 | #define NAU8825_REG_HSVOL_CTRL 0x32 54 | #define NAU8825_REG_DACL_CTRL 0x33 55 | #define NAU8825_REG_DACR_CTRL 0x34 56 | #define NAU8825_REG_ADC_DRC_KNEE_IP12 0x38 57 | #define NAU8825_REG_ADC_DRC_KNEE_IP34 0x39 58 | #define NAU8825_REG_ADC_DRC_SLOPES 0x3a 59 | #define NAU8825_REG_ADC_DRC_ATKDCY 0x3b 60 | #define NAU8825_REG_DAC_DRC_KNEE_IP12 0x45 61 | #define NAU8825_REG_DAC_DRC_KNEE_IP34 0x46 62 | #define NAU8825_REG_DAC_DRC_SLOPES 0x47 63 | #define NAU8825_REG_DAC_DRC_ATKDCY 0x48 64 | #define NAU8825_REG_IMM_MODE_CTRL 0x4c 65 | #define NAU8825_REG_IMM_RMS_L 0x4d 66 | #define NAU8825_REG_IMM_RMS_R 0x4e 67 | #define NAU8825_REG_CLASSG_CTRL 0x50 68 | #define NAU8825_REG_OPT_EFUSE_CTRL 0x51 69 | #define NAU8825_REG_MISC_CTRL 0x55 70 | #define NAU8825_REG_I2C_DEVICE_ID 0x58 71 | #define NAU8825_REG_SARDOUT_RAM_STATUS 0x59 72 | #define NAU8825_REG_BIAS_ADJ 0x66 73 | #define NAU8825_REG_TRIM_SETTINGS 0x68 74 | #define NAU8825_REG_ANALOG_CONTROL_1 0x69 75 | #define NAU8825_REG_ANALOG_CONTROL_2 0x6a 76 | #define NAU8825_REG_ANALOG_ADC_1 0x71 77 | #define NAU8825_REG_ANALOG_ADC_2 0x72 78 | #define NAU8825_REG_RDAC 0x73 79 | #define NAU8825_REG_MIC_BIAS 0x74 80 | #define NAU8825_REG_BOOST 0x76 81 | #define NAU8825_REG_FEPGA 0x77 82 | #define NAU8825_REG_POWER_UP_CONTROL 0x7f 83 | #define NAU8825_REG_CHARGE_PUMP 0x80 84 | #define NAU8825_REG_CHARGE_PUMP_INPUT_READ 0x81 85 | #define NAU8825_REG_GENERAL_STATUS 0x82 86 | #define NAU8825_REG_MAX NAU8825_REG_GENERAL_STATUS 87 | /* 16-bit control register address, and 16-bits control register data */ 88 | #define NAU8825_REG_ADDR_LEN 16 89 | #define NAU8825_REG_DATA_LEN 16 90 | 91 | /* ENA_CTRL (0x1) */ 92 | #define NAU8825_ENABLE_DACR_SFT 10 93 | #define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT) 94 | #define NAU8825_ENABLE_DACL_SFT 9 95 | #define NAU8825_ENABLE_DACL (1 << NAU8825_ENABLE_DACL_SFT) 96 | #define NAU8825_ENABLE_ADC_SFT 8 97 | #define NAU8825_ENABLE_ADC (1 << NAU8825_ENABLE_ADC_SFT) 98 | #define NAU8825_ENABLE_ADC_CLK_SFT 7 99 | #define NAU8825_ENABLE_ADC_CLK (1 << NAU8825_ENABLE_ADC_CLK_SFT) 100 | #define NAU8825_ENABLE_DAC_CLK_SFT 6 101 | #define NAU8825_ENABLE_DAC_CLK (1 << NAU8825_ENABLE_DAC_CLK_SFT) 102 | #define NAU8825_ENABLE_SAR_SFT 1 103 | 104 | /* CLK_DIVIDER (0x3) */ 105 | #define NAU8825_CLK_SRC_SFT 15 106 | #define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT) 107 | #define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT) 108 | #define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT) 109 | #define NAU8825_CLK_ADC_SRC_SFT 6 110 | #define NAU8825_CLK_ADC_SRC_MASK (0x3 << NAU8825_CLK_ADC_SRC_SFT) 111 | #define NAU8825_CLK_DAC_SRC_SFT 4 112 | #define NAU8825_CLK_DAC_SRC_MASK (0x3 << NAU8825_CLK_DAC_SRC_SFT) 113 | #define NAU8825_CLK_MCLK_SRC_MASK (0xf << 0) 114 | 115 | /* FLL1 (0x04) */ 116 | #define NAU8825_ICTRL_LATCH_SFT 10 117 | #define NAU8825_ICTRL_LATCH_MASK (0x7 << NAU8825_ICTRL_LATCH_SFT) 118 | #define NAU8825_FLL_RATIO_MASK (0x7f << 0) 119 | 120 | /* FLL3 (0x06) */ 121 | #define NAU8825_GAIN_ERR_SFT 12 122 | #define NAU8825_GAIN_ERR_MASK (0xf << NAU8825_GAIN_ERR_SFT) 123 | #define NAU8825_FLL_INTEGER_MASK (0x3ff << 0) 124 | #define NAU8825_FLL_CLK_SRC_SFT 10 125 | #define NAU8825_FLL_CLK_SRC_MASK (0x3 << NAU8825_FLL_CLK_SRC_SFT) 126 | #define NAU8825_FLL_CLK_SRC_MCLK (0 << NAU8825_FLL_CLK_SRC_SFT) 127 | #define NAU8825_FLL_CLK_SRC_BLK (0x2 << NAU8825_FLL_CLK_SRC_SFT) 128 | #define NAU8825_FLL_CLK_SRC_FS (0x3 << NAU8825_FLL_CLK_SRC_SFT) 129 | 130 | /* FLL4 (0x07) */ 131 | #define NAU8825_FLL_REF_DIV_SFT 10 132 | #define NAU8825_FLL_REF_DIV_MASK (0x3 << NAU8825_FLL_REF_DIV_SFT) 133 | 134 | /* FLL5 (0x08) */ 135 | #define NAU8825_FLL_PDB_DAC_EN (0x1 << 15) 136 | #define NAU8825_FLL_LOOP_FTR_EN (0x1 << 14) 137 | #define NAU8825_FLL_CLK_SW_MASK (0x1 << 13) 138 | #define NAU8825_FLL_CLK_SW_N2 (0x1 << 13) 139 | #define NAU8825_FLL_CLK_SW_REF (0x0 << 13) 140 | #define NAU8825_FLL_FTR_SW_MASK (0x1 << 12) 141 | #define NAU8825_FLL_FTR_SW_ACCU (0x1 << 12) 142 | #define NAU8825_FLL_FTR_SW_FILTER (0x0 << 12) 143 | 144 | /* FLL6 (0x9) */ 145 | #define NAU8825_DCO_EN (0x1 << 15) 146 | #define NAU8825_SDM_EN (0x1 << 14) 147 | #define NAU8825_CUTOFF500 (0x1 << 13) 148 | 149 | /* HSD_CTRL (0xc) */ 150 | #define NAU8825_HSD_AUTO_MODE (1 << 6) 151 | /* 0 - open, 1 - short to GND */ 152 | #define NAU8825_SPKR_DWN1R (1 << 1) 153 | #define NAU8825_SPKR_DWN1L (1 << 0) 154 | 155 | /* JACK_DET_CTRL (0xd) */ 156 | #define NAU8825_JACK_DET_RESTART (1 << 9) 157 | #define NAU8825_JACK_DET_DB_BYPASS (1 << 8) 158 | #define NAU8825_JACK_INSERT_DEBOUNCE_SFT 5 159 | #define NAU8825_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT) 160 | #define NAU8825_JACK_EJECT_DEBOUNCE_SFT 2 161 | #define NAU8825_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_EJECT_DEBOUNCE_SFT) 162 | #define NAU8825_JACK_POLARITY (1 << 1) /* 0 - active low, 1 - active high */ 163 | 164 | /* INTERRUPT_MASK (0xf) */ 165 | #define NAU8825_IRQ_PIN_PULLUP (1 << 14) 166 | #define NAU8825_IRQ_PIN_PULL_EN (1 << 13) 167 | #define NAU8825_IRQ_OUTPUT_EN (1 << 11) 168 | #define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10) 169 | #define NAU8825_IRQ_RMS_EN (1 << 8) 170 | #define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7) 171 | #define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5) 172 | #define NAU8825_IRQ_EJECT_EN (1 << 2) 173 | #define NAU8825_IRQ_INSERT_EN (1 << 0) 174 | 175 | /* IRQ_STATUS (0x10) */ 176 | #define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10) 177 | #define NAU8825_SHORT_CIRCUIT_IRQ (1 << 9) 178 | #define NAU8825_IMPEDANCE_MEAS_IRQ (1 << 8) 179 | #define NAU8825_KEY_IRQ_MASK (0x7 << 5) 180 | #define NAU8825_KEY_RELEASE_IRQ (1 << 7) 181 | #define NAU8825_KEY_LONG_PRESS_IRQ (1 << 6) 182 | #define NAU8825_KEY_SHORT_PRESS_IRQ (1 << 5) 183 | #define NAU8825_MIC_DETECTION_IRQ (1 << 4) 184 | #define NAU8825_JACK_EJECTION_IRQ_MASK (3 << 2) 185 | #define NAU8825_JACK_EJECTION_DETECTED (1 << 2) 186 | #define NAU8825_JACK_INSERTION_IRQ_MASK (3 << 0) 187 | #define NAU8825_JACK_INSERTION_DETECTED (1 << 0) 188 | 189 | /* INTERRUPT_DIS_CTRL (0x12) */ 190 | #define NAU8825_IRQ_HEADSET_COMPLETE_DIS (1 << 10) 191 | #define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7) 192 | #define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5) 193 | #define NAU8825_IRQ_EJECT_DIS (1 << 2) 194 | #define NAU8825_IRQ_INSERT_DIS (1 << 0) 195 | 196 | /* SAR_CTRL (0x13) */ 197 | #define NAU8825_SAR_ADC_EN_SFT 12 198 | #define NAU8825_SAR_ADC_EN (1 << NAU8825_SAR_ADC_EN_SFT) 199 | #define NAU8825_SAR_INPUT_MASK (1 << 11) 200 | #define NAU8825_SAR_INPUT_JKSLV (1 << 11) 201 | #define NAU8825_SAR_INPUT_JKR2 (0 << 11) 202 | #define NAU8825_SAR_TRACKING_GAIN_SFT 8 203 | #define NAU8825_SAR_TRACKING_GAIN_MASK (0x7 << NAU8825_SAR_TRACKING_GAIN_SFT) 204 | #define NAU8825_SAR_COMPARE_TIME_SFT 2 205 | #define NAU8825_SAR_COMPARE_TIME_MASK (3 << 2) 206 | #define NAU8825_SAR_SAMPLING_TIME_SFT 0 207 | #define NAU8825_SAR_SAMPLING_TIME_MASK (3 << 0) 208 | 209 | /* KEYDET_CTRL (0x14) */ 210 | #define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT 12 211 | #define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT) 212 | #define NAU8825_KEYDET_LEVELS_NR_SFT 8 213 | #define NAU8825_KEYDET_LEVELS_NR_MASK (0x7 << 8) 214 | #define NAU8825_KEYDET_HYSTERESIS_SFT 0 215 | #define NAU8825_KEYDET_HYSTERESIS_MASK 0xf 216 | 217 | /* GPIO12_CTRL (0x1a) */ 218 | #define NAU8825_JKDET_PULL_UP (1 << 11) /* 0 - pull down, 1 - pull up */ 219 | #define NAU8825_JKDET_PULL_EN (1 << 9) /* 0 - enable pull, 1 - disable */ 220 | #define NAU8825_JKDET_OUTPUT_EN (1 << 8) /* 0 - enable input, 1 - enable output */ 221 | 222 | /* I2S_PCM_CTRL1 (0x1c) */ 223 | #define NAU8825_I2S_BP_SFT 7 224 | #define NAU8825_I2S_BP_MASK (1 << NAU8825_I2S_BP_SFT) 225 | #define NAU8825_I2S_BP_INV (1 << NAU8825_I2S_BP_SFT) 226 | #define NAU8825_I2S_PCMB_SFT 6 227 | #define NAU8825_I2S_PCMB_MASK (1 << NAU8825_I2S_PCMB_SFT) 228 | #define NAU8825_I2S_PCMB_EN (1 << NAU8825_I2S_PCMB_SFT) 229 | #define NAU8825_I2S_DL_SFT 2 230 | #define NAU8825_I2S_DL_MASK (0x3 << NAU8825_I2S_DL_SFT) 231 | #define NAU8825_I2S_DL_16 (0 << NAU8825_I2S_DL_SFT) 232 | #define NAU8825_I2S_DL_20 (1 << NAU8825_I2S_DL_SFT) 233 | #define NAU8825_I2S_DL_24 (2 << NAU8825_I2S_DL_SFT) 234 | #define NAU8825_I2S_DL_32 (3 << NAU8825_I2S_DL_SFT) 235 | #define NAU8825_I2S_DF_SFT 0 236 | #define NAU8825_I2S_DF_MASK (0x3 << NAU8825_I2S_DF_SFT) 237 | #define NAU8825_I2S_DF_RIGTH (0 << NAU8825_I2S_DF_SFT) 238 | #define NAU8825_I2S_DF_LEFT (1 << NAU8825_I2S_DF_SFT) 239 | #define NAU8825_I2S_DF_I2S (2 << NAU8825_I2S_DF_SFT) 240 | #define NAU8825_I2S_DF_PCM_AB (3 << NAU8825_I2S_DF_SFT) 241 | 242 | /* I2S_PCM_CTRL2 (0x1d) */ 243 | #define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */ 244 | #define NAU8825_I2S_LRC_DIV_SFT 12 245 | #define NAU8825_I2S_LRC_DIV_MASK (0x3 << NAU8825_I2S_LRC_DIV_SFT) 246 | #define NAU8825_I2S_MS_SFT 3 247 | #define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT) 248 | #define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT) 249 | #define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT) 250 | #define NAU8825_I2S_BLK_DIV_MASK 0x7 251 | 252 | /* LEFT_TIME_SLOT (0x1e) */ 253 | #define NAU8825_FS_ERR_CMP_SEL_SFT 14 254 | #define NAU8825_FS_ERR_CMP_SEL_MASK (0x3 << NAU8825_FS_ERR_CMP_SEL_SFT) 255 | #define NAU8825_DIS_FS_SHORT_DET (1 << 13) 256 | 257 | /* BIQ_CTRL (0x20) */ 258 | #define NAU8825_BIQ_WRT_SFT 4 259 | #define NAU8825_BIQ_WRT_EN (1 << NAU8825_BIQ_WRT_SFT) 260 | #define NAU8825_BIQ_PATH_SFT 0 261 | #define NAU8825_BIQ_PATH_MASK (1 << NAU8825_BIQ_PATH_SFT) 262 | #define NAU8825_BIQ_PATH_ADC (0 << NAU8825_BIQ_PATH_SFT) 263 | #define NAU8825_BIQ_PATH_DAC (1 << NAU8825_BIQ_PATH_SFT) 264 | 265 | /* ADC_RATE (0x2b) */ 266 | #define NAU8825_ADC_SINC4_SFT 4 267 | #define NAU8825_ADC_SINC4_EN (1 << NAU8825_ADC_SINC4_SFT) 268 | #define NAU8825_ADC_SYNC_DOWN_SFT 0 269 | #define NAU8825_ADC_SYNC_DOWN_MASK 0x3 270 | #define NAU8825_ADC_SYNC_DOWN_32 0 271 | #define NAU8825_ADC_SYNC_DOWN_64 1 272 | #define NAU8825_ADC_SYNC_DOWN_128 2 273 | #define NAU8825_ADC_SYNC_DOWN_256 3 274 | 275 | /* DAC_CTRL1 (0x2c) */ 276 | #define NAU8825_DAC_CLIP_OFF (1 << 7) 277 | #define NAU8825_DAC_OVERSAMPLE_SFT 0 278 | #define NAU8825_DAC_OVERSAMPLE_MASK 0x7 279 | #define NAU8825_DAC_OVERSAMPLE_64 0 280 | #define NAU8825_DAC_OVERSAMPLE_256 1 281 | #define NAU8825_DAC_OVERSAMPLE_128 2 282 | #define NAU8825_DAC_OVERSAMPLE_32 4 283 | 284 | /* ADC_DGAIN_CTRL (0x30) */ 285 | #define NAU8825_ADC_DIG_VOL_MASK 0xff 286 | 287 | /* MUTE_CTRL (0x31) */ 288 | #define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9) 289 | #define NAU8825_DAC_SOFT_MUTE (1 << 9) 290 | 291 | /* HSVOL_CTRL (0x32) */ 292 | #define NAU8825_HP_MUTE (1 << 15) 293 | #define NAU8825_HP_MUTE_AUTO (1 << 14) 294 | #define NAU8825_HPL_MUTE (1 << 13) 295 | #define NAU8825_HPR_MUTE (1 << 12) 296 | #define NAU8825_HPL_VOL_SFT 6 297 | #define NAU8825_HPL_VOL_MASK (0x3f << NAU8825_HPL_VOL_SFT) 298 | #define NAU8825_HPR_VOL_SFT 0 299 | #define NAU8825_HPR_VOL_MASK (0x3f << NAU8825_HPR_VOL_SFT) 300 | #define NAU8825_HP_VOL_MIN 0x36 301 | 302 | /* DACL_CTRL (0x33) */ 303 | #define NAU8825_DACL_CH_SEL_SFT 9 304 | #define NAU8825_DACL_CH_SEL_MASK (0x1 << NAU8825_DACL_CH_SEL_SFT) 305 | #define NAU8825_DACL_CH_SEL_L (0x0 << NAU8825_DACL_CH_SEL_SFT) 306 | #define NAU8825_DACL_CH_SEL_R (0x1 << NAU8825_DACL_CH_SEL_SFT) 307 | #define NAU8825_DACL_CH_VOL_MASK 0xff 308 | 309 | /* DACR_CTRL (0x34) */ 310 | #define NAU8825_DACR_CH_SEL_SFT 9 311 | #define NAU8825_DACR_CH_SEL_MASK (0x1 << NAU8825_DACR_CH_SEL_SFT) 312 | #define NAU8825_DACR_CH_SEL_L (0x0 << NAU8825_DACR_CH_SEL_SFT) 313 | #define NAU8825_DACR_CH_SEL_R (0x1 << NAU8825_DACR_CH_SEL_SFT) 314 | #define NAU8825_DACR_CH_VOL_MASK 0xff 315 | 316 | /* IMM_MODE_CTRL (0x4C) */ 317 | #define NAU8825_IMM_THD_SFT 8 318 | #define NAU8825_IMM_THD_MASK (0x3f << NAU8825_IMM_THD_SFT) 319 | #define NAU8825_IMM_GEN_VOL_SFT 6 320 | #define NAU8825_IMM_GEN_VOL_MASK (0x3 << NAU8825_IMM_GEN_VOL_SFT) 321 | #define NAU8825_IMM_GEN_VOL_1_2nd (0x0 << NAU8825_IMM_GEN_VOL_SFT) 322 | #define NAU8825_IMM_GEN_VOL_1_4th (0x1 << NAU8825_IMM_GEN_VOL_SFT) 323 | #define NAU8825_IMM_GEN_VOL_1_8th (0x2 << NAU8825_IMM_GEN_VOL_SFT) 324 | #define NAU8825_IMM_GEN_VOL_1_16th (0x3 << NAU8825_IMM_GEN_VOL_SFT) 325 | 326 | #define NAU8825_IMM_CYC_SFT 4 327 | #define NAU8825_IMM_CYC_MASK (0x3 << NAU8825_IMM_CYC_SFT) 328 | #define NAU8825_IMM_CYC_1024 (0x0 << NAU8825_IMM_CYC_SFT) 329 | #define NAU8825_IMM_CYC_2048 (0x1 << NAU8825_IMM_CYC_SFT) 330 | #define NAU8825_IMM_CYC_4096 (0x2 << NAU8825_IMM_CYC_SFT) 331 | #define NAU8825_IMM_CYC_8192 (0x3 << NAU8825_IMM_CYC_SFT) 332 | #define NAU8825_IMM_EN (1 << 3) 333 | #define NAU8825_IMM_DAC_SRC_MASK 0x7 334 | #define NAU8825_IMM_DAC_SRC_BIQ 0x0 335 | #define NAU8825_IMM_DAC_SRC_DRC 0x1 336 | #define NAU8825_IMM_DAC_SRC_MIX 0x2 337 | #define NAU8825_IMM_DAC_SRC_SIN 0x3 338 | 339 | /* CLASSG_CTRL (0x50) */ 340 | #define NAU8825_CLASSG_TIMER_SFT 8 341 | #define NAU8825_CLASSG_TIMER_MASK (0x3f << NAU8825_CLASSG_TIMER_SFT) 342 | #define NAU8825_CLASSG_TIMER_1ms (0x1 << NAU8825_CLASSG_TIMER_SFT) 343 | #define NAU8825_CLASSG_TIMER_2ms (0x2 << NAU8825_CLASSG_TIMER_SFT) 344 | #define NAU8825_CLASSG_TIMER_8ms (0x4 << NAU8825_CLASSG_TIMER_SFT) 345 | #define NAU8825_CLASSG_TIMER_16ms (0x8 << NAU8825_CLASSG_TIMER_SFT) 346 | #define NAU8825_CLASSG_TIMER_32ms (0x10 << NAU8825_CLASSG_TIMER_SFT) 347 | #define NAU8825_CLASSG_TIMER_64ms (0x20 << NAU8825_CLASSG_TIMER_SFT) 348 | #define NAU8825_CLASSG_LDAC_EN (0x1 << 2) 349 | #define NAU8825_CLASSG_RDAC_EN (0x1 << 1) 350 | #define NAU8825_CLASSG_EN (1 << 0) 351 | 352 | /* I2C_DEVICE_ID (0x58) */ 353 | #define NAU8825_GPIO2JD1 (1 << 7) 354 | #define NAU8825_SOFTWARE_ID_MASK 0x3 355 | #define NAU8825_SOFTWARE_ID_NAU8825 0x0 356 | 357 | /* BIAS_ADJ (0x66) */ 358 | #define NAU8825_BIAS_HPR_IMP (1 << 15) 359 | #define NAU8825_BIAS_HPL_IMP (1 << 14) 360 | #define NAU8825_BIAS_TESTDAC_SFT 8 361 | #define NAU8825_BIAS_TESTDAC_EN (0x3 << NAU8825_BIAS_TESTDAC_SFT) 362 | #define NAU8825_BIAS_TESTDACR_EN (0x2 << NAU8825_BIAS_TESTDAC_SFT) 363 | #define NAU8825_BIAS_TESTDACL_EN (0x1 << NAU8825_BIAS_TESTDAC_SFT) 364 | #define NAU8825_BIAS_VMID (1 << 6) 365 | #define NAU8825_BIAS_VMID_SEL_SFT 4 366 | #define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT) 367 | 368 | /* ANALOG_CONTROL_2 (0x6a) */ 369 | #define NAU8825_HP_NON_CLASSG_CURRENT_2xADJ (1 << 12) 370 | #define NAU8825_DAC_CAPACITOR_MSB (1 << 1) 371 | #define NAU8825_DAC_CAPACITOR_LSB (1 << 0) 372 | 373 | /* ANALOG_ADC_2 (0x72) */ 374 | #define NAU8825_ADC_VREFSEL_MASK (0x3 << 8) 375 | #define NAU8825_ADC_VREFSEL_ANALOG (0 << 8) 376 | #define NAU8825_ADC_VREFSEL_VMID (1 << 8) 377 | #define NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB (2 << 8) 378 | #define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8) 379 | #define NAU8825_POWERUP_ADCL (1 << 6) 380 | 381 | /* RDAC (0x73) */ 382 | #define NAU8825_RDAC_FS_BCLK_ENB (1 << 15) 383 | #define NAU8825_RDAC_EN_SFT 12 384 | #define NAU8825_RDAC_EN (0x3 << NAU8825_RDAC_EN_SFT) 385 | #define NAU8825_RDAC_CLK_EN_SFT 8 386 | #define NAU8825_RDAC_CLK_EN (0x3 << NAU8825_RDAC_CLK_EN_SFT) 387 | #define NAU8825_RDAC_CLK_DELAY_SFT 4 388 | #define NAU8825_RDAC_CLK_DELAY_MASK (0x7 << NAU8825_RDAC_CLK_DELAY_SFT) 389 | #define NAU8825_RDAC_VREF_SFT 2 390 | #define NAU8825_RDAC_VREF_MASK (0x3 << NAU8825_RDAC_VREF_SFT) 391 | 392 | /* MIC_BIAS (0x74) */ 393 | #define NAU8825_MICBIAS_JKSLV (1 << 14) 394 | #define NAU8825_MICBIAS_JKR2 (1 << 12) 395 | #define NAU8825_MICBIAS_POWERUP_SFT 8 396 | #define NAU8825_MICBIAS_VOLTAGE_SFT 0 397 | #define NAU8825_MICBIAS_VOLTAGE_MASK 0x7 398 | 399 | /* BOOST (0x76) */ 400 | #define NAU8825_PRECHARGE_DIS (1 << 13) 401 | #define NAU8825_GLOBAL_BIAS_EN (1 << 12) 402 | #define NAU8825_HP_BOOST_DIS (1 << 9) 403 | #define NAU8825_HP_BOOST_G_DIS (1 << 8) 404 | #define NAU8825_SHORT_SHUTDOWN_EN (1 << 6) 405 | 406 | /* POWER_UP_CONTROL (0x7f) */ 407 | #define NAU8825_POWERUP_INTEGR_R (1 << 5) 408 | #define NAU8825_POWERUP_INTEGR_L (1 << 4) 409 | #define NAU8825_POWERUP_DRV_IN_R (1 << 3) 410 | #define NAU8825_POWERUP_DRV_IN_L (1 << 2) 411 | #define NAU8825_POWERUP_HP_DRV_R (1 << 1) 412 | #define NAU8825_POWERUP_HP_DRV_L (1 << 0) 413 | 414 | /* CHARGE_PUMP (0x80) */ 415 | #define NAU8825_JAMNODCLOW (1 << 10) 416 | #define NAU8825_POWER_DOWN_DACR (1 << 9) 417 | #define NAU8825_POWER_DOWN_DACL (1 << 8) 418 | #define NAU8825_CHANRGE_PUMP_EN (1 << 5) 419 | 420 | 421 | /* System Clock Source */ 422 | enum { 423 | NAU8825_CLK_DIS = 0, 424 | NAU8825_CLK_MCLK, 425 | NAU8825_CLK_INTERNAL, 426 | NAU8825_CLK_FLL_MCLK, 427 | NAU8825_CLK_FLL_BLK, 428 | NAU8825_CLK_FLL_FS, 429 | }; 430 | 431 | /* Cross talk detection state */ 432 | enum { 433 | NAU8825_XTALK_PREPARE = 0, 434 | NAU8825_XTALK_HPR_R2L, 435 | NAU8825_XTALK_HPL_R2L, 436 | NAU8825_XTALK_IMM, 437 | NAU8825_XTALK_DONE, 438 | }; 439 | #endif -------------------------------------------------------------------------------- /nau8825/nau8825.c: -------------------------------------------------------------------------------- 1 | #define DESCRIPTOR_DEF 2 | #include "nau8825.h" 3 | #include "registers.h" 4 | 5 | #define bool int 6 | 7 | static ULONG Nau8825DebugLevel = 100; 8 | static ULONG Nau8825DebugCatagories = DBG_INIT || DBG_PNP || DBG_IOCTL; 9 | 10 | void nau8825_update_reclock(PNAU8825_CONTEXT pDevice); 11 | NTSTATUS nau8825_configure_sysclk(PNAU8825_CONTEXT pDevice, int clk_id, 12 | unsigned int freq); 13 | 14 | NTSTATUS 15 | DriverEntry( 16 | __in PDRIVER_OBJECT DriverObject, 17 | __in PUNICODE_STRING RegistryPath 18 | ) 19 | { 20 | NTSTATUS status = STATUS_SUCCESS; 21 | WDF_DRIVER_CONFIG config; 22 | WDF_OBJECT_ATTRIBUTES attributes; 23 | 24 | Nau8825Print(DEBUG_LEVEL_INFO, DBG_INIT, 25 | "Driver Entry\n"); 26 | 27 | WDF_DRIVER_CONFIG_INIT(&config, Nau8825EvtDeviceAdd); 28 | 29 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 30 | 31 | // 32 | // Create a framework driver object to represent our driver. 33 | // 34 | 35 | status = WdfDriverCreate(DriverObject, 36 | RegistryPath, 37 | &attributes, 38 | &config, 39 | WDF_NO_HANDLE 40 | ); 41 | 42 | if (!NT_SUCCESS(status)) 43 | { 44 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_INIT, 45 | "WdfDriverCreate failed with status 0x%x\n", status); 46 | } 47 | 48 | return status; 49 | } 50 | 51 | typedef enum platform { 52 | PlatformNone, 53 | PlatformRyzen, 54 | PlatformSkylake, 55 | PlatformAlderLake 56 | } Platform; 57 | 58 | static Platform GetPlatform() { 59 | int cpuinfo[4]; 60 | __cpuidex(cpuinfo, 0, 0); 61 | 62 | int temp = cpuinfo[2]; 63 | cpuinfo[2] = cpuinfo[3]; 64 | cpuinfo[3] = temp; 65 | 66 | char vendorName[13]; 67 | RtlZeroMemory(vendorName, 13); 68 | memcpy(vendorName, &cpuinfo[1], 12); 69 | 70 | __cpuidex(cpuinfo, 1, 0); 71 | 72 | UINT16 family = (cpuinfo[0] >> 8) & 0xF; 73 | UINT8 model = (cpuinfo[0] >> 4) & 0xF; 74 | UINT8 stepping = cpuinfo[0] & 0xF; 75 | if (family == 0xF || family == 0x6) { 76 | model += (((cpuinfo[0] >> 16) & 0xF) << 4); 77 | } 78 | if (family == 0xF) { 79 | family += (cpuinfo[0] >> 20) & 0xFF; 80 | } 81 | 82 | if (strcmp(vendorName, "AuthenticAMD") == 0) { 83 | return PlatformRyzen; //family 23 for Picasso / Dali 84 | } 85 | else if (strcmp(vendorName, "GenuineIntel") == 0) { 86 | if (model == 78) 87 | return PlatformSkylake; 88 | else 89 | return PlatformAlderLake; 90 | } 91 | return PlatformNone; 92 | } 93 | 94 | NTSTATUS nau8825_reg_read( 95 | _In_ PNAU8825_CONTEXT pDevice, 96 | uint16_t reg, 97 | unsigned int* data 98 | ) { 99 | reg = RtlUshortByteSwap(reg); 100 | uint16_t raw_data = 0; 101 | NTSTATUS status = SpbXferDataSynchronously(&pDevice->I2CContext, ®, sizeof(uint16_t), &raw_data, sizeof(uint16_t)); 102 | raw_data = RtlUshortByteSwap(raw_data); 103 | *data = raw_data; 104 | return status; 105 | } 106 | 107 | NTSTATUS nau8825_reg_write( 108 | _In_ PNAU8825_CONTEXT pDevice, 109 | uint16_t reg, 110 | unsigned int data 111 | ) { 112 | reg = RtlUshortByteSwap(reg); 113 | data = RtlUshortByteSwap(data); 114 | 115 | uint16_t buf[2]; 116 | buf[0] = reg; 117 | buf[1] = data; 118 | return SpbWriteDataSynchronously(&pDevice->I2CContext, buf, sizeof(buf)); 119 | } 120 | 121 | NTSTATUS nau8825_reg_update( 122 | _In_ PNAU8825_CONTEXT pDevice, 123 | uint16_t reg, 124 | unsigned int mask, 125 | unsigned int val 126 | ) { 127 | unsigned int tmp = 0, orig = 0; 128 | 129 | NTSTATUS status = nau8825_reg_read(pDevice, reg, &orig); 130 | if (!NT_SUCCESS(status)) { 131 | return status; 132 | } 133 | 134 | tmp = orig & ~mask; 135 | tmp |= val & mask; 136 | 137 | if (tmp != orig) { 138 | status = nau8825_reg_write(pDevice, reg, tmp); 139 | } 140 | return status; 141 | } 142 | 143 | int CsAudioArg2 = 1; 144 | 145 | VOID 146 | CSAudioRegisterEndpoint( 147 | PNAU8825_CONTEXT pDevice 148 | ) { 149 | CsAudioArg arg; 150 | RtlZeroMemory(&arg, sizeof(CsAudioArg)); 151 | arg.argSz = sizeof(CsAudioArg); 152 | arg.endpointType = CSAudioEndpointTypeHeadphone; 153 | arg.endpointRequest = CSAudioEndpointRegister; 154 | ExNotifyCallback(pDevice->CSAudioAPICallback, &arg, &CsAudioArg2); 155 | 156 | arg.endpointType = CSAudioEndpointTypeMicJack; 157 | ExNotifyCallback(pDevice->CSAudioAPICallback, &arg, &CsAudioArg2); //register both in case user decides to record first 158 | } 159 | 160 | NTSTATUS 161 | OnPrepareHardware( 162 | _In_ WDFDEVICE FxDevice, 163 | _In_ WDFCMRESLIST FxResourcesRaw, 164 | _In_ WDFCMRESLIST FxResourcesTranslated 165 | ) 166 | /*++ 167 | 168 | Routine Description: 169 | 170 | This routine caches the SPB resource connection ID. 171 | 172 | Arguments: 173 | 174 | FxDevice - a handle to the framework device object 175 | FxResourcesRaw - list of translated hardware resources that 176 | the PnP manager has assigned to the device 177 | FxResourcesTranslated - list of raw hardware resources that 178 | the PnP manager has assigned to the device 179 | 180 | Return Value: 181 | 182 | Status 183 | 184 | --*/ 185 | { 186 | PNAU8825_CONTEXT pDevice = GetDeviceContext(FxDevice); 187 | BOOLEAN fSpbResourceFound = FALSE; 188 | NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES; 189 | 190 | UNREFERENCED_PARAMETER(FxResourcesRaw); 191 | 192 | // 193 | // Parse the peripheral's resources. 194 | // 195 | 196 | ULONG resourceCount = WdfCmResourceListGetCount(FxResourcesTranslated); 197 | 198 | for (ULONG i = 0; i < resourceCount; i++) 199 | { 200 | PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor; 201 | UCHAR Class; 202 | UCHAR Type; 203 | 204 | pDescriptor = WdfCmResourceListGetDescriptor( 205 | FxResourcesTranslated, i); 206 | 207 | switch (pDescriptor->Type) 208 | { 209 | case CmResourceTypeConnection: 210 | // 211 | // Look for I2C or SPI resource and save connection ID. 212 | // 213 | Class = pDescriptor->u.Connection.Class; 214 | Type = pDescriptor->u.Connection.Type; 215 | if (Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL && 216 | Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C) 217 | { 218 | if (fSpbResourceFound == FALSE) 219 | { 220 | status = STATUS_SUCCESS; 221 | pDevice->I2CContext.I2cResHubId.LowPart = pDescriptor->u.Connection.IdLowPart; 222 | pDevice->I2CContext.I2cResHubId.HighPart = pDescriptor->u.Connection.IdHighPart; 223 | fSpbResourceFound = TRUE; 224 | } 225 | else 226 | { 227 | } 228 | } 229 | break; 230 | default: 231 | // 232 | // Ignoring all other resource types. 233 | // 234 | break; 235 | } 236 | } 237 | 238 | // 239 | // An SPB resource is required. 240 | // 241 | 242 | if (fSpbResourceFound == FALSE) 243 | { 244 | status = STATUS_NOT_FOUND; 245 | } 246 | 247 | status = SpbTargetInitialize(FxDevice, &pDevice->I2CContext); 248 | 249 | if (!NT_SUCCESS(status)) 250 | { 251 | return status; 252 | } 253 | 254 | //Defaults for skylake 255 | pDevice->bclkRate = 48000; 256 | pDevice->freq = 48000; 257 | pDevice->validBits = 24; 258 | 259 | return status; 260 | } 261 | 262 | NTSTATUS 263 | OnReleaseHardware( 264 | _In_ WDFDEVICE FxDevice, 265 | _In_ WDFCMRESLIST FxResourcesTranslated 266 | ) 267 | /*++ 268 | 269 | Routine Description: 270 | 271 | Arguments: 272 | 273 | FxDevice - a handle to the framework device object 274 | FxResourcesTranslated - list of raw hardware resources that 275 | the PnP manager has assigned to the device 276 | 277 | Return Value: 278 | 279 | Status 280 | 281 | --*/ 282 | { 283 | PNAU8825_CONTEXT pDevice = GetDeviceContext(FxDevice); 284 | NTSTATUS status = STATUS_SUCCESS; 285 | 286 | UNREFERENCED_PARAMETER(FxResourcesTranslated); 287 | 288 | SpbTargetDeinitialize(FxDevice, &pDevice->I2CContext); 289 | 290 | if (pDevice->CSAudioAPICallbackObj) { 291 | ExUnregisterCallback(pDevice->CSAudioAPICallbackObj); 292 | pDevice->CSAudioAPICallbackObj = NULL; 293 | } 294 | 295 | if (pDevice->CSAudioAPICallback) { 296 | ObfDereferenceObject(pDevice->CSAudioAPICallback); 297 | pDevice->CSAudioAPICallback = NULL; 298 | } 299 | 300 | return status; 301 | } 302 | 303 | static NTSTATUS GetIntegerProperty( 304 | _In_ WDFDEVICE FxDevice, 305 | char* propertyStr, 306 | UINT8* property 307 | ) { 308 | PNAU8825_CONTEXT pDevice = GetDeviceContext(FxDevice); 309 | WDFMEMORY outputMemory = WDF_NO_HANDLE; 310 | 311 | NTSTATUS status = STATUS_ACPI_NOT_INITIALIZED; 312 | 313 | size_t inputBufferLen = sizeof(ACPI_GET_DEVICE_SPECIFIC_DATA) + strlen(propertyStr) + 1; 314 | ACPI_GET_DEVICE_SPECIFIC_DATA* inputBuffer = ExAllocatePoolWithTag(NonPagedPool, inputBufferLen, NAU8825_POOL_TAG); 315 | if (!inputBuffer) { 316 | goto Exit; 317 | } 318 | RtlZeroMemory(inputBuffer, inputBufferLen); 319 | 320 | inputBuffer->Signature = IOCTL_ACPI_GET_DEVICE_SPECIFIC_DATA_SIGNATURE; 321 | 322 | unsigned char uuidend[] = { 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01 }; 323 | 324 | inputBuffer->Section.Data1 = 0xdaffd814; 325 | inputBuffer->Section.Data2 = 0x6eba; 326 | inputBuffer->Section.Data3 = 0x4d8c; 327 | memcpy(inputBuffer->Section.Data4, uuidend, sizeof(uuidend)); //Avoid Windows defender false positive 328 | 329 | strcpy(inputBuffer->PropertyName, propertyStr); 330 | inputBuffer->PropertyNameLength = strlen(propertyStr) + 1; 331 | 332 | PACPI_EVAL_OUTPUT_BUFFER outputBuffer; 333 | size_t outputArgumentBufferSize = 8; 334 | size_t outputBufferSize = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) + sizeof(ACPI_METHOD_ARGUMENT_V1) + outputArgumentBufferSize; 335 | sizeof(ACPI_EVAL_OUTPUT_BUFFER_V1); 336 | 337 | WDF_OBJECT_ATTRIBUTES attributes; 338 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 339 | attributes.ParentObject = FxDevice; 340 | status = WdfMemoryCreate(&attributes, 341 | NonPagedPoolNx, 342 | 0, 343 | outputBufferSize, 344 | &outputMemory, 345 | &outputBuffer); 346 | if (!NT_SUCCESS(status)) { 347 | goto Exit; 348 | } 349 | 350 | WDF_MEMORY_DESCRIPTOR inputMemDesc; 351 | WDF_MEMORY_DESCRIPTOR outputMemDesc; 352 | WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputMemDesc, inputBuffer, (ULONG)inputBufferLen); 353 | WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&outputMemDesc, outputMemory, NULL); 354 | 355 | status = WdfIoTargetSendInternalIoctlSynchronously( 356 | WdfDeviceGetIoTarget(FxDevice), 357 | NULL, 358 | IOCTL_ACPI_GET_DEVICE_SPECIFIC_DATA, 359 | &inputMemDesc, 360 | &outputMemDesc, 361 | NULL, 362 | NULL 363 | ); 364 | if (!NT_SUCCESS(status)) { 365 | Nau8825Print( 366 | DEBUG_LEVEL_ERROR, 367 | DBG_IOCTL, 368 | "Error getting device data for key %s - 0x%x\n", 369 | propertyStr, 370 | status); 371 | goto Exit; 372 | } 373 | 374 | if (outputBuffer->Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE_V1 && 375 | outputBuffer->Count < 1 && 376 | outputBuffer->Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER && 377 | outputBuffer->Argument->DataLength < 1) { 378 | status = STATUS_ACPI_INVALID_ARGUMENT; 379 | goto Exit; 380 | } 381 | 382 | if (property) { 383 | *property = outputBuffer->Argument->Data[0] & 0xF; 384 | } 385 | 386 | Exit: 387 | if (inputBuffer) { 388 | ExFreePoolWithTag(inputBuffer, NAU8825_POOL_TAG); 389 | } 390 | if (outputMemory != WDF_NO_HANDLE) { 391 | WdfObjectDelete(outputMemory); 392 | } 393 | return status; 394 | } 395 | 396 | static NTSTATUS GetIntegerArrayProperty( 397 | _In_ WDFDEVICE FxDevice, 398 | char* propertyStr, 399 | UINT8 arrayCnt, 400 | UINT8 *propertyArray 401 | ) { 402 | PNAU8825_CONTEXT pDevice = GetDeviceContext(FxDevice); 403 | WDFMEMORY outputMemory = WDF_NO_HANDLE; 404 | 405 | NTSTATUS status = STATUS_ACPI_NOT_INITIALIZED; 406 | 407 | size_t inputBufferLen = sizeof(ACPI_GET_DEVICE_SPECIFIC_DATA) + strlen(propertyStr) + 1; 408 | ACPI_GET_DEVICE_SPECIFIC_DATA* inputBuffer = ExAllocatePoolWithTag(NonPagedPool, inputBufferLen, NAU8825_POOL_TAG); 409 | if (!inputBuffer) { 410 | goto Exit; 411 | } 412 | RtlZeroMemory(inputBuffer, inputBufferLen); 413 | 414 | inputBuffer->Signature = IOCTL_ACPI_GET_DEVICE_SPECIFIC_DATA_SIGNATURE; 415 | 416 | unsigned char uuidend[] = { 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01 }; 417 | 418 | inputBuffer->Section.Data1 = 0xdaffd814; 419 | inputBuffer->Section.Data2 = 0x6eba; 420 | inputBuffer->Section.Data3 = 0x4d8c; 421 | memcpy(inputBuffer->Section.Data4, uuidend, sizeof(uuidend)); //Avoid Windows defender false positive 422 | 423 | strcpy(inputBuffer->PropertyName, propertyStr); 424 | inputBuffer->PropertyNameLength = strlen(propertyStr) + 1; 425 | 426 | PACPI_EVAL_OUTPUT_BUFFER outputBuffer; 427 | size_t outputArgumentBufferSize = (sizeof(ACPI_METHOD_ARGUMENT_V1) + 4) * arrayCnt; 428 | size_t outputBufferSize = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) + outputArgumentBufferSize; 429 | 430 | WDF_OBJECT_ATTRIBUTES attributes; 431 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 432 | attributes.ParentObject = FxDevice; 433 | status = WdfMemoryCreate(&attributes, 434 | NonPagedPoolNx, 435 | 0, 436 | outputBufferSize, 437 | &outputMemory, 438 | &outputBuffer); 439 | if (!NT_SUCCESS(status)) { 440 | goto Exit; 441 | } 442 | 443 | WDF_MEMORY_DESCRIPTOR inputMemDesc; 444 | WDF_MEMORY_DESCRIPTOR outputMemDesc; 445 | WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputMemDesc, inputBuffer, (ULONG)inputBufferLen); 446 | WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&outputMemDesc, outputMemory, NULL); 447 | 448 | status = WdfIoTargetSendInternalIoctlSynchronously( 449 | WdfDeviceGetIoTarget(FxDevice), 450 | NULL, 451 | IOCTL_ACPI_GET_DEVICE_SPECIFIC_DATA, 452 | &inputMemDesc, 453 | &outputMemDesc, 454 | NULL, 455 | NULL 456 | ); 457 | if (!NT_SUCCESS(status)) { 458 | Nau8825Print( 459 | DEBUG_LEVEL_ERROR, 460 | DBG_IOCTL, 461 | "Error getting device data for key %s - 0x%x\n", 462 | propertyStr, 463 | status); 464 | goto Exit; 465 | } 466 | 467 | if (outputBuffer->Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE_V1 && 468 | outputBuffer->Count < arrayCnt && 469 | outputBuffer->Argument->Type != ACPI_METHOD_ARGUMENT_PACKAGE) { 470 | status = STATUS_ACPI_INVALID_ARGUMENT; 471 | goto Exit; 472 | } 473 | 474 | UINT8 i = 0; 475 | FOR_EACH_ACPI_METHOD_ARGUMENT(arrParameter, outputBuffer->Argument, (UINT8 *)outputBuffer + outputBuffer->Length){ 476 | if (propertyArray) { 477 | propertyArray[i] = arrParameter->Data[0] & 0xFF; 478 | } 479 | i++; 480 | } 481 | 482 | Exit: 483 | if (inputBuffer) { 484 | ExFreePoolWithTag(inputBuffer, NAU8825_POOL_TAG); 485 | } 486 | if (outputMemory != WDF_NO_HANDLE) { 487 | WdfObjectDelete(outputMemory); 488 | } 489 | return status; 490 | } 491 | 492 | static void nau8825_reset_chip(PNAU8825_CONTEXT pDevice) { 493 | nau8825_reg_write(pDevice, NAU8825_REG_RESET, 0x00); 494 | nau8825_reg_write(pDevice, NAU8825_REG_RESET, 0x00); 495 | } 496 | 497 | static void nau8825_setup_buttons(PNAU8825_CONTEXT pDevice) { 498 | nau8825_reg_update(pDevice, NAU8825_REG_SAR_CTRL, 499 | NAU8825_SAR_TRACKING_GAIN_MASK, 500 | pDevice->sar_voltage << NAU8825_SAR_TRACKING_GAIN_SFT); 501 | nau8825_reg_update(pDevice, NAU8825_REG_SAR_CTRL, 502 | NAU8825_SAR_COMPARE_TIME_MASK, 503 | pDevice->sar_compare_time << NAU8825_SAR_COMPARE_TIME_SFT); 504 | nau8825_reg_update(pDevice, NAU8825_REG_SAR_CTRL, 505 | NAU8825_SAR_SAMPLING_TIME_MASK, 506 | pDevice->sar_sampling_time << NAU8825_SAR_SAMPLING_TIME_SFT); 507 | 508 | nau8825_reg_update(pDevice, NAU8825_REG_KEYDET_CTRL, 509 | NAU8825_KEYDET_LEVELS_NR_MASK, 510 | (pDevice->sar_threshold_num - 1) << NAU8825_KEYDET_LEVELS_NR_SFT); 511 | nau8825_reg_update(pDevice, NAU8825_REG_KEYDET_CTRL, 512 | NAU8825_KEYDET_HYSTERESIS_MASK, 513 | pDevice->sar_hysteresis << NAU8825_KEYDET_HYSTERESIS_SFT); 514 | nau8825_reg_update(pDevice, NAU8825_REG_KEYDET_CTRL, 515 | NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK, 516 | pDevice->key_debounce << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT); 517 | 518 | nau8825_reg_write(pDevice, NAU8825_REG_VDET_THRESHOLD_1, 519 | (pDevice->sar_threshold[0] << 8) | pDevice->sar_threshold[1]); 520 | nau8825_reg_write(pDevice, NAU8825_REG_VDET_THRESHOLD_2, 521 | (pDevice->sar_threshold[2] << 8) | pDevice->sar_threshold[3]); 522 | nau8825_reg_write(pDevice, NAU8825_REG_VDET_THRESHOLD_3, 523 | (pDevice->sar_threshold[4] << 8) | pDevice->sar_threshold[5]); 524 | nau8825_reg_write(pDevice, NAU8825_REG_VDET_THRESHOLD_4, 525 | (pDevice->sar_threshold[6] << 8) | pDevice->sar_threshold[7]); 526 | 527 | /* Enable short press and release interruptions */ 528 | nau8825_reg_update(pDevice, NAU8825_REG_INTERRUPT_MASK, 529 | NAU8825_IRQ_KEY_SHORT_PRESS_EN | NAU8825_IRQ_KEY_RELEASE_EN, 530 | 0); 531 | } 532 | 533 | static bool nau8825_is_jack_inserted(PNAU8825_CONTEXT pDevice) { 534 | bool active_high, is_high; 535 | int status, jkdet; 536 | 537 | nau8825_reg_read(pDevice, NAU8825_REG_JACK_DET_CTRL, &jkdet); 538 | active_high = jkdet & NAU8825_JACK_POLARITY; 539 | nau8825_reg_read(pDevice, NAU8825_REG_I2C_DEVICE_ID, &status); 540 | is_high = status & NAU8825_GPIO2JD1; 541 | /* return jack connection status according to jack insertion logic 542 | * active high or active low. 543 | */ 544 | return active_high == is_high; 545 | } 546 | 547 | static void nau8825_configure_mclk_as_sysclk(PNAU8825_CONTEXT pDevice) 548 | { 549 | nau8825_reg_update(pDevice, NAU8825_REG_CLK_DIVIDER, 550 | NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK); 551 | nau8825_reg_update(pDevice, NAU8825_REG_FLL6, 552 | NAU8825_DCO_EN, 0); 553 | /* Make DSP operate as default setting for power saving. */ 554 | nau8825_reg_update(pDevice, NAU8825_REG_FLL1, 555 | NAU8825_ICTRL_LATCH_MASK, 0); 556 | } 557 | 558 | /** 559 | * nau8825_enable_jack_detect - Specify a jack for event reporting 560 | * 561 | * @component: component to register the jack with 562 | * @jack: jack to use to report headset and button events on 563 | * 564 | * After this function has been called the headset insert/remove and button 565 | * events will be routed to the given jack. Jack can be null to stop 566 | * reporting. 567 | */ 568 | NTSTATUS nau8825_enable_jack_detect(PNAU8825_CONTEXT pDevice) 569 | { 570 | /* Ground HP Outputs[1:0], needed for headset auto detection 571 | * Enable Automatic Mic/Gnd switching reading on insert interrupt[6] 572 | */ 573 | nau8825_reg_update(pDevice, NAU8825_REG_HSD_CTRL, 574 | NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, 575 | NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L); 576 | 577 | return STATUS_SUCCESS; 578 | } 579 | 580 | void nau8825_int_status_clear_all(PNAU8825_CONTEXT pDevice) 581 | { 582 | int active_irq, clear_irq, i; 583 | 584 | /* Reset the intrruption status from rightmost bit if the corres- 585 | * ponding irq event occurs. 586 | */ 587 | nau8825_reg_read(pDevice, NAU8825_REG_IRQ_STATUS, &active_irq); 588 | for (i = 0; i < NAU8825_REG_DATA_LEN; i++) { 589 | clear_irq = (0x1 << i); 590 | if (active_irq & clear_irq) 591 | nau8825_reg_write(pDevice, 592 | NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq); 593 | } 594 | } 595 | 596 | static NTSTATUS nau8825_load_settings(PNAU8825_CONTEXT pDevice) { 597 | NTSTATUS status; 598 | RtlZeroMemory(&pDevice->sar_threshold, sizeof(pDevice->sar_threshold)); 599 | 600 | //Default from Lars (for now) 601 | /*pDevice->jkdet_enable = 1; 602 | pDevice->jkdet_pull_enable = 1; 603 | pDevice->jkdet_pull_up = 1; 604 | pDevice->jkdet_polarity = 1; 605 | pDevice->vref_impedance = 2; 606 | pDevice->micbias_voltage = 6; 607 | pDevice->sar_threshold_num = 4; 608 | pDevice->sar_threshold[0] = 0x08; 609 | pDevice->sar_threshold[1] = 0x12; 610 | pDevice->sar_threshold[2] = 0x26; 611 | pDevice->sar_threshold[3] = 0x73; 612 | pDevice->sar_hysteresis = 0; 613 | pDevice->sar_voltage = 6; 614 | pDevice->sar_compare_time = 1; 615 | pDevice->sar_sampling_time = 1; 616 | pDevice->key_debounce = 3; 617 | pDevice->jack_insert_debounce = 7; 618 | pDevice->jack_eject_debounce = 0;*/ 619 | 620 | pDevice->jkdet_enable = 1; 621 | pDevice->jkdet_pull_enable = 1; 622 | pDevice->jkdet_pull_up = 1; 623 | 624 | status = GetIntegerProperty(pDevice->FxDevice, "nuvoton,jkdet-polarity", &pDevice->jkdet_polarity); 625 | if (!NT_SUCCESS(status)) { 626 | return status; 627 | } 628 | status = GetIntegerProperty(pDevice->FxDevice, "nuvoton,vref-impedance", &pDevice->vref_impedance); 629 | if (!NT_SUCCESS(status)) { 630 | return status; 631 | } 632 | status = GetIntegerProperty(pDevice->FxDevice, "nuvoton,micbias-voltage", &pDevice->micbias_voltage); 633 | if (!NT_SUCCESS(status)) { 634 | return status; 635 | } 636 | status = GetIntegerProperty(pDevice->FxDevice, "nuvoton,sar-threshold-num", &pDevice->sar_threshold_num); 637 | if (!NT_SUCCESS(status)) { 638 | return status; 639 | } 640 | 641 | status = GetIntegerArrayProperty(pDevice->FxDevice, "nuvoton,sar-threshold", pDevice->sar_threshold_num, pDevice->sar_threshold); 642 | if (!NT_SUCCESS(status)) { 643 | return status; 644 | } 645 | 646 | //TODO: Get SAR threshold 647 | 648 | status = GetIntegerProperty(pDevice->FxDevice, "nuvoton,sar-hysteresis", &pDevice->sar_hysteresis); 649 | if (!NT_SUCCESS(status)) { 650 | return status; 651 | } 652 | status = GetIntegerProperty(pDevice->FxDevice, "nuvoton,sar-voltage", &pDevice->sar_voltage); 653 | if (!NT_SUCCESS(status)) { 654 | return status; 655 | } 656 | status = GetIntegerProperty(pDevice->FxDevice, "nuvoton,sar-compare-time", &pDevice->sar_compare_time); 657 | if (!NT_SUCCESS(status)) { 658 | return status; 659 | } 660 | status = GetIntegerProperty(pDevice->FxDevice, "nuvoton,sar-sampling-time", &pDevice->sar_sampling_time); 661 | if (!NT_SUCCESS(status)) { 662 | return status; 663 | } 664 | status = GetIntegerProperty(pDevice->FxDevice, "nuvoton,short-key-debounce", &pDevice->key_debounce); 665 | if (!NT_SUCCESS(status)) { 666 | return status; 667 | } 668 | status = GetIntegerProperty(pDevice->FxDevice, "nuvoton,jack-insert-debounce", &pDevice->jack_insert_debounce); 669 | if (!NT_SUCCESS(status)) { 670 | return status; 671 | } 672 | GetIntegerProperty(pDevice->FxDevice, "nuvoton,jack-eject-debounce", &pDevice->jack_eject_debounce); 673 | return status; 674 | } 675 | 676 | static void nau8825_init_regs(PNAU8825_CONTEXT pDevice) { 677 | /* Latch IIC LSB value */ 678 | nau8825_reg_write(pDevice, NAU8825_REG_IIC_ADDR_SET, 0x0001); 679 | /* Enable Bias/Vmid */ 680 | nau8825_reg_update(pDevice, NAU8825_REG_BIAS_ADJ, 681 | NAU8825_BIAS_VMID, NAU8825_BIAS_VMID); 682 | nau8825_reg_update(pDevice, NAU8825_REG_BOOST, 683 | NAU8825_GLOBAL_BIAS_EN, NAU8825_GLOBAL_BIAS_EN); 684 | 685 | /* VMID Tieoff */ 686 | nau8825_reg_update(pDevice, NAU8825_REG_BIAS_ADJ, 687 | NAU8825_BIAS_VMID_SEL_MASK, 688 | pDevice->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT); 689 | /* Disable Boost Driver, Automatic Short circuit protection enable */ 690 | nau8825_reg_update(pDevice, NAU8825_REG_BOOST, 691 | NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS | 692 | NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN, 693 | NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS | 694 | NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN); 695 | 696 | nau8825_reg_update(pDevice, NAU8825_REG_GPIO12_CTRL, 697 | NAU8825_JKDET_OUTPUT_EN, 698 | pDevice->jkdet_enable ? 0 : NAU8825_JKDET_OUTPUT_EN); 699 | nau8825_reg_update(pDevice, NAU8825_REG_GPIO12_CTRL, 700 | NAU8825_JKDET_PULL_EN, 701 | pDevice->jkdet_pull_enable ? 0 : NAU8825_JKDET_PULL_EN); 702 | nau8825_reg_update(pDevice, NAU8825_REG_GPIO12_CTRL, 703 | NAU8825_JKDET_PULL_UP, 704 | pDevice->jkdet_pull_up ? NAU8825_JKDET_PULL_UP : 0); 705 | nau8825_reg_update(pDevice, NAU8825_REG_JACK_DET_CTRL, 706 | NAU8825_JACK_POLARITY, 707 | /* jkdet_polarity - 1 is for active-low */ 708 | pDevice->jkdet_polarity ? 0 : NAU8825_JACK_POLARITY); 709 | 710 | nau8825_reg_update(pDevice, NAU8825_REG_JACK_DET_CTRL, 711 | NAU8825_JACK_INSERT_DEBOUNCE_MASK, 712 | pDevice->jack_insert_debounce << NAU8825_JACK_INSERT_DEBOUNCE_SFT); 713 | nau8825_reg_update(pDevice, NAU8825_REG_JACK_DET_CTRL, 714 | NAU8825_JACK_EJECT_DEBOUNCE_MASK, 715 | pDevice->jack_eject_debounce << NAU8825_JACK_EJECT_DEBOUNCE_SFT); 716 | 717 | /* Pull up IRQ pin */ 718 | nau8825_reg_update(pDevice, NAU8825_REG_INTERRUPT_MASK, 719 | NAU8825_IRQ_PIN_PULLUP | NAU8825_IRQ_PIN_PULL_EN, 720 | NAU8825_IRQ_PIN_PULLUP | NAU8825_IRQ_PIN_PULL_EN); 721 | /* Mask unneeded IRQs: 1 - disable, 0 - enable */ 722 | nau8825_reg_update(pDevice, NAU8825_REG_INTERRUPT_MASK, 0x7ff, 0x7ff); 723 | 724 | nau8825_reg_update(pDevice, NAU8825_REG_MIC_BIAS, 725 | NAU8825_MICBIAS_VOLTAGE_MASK, pDevice->micbias_voltage); 726 | 727 | if (pDevice->sar_threshold_num) 728 | nau8825_setup_buttons(pDevice); 729 | 730 | /* Default oversampling/decimations settings are unusable 731 | * (audible hiss). Set it to something better. 732 | */ 733 | nau8825_reg_update(pDevice, NAU8825_REG_ADC_RATE, 734 | NAU8825_ADC_SYNC_DOWN_MASK | NAU8825_ADC_SINC4_EN, 735 | NAU8825_ADC_SYNC_DOWN_64); 736 | nau8825_reg_update(pDevice, NAU8825_REG_DAC_CTRL1, 737 | NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_64); 738 | /* Disable DACR/L power */ 739 | nau8825_reg_update(pDevice, NAU8825_REG_CHARGE_PUMP, 740 | NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 741 | NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); 742 | /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input 743 | * signal to avoid any glitches due to power up transients in both 744 | * the analog and digital DAC circuit. 745 | */ 746 | nau8825_reg_update(pDevice, NAU8825_REG_BIAS_ADJ, 747 | NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN); 748 | /* CICCLP off */ 749 | nau8825_reg_update(pDevice, NAU8825_REG_DAC_CTRL1, 750 | NAU8825_DAC_CLIP_OFF, NAU8825_DAC_CLIP_OFF); 751 | 752 | /* Class AB bias current to 2x, DAC Capacitor enable MSB/LSB */ 753 | nau8825_reg_update(pDevice, NAU8825_REG_ANALOG_CONTROL_2, 754 | NAU8825_HP_NON_CLASSG_CURRENT_2xADJ | 755 | NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB, 756 | NAU8825_HP_NON_CLASSG_CURRENT_2xADJ | 757 | NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB); 758 | /* Class G timer 64ms */ 759 | nau8825_reg_update(pDevice, NAU8825_REG_CLASSG_CTRL, 760 | NAU8825_CLASSG_TIMER_MASK, 761 | 0x20 << NAU8825_CLASSG_TIMER_SFT); 762 | /* DAC clock delay 2ns, VREF */ 763 | nau8825_reg_update(pDevice, NAU8825_REG_RDAC, 764 | NAU8825_RDAC_CLK_DELAY_MASK | NAU8825_RDAC_VREF_MASK, 765 | (0x2 << NAU8825_RDAC_CLK_DELAY_SFT) | 766 | (0x3 << NAU8825_RDAC_VREF_SFT)); 767 | /* Config L/R channel */ 768 | nau8825_reg_update(pDevice, NAU8825_REG_DACL_CTRL, 769 | NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_L); 770 | nau8825_reg_update(pDevice, NAU8825_REG_DACR_CTRL, 771 | NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_R); 772 | /* Disable short Frame Sync detection logic */ 773 | nau8825_reg_update(pDevice, NAU8825_REG_LEFT_TIME_SLOT, 774 | NAU8825_DIS_FS_SHORT_DET, NAU8825_DIS_FS_SHORT_DET); 775 | 776 | { 777 | //Resume setup 778 | 779 | /* Clock provided externally and disable internal VCO clock */ 780 | nau8825_configure_mclk_as_sysclk(pDevice); 781 | 782 | nau8825_int_status_clear_all(pDevice); 783 | 784 | /* Enable both insertion and ejection interruptions, and then 785 | * bypass de-bounce circuit. 786 | */ 787 | nau8825_reg_update(pDevice, NAU8825_REG_INTERRUPT_MASK, 788 | NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN | 789 | NAU8825_IRQ_EJECT_EN | NAU8825_IRQ_INSERT_EN, 790 | NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN); 791 | nau8825_reg_update(pDevice, NAU8825_REG_JACK_DET_CTRL, 792 | NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS); 793 | nau8825_reg_update(pDevice, NAU8825_REG_INTERRUPT_DIS_CTRL, 794 | NAU8825_IRQ_INSERT_DIS | NAU8825_IRQ_EJECT_DIS, 0); 795 | } 796 | 797 | if (GetPlatform() == PlatformAlderLake) { 798 | nau8825_configure_sysclk(pDevice, NAU8825_CLK_FLL_BLK, 0); 799 | } 800 | 801 | nau8825_update_reclock(pDevice); 802 | 803 | { 804 | //Set sane defaults from Linux 805 | nau8825_reg_update(pDevice, NAU8825_REG_ENA_CTRL, 0x0700, 0x0700); //nothing running is 0x013f, headphones is 0x077f, both running is 0x07ff 806 | 807 | nau8825_reg_update(pDevice, NAU8825_REG_INTERRUPT_MASK, 0xf, 0xe); 808 | 809 | if (GetPlatform() == PlatformAlderLake) { 810 | nau8825_reg_update(pDevice, NAU8825_REG_ADC_DGAIN_CTRL, 0xff, 0xe6); 811 | 812 | nau8825_reg_update(pDevice, NAU8825_REG_POWER_UP_CONTROL, 0x1f00, 0x1800); 813 | } else { 814 | nau8825_reg_update(pDevice, NAU8825_REG_ADC_DGAIN_CTRL, 0xff, 0xff); 815 | } 816 | 817 | nau8825_reg_update(pDevice, NAU8825_REG_CHARGE_PUMP, 0xf00, 0x300); 818 | } 819 | 820 | nau8825_enable_jack_detect(pDevice); 821 | } 822 | 823 | #define NAU_FREF_MAX 13500000 824 | #define NAU_FVCO_MAX 124000000 825 | #define NAU_FVCO_MIN 90000000 826 | 827 | struct nau8825_fll { 828 | int mclk_src; 829 | int ratio; 830 | int fll_frac; 831 | int fll_int; 832 | int clk_ref_div; 833 | }; 834 | 835 | struct nau8825_fll_attr { 836 | unsigned int param; 837 | unsigned int val; 838 | }; 839 | 840 | /* scaling for mclk from sysclk_src output */ 841 | static const struct nau8825_fll_attr mclk_src_scaling[] = { 842 | { 1, 0x0 }, 843 | { 2, 0x2 }, 844 | { 4, 0x3 }, 845 | { 8, 0x4 }, 846 | { 16, 0x5 }, 847 | { 32, 0x6 }, 848 | { 3, 0x7 }, 849 | { 6, 0xa }, 850 | { 12, 0xb }, 851 | { 24, 0xc }, 852 | { 48, 0xd }, 853 | { 96, 0xe }, 854 | { 5, 0xf }, 855 | }; 856 | 857 | /* ratio for input clk freq */ 858 | static const struct nau8825_fll_attr fll_ratio[] = { 859 | { 512000, 0x01 }, 860 | { 256000, 0x02 }, 861 | { 128000, 0x04 }, 862 | { 64000, 0x08 }, 863 | { 32000, 0x10 }, 864 | { 8000, 0x20 }, 865 | { 4000, 0x40 }, 866 | }; 867 | 868 | static const struct nau8825_fll_attr fll_pre_scalar[] = { 869 | { 1, 0x0 }, 870 | { 2, 0x1 }, 871 | { 4, 0x2 }, 872 | { 8, 0x3 }, 873 | }; 874 | 875 | /** 876 | * nau8825_calc_fll_param - Calculate FLL parameters. 877 | * @fll_in: external clock provided to codec. 878 | * @fs: sampling rate. 879 | * @fll_param: Pointer to structure of FLL parameters. 880 | * 881 | * Calculate FLL parameters to configure codec. 882 | * 883 | * Returns 0 for success or negative error code. 884 | */ 885 | static NTSTATUS nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, 886 | struct nau8825_fll* fll_param) 887 | { 888 | UINT64 fvco, fvco_max; 889 | unsigned int fref, i, fvco_sel; 890 | 891 | /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing 892 | * freq_in by 1, 2, 4, or 8 using FLL pre-scalar. 893 | * FREF = freq_in / NAU8825_FLL_REF_DIV_MASK 894 | */ 895 | for (i = 0; i < ARRAYSIZE(fll_pre_scalar); i++) { 896 | fref = fll_in / fll_pre_scalar[i].param; 897 | if (fref <= NAU_FREF_MAX) 898 | break; 899 | } 900 | if (i == ARRAYSIZE(fll_pre_scalar)) 901 | return STATUS_INVALID_PARAMETER; 902 | fll_param->clk_ref_div = fll_pre_scalar[i].val; 903 | 904 | /* Choose the FLL ratio based on FREF */ 905 | for (i = 0; i < ARRAYSIZE(fll_ratio); i++) { 906 | if (fref >= fll_ratio[i].param) 907 | break; 908 | } 909 | if (i == ARRAYSIZE(fll_ratio)) 910 | return STATUS_INVALID_PARAMETER; 911 | fll_param->ratio = fll_ratio[i].val; 912 | 913 | /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs. 914 | * FDCO must be within the 90MHz - 124MHz or the FFL cannot be 915 | * guaranteed across the full range of operation. 916 | * FDCO = freq_out * 2 * mclk_src_scaling 917 | */ 918 | fvco_max = 0; 919 | fvco_sel = ARRAYSIZE(mclk_src_scaling); 920 | for (i = 0; i < ARRAYSIZE(mclk_src_scaling); i++) { 921 | fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param; 922 | if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && 923 | fvco_max < fvco) { 924 | fvco_max = fvco; 925 | fvco_sel = i; 926 | } 927 | } 928 | if (ARRAYSIZE(mclk_src_scaling) == fvco_sel) 929 | return STATUS_INVALID_PARAMETER; 930 | fll_param->mclk_src = mclk_src_scaling[fvco_sel].val; 931 | 932 | /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional 933 | * input based on FDCO, FREF and FLL ratio. 934 | */ 935 | fvco = (fvco_max << 16) / ((UINT64)fref * (UINT64)fll_param->ratio); 936 | fll_param->fll_int = (fvco >> 16) & 0x3FF; 937 | fll_param->fll_frac = fvco & 0xFFFF; 938 | return STATUS_SUCCESS; 939 | } 940 | 941 | static void nau8825_fll_apply(PNAU8825_CONTEXT pDevice, 942 | struct nau8825_fll* fll_param) 943 | { 944 | nau8825_reg_update(pDevice, NAU8825_REG_CLK_DIVIDER, 945 | NAU8825_CLK_SRC_MASK | NAU8825_CLK_MCLK_SRC_MASK, 946 | NAU8825_CLK_SRC_MCLK | fll_param->mclk_src); 947 | /* Make DSP operate at high speed for better performance. */ 948 | nau8825_reg_update(pDevice, NAU8825_REG_FLL1, 949 | NAU8825_FLL_RATIO_MASK | NAU8825_ICTRL_LATCH_MASK, 950 | fll_param->ratio | (0x6 << NAU8825_ICTRL_LATCH_SFT)); 951 | /* FLL 16-bit fractional input */ 952 | nau8825_reg_write(pDevice, NAU8825_REG_FLL2, fll_param->fll_frac); 953 | /* FLL 10-bit integer input */ 954 | nau8825_reg_update(pDevice, NAU8825_REG_FLL3, 955 | NAU8825_FLL_INTEGER_MASK, fll_param->fll_int); 956 | /* FLL pre-scaler */ 957 | nau8825_reg_update(pDevice, NAU8825_REG_FLL4, 958 | NAU8825_FLL_REF_DIV_MASK, 959 | fll_param->clk_ref_div << NAU8825_FLL_REF_DIV_SFT); 960 | /* select divided VCO input */ 961 | nau8825_reg_update(pDevice, NAU8825_REG_FLL5, 962 | NAU8825_FLL_CLK_SW_MASK, NAU8825_FLL_CLK_SW_REF); 963 | /* Disable free-running mode */ 964 | nau8825_reg_update(pDevice, 965 | NAU8825_REG_FLL6, NAU8825_DCO_EN, 0); 966 | if (fll_param->fll_frac) { 967 | /* set FLL loop filter enable and cutoff frequency at 500Khz */ 968 | nau8825_reg_update(pDevice, NAU8825_REG_FLL5, 969 | NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN | 970 | NAU8825_FLL_FTR_SW_MASK, 971 | NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN | 972 | NAU8825_FLL_FTR_SW_FILTER); 973 | nau8825_reg_update(pDevice, NAU8825_REG_FLL6, 974 | NAU8825_SDM_EN | NAU8825_CUTOFF500, 975 | NAU8825_SDM_EN | NAU8825_CUTOFF500); 976 | } 977 | else { 978 | /* disable FLL loop filter and cutoff frequency */ 979 | nau8825_reg_update(pDevice, NAU8825_REG_FLL5, 980 | NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN | 981 | NAU8825_FLL_FTR_SW_MASK, NAU8825_FLL_FTR_SW_ACCU); 982 | nau8825_reg_update(pDevice, NAU8825_REG_FLL6, 983 | NAU8825_SDM_EN | NAU8825_CUTOFF500, 0); 984 | } 985 | } 986 | 987 | void nau8825_update_reclock(PNAU8825_CONTEXT pDevice) { 988 | if (pDevice->ReclockRequested) { 989 | struct nau8825_fll fll_param; 990 | int fs = pDevice->freq; 991 | 992 | NTSTATUS status = nau8825_calc_fll_param(pDevice->bclkRate, fs, &fll_param); 993 | if (!NT_SUCCESS(status)) { 994 | DbgPrint("Failed to calculate parameters\n"); 995 | return status; 996 | } 997 | 998 | nau8825_fll_apply(pDevice, &fll_param); 999 | 1000 | nau8825_reg_update(pDevice, NAU8825_REG_CLK_DIVIDER, 1001 | NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); 1002 | } 1003 | 1004 | nau8825_reg_update(pDevice, NAU8825_REG_I2S_PCM_CTRL1, NAU8825_I2S_DF_MASK, NAU8825_I2S_DF_I2S); 1005 | 1006 | switch (pDevice->validBits) { 1007 | case 16: 1008 | nau8825_reg_update(pDevice, NAU8825_REG_I2S_PCM_CTRL1, NAU8825_I2S_DL_MASK, NAU8825_I2S_DL_16); 1009 | break; 1010 | case 20: 1011 | nau8825_reg_update(pDevice, NAU8825_REG_I2S_PCM_CTRL1, NAU8825_I2S_DL_MASK, NAU8825_I2S_DL_20); 1012 | break; 1013 | case 32: 1014 | nau8825_reg_update(pDevice, NAU8825_REG_I2S_PCM_CTRL1, NAU8825_I2S_DL_MASK, NAU8825_I2S_DL_32); 1015 | break; 1016 | default: 1017 | case 24: 1018 | nau8825_reg_update(pDevice, NAU8825_REG_I2S_PCM_CTRL1, NAU8825_I2S_DL_MASK, NAU8825_I2S_DL_24); 1019 | } 1020 | } 1021 | 1022 | VOID 1023 | CsAudioCallbackFunction( 1024 | IN PNAU8825_CONTEXT pDevice, 1025 | CsAudioArg* arg, 1026 | PVOID Argument2 1027 | ) { 1028 | if (!pDevice) { 1029 | return; 1030 | } 1031 | 1032 | if (Argument2 == &CsAudioArg2) { 1033 | return; 1034 | } 1035 | 1036 | pDevice->CSAudioManaged = TRUE; 1037 | 1038 | CsAudioArg localArg; 1039 | RtlZeroMemory(&localArg, sizeof(CsAudioArg)); 1040 | RtlCopyMemory(&localArg, arg, min(arg->argSz, sizeof(CsAudioArg))); 1041 | 1042 | if (localArg.endpointType == CSAudioEndpointTypeDSP && localArg.endpointRequest == CSAudioEndpointRegister) { 1043 | CSAudioRegisterEndpoint(pDevice); 1044 | } 1045 | else if (localArg.endpointType != CSAudioEndpointTypeHeadphone && 1046 | localArg.endpointType != CSAudioEndpointTypeMicJack) { //check both in case user decides to record first 1047 | return; 1048 | } 1049 | 1050 | if (localArg.endpointRequest == CSAudioEndpointI2SParameters && 1051 | localArg.i2sParameters.version >= 1) { //Supports version 1 or higher 1052 | 1053 | //Reclock requested 1054 | UINT32 bclk_rate = localArg.i2sParameters.bclk_rate; 1055 | UINT32 freq = localArg.i2sParameters.frequency; 1056 | UINT32 validBits = localArg.i2sParameters.valid_bits; 1057 | 1058 | if (!pDevice->ReclockRequested) { 1059 | pDevice->bclkRate = bclk_rate; 1060 | pDevice->freq = freq; 1061 | pDevice->validBits = validBits; 1062 | pDevice->ReclockRequested = TRUE; 1063 | 1064 | nau8825_reset_chip(pDevice); 1065 | nau8825_init_regs(pDevice); 1066 | } 1067 | } 1068 | } 1069 | 1070 | NTSTATUS 1071 | OnSelfManagedIoInit( 1072 | _In_ 1073 | WDFDEVICE FxDevice 1074 | ) { 1075 | PNAU8825_CONTEXT pDevice = GetDeviceContext(FxDevice); 1076 | NTSTATUS status = STATUS_SUCCESS; 1077 | 1078 | // CS Audio Callback 1079 | 1080 | UNICODE_STRING CSAudioCallbackAPI; 1081 | RtlInitUnicodeString(&CSAudioCallbackAPI, L"\\CallBack\\CsAudioCallbackAPI"); 1082 | 1083 | 1084 | OBJECT_ATTRIBUTES attributes; 1085 | InitializeObjectAttributes(&attributes, 1086 | &CSAudioCallbackAPI, 1087 | OBJ_KERNEL_HANDLE | OBJ_OPENIF | OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, 1088 | NULL, 1089 | NULL 1090 | ); 1091 | status = ExCreateCallback(&pDevice->CSAudioAPICallback, &attributes, TRUE, TRUE); 1092 | if (!NT_SUCCESS(status)) { 1093 | 1094 | return status; 1095 | } 1096 | 1097 | pDevice->CSAudioAPICallbackObj = ExRegisterCallback(pDevice->CSAudioAPICallback, 1098 | CsAudioCallbackFunction, 1099 | pDevice 1100 | ); 1101 | if (!pDevice->CSAudioAPICallbackObj) { 1102 | 1103 | return STATUS_NO_CALLBACK_ACTIVE; 1104 | } 1105 | 1106 | CSAudioRegisterEndpoint(pDevice); 1107 | 1108 | return status; 1109 | } 1110 | 1111 | NTSTATUS 1112 | OnD0Entry( 1113 | _In_ WDFDEVICE FxDevice, 1114 | _In_ WDF_POWER_DEVICE_STATE FxPreviousState 1115 | ) 1116 | /*++ 1117 | 1118 | Routine Description: 1119 | 1120 | This routine allocates objects needed by the driver. 1121 | 1122 | Arguments: 1123 | 1124 | FxDevice - a handle to the framework device object 1125 | FxPreviousState - previous power state 1126 | 1127 | Return Value: 1128 | 1129 | Status 1130 | 1131 | --*/ 1132 | { 1133 | UNREFERENCED_PARAMETER(FxPreviousState); 1134 | 1135 | PNAU8825_CONTEXT pDevice = GetDeviceContext(FxDevice); 1136 | NTSTATUS status = nau8825_load_settings(pDevice); 1137 | 1138 | if (!NT_SUCCESS(status)) { 1139 | return status; 1140 | } 1141 | 1142 | pDevice->JackType = 0; 1143 | 1144 | nau8825_reset_chip(pDevice); 1145 | int value; 1146 | status = nau8825_reg_read(pDevice, NAU8825_REG_I2C_DEVICE_ID, &value); 1147 | if (!NT_SUCCESS(status)) { 1148 | return status; 1149 | } 1150 | 1151 | if ((value & NAU8825_SOFTWARE_ID_MASK) != 1152 | NAU8825_SOFTWARE_ID_NAU8825) { 1153 | DbgPrint("Not a NAU8825 chip\n"); 1154 | return; 1155 | } 1156 | 1157 | nau8825_init_regs(pDevice); 1158 | 1159 | pDevice->DevicePoweredOn = true; 1160 | 1161 | return status; 1162 | } 1163 | 1164 | NTSTATUS 1165 | OnD0Exit( 1166 | _In_ WDFDEVICE FxDevice, 1167 | _In_ WDF_POWER_DEVICE_STATE FxPreviousState 1168 | ) 1169 | /*++ 1170 | 1171 | Routine Description: 1172 | 1173 | This routine destroys objects needed by the driver. 1174 | 1175 | Arguments: 1176 | 1177 | FxDevice - a handle to the framework device object 1178 | FxPreviousState - previous power state 1179 | 1180 | Return Value: 1181 | 1182 | Status 1183 | 1184 | --*/ 1185 | { 1186 | UNREFERENCED_PARAMETER(FxPreviousState); 1187 | 1188 | PNAU8825_CONTEXT pDevice = GetDeviceContext(FxDevice); 1189 | NTSTATUS status = STATUS_SUCCESS; 1190 | 1191 | pDevice->DevicePoweredOn = FALSE; 1192 | 1193 | return STATUS_SUCCESS; 1194 | } 1195 | 1196 | static void nau8825_restart_jack_detection(PNAU8825_CONTEXT pDevice) 1197 | { 1198 | /* this will restart the entire jack detection process including MIC/GND 1199 | * switching and create interrupts. We have to go from 0 to 1 and back 1200 | * to 0 to restart. 1201 | */ 1202 | nau8825_reg_update(pDevice, NAU8825_REG_JACK_DET_CTRL, 1203 | NAU8825_JACK_DET_RESTART, NAU8825_JACK_DET_RESTART); 1204 | nau8825_reg_update(pDevice, NAU8825_REG_JACK_DET_CTRL, 1205 | NAU8825_JACK_DET_RESTART, 0); 1206 | } 1207 | 1208 | NTSTATUS nau8825_configure_sysclk(PNAU8825_CONTEXT pDevice, int clk_id, 1209 | unsigned int freq) 1210 | { 1211 | NTSTATUS status; 1212 | 1213 | switch (clk_id) { 1214 | case NAU8825_CLK_DIS: 1215 | /* Clock provided externally and disable internal VCO clock */ 1216 | nau8825_configure_mclk_as_sysclk(pDevice); 1217 | break; 1218 | case NAU8825_CLK_MCLK: 1219 | /* Acquire the semaphore to synchronize the playback and 1220 | * interrupt handler. In order to avoid the playback inter- 1221 | * fered by cross talk process, the driver make the playback 1222 | * preparation halted until cross talk process finish. 1223 | */ 1224 | nau8825_configure_mclk_as_sysclk(pDevice); 1225 | /* MCLK not changed by clock tree */ 1226 | nau8825_reg_update(pDevice, NAU8825_REG_CLK_DIVIDER, 1227 | NAU8825_CLK_MCLK_SRC_MASK, 0); 1228 | 1229 | break; 1230 | case NAU8825_CLK_INTERNAL: 1231 | if (nau8825_is_jack_inserted(pDevice)) { 1232 | nau8825_reg_update(pDevice, NAU8825_REG_FLL6, 1233 | NAU8825_DCO_EN, NAU8825_DCO_EN); 1234 | nau8825_reg_update(pDevice, NAU8825_REG_CLK_DIVIDER, 1235 | NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); 1236 | /* Decrease the VCO frequency and make DSP operate 1237 | * as default setting for power saving. 1238 | */ 1239 | nau8825_reg_update(pDevice, NAU8825_REG_CLK_DIVIDER, 1240 | NAU8825_CLK_MCLK_SRC_MASK, 0xf); 1241 | nau8825_reg_update(pDevice, NAU8825_REG_FLL1, 1242 | NAU8825_ICTRL_LATCH_MASK | 1243 | NAU8825_FLL_RATIO_MASK, 0x10); 1244 | nau8825_reg_update(pDevice, NAU8825_REG_FLL6, 1245 | NAU8825_SDM_EN, NAU8825_SDM_EN); 1246 | } 1247 | else { 1248 | /* The clock turns off intentionally for power saving 1249 | * when no headset connected. 1250 | */ 1251 | nau8825_configure_mclk_as_sysclk(pDevice); 1252 | Nau8825Print(DEBUG_LEVEL_INFO, DBG_IOCTL, "Disable clock for power saving when no headset connected\n"); 1253 | } 1254 | break; 1255 | case NAU8825_CLK_FLL_MCLK: 1256 | /* Higher FLL reference input frequency can only set lower 1257 | * gain error, such as 0000 for input reference from MCLK 1258 | * 12.288Mhz. 1259 | */ 1260 | nau8825_reg_update(pDevice, NAU8825_REG_FLL3, 1261 | NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK, 1262 | NAU8825_FLL_CLK_SRC_MCLK | 0); 1263 | 1264 | break; 1265 | case NAU8825_CLK_FLL_BLK: 1266 | /* If FLL reference input is from low frequency source, 1267 | * higher error gain can apply such as 0xf which has 1268 | * the most sensitive gain error correction threshold, 1269 | * Therefore, FLL has the most accurate DCO to 1270 | * target frequency. 1271 | */ 1272 | nau8825_reg_update(pDevice, NAU8825_REG_FLL3, 1273 | NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK, 1274 | NAU8825_FLL_CLK_SRC_BLK | 1275 | (0xf << NAU8825_GAIN_ERR_SFT)); 1276 | break; 1277 | case NAU8825_CLK_FLL_FS: 1278 | /* If FLL reference input is from low frequency source, 1279 | * higher error gain can apply such as 0xf which has 1280 | * the most sensitive gain error correction threshold, 1281 | * Therefore, FLL has the most accurate DCO to 1282 | * target frequency. 1283 | */ 1284 | nau8825_reg_update(pDevice, NAU8825_REG_FLL3, 1285 | NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK, 1286 | NAU8825_FLL_CLK_SRC_FS | 1287 | (0xf << NAU8825_GAIN_ERR_SFT)); 1288 | break; 1289 | default: 1290 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, "Invalid clock id (%d)\n", clk_id); 1291 | return STATUS_INVALID_DEVICE_STATE; 1292 | } 1293 | 1294 | Nau8825Print(DEBUG_LEVEL_INFO, DBG_IOCTL, "Sysclk is %dHz and clock id is %d\n", freq, 1295 | clk_id); 1296 | return STATUS_SUCCESS; 1297 | } 1298 | 1299 | /* Enable audo mode interruptions with internal clock. */ 1300 | static void nau8825_setup_auto_irq(PNAU8825_CONTEXT pDevice) 1301 | { 1302 | /* Enable headset jack type detection complete interruption and 1303 | * jack ejection interruption. 1304 | */ 1305 | nau8825_reg_update(pDevice, NAU8825_REG_INTERRUPT_MASK, 1306 | NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0); 1307 | 1308 | /* Enable internal VCO needed for interruptions */ 1309 | nau8825_configure_sysclk(pDevice, NAU8825_CLK_INTERNAL, 0); 1310 | 1311 | /* Enable ADC needed for interruptions */ 1312 | nau8825_reg_update(pDevice, NAU8825_REG_ENA_CTRL, 1313 | NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC); 1314 | 1315 | /* Chip needs one FSCLK cycle in order to generate interruptions, 1316 | * as we cannot guarantee one will be provided by the system. Turning 1317 | * master mode on then off enables us to generate that FSCLK cycle 1318 | * with a minimum of contention on the clock bus. 1319 | */ 1320 | nau8825_reg_update(pDevice, NAU8825_REG_I2S_PCM_CTRL2, 1321 | NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER); 1322 | nau8825_reg_update(pDevice, NAU8825_REG_I2S_PCM_CTRL2, 1323 | NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE); 1324 | 1325 | /* Not bypass de-bounce circuit */ 1326 | nau8825_reg_update(pDevice, NAU8825_REG_JACK_DET_CTRL, 1327 | NAU8825_JACK_DET_DB_BYPASS, 0); 1328 | 1329 | /* Unmask all interruptions */ 1330 | nau8825_reg_write(pDevice, NAU8825_REG_INTERRUPT_DIS_CTRL, 0); 1331 | 1332 | /* Restart the jack detection process at auto mode */ 1333 | nau8825_restart_jack_detection(pDevice); 1334 | } 1335 | 1336 | static int nau8825_button_decode(int value) 1337 | { 1338 | int buttons = 0; 1339 | 1340 | /* The chip supports up to 8 buttons, but HID defines only 4 buttons */ 1341 | buttons = value & 0xF; 1342 | 1343 | return buttons; 1344 | } 1345 | 1346 | static void nau8825_eject_jack(PNAU8825_CONTEXT pDevice) 1347 | { 1348 | { 1349 | //Unset 2nd defaults set from Linux 1350 | nau8825_reg_update(pDevice, NAU8825_REG_SAR_CTRL, NAU8825_SAR_ADC_EN, 0); 1351 | 1352 | nau8825_reg_update(pDevice, NAU8825_REG_CLASSG_CTRL, NAU8825_CLASSG_LDAC_EN | NAU8825_CLASSG_RDAC_EN | NAU8825_CLASSG_EN, 0); 1353 | 1354 | nau8825_reg_update(pDevice, NAU8825_REG_BIAS_ADJ, NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN); 1355 | 1356 | nau8825_reg_update(pDevice, NAU8825_REG_ANALOG_ADC_2, NAU8825_POWERUP_ADCL, 0); 1357 | 1358 | nau8825_reg_update(pDevice, NAU8825_REG_RDAC, NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN, 0); 1359 | 1360 | nau8825_reg_update(pDevice, NAU8825_REG_MIC_BIAS, 1361 | (1 << 8), 1362 | (0 << 8)); //Disable Mic Bias 1363 | 1364 | nau8825_reg_update(pDevice, NAU8825_REG_BOOST, NAU8825_HP_BOOST_DIS, NAU8825_HP_BOOST_DIS); 1365 | 1366 | nau8825_reg_update(pDevice, NAU8825_REG_POWER_UP_CONTROL, 1367 | NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L | NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L | NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L, 1368 | 0); 1369 | 1370 | nau8825_reg_update(pDevice, NAU8825_REG_POWER_UP_CONTROL, 1371 | (1 << 14), 1372 | (0 << 14)); //Frontend PGA Disable 1373 | 1374 | nau8825_reg_update(pDevice, NAU8825_REG_CHARGE_PUMP, 1375 | NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN, 1376 | 0); 1377 | 1378 | nau8825_reg_update(pDevice, NAU8825_REG_CHARGE_PUMP, 1379 | NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 1380 | NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); 1381 | } 1382 | 1383 | /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */ 1384 | nau8825_reg_update(pDevice, NAU8825_REG_MIC_BIAS, 1385 | NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0); 1386 | /* ground HPL/HPR, MICGRND1/2 */ 1387 | nau8825_reg_update(pDevice, NAU8825_REG_HSD_CTRL, 0xf, 0xf); 1388 | 1389 | /* Clear all interruption status */ 1390 | nau8825_int_status_clear_all(pDevice); 1391 | 1392 | /* Enable the insertion interruption, disable the ejection inter- 1393 | * ruption, and then bypass de-bounce circuit. 1394 | */ 1395 | nau8825_reg_update(pDevice, NAU8825_REG_INTERRUPT_DIS_CTRL, 1396 | NAU8825_IRQ_EJECT_DIS | NAU8825_IRQ_INSERT_DIS, 1397 | NAU8825_IRQ_EJECT_DIS); 1398 | nau8825_reg_update(pDevice, NAU8825_REG_INTERRUPT_MASK, 1399 | NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN | 1400 | NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_INSERT_EN, 1401 | NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN | 1402 | NAU8825_IRQ_HEADSET_COMPLETE_EN); 1403 | nau8825_reg_update(pDevice, NAU8825_REG_JACK_DET_CTRL, 1404 | NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS); 1405 | 1406 | /* Disable ADC needed for interruptions at audo mode */ 1407 | nau8825_reg_update(pDevice, NAU8825_REG_ENA_CTRL, 1408 | NAU8825_ENABLE_ADC, 0); 1409 | 1410 | /* Close clock for jack type detection at manual mode */ 1411 | nau8825_configure_sysclk(pDevice, NAU8825_CLK_DIS, 0); 1412 | } 1413 | 1414 | static int nau8825_jack_insert(PNAU8825_CONTEXT pDevice) 1415 | { 1416 | int jack_status_reg, mic_detected; 1417 | int type = 0; 1418 | 1419 | nau8825_reg_read(pDevice, NAU8825_REG_GENERAL_STATUS, &jack_status_reg); 1420 | mic_detected = (jack_status_reg >> 10) & 3; 1421 | 1422 | switch (mic_detected) { 1423 | case 0: 1424 | /* no mic */ 1425 | type = SND_JACK_HEADPHONE; 1426 | break; 1427 | case 1: 1428 | Nau8825Print(DEBUG_LEVEL_INFO, DBG_IOCTL, "OMTP (micgnd1) mic connected\n"); 1429 | type = SND_JACK_HEADSET; 1430 | 1431 | /* Unground MICGND1 */ 1432 | nau8825_reg_update(pDevice, NAU8825_REG_HSD_CTRL, 3 << 2, 1433 | 1 << 2); 1434 | /* Attach 2kOhm Resistor from MICBIAS to MICGND1 */ 1435 | nau8825_reg_update(pDevice, NAU8825_REG_MIC_BIAS, 1436 | NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 1437 | NAU8825_MICBIAS_JKR2); 1438 | /* Attach SARADC to MICGND1 */ 1439 | nau8825_reg_update(pDevice, NAU8825_REG_SAR_CTRL, 1440 | NAU8825_SAR_INPUT_MASK, 1441 | NAU8825_SAR_INPUT_JKR2); 1442 | break; 1443 | case 2: 1444 | Nau8825Print(DEBUG_LEVEL_INFO, DBG_IOCTL, "CTIA (micgnd2) mic connected\n"); 1445 | type = SND_JACK_HEADSET; 1446 | 1447 | /* Unground MICGND2 */ 1448 | nau8825_reg_update(pDevice, NAU8825_REG_HSD_CTRL, 3 << 2, 1449 | 2 << 2); 1450 | /* Attach 2kOhm Resistor from MICBIAS to MICGND2 */ 1451 | nau8825_reg_update(pDevice, NAU8825_REG_MIC_BIAS, 1452 | NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 1453 | NAU8825_MICBIAS_JKSLV); 1454 | /* Attach SARADC to MICGND2 */ 1455 | nau8825_reg_update(pDevice, NAU8825_REG_SAR_CTRL, 1456 | NAU8825_SAR_INPUT_MASK, 1457 | NAU8825_SAR_INPUT_JKSLV); 1458 | break; 1459 | case 3: 1460 | /* detect error case */ 1461 | DbgPrint("detection error; disable mic function\n"); 1462 | type = SND_JACK_HEADPHONE; 1463 | break; 1464 | } 1465 | 1466 | { 1467 | //Set 2nd defaults set from Linux 1468 | if (!pDevice->ReclockRequested) { 1469 | nau8825_reg_update(pDevice, NAU8825_REG_CLK_DIVIDER, NAU8825_CLK_SRC_MASK | NAU8825_CLK_MCLK_SRC_MASK, 0x0); 1470 | 1471 | nau8825_reg_update(pDevice, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0x0); 1472 | } 1473 | else { 1474 | nau8825_update_reclock(pDevice); 1475 | } 1476 | 1477 | nau8825_reg_update(pDevice, NAU8825_REG_HSD_CTRL, NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, 0x0); 1478 | 1479 | nau8825_reg_update(pDevice, NAU8825_REG_SAR_CTRL, NAU8825_SAR_ADC_EN, NAU8825_SAR_ADC_EN); 1480 | 1481 | nau8825_reg_update(pDevice, NAU8825_REG_I2S_PCM_CTRL2, NAU8825_I2S_TRISTATE, 0); 1482 | 1483 | if (GetPlatform() == PlatformSkylake) 1484 | { 1485 | nau8825_reg_update(pDevice, NAU8825_REG_CLK_DIVIDER, NAU8825_CLK_ADC_SRC_MASK | NAU8825_CLK_DAC_SRC_MASK, (2 << NAU8825_CLK_ADC_SRC_SFT) | (1 << NAU8825_CLK_DAC_SRC_SFT)); 1486 | nau8825_reg_update(pDevice, NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128); 1487 | } 1488 | else { 1489 | nau8825_reg_update(pDevice, NAU8825_REG_CLK_DIVIDER, NAU8825_CLK_ADC_SRC_MASK | NAU8825_CLK_DAC_SRC_MASK, (2 << NAU8825_CLK_ADC_SRC_SFT) | (2 << NAU8825_CLK_DAC_SRC_SFT)); 1490 | nau8825_reg_update(pDevice, NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_64); 1491 | } 1492 | 1493 | nau8825_reg_update(pDevice, NAU8825_REG_CLASSG_CTRL, NAU8825_CLASSG_LDAC_EN | NAU8825_CLASSG_RDAC_EN | NAU8825_CLASSG_EN, NAU8825_CLASSG_LDAC_EN | NAU8825_CLASSG_RDAC_EN | NAU8825_CLASSG_EN); 1494 | 1495 | nau8825_reg_update(pDevice, NAU8825_REG_BIAS_ADJ, NAU8825_BIAS_TESTDAC_EN, 0); 1496 | 1497 | nau8825_reg_update(pDevice, NAU8825_REG_ANALOG_ADC_2, NAU8825_POWERUP_ADCL, NAU8825_POWERUP_ADCL); 1498 | 1499 | nau8825_reg_update(pDevice, NAU8825_REG_RDAC, NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN, NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN); 1500 | 1501 | nau8825_reg_update(pDevice, NAU8825_REG_MIC_BIAS, 1502 | (1 << 8), 1503 | (1 << 8)); //Enable Mic Bias 1504 | 1505 | nau8825_reg_update(pDevice, NAU8825_REG_BOOST, NAU8825_HP_BOOST_DIS, 0); 1506 | 1507 | nau8825_reg_update(pDevice, NAU8825_REG_POWER_UP_CONTROL, 1508 | NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L | NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L | NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L, 1509 | NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L | NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L | NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L); 1510 | 1511 | nau8825_reg_update(pDevice, NAU8825_REG_POWER_UP_CONTROL, 1512 | (1 << 14), 1513 | (1 << 14)); //Frontend PGA Enable 1514 | 1515 | nau8825_reg_update(pDevice, NAU8825_REG_CHARGE_PUMP, 1516 | NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN, 1517 | NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN); 1518 | 1519 | nau8825_reg_update(pDevice, NAU8825_REG_CHARGE_PUMP, 1520 | NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 1521 | 0); 1522 | } 1523 | 1524 | /* Leaving HPOL/R grounded after jack insert by default. They will be 1525 | * ungrounded as part of the widget power up sequence at the beginning 1526 | * of playback to reduce pop. 1527 | */ 1528 | return type; 1529 | } 1530 | 1531 | BOOLEAN OnInterruptIsr( 1532 | WDFINTERRUPT Interrupt, 1533 | ULONG MessageID) { 1534 | UNREFERENCED_PARAMETER(MessageID); 1535 | 1536 | WDFDEVICE Device = WdfInterruptGetDevice(Interrupt); 1537 | PNAU8825_CONTEXT pDevice = GetDeviceContext(Device); 1538 | 1539 | if (!pDevice->DevicePoweredOn) 1540 | return false; 1541 | 1542 | int active_irq, clear_irq = 0, event = 0, event_mask = 0; 1543 | NTSTATUS status = nau8825_reg_read(pDevice, NAU8825_REG_IRQ_STATUS, &active_irq); 1544 | if (!NT_SUCCESS(status)) { 1545 | DbgPrint("failed to read irq status\n"); 1546 | return false; 1547 | } 1548 | 1549 | if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) == 1550 | NAU8825_JACK_EJECTION_DETECTED) { 1551 | 1552 | nau8825_eject_jack(pDevice); 1553 | 1554 | pDevice->JackType = 0; 1555 | 1556 | CsAudioSpecialKeyReport report; 1557 | report.ReportID = REPORTID_SPECKEYS; 1558 | report.ControlCode = CONTROL_CODE_JACK_TYPE; 1559 | report.ControlValue = pDevice->JackType; 1560 | 1561 | size_t bytesWritten; 1562 | Nau8825ProcessVendorReport(pDevice, &report, sizeof(report), &bytesWritten); 1563 | 1564 | clear_irq = NAU8825_JACK_EJECTION_IRQ_MASK; 1565 | } 1566 | else if (active_irq & NAU8825_KEY_SHORT_PRESS_IRQ) { 1567 | clear_irq = NAU8825_KEY_SHORT_PRESS_IRQ; 1568 | } 1569 | else if (active_irq & NAU8825_KEY_RELEASE_IRQ) { 1570 | clear_irq = NAU8825_KEY_RELEASE_IRQ; 1571 | 1572 | if (nau8825_is_jack_inserted(pDevice)) { 1573 | int key_status; 1574 | 1575 | nau8825_reg_read(pDevice, NAU8825_REG_INT_CLR_KEY_STATUS, 1576 | &key_status); 1577 | 1578 | Nau8825MediaReport report; 1579 | report.ReportID = REPORTID_MEDIA; 1580 | report.ControlCode = nau8825_button_decode(key_status >> 8); 1581 | 1582 | size_t bytesWritten; 1583 | Nau8825ProcessVendorReport(pDevice, &report, sizeof(report), &bytesWritten); 1584 | } 1585 | } 1586 | else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) { 1587 | if (nau8825_is_jack_inserted(pDevice)) { 1588 | pDevice->JackType = nau8825_jack_insert(pDevice);; 1589 | 1590 | CsAudioSpecialKeyReport report; 1591 | report.ReportID = REPORTID_SPECKEYS; 1592 | report.ControlCode = CONTROL_CODE_JACK_TYPE; 1593 | report.ControlValue = pDevice->JackType; 1594 | 1595 | size_t bytesWritten; 1596 | Nau8825ProcessVendorReport(pDevice, &report, sizeof(report), &bytesWritten); 1597 | } 1598 | clear_irq = NAU8825_HEADSET_COMPLETION_IRQ; 1599 | } 1600 | else if (active_irq & NAU8825_IMPEDANCE_MEAS_IRQ) { 1601 | clear_irq = NAU8825_IMPEDANCE_MEAS_IRQ; 1602 | } 1603 | else if ((active_irq & NAU8825_JACK_INSERTION_IRQ_MASK) == 1604 | NAU8825_JACK_INSERTION_DETECTED) { 1605 | /* One more step to check GPIO status directly. Thus, the 1606 | * driver can confirm the real insertion interruption because 1607 | * the intrruption at manual mode has bypassed debounce 1608 | * circuit which can get rid of unstable status. 1609 | */ 1610 | if (nau8825_is_jack_inserted(pDevice)) { 1611 | /* Turn off insertion interruption at manual mode */ 1612 | nau8825_reg_update(pDevice, 1613 | NAU8825_REG_INTERRUPT_DIS_CTRL, 1614 | NAU8825_IRQ_INSERT_DIS, 1615 | NAU8825_IRQ_INSERT_DIS); 1616 | nau8825_reg_update(pDevice, NAU8825_REG_INTERRUPT_MASK, 1617 | NAU8825_IRQ_INSERT_EN, NAU8825_IRQ_INSERT_EN); 1618 | /* Enable interruption for jack type detection at audo 1619 | * mode which can detect microphone and jack type. 1620 | */ 1621 | nau8825_setup_auto_irq(pDevice); 1622 | } 1623 | } 1624 | 1625 | if (!clear_irq) 1626 | clear_irq = active_irq; 1627 | /* clears the rightmost interruption */ 1628 | nau8825_reg_write(pDevice, NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq); 1629 | 1630 | return true; 1631 | } 1632 | 1633 | NTSTATUS 1634 | Nau8825EvtDeviceAdd( 1635 | IN WDFDRIVER Driver, 1636 | IN PWDFDEVICE_INIT DeviceInit 1637 | ) 1638 | { 1639 | NTSTATUS status = STATUS_SUCCESS; 1640 | WDF_IO_QUEUE_CONFIG queueConfig; 1641 | WDF_OBJECT_ATTRIBUTES attributes; 1642 | WDFDEVICE device; 1643 | WDF_INTERRUPT_CONFIG interruptConfig; 1644 | WDFQUEUE queue; 1645 | UCHAR minorFunction; 1646 | PNAU8825_CONTEXT devContext; 1647 | 1648 | UNREFERENCED_PARAMETER(Driver); 1649 | 1650 | PAGED_CODE(); 1651 | 1652 | Nau8825Print(DEBUG_LEVEL_INFO, DBG_PNP, 1653 | "Nau8825EvtDeviceAdd called\n"); 1654 | 1655 | // 1656 | // Tell framework this is a filter driver. Filter drivers by default are 1657 | // not power policy owners. This works well for this driver because 1658 | // HIDclass driver is the power policy owner for HID minidrivers. 1659 | // 1660 | 1661 | WdfFdoInitSetFilter(DeviceInit); 1662 | 1663 | { 1664 | WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks; 1665 | WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks); 1666 | 1667 | pnpCallbacks.EvtDevicePrepareHardware = OnPrepareHardware; 1668 | pnpCallbacks.EvtDeviceReleaseHardware = OnReleaseHardware; 1669 | pnpCallbacks.EvtDeviceSelfManagedIoInit = OnSelfManagedIoInit; 1670 | pnpCallbacks.EvtDeviceD0Entry = OnD0Entry; 1671 | pnpCallbacks.EvtDeviceD0Exit = OnD0Exit; 1672 | 1673 | WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpCallbacks); 1674 | } 1675 | 1676 | // 1677 | // Setup the device context 1678 | // 1679 | 1680 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, NAU8825_CONTEXT); 1681 | 1682 | // 1683 | // Create a framework device object.This call will in turn create 1684 | // a WDM device object, attach to the lower stack, and set the 1685 | // appropriate flags and attributes. 1686 | // 1687 | 1688 | status = WdfDeviceCreate(&DeviceInit, &attributes, &device); 1689 | 1690 | if (!NT_SUCCESS(status)) 1691 | { 1692 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_PNP, 1693 | "WdfDeviceCreate failed with status code 0x%x\n", status); 1694 | 1695 | return status; 1696 | } 1697 | 1698 | { 1699 | WDF_DEVICE_STATE deviceState; 1700 | WDF_DEVICE_STATE_INIT(&deviceState); 1701 | 1702 | deviceState.NotDisableable = WdfFalse; 1703 | WdfDeviceSetDeviceState(device, &deviceState); 1704 | } 1705 | 1706 | WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel); 1707 | 1708 | queueConfig.EvtIoInternalDeviceControl = Nau8825EvtInternalDeviceControl; 1709 | 1710 | status = WdfIoQueueCreate(device, 1711 | &queueConfig, 1712 | WDF_NO_OBJECT_ATTRIBUTES, 1713 | &queue 1714 | ); 1715 | 1716 | if (!NT_SUCCESS(status)) 1717 | { 1718 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_PNP, 1719 | "WdfIoQueueCreate failed 0x%x\n", status); 1720 | 1721 | return status; 1722 | } 1723 | 1724 | // 1725 | // Create manual I/O queue to take care of hid report read requests 1726 | // 1727 | 1728 | devContext = GetDeviceContext(device); 1729 | 1730 | devContext->FxDevice = device; 1731 | 1732 | WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchManual); 1733 | 1734 | queueConfig.PowerManaged = WdfFalse; 1735 | 1736 | status = WdfIoQueueCreate(device, 1737 | &queueConfig, 1738 | WDF_NO_OBJECT_ATTRIBUTES, 1739 | &devContext->ReportQueue 1740 | ); 1741 | 1742 | if (!NT_SUCCESS(status)) 1743 | { 1744 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_PNP, 1745 | "WdfIoQueueCreate failed 0x%x\n", status); 1746 | 1747 | return status; 1748 | } 1749 | 1750 | // 1751 | // Create an interrupt object for hardware notifications 1752 | // 1753 | WDF_INTERRUPT_CONFIG_INIT( 1754 | &interruptConfig, 1755 | OnInterruptIsr, 1756 | NULL); 1757 | interruptConfig.PassiveHandling = TRUE; 1758 | 1759 | status = WdfInterruptCreate( 1760 | device, 1761 | &interruptConfig, 1762 | WDF_NO_OBJECT_ATTRIBUTES, 1763 | &devContext->Interrupt); 1764 | 1765 | if (!NT_SUCCESS(status)) 1766 | { 1767 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_PNP, 1768 | "Error creating WDF interrupt object - %!STATUS!", 1769 | status); 1770 | 1771 | return status; 1772 | } 1773 | 1774 | return status; 1775 | } 1776 | 1777 | VOID 1778 | Nau8825EvtInternalDeviceControl( 1779 | IN WDFQUEUE Queue, 1780 | IN WDFREQUEST Request, 1781 | IN size_t OutputBufferLength, 1782 | IN size_t InputBufferLength, 1783 | IN ULONG IoControlCode 1784 | ) 1785 | { 1786 | NTSTATUS status = STATUS_SUCCESS; 1787 | WDFDEVICE device; 1788 | PNAU8825_CONTEXT devContext; 1789 | BOOLEAN completeRequest = TRUE; 1790 | 1791 | UNREFERENCED_PARAMETER(OutputBufferLength); 1792 | UNREFERENCED_PARAMETER(InputBufferLength); 1793 | 1794 | device = WdfIoQueueGetDevice(Queue); 1795 | devContext = GetDeviceContext(device); 1796 | 1797 | Nau8825Print(DEBUG_LEVEL_INFO, DBG_IOCTL, 1798 | "%s, Queue:0x%p, Request:0x%p\n", 1799 | DbgHidInternalIoctlString(IoControlCode), 1800 | Queue, 1801 | Request 1802 | ); 1803 | 1804 | // 1805 | // Please note that HIDCLASS provides the buffer in the Irp->UserBuffer 1806 | // field irrespective of the ioctl buffer type. However, framework is very 1807 | // strict about type checking. You cannot get Irp->UserBuffer by using 1808 | // WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER 1809 | // internal ioctl. So depending on the ioctl code, we will either 1810 | // use retreive function or escape to WDM to get the UserBuffer. 1811 | // 1812 | 1813 | switch (IoControlCode) 1814 | { 1815 | 1816 | case IOCTL_HID_GET_DEVICE_DESCRIPTOR: 1817 | // 1818 | // Retrieves the device's HID descriptor. 1819 | // 1820 | status = Nau8825GetHidDescriptor(device, Request); 1821 | break; 1822 | 1823 | case IOCTL_HID_GET_DEVICE_ATTRIBUTES: 1824 | // 1825 | //Retrieves a device's attributes in a HID_DEVICE_ATTRIBUTES structure. 1826 | // 1827 | status = Nau8825GetDeviceAttributes(Request); 1828 | break; 1829 | 1830 | case IOCTL_HID_GET_REPORT_DESCRIPTOR: 1831 | // 1832 | //Obtains the report descriptor for the HID device. 1833 | // 1834 | status = Nau8825GetReportDescriptor(device, Request); 1835 | break; 1836 | 1837 | case IOCTL_HID_GET_STRING: 1838 | // 1839 | // Requests that the HID minidriver retrieve a human-readable string 1840 | // for either the manufacturer ID, the product ID, or the serial number 1841 | // from the string descriptor of the device. The minidriver must send 1842 | // a Get String Descriptor request to the device, in order to retrieve 1843 | // the string descriptor, then it must extract the string at the 1844 | // appropriate index from the string descriptor and return it in the 1845 | // output buffer indicated by the IRP. Before sending the Get String 1846 | // Descriptor request, the minidriver must retrieve the appropriate 1847 | // index for the manufacturer ID, the product ID or the serial number 1848 | // from the device extension of a top level collection associated with 1849 | // the device. 1850 | // 1851 | status = Nau8825GetString(Request); 1852 | break; 1853 | 1854 | case IOCTL_HID_WRITE_REPORT: 1855 | case IOCTL_HID_SET_OUTPUT_REPORT: 1856 | // 1857 | //Transmits a class driver-supplied report to the device. 1858 | // 1859 | status = Nau8825WriteReport(devContext, Request); 1860 | break; 1861 | 1862 | case IOCTL_HID_READ_REPORT: 1863 | case IOCTL_HID_GET_INPUT_REPORT: 1864 | // 1865 | // Returns a report from the device into a class driver-supplied buffer. 1866 | // 1867 | status = Nau8825ReadReport(devContext, Request, &completeRequest); 1868 | break; 1869 | 1870 | case IOCTL_HID_SET_FEATURE: 1871 | // 1872 | // This sends a HID class feature report to a top-level collection of 1873 | // a HID class device. 1874 | // 1875 | status = Nau8825SetFeature(devContext, Request, &completeRequest); 1876 | break; 1877 | 1878 | case IOCTL_HID_GET_FEATURE: 1879 | // 1880 | // returns a feature report associated with a top-level collection 1881 | status = Nau8825GetFeature(devContext, Request, &completeRequest); 1882 | break; 1883 | 1884 | case IOCTL_HID_ACTIVATE_DEVICE: 1885 | // 1886 | // Makes the device ready for I/O operations. 1887 | // 1888 | case IOCTL_HID_DEACTIVATE_DEVICE: 1889 | // 1890 | // Causes the device to cease operations and terminate all outstanding 1891 | // I/O requests. 1892 | // 1893 | default: 1894 | status = STATUS_NOT_SUPPORTED; 1895 | break; 1896 | } 1897 | 1898 | if (completeRequest) 1899 | { 1900 | WdfRequestComplete(Request, status); 1901 | 1902 | Nau8825Print(DEBUG_LEVEL_INFO, DBG_IOCTL, 1903 | "%s completed, Queue:0x%p, Request:0x%p\n", 1904 | DbgHidInternalIoctlString(IoControlCode), 1905 | Queue, 1906 | Request 1907 | ); 1908 | } 1909 | else 1910 | { 1911 | Nau8825Print(DEBUG_LEVEL_INFO, DBG_IOCTL, 1912 | "%s deferred, Queue:0x%p, Request:0x%p\n", 1913 | DbgHidInternalIoctlString(IoControlCode), 1914 | Queue, 1915 | Request 1916 | ); 1917 | } 1918 | 1919 | return; 1920 | } 1921 | 1922 | NTSTATUS 1923 | Nau8825GetHidDescriptor( 1924 | IN WDFDEVICE Device, 1925 | IN WDFREQUEST Request 1926 | ) 1927 | { 1928 | NTSTATUS status = STATUS_SUCCESS; 1929 | size_t bytesToCopy = 0; 1930 | WDFMEMORY memory; 1931 | 1932 | UNREFERENCED_PARAMETER(Device); 1933 | 1934 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 1935 | "Nau8825GetHidDescriptor Entry\n"); 1936 | 1937 | // 1938 | // This IOCTL is METHOD_NEITHER so WdfRequestRetrieveOutputMemory 1939 | // will correctly retrieve buffer from Irp->UserBuffer. 1940 | // Remember that HIDCLASS provides the buffer in the Irp->UserBuffer 1941 | // field irrespective of the ioctl buffer type. However, framework is very 1942 | // strict about type checking. You cannot get Irp->UserBuffer by using 1943 | // WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER 1944 | // internal ioctl. 1945 | // 1946 | status = WdfRequestRetrieveOutputMemory(Request, &memory); 1947 | 1948 | if (!NT_SUCCESS(status)) 1949 | { 1950 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 1951 | "WdfRequestRetrieveOutputMemory failed 0x%x\n", status); 1952 | 1953 | return status; 1954 | } 1955 | 1956 | // 1957 | // Use hardcoded "HID Descriptor" 1958 | // 1959 | bytesToCopy = DefaultHidDescriptor.bLength; 1960 | 1961 | if (bytesToCopy == 0) 1962 | { 1963 | status = STATUS_INVALID_DEVICE_STATE; 1964 | 1965 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 1966 | "DefaultHidDescriptor is zero, 0x%x\n", status); 1967 | 1968 | return status; 1969 | } 1970 | 1971 | status = WdfMemoryCopyFromBuffer(memory, 1972 | 0, // Offset 1973 | (PVOID)&DefaultHidDescriptor, 1974 | bytesToCopy); 1975 | 1976 | if (!NT_SUCCESS(status)) 1977 | { 1978 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 1979 | "WdfMemoryCopyFromBuffer failed 0x%x\n", status); 1980 | 1981 | return status; 1982 | } 1983 | 1984 | // 1985 | // Report how many bytes were copied 1986 | // 1987 | WdfRequestSetInformation(Request, bytesToCopy); 1988 | 1989 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 1990 | "Nau8825GetHidDescriptor Exit = 0x%x\n", status); 1991 | 1992 | return status; 1993 | } 1994 | 1995 | NTSTATUS 1996 | Nau8825GetReportDescriptor( 1997 | IN WDFDEVICE Device, 1998 | IN WDFREQUEST Request 1999 | ) 2000 | { 2001 | NTSTATUS status = STATUS_SUCCESS; 2002 | ULONG_PTR bytesToCopy; 2003 | WDFMEMORY memory; 2004 | 2005 | PNAU8825_CONTEXT devContext = GetDeviceContext(Device); 2006 | 2007 | UNREFERENCED_PARAMETER(Device); 2008 | 2009 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2010 | "Nau8825GetReportDescriptor Entry\n"); 2011 | 2012 | // 2013 | // This IOCTL is METHOD_NEITHER so WdfRequestRetrieveOutputMemory 2014 | // will correctly retrieve buffer from Irp->UserBuffer. 2015 | // Remember that HIDCLASS provides the buffer in the Irp->UserBuffer 2016 | // field irrespective of the ioctl buffer type. However, framework is very 2017 | // strict about type checking. You cannot get Irp->UserBuffer by using 2018 | // WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER 2019 | // internal ioctl. 2020 | // 2021 | status = WdfRequestRetrieveOutputMemory(Request, &memory); 2022 | if (!NT_SUCCESS(status)) 2023 | { 2024 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2025 | "WdfRequestRetrieveOutputMemory failed 0x%x\n", status); 2026 | 2027 | return status; 2028 | } 2029 | 2030 | // 2031 | // Use hardcoded Report descriptor 2032 | // 2033 | bytesToCopy = DefaultHidDescriptor.DescriptorList[0].wReportLength; 2034 | 2035 | if (bytesToCopy == 0) 2036 | { 2037 | status = STATUS_INVALID_DEVICE_STATE; 2038 | 2039 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2040 | "DefaultHidDescriptor's reportLength is zero, 0x%x\n", status); 2041 | 2042 | return status; 2043 | } 2044 | 2045 | status = WdfMemoryCopyFromBuffer(memory, 2046 | 0, 2047 | (PVOID)DefaultReportDescriptor, 2048 | bytesToCopy); 2049 | if (!NT_SUCCESS(status)) 2050 | { 2051 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2052 | "WdfMemoryCopyFromBuffer failed 0x%x\n", status); 2053 | 2054 | return status; 2055 | } 2056 | 2057 | // 2058 | // Report how many bytes were copied 2059 | // 2060 | WdfRequestSetInformation(Request, bytesToCopy); 2061 | 2062 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2063 | "Nau8825GetReportDescriptor Exit = 0x%x\n", status); 2064 | 2065 | return status; 2066 | } 2067 | 2068 | 2069 | NTSTATUS 2070 | Nau8825GetDeviceAttributes( 2071 | IN WDFREQUEST Request 2072 | ) 2073 | { 2074 | NTSTATUS status = STATUS_SUCCESS; 2075 | PHID_DEVICE_ATTRIBUTES deviceAttributes = NULL; 2076 | 2077 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2078 | "Nau8825GetDeviceAttributes Entry\n"); 2079 | 2080 | // 2081 | // This IOCTL is METHOD_NEITHER so WdfRequestRetrieveOutputMemory 2082 | // will correctly retrieve buffer from Irp->UserBuffer. 2083 | // Remember that HIDCLASS provides the buffer in the Irp->UserBuffer 2084 | // field irrespective of the ioctl buffer type. However, framework is very 2085 | // strict about type checking. You cannot get Irp->UserBuffer by using 2086 | // WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER 2087 | // internal ioctl. 2088 | // 2089 | status = WdfRequestRetrieveOutputBuffer(Request, 2090 | sizeof(HID_DEVICE_ATTRIBUTES), 2091 | (PVOID*)&deviceAttributes, 2092 | NULL); 2093 | if (!NT_SUCCESS(status)) 2094 | { 2095 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2096 | "WdfRequestRetrieveOutputBuffer failed 0x%x\n", status); 2097 | 2098 | return status; 2099 | } 2100 | 2101 | // 2102 | // Set USB device descriptor 2103 | // 2104 | 2105 | deviceAttributes->Size = sizeof(HID_DEVICE_ATTRIBUTES); 2106 | deviceAttributes->VendorID = NAU8825_VID; 2107 | deviceAttributes->ProductID = NAU8825_PID; 2108 | deviceAttributes->VersionNumber = NAU8825_VERSION; 2109 | 2110 | // 2111 | // Report how many bytes were copied 2112 | // 2113 | WdfRequestSetInformation(Request, sizeof(HID_DEVICE_ATTRIBUTES)); 2114 | 2115 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2116 | "Nau8825GetDeviceAttributes Exit = 0x%x\n", status); 2117 | 2118 | return status; 2119 | } 2120 | 2121 | NTSTATUS 2122 | Nau8825GetString( 2123 | IN WDFREQUEST Request 2124 | ) 2125 | { 2126 | 2127 | NTSTATUS status = STATUS_SUCCESS; 2128 | PWSTR pwstrID; 2129 | size_t lenID; 2130 | WDF_REQUEST_PARAMETERS params; 2131 | void* pStringBuffer = NULL; 2132 | 2133 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2134 | "Nau8825GetString Entry\n"); 2135 | 2136 | WDF_REQUEST_PARAMETERS_INIT(¶ms); 2137 | WdfRequestGetParameters(Request, ¶ms); 2138 | 2139 | switch ((ULONG_PTR)params.Parameters.DeviceIoControl.Type3InputBuffer & 0xFFFF) 2140 | { 2141 | case HID_STRING_ID_IMANUFACTURER: 2142 | pwstrID = L"Nau8825.\0"; 2143 | break; 2144 | 2145 | case HID_STRING_ID_IPRODUCT: 2146 | pwstrID = L"MaxTouch Touch Screen\0"; 2147 | break; 2148 | 2149 | case HID_STRING_ID_ISERIALNUMBER: 2150 | pwstrID = L"123123123\0"; 2151 | break; 2152 | 2153 | default: 2154 | pwstrID = NULL; 2155 | break; 2156 | } 2157 | 2158 | lenID = pwstrID ? wcslen(pwstrID) * sizeof(WCHAR) + sizeof(UNICODE_NULL) : 0; 2159 | 2160 | if (pwstrID == NULL) 2161 | { 2162 | 2163 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2164 | "Nau8825GetString Invalid request type\n"); 2165 | 2166 | status = STATUS_INVALID_PARAMETER; 2167 | 2168 | return status; 2169 | } 2170 | 2171 | status = WdfRequestRetrieveOutputBuffer(Request, 2172 | lenID, 2173 | &pStringBuffer, 2174 | &lenID); 2175 | 2176 | if (!NT_SUCCESS(status)) 2177 | { 2178 | 2179 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2180 | "Nau8825GetString WdfRequestRetrieveOutputBuffer failed Status 0x%x\n", status); 2181 | 2182 | return status; 2183 | } 2184 | 2185 | RtlCopyMemory(pStringBuffer, pwstrID, lenID); 2186 | 2187 | WdfRequestSetInformation(Request, lenID); 2188 | 2189 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2190 | "Nau8825GetString Exit = 0x%x\n", status); 2191 | 2192 | return status; 2193 | } 2194 | 2195 | NTSTATUS 2196 | Nau8825WriteReport( 2197 | IN PNAU8825_CONTEXT DevContext, 2198 | IN WDFREQUEST Request 2199 | ) 2200 | { 2201 | NTSTATUS status = STATUS_SUCCESS; 2202 | WDF_REQUEST_PARAMETERS params; 2203 | PHID_XFER_PACKET transferPacket = NULL; 2204 | size_t bytesWritten = 0; 2205 | 2206 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2207 | "Nau8825WriteReport Entry\n"); 2208 | 2209 | WDF_REQUEST_PARAMETERS_INIT(¶ms); 2210 | WdfRequestGetParameters(Request, ¶ms); 2211 | 2212 | if (params.Parameters.DeviceIoControl.InputBufferLength < sizeof(HID_XFER_PACKET)) 2213 | { 2214 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2215 | "Nau8825WriteReport Xfer packet too small\n"); 2216 | 2217 | status = STATUS_BUFFER_TOO_SMALL; 2218 | } 2219 | else 2220 | { 2221 | 2222 | transferPacket = (PHID_XFER_PACKET)WdfRequestWdmGetIrp(Request)->UserBuffer; 2223 | 2224 | if (transferPacket == NULL) 2225 | { 2226 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2227 | "Nau8825WriteReport No xfer packet\n"); 2228 | 2229 | status = STATUS_INVALID_DEVICE_REQUEST; 2230 | } 2231 | else 2232 | { 2233 | // 2234 | // switch on the report id 2235 | // 2236 | 2237 | switch (transferPacket->reportId) 2238 | { 2239 | case REPORTID_SPECKEYS: 2240 | status = STATUS_SUCCESS; 2241 | 2242 | CsAudioSpecialKeyReport report; 2243 | report.ReportID = REPORTID_SPECKEYS; 2244 | report.ControlCode = CONTROL_CODE_JACK_TYPE; 2245 | report.ControlValue = DevContext->JackType; 2246 | 2247 | size_t bytesWritten; 2248 | Nau8825ProcessVendorReport(DevContext, &report, sizeof(report), &bytesWritten); 2249 | break; 2250 | default: 2251 | 2252 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2253 | "Nau8825WriteReport Unhandled report type %d\n", transferPacket->reportId); 2254 | 2255 | status = STATUS_INVALID_PARAMETER; 2256 | 2257 | break; 2258 | } 2259 | } 2260 | } 2261 | 2262 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2263 | "Nau8825WriteReport Exit = 0x%x\n", status); 2264 | 2265 | return status; 2266 | 2267 | } 2268 | 2269 | NTSTATUS 2270 | Nau8825ProcessVendorReport( 2271 | IN PNAU8825_CONTEXT DevContext, 2272 | IN PVOID ReportBuffer, 2273 | IN ULONG ReportBufferLen, 2274 | OUT size_t* BytesWritten 2275 | ) 2276 | { 2277 | NTSTATUS status = STATUS_SUCCESS; 2278 | WDFREQUEST reqRead; 2279 | PVOID pReadReport = NULL; 2280 | size_t bytesReturned = 0; 2281 | 2282 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2283 | "Nau8825ProcessVendorReport Entry\n"); 2284 | 2285 | status = WdfIoQueueRetrieveNextRequest(DevContext->ReportQueue, 2286 | &reqRead); 2287 | 2288 | if (NT_SUCCESS(status)) 2289 | { 2290 | status = WdfRequestRetrieveOutputBuffer(reqRead, 2291 | ReportBufferLen, 2292 | &pReadReport, 2293 | &bytesReturned); 2294 | 2295 | if (NT_SUCCESS(status)) 2296 | { 2297 | // 2298 | // Copy ReportBuffer into read request 2299 | // 2300 | 2301 | if (bytesReturned > ReportBufferLen) 2302 | { 2303 | bytesReturned = ReportBufferLen; 2304 | } 2305 | 2306 | RtlCopyMemory(pReadReport, 2307 | ReportBuffer, 2308 | bytesReturned); 2309 | 2310 | // 2311 | // Complete read with the number of bytes returned as info 2312 | // 2313 | 2314 | WdfRequestCompleteWithInformation(reqRead, 2315 | status, 2316 | bytesReturned); 2317 | 2318 | Nau8825Print(DEBUG_LEVEL_INFO, DBG_IOCTL, 2319 | "Nau8825ProcessVendorReport %d bytes returned\n", bytesReturned); 2320 | 2321 | // 2322 | // Return the number of bytes written for the write request completion 2323 | // 2324 | 2325 | *BytesWritten = bytesReturned; 2326 | 2327 | Nau8825Print(DEBUG_LEVEL_INFO, DBG_IOCTL, 2328 | "%s completed, Queue:0x%p, Request:0x%p\n", 2329 | DbgHidInternalIoctlString(IOCTL_HID_READ_REPORT), 2330 | DevContext->ReportQueue, 2331 | reqRead); 2332 | } 2333 | else 2334 | { 2335 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2336 | "WdfRequestRetrieveOutputBuffer failed Status 0x%x\n", status); 2337 | } 2338 | } 2339 | else 2340 | { 2341 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2342 | "WdfIoQueueRetrieveNextRequest failed Status 0x%x\n", status); 2343 | } 2344 | 2345 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2346 | "Nau8825ProcessVendorReport Exit = 0x%x\n", status); 2347 | 2348 | return status; 2349 | } 2350 | 2351 | NTSTATUS 2352 | Nau8825ReadReport( 2353 | IN PNAU8825_CONTEXT DevContext, 2354 | IN WDFREQUEST Request, 2355 | OUT BOOLEAN* CompleteRequest 2356 | ) 2357 | { 2358 | NTSTATUS status = STATUS_SUCCESS; 2359 | 2360 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2361 | "Nau8825ReadReport Entry\n"); 2362 | 2363 | // 2364 | // Forward this read request to our manual queue 2365 | // (in other words, we are going to defer this request 2366 | // until we have a corresponding write request to 2367 | // match it with) 2368 | // 2369 | 2370 | status = WdfRequestForwardToIoQueue(Request, DevContext->ReportQueue); 2371 | 2372 | if (!NT_SUCCESS(status)) 2373 | { 2374 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2375 | "WdfRequestForwardToIoQueue failed Status 0x%x\n", status); 2376 | } 2377 | else 2378 | { 2379 | *CompleteRequest = FALSE; 2380 | } 2381 | 2382 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2383 | "Nau8825ReadReport Exit = 0x%x\n", status); 2384 | 2385 | return status; 2386 | } 2387 | 2388 | NTSTATUS 2389 | Nau8825SetFeature( 2390 | IN PNAU8825_CONTEXT DevContext, 2391 | IN WDFREQUEST Request, 2392 | OUT BOOLEAN* CompleteRequest 2393 | ) 2394 | { 2395 | NTSTATUS status = STATUS_SUCCESS; 2396 | WDF_REQUEST_PARAMETERS params; 2397 | PHID_XFER_PACKET transferPacket = NULL; 2398 | 2399 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2400 | "Nau8825SetFeature Entry\n"); 2401 | 2402 | WDF_REQUEST_PARAMETERS_INIT(¶ms); 2403 | WdfRequestGetParameters(Request, ¶ms); 2404 | 2405 | if (params.Parameters.DeviceIoControl.InputBufferLength < sizeof(HID_XFER_PACKET)) 2406 | { 2407 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2408 | "Nau8825SetFeature Xfer packet too small\n"); 2409 | 2410 | status = STATUS_BUFFER_TOO_SMALL; 2411 | } 2412 | else 2413 | { 2414 | 2415 | transferPacket = (PHID_XFER_PACKET)WdfRequestWdmGetIrp(Request)->UserBuffer; 2416 | 2417 | if (transferPacket == NULL) 2418 | { 2419 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2420 | "Nau8825WriteReport No xfer packet\n"); 2421 | 2422 | status = STATUS_INVALID_DEVICE_REQUEST; 2423 | } 2424 | else 2425 | { 2426 | // 2427 | // switch on the report id 2428 | // 2429 | 2430 | switch (transferPacket->reportId) 2431 | { 2432 | default: 2433 | 2434 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2435 | "Nau8825SetFeature Unhandled report type %d\n", transferPacket->reportId); 2436 | 2437 | status = STATUS_INVALID_PARAMETER; 2438 | 2439 | break; 2440 | } 2441 | } 2442 | } 2443 | 2444 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2445 | "Nau8825SetFeature Exit = 0x%x\n", status); 2446 | 2447 | return status; 2448 | } 2449 | 2450 | NTSTATUS 2451 | Nau8825GetFeature( 2452 | IN PNAU8825_CONTEXT DevContext, 2453 | IN WDFREQUEST Request, 2454 | OUT BOOLEAN* CompleteRequest 2455 | ) 2456 | { 2457 | NTSTATUS status = STATUS_SUCCESS; 2458 | WDF_REQUEST_PARAMETERS params; 2459 | PHID_XFER_PACKET transferPacket = NULL; 2460 | 2461 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2462 | "Nau8825GetFeature Entry\n"); 2463 | 2464 | WDF_REQUEST_PARAMETERS_INIT(¶ms); 2465 | WdfRequestGetParameters(Request, ¶ms); 2466 | 2467 | if (params.Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_XFER_PACKET)) 2468 | { 2469 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2470 | "Nau8825GetFeature Xfer packet too small\n"); 2471 | 2472 | status = STATUS_BUFFER_TOO_SMALL; 2473 | } 2474 | else 2475 | { 2476 | 2477 | transferPacket = (PHID_XFER_PACKET)WdfRequestWdmGetIrp(Request)->UserBuffer; 2478 | 2479 | if (transferPacket == NULL) 2480 | { 2481 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2482 | "Nau8825GetFeature No xfer packet\n"); 2483 | 2484 | status = STATUS_INVALID_DEVICE_REQUEST; 2485 | } 2486 | else 2487 | { 2488 | // 2489 | // switch on the report id 2490 | // 2491 | 2492 | switch (transferPacket->reportId) 2493 | { 2494 | default: 2495 | 2496 | Nau8825Print(DEBUG_LEVEL_ERROR, DBG_IOCTL, 2497 | "Nau8825GetFeature Unhandled report type %d\n", transferPacket->reportId); 2498 | 2499 | status = STATUS_INVALID_PARAMETER; 2500 | 2501 | break; 2502 | } 2503 | } 2504 | } 2505 | 2506 | Nau8825Print(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 2507 | "Nau8825GetFeature Exit = 0x%x\n", status); 2508 | 2509 | return status; 2510 | } 2511 | 2512 | PCHAR 2513 | DbgHidInternalIoctlString( 2514 | IN ULONG IoControlCode 2515 | ) 2516 | { 2517 | switch (IoControlCode) 2518 | { 2519 | case IOCTL_HID_GET_DEVICE_DESCRIPTOR: 2520 | return "IOCTL_HID_GET_DEVICE_DESCRIPTOR"; 2521 | case IOCTL_HID_GET_REPORT_DESCRIPTOR: 2522 | return "IOCTL_HID_GET_REPORT_DESCRIPTOR"; 2523 | case IOCTL_HID_READ_REPORT: 2524 | return "IOCTL_HID_READ_REPORT"; 2525 | case IOCTL_HID_GET_DEVICE_ATTRIBUTES: 2526 | return "IOCTL_HID_GET_DEVICE_ATTRIBUTES"; 2527 | case IOCTL_HID_WRITE_REPORT: 2528 | return "IOCTL_HID_WRITE_REPORT"; 2529 | case IOCTL_HID_SET_FEATURE: 2530 | return "IOCTL_HID_SET_FEATURE"; 2531 | case IOCTL_HID_GET_FEATURE: 2532 | return "IOCTL_HID_GET_FEATURE"; 2533 | case IOCTL_HID_GET_STRING: 2534 | return "IOCTL_HID_GET_STRING"; 2535 | case IOCTL_HID_ACTIVATE_DEVICE: 2536 | return "IOCTL_HID_ACTIVATE_DEVICE"; 2537 | case IOCTL_HID_DEACTIVATE_DEVICE: 2538 | return "IOCTL_HID_DEACTIVATE_DEVICE"; 2539 | case IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST: 2540 | return "IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST"; 2541 | case IOCTL_HID_SET_OUTPUT_REPORT: 2542 | return "IOCTL_HID_SET_OUTPUT_REPORT"; 2543 | case IOCTL_HID_GET_INPUT_REPORT: 2544 | return "IOCTL_HID_GET_INPUT_REPORT"; 2545 | default: 2546 | return "Unknown IOCTL"; 2547 | } 2548 | } --------------------------------------------------------------------------------