├── .gitattributes ├── LICENSE.txt ├── max98090 ├── gpiowrapper.h ├── spb.h ├── trace.h ├── max98090.inf ├── driver.h ├── gpio.c ├── spb.c ├── max98090.vcxproj ├── spb.cpp ├── max98090.c └── max98090.h ├── max98090.sln ├── .gitignore └── max98090 Package └── max98090 Package.vcxproj /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016 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. -------------------------------------------------------------------------------- /max98090/gpiowrapper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define DEFAULT_SPB_BUFFER_SIZE 64 7 | #define RESHUB_USE_HELPER_ROUTINES 8 | 9 | // 10 | // SPB (I2C) context 11 | // 12 | 13 | typedef struct _GPIO_CONTEXT 14 | { 15 | WDFIOTARGET GpioIoTarget; 16 | LARGE_INTEGER GpioResHubId; 17 | WDFWAITLOCK GpioLock; 18 | } GPIO_CONTEXT; 19 | 20 | VOID 21 | GpioTargetDeinitialize( 22 | IN WDFDEVICE FxDevice, 23 | IN GPIO_CONTEXT *GpioContext 24 | ); 25 | 26 | NTSTATUS 27 | GpioTargetInitialize( 28 | IN WDFDEVICE FxDevice, 29 | IN GPIO_CONTEXT *GpioContext 30 | ); 31 | 32 | NTSTATUS 33 | GpioReadDataSynchronously( 34 | _In_ GPIO_CONTEXT *GpioContext, 35 | _In_reads_bytes_(Length) PVOID Data, 36 | _In_ ULONG Length 37 | ); -------------------------------------------------------------------------------- /max98090/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 | SpbReadDataSynchronously( 44 | _In_ SPB_CONTEXT *SpbContext, 45 | _In_ UCHAR Address, 46 | _In_reads_bytes_(Length) PVOID Data, 47 | _In_ ULONG Length 48 | ); 49 | 50 | VOID 51 | SpbTargetDeinitialize( 52 | IN WDFDEVICE FxDevice, 53 | IN SPB_CONTEXT *SpbContext 54 | ); 55 | 56 | NTSTATUS 57 | SpbTargetInitialize( 58 | IN WDFDEVICE FxDevice, 59 | IN SPB_CONTEXT *SpbContext 60 | ); 61 | 62 | NTSTATUS 63 | SpbWriteDataSynchronously( 64 | IN SPB_CONTEXT *SpbContext, 65 | IN UCHAR Address, 66 | IN PVOID Data, 67 | IN ULONG Length 68 | ); -------------------------------------------------------------------------------- /max98090/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 | -------------------------------------------------------------------------------- /max98090/max98090.inf: -------------------------------------------------------------------------------- 1 | ;/*++ 2 | ; 3 | ;Copyright (c) CoolStar. All rights reserved. 4 | ; 5 | ;Module Name: 6 | ; max98090.inf 7 | ; 8 | ;Abstract: 9 | ; INF file for installing the Maxim 98090 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 = 06/21/2006,6.1.7779.2 20 | CatalogFile = max98090.cat 21 | PnpLockdown = 1 22 | 23 | [DestinationDirs] 24 | DefaultDestDir = 12 25 | 26 | ; ================= Class section ===================== 27 | 28 | [SourceDisksNames] 29 | 1 = %DiskId1%,,,"" 30 | 31 | [SourceDisksFiles] 32 | max98090.sys = 1,, 33 | 34 | ;***************************************** 35 | ; Max98090 Install Section 36 | ;***************************************** 37 | 38 | [Manufacturer] 39 | %StdMfg%=Standard,NT$ARCH$ 40 | 41 | ; Decorated model section take precedence over undecorated 42 | ; ones on XP and later. 43 | [Standard.NT$ARCH$] 44 | %Max98090.DeviceDesc%=Max98090_Device, ACPI\193C9890 45 | 46 | [Max98090_Device.NT] 47 | CopyFiles=Drivers_Dir 48 | 49 | [Max98090_Device.NT.HW] 50 | AddReg=Max98090_AddReg 51 | 52 | [Drivers_Dir] 53 | max98090.sys 54 | 55 | [Max98090_AddReg] 56 | ; Set to 1 to connect the first interrupt resource found, 0 to leave disconnected 57 | HKR,Settings,"ConnectInterrupt",0x00010001,0 58 | 59 | ;-------------- Service installation 60 | [Max98090_Device.NT.Services] 61 | AddService = Max98090,%SPSVCINST_ASSOCSERVICE%, Max98090_Service_Inst 62 | 63 | ; -------------- Max98090 driver install sections 64 | [Max98090_Service_Inst] 65 | DisplayName = %Max98090.SVCDESC% 66 | ServiceType = 1 ; SERVICE_KERNEL_DRIVER 67 | StartType = 3 ; SERVICE_DEMAND_START 68 | ErrorControl = 1 ; SERVICE_ERROR_NORMAL 69 | ServiceBinary = %12%\max98090.sys 70 | LoadOrderGroup = Base 71 | 72 | [Strings] 73 | SPSVCINST_ASSOCSERVICE= 0x00000002 74 | StdMfg = "CoolStar" 75 | DiskId1 = "Maxim 98090 Installation Disk #1" 76 | Max98090.DeviceDesc = "Maxim 98090 I2S Audio" 77 | Max98090.SVCDESC = "Max98090 Service" 78 | -------------------------------------------------------------------------------- /max98090/driver.h: -------------------------------------------------------------------------------- 1 | #if !defined(_MAXM_H_) 2 | #define _MAXM_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 "max98090.h" 20 | #include "spb.h" 21 | #include "gpiowrapper.h" 22 | 23 | // 24 | // String definitions 25 | // 26 | 27 | #define DRIVERNAME "max98090.sys: " 28 | 29 | #define MAXM_POOL_TAG (ULONG) 'mxaM' 30 | #define MAXM_HARDWARE_IDS L"CoolStar\\MAXM9890\0\0" 31 | #define MAXM_HARDWARE_IDS_LENGTH sizeof(MAXM_HARDWARE_IDS) 32 | 33 | #define NTDEVICE_NAME_STRING L"\\Device\\MAXM9890" 34 | #define SYMBOLIC_NAME_STRING L"\\DosDevices\\MAXM9890" 35 | 36 | #define true 1 37 | #define false 0 38 | 39 | typedef struct _MAXM_CONTEXT 40 | { 41 | 42 | // 43 | // Handle back to the WDFDEVICE 44 | // 45 | 46 | WDFDEVICE FxDevice; 47 | 48 | WDFQUEUE ReportQueue; 49 | 50 | SPB_CONTEXT I2CContext; 51 | 52 | GPIO_CONTEXT GpioContext; 53 | 54 | GPIO_CONTEXT MicGpioContext; 55 | 56 | WDFTIMER Timer; 57 | 58 | BOOLEAN ConnectInterrupt; 59 | 60 | BOOLEAN HeadphonesConnected; 61 | 62 | BOOLEAN HeadsetMicConnected; 63 | 64 | } MAXM_CONTEXT, *PMAXM_CONTEXT; 65 | 66 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(MAXM_CONTEXT, GetDeviceContext) 67 | 68 | // 69 | // Function definitions 70 | // 71 | 72 | DRIVER_INITIALIZE DriverEntry; 73 | 74 | EVT_WDF_DRIVER_UNLOAD MaxmDriverUnload; 75 | 76 | EVT_WDF_DRIVER_DEVICE_ADD MaxmEvtDeviceAdd; 77 | 78 | EVT_WDFDEVICE_WDM_IRP_PREPROCESS MaxmEvtWdmPreprocessMnQueryId; 79 | 80 | EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL MaxmEvtInternalDeviceControl; 81 | 82 | // 83 | // Helper macros 84 | // 85 | 86 | #define DEBUG_LEVEL_ERROR 1 87 | #define DEBUG_LEVEL_INFO 2 88 | #define DEBUG_LEVEL_VERBOSE 3 89 | 90 | #define DBG_INIT 1 91 | #define DBG_PNP 2 92 | #define DBG_IOCTL 4 93 | 94 | #if 0 95 | #define MaxmPrint(dbglevel, dbgcatagory, fmt, ...) { \ 96 | if (MaxmDebugLevel >= dbglevel && \ 97 | (MaxmDebugCatagories && dbgcatagory)) \ 98 | { \ 99 | DbgPrint(DRIVERNAME); \ 100 | DbgPrint(fmt, __VA_ARGS__); \ 101 | } \ 102 | } 103 | #else 104 | #define MaxmPrint(dbglevel, fmt, ...) { \ 105 | } 106 | #endif 107 | #endif -------------------------------------------------------------------------------- /max98090.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}") = "max98090", "max98090\max98090.vcxproj", "{B3E71397-9BE4-492B-AAED-4D056E59CB1F}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "max98090 Package", "max98090 Package\max98090 Package.vcxproj", "{EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}" 9 | ProjectSection(ProjectDependencies) = postProject 10 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F} = {B3E71397-9BE4-492B-AAED-4D056E59CB1F} 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Win8 Debug|Win32 = Win8 Debug|Win32 16 | Win8 Debug|x64 = Win8 Debug|x64 17 | Win8 Release|Win32 = Win8 Release|Win32 18 | Win8 Release|x64 = Win8 Release|x64 19 | Win8.1 Debug|Win32 = Win8.1 Debug|Win32 20 | Win8.1 Debug|x64 = Win8.1 Debug|x64 21 | Win8.1 Release|Win32 = Win8.1 Release|Win32 22 | Win8.1 Release|x64 = Win8.1 Release|x64 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8 Debug|Win32.ActiveCfg = Win8 Debug|Win32 26 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8 Debug|Win32.Build.0 = Win8 Debug|Win32 27 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8 Debug|Win32.Deploy.0 = Win8 Debug|Win32 28 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8 Debug|x64.ActiveCfg = Win8 Debug|x64 29 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8 Debug|x64.Build.0 = Win8 Debug|x64 30 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8 Debug|x64.Deploy.0 = Win8 Debug|x64 31 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8 Release|Win32.ActiveCfg = Win8 Release|Win32 32 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8 Release|Win32.Build.0 = Win8 Release|Win32 33 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8 Release|Win32.Deploy.0 = Win8 Release|Win32 34 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8 Release|x64.ActiveCfg = Win8 Release|x64 35 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8 Release|x64.Build.0 = Win8 Release|x64 36 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8 Release|x64.Deploy.0 = Win8 Release|x64 37 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8.1 Debug|Win32.ActiveCfg = Win8.1 Debug|Win32 38 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8.1 Debug|Win32.Build.0 = Win8.1 Debug|Win32 39 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8.1 Debug|Win32.Deploy.0 = Win8.1 Debug|Win32 40 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8.1 Debug|x64.ActiveCfg = Win8.1 Debug|x64 41 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8.1 Debug|x64.Build.0 = Win8.1 Debug|x64 42 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8.1 Debug|x64.Deploy.0 = Win8.1 Debug|x64 43 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8.1 Release|Win32.ActiveCfg = Win8.1 Release|Win32 44 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8.1 Release|Win32.Build.0 = Win8.1 Release|Win32 45 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8.1 Release|Win32.Deploy.0 = Win8.1 Release|Win32 46 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8.1 Release|x64.ActiveCfg = Win8.1 Release|x64 47 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8.1 Release|x64.Build.0 = Win8.1 Release|x64 48 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Win8.1 Release|x64.Deploy.0 = Win8.1 Release|x64 49 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8 Debug|Win32.ActiveCfg = Win8 Debug|Win32 50 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8 Debug|Win32.Build.0 = Win8 Debug|Win32 51 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8 Debug|Win32.Deploy.0 = Win8 Debug|Win32 52 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8 Debug|x64.ActiveCfg = Win8 Debug|x64 53 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8 Debug|x64.Build.0 = Win8 Debug|x64 54 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8 Debug|x64.Deploy.0 = Win8 Debug|x64 55 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8 Release|Win32.ActiveCfg = Win8 Release|Win32 56 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8 Release|Win32.Build.0 = Win8 Release|Win32 57 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8 Release|Win32.Deploy.0 = Win8 Release|Win32 58 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8 Release|x64.ActiveCfg = Win8 Release|x64 59 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8 Release|x64.Build.0 = Win8 Release|x64 60 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8 Release|x64.Deploy.0 = Win8 Release|x64 61 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8.1 Debug|Win32.ActiveCfg = Win8.1 Debug|Win32 62 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8.1 Debug|Win32.Build.0 = Win8.1 Debug|Win32 63 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8.1 Debug|Win32.Deploy.0 = Win8.1 Debug|Win32 64 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8.1 Debug|x64.ActiveCfg = Win8.1 Debug|x64 65 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8.1 Debug|x64.Build.0 = Win8.1 Debug|x64 66 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8.1 Debug|x64.Deploy.0 = Win8.1 Debug|x64 67 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8.1 Release|Win32.ActiveCfg = Win8.1 Release|Win32 68 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8.1 Release|Win32.Build.0 = Win8.1 Release|Win32 69 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8.1 Release|Win32.Deploy.0 = Win8.1 Release|Win32 70 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8.1 Release|x64.ActiveCfg = Win8.1 Release|x64 71 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8.1 Release|x64.Build.0 = Win8.1 Release|x64 72 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Win8.1 Release|x64.Deploy.0 = Win8.1 Release|x64 73 | EndGlobalSection 74 | GlobalSection(SolutionProperties) = preSolution 75 | HideSolutionNode = FALSE 76 | EndGlobalSection 77 | GlobalSection(ExtensibilityGlobals) = postSolution 78 | SolutionGuid = {86D249D6-FF1E-41F4-AA9B-3813428D6C1A} 79 | EndGlobalSection 80 | EndGlobal 81 | -------------------------------------------------------------------------------- /max98090/gpio.c: -------------------------------------------------------------------------------- 1 | #include "driver.h" 2 | #include "gpiowrapper.h" 3 | #include 4 | #include 5 | 6 | static ULONG MaxmDebugLevel = 100; 7 | static ULONG MaxmDebugCatagories = DBG_INIT || DBG_PNP || DBG_IOCTL; 8 | 9 | NTSTATUS 10 | GpioReadDataSynchronously( 11 | _In_ GPIO_CONTEXT *GpioContext, 12 | _In_reads_bytes_(Length) PVOID Data, 13 | _In_ ULONG Length 14 | ) 15 | /*++ 16 | 17 | Routine Description: 18 | 19 | This helper routine abstracts creating and sending an I/O 20 | request (I2C Read) to the Spb I/O target. 21 | 22 | Arguments: 23 | 24 | SpbContext - Pointer to the current device context 25 | Data - A buffer to receive the data at at the above address 26 | Length - The amount of data to be read from the above address 27 | 28 | Return Value: 29 | 30 | NTSTATUS Status indicating success or failure 31 | 32 | --*/ 33 | { 34 | WDF_OBJECT_ATTRIBUTES Attributes; 35 | WDFREQUEST IoctlRequest; 36 | WDFMEMORY WdfMemory; 37 | WDF_OBJECT_ATTRIBUTES RequestAttributes; 38 | WDF_REQUEST_SEND_OPTIONS SendOptions; 39 | NTSTATUS status; 40 | 41 | WdfWaitLockAcquire(GpioContext->GpioLock, NULL); 42 | 43 | status = STATUS_INVALID_PARAMETER; 44 | 45 | WDF_OBJECT_ATTRIBUTES_INIT(&RequestAttributes); 46 | status = WdfRequestCreate(&RequestAttributes, GpioContext->GpioIoTarget, &IoctlRequest); 47 | if (!NT_SUCCESS(status)) { 48 | MaxmPrint( 49 | DEBUG_LEVEL_ERROR, 50 | DBG_IOCTL, 51 | "Error creating request - %x\n", 52 | status); 53 | goto exit; 54 | } 55 | 56 | WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); 57 | Attributes.ParentObject = IoctlRequest; 58 | status = WdfMemoryCreatePreallocated(&Attributes, Data, Length, &WdfMemory); 59 | if (!NT_SUCCESS(status)) { 60 | MaxmPrint( 61 | DEBUG_LEVEL_ERROR, 62 | DBG_IOCTL, 63 | "Error making preallocated memory - %x\n", 64 | status); 65 | goto exit; 66 | } 67 | 68 | status = WdfIoTargetFormatRequestForIoctl(GpioContext->GpioIoTarget, 69 | IoctlRequest, 70 | IOCTL_GPIO_READ_PINS, 71 | NULL, 72 | 0, 73 | WdfMemory, 74 | 0); 75 | if (!NT_SUCCESS(status)) 76 | { 77 | MaxmPrint( 78 | DEBUG_LEVEL_ERROR, 79 | DBG_IOCTL, 80 | "Error formatting request for ioctl - %x\n", 81 | status); 82 | goto exit; 83 | } 84 | 85 | WDF_REQUEST_SEND_OPTIONS_INIT(&SendOptions, 86 | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS); 87 | 88 | if (!WdfRequestSend(IoctlRequest, GpioContext->GpioIoTarget, &SendOptions)) { 89 | status = WdfRequestGetStatus(IoctlRequest); 90 | } 91 | 92 | if (!NT_SUCCESS(status)) 93 | { 94 | MaxmPrint( 95 | DEBUG_LEVEL_ERROR, 96 | DBG_IOCTL, 97 | "Error reading from Gpio - %x\n", 98 | status); 99 | goto exit; 100 | } 101 | exit: 102 | if (IoctlRequest != NULL) { 103 | WdfObjectDelete(IoctlRequest); 104 | } 105 | 106 | WdfWaitLockRelease(GpioContext->GpioLock); 107 | 108 | return status; 109 | } 110 | 111 | VOID 112 | GpioTargetDeinitialize( 113 | IN WDFDEVICE FxDevice, 114 | IN GPIO_CONTEXT *GpioContext 115 | ) 116 | /*++ 117 | 118 | Routine Description: 119 | 120 | This helper routine is used to free any members added to the SPB_CONTEXT, 121 | note the SPB I/O target is parented to the device and will be 122 | closed and free'd when the device is removed. 123 | 124 | Arguments: 125 | 126 | FxDevice - Handle to the framework device object 127 | SpbContext - Pointer to the current device context 128 | 129 | Return Value: 130 | 131 | NTSTATUS Status indicating success or failure 132 | 133 | --*/ 134 | { 135 | UNREFERENCED_PARAMETER(FxDevice); 136 | UNREFERENCED_PARAMETER(GpioContext); 137 | 138 | // 139 | // Free any SPB_CONTEXT allocations here 140 | // 141 | if (GpioContext->GpioLock != NULL) 142 | { 143 | WdfObjectDelete(GpioContext->GpioLock); 144 | } 145 | } 146 | 147 | NTSTATUS 148 | GpioTargetInitialize( 149 | IN WDFDEVICE FxDevice, 150 | IN GPIO_CONTEXT *GpioContext 151 | ) 152 | /*++ 153 | 154 | Routine Description: 155 | 156 | This helper routine opens the Spb I/O target and 157 | initializes a request object used for the lifetime 158 | of communication between this driver and Spb. 159 | 160 | Arguments: 161 | 162 | FxDevice - Handle to the framework device object 163 | SpbContext - Pointer to the current device context 164 | 165 | Return Value: 166 | 167 | NTSTATUS Status indicating success or failure 168 | 169 | --*/ 170 | { 171 | WDF_OBJECT_ATTRIBUTES objectAttributes; 172 | WDF_IO_TARGET_OPEN_PARAMS openParams; 173 | UNICODE_STRING gpioDeviceName; 174 | WCHAR gpioDeviceNameBuffer[RESOURCE_HUB_PATH_SIZE]; 175 | NTSTATUS status; 176 | 177 | WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); 178 | objectAttributes.ParentObject = FxDevice; 179 | 180 | status = WdfIoTargetCreate( 181 | FxDevice, 182 | &objectAttributes, 183 | &GpioContext->GpioIoTarget); 184 | 185 | if (!NT_SUCCESS(status)) 186 | { 187 | MaxmPrint( 188 | DEBUG_LEVEL_ERROR, 189 | DBG_IOCTL, 190 | "Error creating IoTarget object - %!STATUS!", 191 | status); 192 | 193 | WdfObjectDelete(GpioContext->GpioIoTarget); 194 | goto exit; 195 | } 196 | 197 | RtlInitEmptyUnicodeString( 198 | &gpioDeviceName, 199 | gpioDeviceNameBuffer, 200 | sizeof(gpioDeviceNameBuffer)); 201 | 202 | status = RESOURCE_HUB_CREATE_PATH_FROM_ID( 203 | &gpioDeviceName, 204 | GpioContext->GpioResHubId.LowPart, 205 | GpioContext->GpioResHubId.HighPart); 206 | 207 | if (!NT_SUCCESS(status)) 208 | { 209 | MaxmPrint( 210 | DEBUG_LEVEL_ERROR, 211 | DBG_IOCTL, 212 | "Error creating GPIO resource hub path string - %!STATUS!", 213 | status); 214 | goto exit; 215 | } 216 | 217 | WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( 218 | &openParams, 219 | &gpioDeviceName, 220 | (FILE_GENERIC_READ)); 221 | 222 | status = WdfIoTargetOpen(GpioContext->GpioIoTarget, &openParams); 223 | 224 | if (!NT_SUCCESS(status)) 225 | { 226 | MaxmPrint( 227 | DEBUG_LEVEL_ERROR, 228 | DBG_IOCTL, 229 | "Error opening GPIO target for communication - %!STATUS!", 230 | status); 231 | goto exit; 232 | } 233 | 234 | // 235 | // Allocate a waitlock to guard access to the default buffers 236 | // 237 | status = WdfWaitLockCreate( 238 | WDF_NO_OBJECT_ATTRIBUTES, 239 | &GpioContext->GpioLock); 240 | 241 | if (!NT_SUCCESS(status)) 242 | { 243 | MaxmPrint( 244 | DEBUG_LEVEL_ERROR, 245 | DBG_IOCTL, 246 | "Error creating Spb Waitlock - %!STATUS!", 247 | status); 248 | goto exit; 249 | } 250 | 251 | exit: 252 | 253 | if (!NT_SUCCESS(status)) 254 | { 255 | GpioTargetDeinitialize(FxDevice, GpioContext); 256 | } 257 | 258 | return status; 259 | } -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /max98090/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 "driver.h" 22 | #include "spb.h" 23 | #include 24 | 25 | static ULONG MaxmDebugLevel = 100; 26 | static ULONG MaxmDebugCatagories = DBG_INIT || DBG_PNP || DBG_IOCTL; 27 | 28 | NTSTATUS 29 | SpbDoWriteDataSynchronously( 30 | IN SPB_CONTEXT *SpbContext, 31 | IN UCHAR Address, 32 | IN PVOID Data, 33 | IN ULONG Length 34 | ) 35 | /*++ 36 | 37 | Routine Description: 38 | 39 | This helper routine abstracts creating and sending an I/O 40 | request (I2C Write) to the Spb I/O target. 41 | 42 | Arguments: 43 | 44 | SpbContext - Pointer to the current device context 45 | Address - The I2C register address to write to 46 | Data - A buffer to receive the data at at the above address 47 | Length - The amount of data to be read from the above address 48 | 49 | Return Value: 50 | 51 | NTSTATUS Status indicating success or failure 52 | 53 | --*/ 54 | { 55 | PUCHAR buffer; 56 | ULONG length; 57 | WDFMEMORY memory; 58 | WDF_MEMORY_DESCRIPTOR memoryDescriptor; 59 | NTSTATUS status; 60 | 61 | // 62 | // The address pointer and data buffer must be combined 63 | // into one contiguous buffer representing the write transaction. 64 | // 65 | length = Length + 1; 66 | memory = NULL; 67 | 68 | if (length > DEFAULT_SPB_BUFFER_SIZE) 69 | { 70 | status = WdfMemoryCreate( 71 | WDF_NO_OBJECT_ATTRIBUTES, 72 | NonPagedPool, 73 | MAXM_POOL_TAG, 74 | length, 75 | &memory, 76 | (PVOID *)&buffer); 77 | 78 | if (!NT_SUCCESS(status)) 79 | { 80 | MaxmPrint( 81 | DEBUG_LEVEL_ERROR, 82 | DBG_IOCTL, 83 | "Error allocating memory for Spb write - %x\n", 84 | status); 85 | goto exit; 86 | } 87 | 88 | WDF_MEMORY_DESCRIPTOR_INIT_HANDLE( 89 | &memoryDescriptor, 90 | memory, 91 | NULL); 92 | } 93 | else 94 | { 95 | buffer = (PUCHAR)WdfMemoryGetBuffer(SpbContext->WriteMemory, NULL); 96 | 97 | WDF_MEMORY_DESCRIPTOR_INIT_BUFFER( 98 | &memoryDescriptor, 99 | (PVOID)buffer, 100 | length); 101 | } 102 | 103 | // 104 | // Transaction starts by specifying the address bytes 105 | // 106 | RtlCopyMemory(buffer, &Address, sizeof(Address)); 107 | 108 | // 109 | // Address is followed by the data payload 110 | // 111 | RtlCopyMemory((buffer + sizeof(Address)), Data, length - sizeof(Address)); 112 | 113 | status = WdfIoTargetSendWriteSynchronously( 114 | SpbContext->SpbIoTarget, 115 | NULL, 116 | &memoryDescriptor, 117 | NULL, 118 | NULL, 119 | NULL); 120 | 121 | if (!NT_SUCCESS(status)) 122 | { 123 | MaxmPrint( 124 | DEBUG_LEVEL_ERROR, 125 | DBG_IOCTL, 126 | "Error writing to Spb - %x\n", 127 | status); 128 | goto exit; 129 | } 130 | 131 | exit: 132 | 133 | if (NULL != memory) 134 | { 135 | WdfObjectDelete(memory); 136 | } 137 | 138 | return status; 139 | } 140 | 141 | NTSTATUS 142 | SpbWriteDataSynchronously( 143 | IN SPB_CONTEXT *SpbContext, 144 | IN UCHAR Address, 145 | IN PVOID Data, 146 | IN ULONG Length 147 | ) 148 | /*++ 149 | 150 | Routine Description: 151 | 152 | This routine abstracts creating and sending an I/O 153 | request (I2C Write) to the Spb I/O target and utilizes 154 | a helper routine to do work inside of locked code. 155 | 156 | Arguments: 157 | 158 | SpbContext - Pointer to the current device context 159 | Address - The I2C register address to write to 160 | Data - A buffer to receive the data at at the above address 161 | Length - The amount of data to be read from the above address 162 | 163 | Return Value: 164 | 165 | NTSTATUS Status indicating success or failure 166 | 167 | --*/ 168 | { 169 | NTSTATUS status; 170 | 171 | WdfWaitLockAcquire(SpbContext->SpbLock, NULL); 172 | 173 | status = SpbDoWriteDataSynchronously( 174 | SpbContext, 175 | Address, 176 | Data, 177 | Length); 178 | 179 | WdfWaitLockRelease(SpbContext->SpbLock); 180 | 181 | return status; 182 | } 183 | 184 | NTSTATUS 185 | SpbReadDataSynchronously( 186 | _In_ SPB_CONTEXT *SpbContext, 187 | _In_ UCHAR Address, 188 | _In_reads_bytes_(Length) PVOID Data, 189 | _In_ ULONG Length 190 | ) 191 | /*++ 192 | 193 | Routine Description: 194 | 195 | This helper routine abstracts creating and sending an I/O 196 | request (I2C Read) to the Spb I/O target. 197 | 198 | Arguments: 199 | 200 | SpbContext - Pointer to the current device context 201 | Address - The I2C register address to read from 202 | Data - A buffer to receive the data at at the above address 203 | Length - The amount of data to be read from the above address 204 | 205 | Return Value: 206 | 207 | NTSTATUS Status indicating success or failure 208 | 209 | --*/ 210 | { 211 | PUCHAR buffer; 212 | WDFMEMORY memory; 213 | WDF_MEMORY_DESCRIPTOR memoryDescriptor; 214 | NTSTATUS status; 215 | ULONG_PTR bytesRead; 216 | 217 | WdfWaitLockAcquire(SpbContext->SpbLock, NULL); 218 | 219 | memory = NULL; 220 | status = STATUS_INVALID_PARAMETER; 221 | bytesRead = 0; 222 | 223 | // 224 | // Read transactions start by writing an address pointer 225 | // 226 | status = SpbDoWriteDataSynchronously( 227 | SpbContext, 228 | Address, 229 | NULL, 230 | 0); 231 | 232 | if (!NT_SUCCESS(status)) 233 | { 234 | MaxmPrint( 235 | DEBUG_LEVEL_ERROR, 236 | DBG_IOCTL, 237 | "Error setting address pointer for Spb read - %x\n", 238 | status); 239 | goto exit; 240 | } 241 | 242 | if (Length > DEFAULT_SPB_BUFFER_SIZE) 243 | { 244 | status = WdfMemoryCreate( 245 | WDF_NO_OBJECT_ATTRIBUTES, 246 | NonPagedPool, 247 | MAXM_POOL_TAG, 248 | Length, 249 | &memory, 250 | (PVOID *)&buffer); 251 | 252 | if (!NT_SUCCESS(status)) 253 | { 254 | MaxmPrint( 255 | DEBUG_LEVEL_ERROR, 256 | DBG_IOCTL, 257 | "Error allocating memory for Spb read - %x\n", 258 | status); 259 | goto exit; 260 | } 261 | 262 | WDF_MEMORY_DESCRIPTOR_INIT_HANDLE( 263 | &memoryDescriptor, 264 | memory, 265 | NULL); 266 | } 267 | else 268 | { 269 | buffer = (PUCHAR)WdfMemoryGetBuffer(SpbContext->ReadMemory, NULL); 270 | 271 | WDF_MEMORY_DESCRIPTOR_INIT_BUFFER( 272 | &memoryDescriptor, 273 | (PVOID)buffer, 274 | Length); 275 | } 276 | 277 | 278 | status = WdfIoTargetSendReadSynchronously( 279 | SpbContext->SpbIoTarget, 280 | NULL, 281 | &memoryDescriptor, 282 | NULL, 283 | NULL, 284 | &bytesRead); 285 | 286 | if (!NT_SUCCESS(status) || 287 | bytesRead != Length) 288 | { 289 | MaxmPrint( 290 | DEBUG_LEVEL_ERROR, 291 | DBG_IOCTL, 292 | "Error reading from Spb - %x\n", 293 | status); 294 | goto exit; 295 | } 296 | 297 | // 298 | // Copy back to the caller's buffer 299 | // 300 | RtlCopyMemory(Data, buffer, Length); 301 | 302 | exit: 303 | if (NULL != memory) 304 | { 305 | WdfObjectDelete(memory); 306 | } 307 | 308 | WdfWaitLockRelease(SpbContext->SpbLock); 309 | 310 | return status; 311 | } 312 | 313 | VOID 314 | SpbTargetDeinitialize( 315 | IN WDFDEVICE FxDevice, 316 | IN SPB_CONTEXT *SpbContext 317 | ) 318 | /*++ 319 | 320 | Routine Description: 321 | 322 | This helper routine is used to free any members added to the SPB_CONTEXT, 323 | note the SPB I/O target is parented to the device and will be 324 | closed and free'd when the device is removed. 325 | 326 | Arguments: 327 | 328 | FxDevice - Handle to the framework device object 329 | SpbContext - Pointer to the current device context 330 | 331 | Return Value: 332 | 333 | NTSTATUS Status indicating success or failure 334 | 335 | --*/ 336 | { 337 | UNREFERENCED_PARAMETER(FxDevice); 338 | UNREFERENCED_PARAMETER(SpbContext); 339 | 340 | // 341 | // Free any SPB_CONTEXT allocations here 342 | // 343 | if (SpbContext->SpbLock != NULL) 344 | { 345 | WdfObjectDelete(SpbContext->SpbLock); 346 | } 347 | 348 | if (SpbContext->ReadMemory != NULL) 349 | { 350 | WdfObjectDelete(SpbContext->ReadMemory); 351 | } 352 | 353 | if (SpbContext->WriteMemory != NULL) 354 | { 355 | WdfObjectDelete(SpbContext->WriteMemory); 356 | } 357 | } 358 | 359 | NTSTATUS 360 | SpbTargetInitialize( 361 | IN WDFDEVICE FxDevice, 362 | IN SPB_CONTEXT *SpbContext 363 | ) 364 | /*++ 365 | 366 | Routine Description: 367 | 368 | This helper routine opens the Spb I/O target and 369 | initializes a request object used for the lifetime 370 | of communication between this driver and Spb. 371 | 372 | Arguments: 373 | 374 | FxDevice - Handle to the framework device object 375 | SpbContext - Pointer to the current device context 376 | 377 | Return Value: 378 | 379 | NTSTATUS Status indicating success or failure 380 | 381 | --*/ 382 | { 383 | WDF_OBJECT_ATTRIBUTES objectAttributes; 384 | WDF_IO_TARGET_OPEN_PARAMS openParams; 385 | UNICODE_STRING spbDeviceName; 386 | WCHAR spbDeviceNameBuffer[RESOURCE_HUB_PATH_SIZE]; 387 | NTSTATUS status; 388 | 389 | WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); 390 | objectAttributes.ParentObject = FxDevice; 391 | 392 | status = WdfIoTargetCreate( 393 | FxDevice, 394 | &objectAttributes, 395 | &SpbContext->SpbIoTarget); 396 | 397 | if (!NT_SUCCESS(status)) 398 | { 399 | MaxmPrint( 400 | DEBUG_LEVEL_ERROR, 401 | DBG_IOCTL, 402 | "Error creating IoTarget object - %!STATUS!", 403 | status); 404 | 405 | WdfObjectDelete(SpbContext->SpbIoTarget); 406 | goto exit; 407 | } 408 | 409 | RtlInitEmptyUnicodeString( 410 | &spbDeviceName, 411 | spbDeviceNameBuffer, 412 | sizeof(spbDeviceNameBuffer)); 413 | 414 | status = RESOURCE_HUB_CREATE_PATH_FROM_ID( 415 | &spbDeviceName, 416 | SpbContext->I2cResHubId.LowPart, 417 | SpbContext->I2cResHubId.HighPart); 418 | 419 | if (!NT_SUCCESS(status)) 420 | { 421 | MaxmPrint( 422 | DEBUG_LEVEL_ERROR, 423 | DBG_IOCTL, 424 | "Error creating Spb resource hub path string - %!STATUS!", 425 | status); 426 | goto exit; 427 | } 428 | 429 | WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( 430 | &openParams, 431 | &spbDeviceName, 432 | (GENERIC_READ | GENERIC_WRITE)); 433 | 434 | openParams.ShareAccess = 0; 435 | openParams.CreateDisposition = FILE_OPEN; 436 | openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL; 437 | 438 | status = WdfIoTargetOpen(SpbContext->SpbIoTarget, &openParams); 439 | 440 | if (!NT_SUCCESS(status)) 441 | { 442 | MaxmPrint( 443 | DEBUG_LEVEL_ERROR, 444 | DBG_IOCTL, 445 | "Error opening Spb target for communication - %!STATUS!", 446 | status); 447 | goto exit; 448 | } 449 | 450 | // 451 | // Allocate some fixed-size buffers from NonPagedPool for typical 452 | // Spb transaction sizes to avoid pool fragmentation in most cases 453 | // 454 | status = WdfMemoryCreate( 455 | WDF_NO_OBJECT_ATTRIBUTES, 456 | NonPagedPool, 457 | MAXM_POOL_TAG, 458 | DEFAULT_SPB_BUFFER_SIZE, 459 | &SpbContext->WriteMemory, 460 | NULL); 461 | 462 | if (!NT_SUCCESS(status)) 463 | { 464 | MaxmPrint( 465 | DEBUG_LEVEL_ERROR, 466 | DBG_IOCTL, 467 | "Error allocating default memory for Spb write - %!STATUS!", 468 | status); 469 | goto exit; 470 | } 471 | 472 | status = WdfMemoryCreate( 473 | WDF_NO_OBJECT_ATTRIBUTES, 474 | NonPagedPool, 475 | MAXM_POOL_TAG, 476 | DEFAULT_SPB_BUFFER_SIZE, 477 | &SpbContext->ReadMemory, 478 | NULL); 479 | 480 | if (!NT_SUCCESS(status)) 481 | { 482 | MaxmPrint( 483 | DEBUG_LEVEL_ERROR, 484 | DBG_IOCTL, 485 | "Error allocating default memory for Spb read - %!STATUS!", 486 | status); 487 | goto exit; 488 | } 489 | 490 | // 491 | // Allocate a waitlock to guard access to the default buffers 492 | // 493 | status = WdfWaitLockCreate( 494 | WDF_NO_OBJECT_ATTRIBUTES, 495 | &SpbContext->SpbLock); 496 | 497 | if (!NT_SUCCESS(status)) 498 | { 499 | MaxmPrint( 500 | DEBUG_LEVEL_ERROR, 501 | DBG_IOCTL, 502 | "Error creating Spb Waitlock - %!STATUS!", 503 | status); 504 | goto exit; 505 | } 506 | 507 | exit: 508 | 509 | if (!NT_SUCCESS(status)) 510 | { 511 | SpbTargetDeinitialize(FxDevice, SpbContext); 512 | } 513 | 514 | return status; 515 | } -------------------------------------------------------------------------------- /max98090/max98090.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Win8.1 Debug 6 | Win32 7 | 8 | 9 | Win8.1 Release 10 | Win32 11 | 12 | 13 | Win8 Debug 14 | Win32 15 | 16 | 17 | Win8 Release 18 | Win32 19 | 20 | 21 | Win7 Debug 22 | Win32 23 | 24 | 25 | Win7 Release 26 | Win32 27 | 28 | 29 | Win8.1 Debug 30 | x64 31 | 32 | 33 | Win8.1 Release 34 | x64 35 | 36 | 37 | Win8 Debug 38 | x64 39 | 40 | 41 | Win8 Release 42 | x64 43 | 44 | 45 | Win7 Debug 46 | x64 47 | 48 | 49 | Win7 Release 50 | x64 51 | 52 | 53 | 54 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F} 55 | {1bc93793-694f-48fe-9372-81e2b05556fd} 56 | v4.5 57 | 11.0 58 | Win8.1 Debug 59 | Win32 60 | max98090 61 | 10.0.22000.0 62 | max98090 63 | 64 | 65 | 66 | WindowsV6.3 67 | true 68 | WindowsKernelModeDriver10.0 69 | Driver 70 | KMDF 71 | 72 | 73 | WindowsV6.3 74 | false 75 | WindowsKernelModeDriver10.0 76 | Driver 77 | KMDF 78 | 79 | 80 | Windows8 81 | true 82 | WindowsKernelModeDriver10.0 83 | Driver 84 | KMDF 85 | 86 | 87 | Windows8 88 | false 89 | WindowsKernelModeDriver10.0 90 | Driver 91 | KMDF 92 | 93 | 94 | Windows7 95 | true 96 | WindowsKernelModeDriver10.0 97 | Driver 98 | KMDF 99 | 100 | 101 | Windows7 102 | false 103 | WindowsKernelModeDriver10.0 104 | Driver 105 | KMDF 106 | 107 | 108 | WindowsV6.3 109 | true 110 | WindowsKernelModeDriver10.0 111 | Driver 112 | KMDF 113 | 114 | 115 | WindowsV6.3 116 | false 117 | WindowsKernelModeDriver10.0 118 | Driver 119 | KMDF 120 | 121 | 122 | Windows8 123 | true 124 | WindowsKernelModeDriver10.0 125 | Driver 126 | KMDF 127 | 128 | 129 | Windows8 130 | false 131 | WindowsKernelModeDriver10.0 132 | Driver 133 | KMDF 134 | 135 | 136 | Windows7 137 | true 138 | WindowsKernelModeDriver10.0 139 | Driver 140 | KMDF 141 | 142 | 143 | Windows7 144 | false 145 | WindowsKernelModeDriver10.0 146 | Driver 147 | KMDF 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | DbgengKernelDebugger 159 | 160 | 161 | DbgengKernelDebugger 162 | 163 | 164 | DbgengKernelDebugger 165 | 166 | 167 | DbgengKernelDebugger 168 | 169 | 170 | DbgengKernelDebugger 171 | 172 | 173 | DbgengKernelDebugger 174 | 175 | 176 | DbgengKernelDebugger 177 | 178 | 179 | DbgengKernelDebugger 180 | 181 | 182 | DbgengKernelDebugger 183 | 184 | 185 | DbgengKernelDebugger 186 | 187 | 188 | DbgengKernelDebugger 189 | 190 | 191 | DbgengKernelDebugger 192 | 193 | 194 | 195 | true 196 | trace.h 197 | true 198 | false 199 | 200 | 201 | 1.0.4 202 | 203 | 204 | SHA256 205 | 206 | 207 | 208 | 209 | true 210 | trace.h 211 | true 212 | false 213 | 214 | 215 | 1.0.4 216 | 217 | 218 | SHA256 219 | 220 | 221 | 222 | 223 | true 224 | trace.h 225 | true 226 | false 227 | 228 | 229 | 1.0.4 230 | 231 | 232 | SHA256 233 | 234 | 235 | 236 | 237 | true 238 | trace.h 239 | true 240 | false 241 | 242 | 243 | 1.0.4 244 | 245 | 246 | SHA256 247 | 248 | 249 | 250 | 251 | true 252 | trace.h 253 | true 254 | false 255 | 256 | 257 | 1.0.4 258 | 259 | 260 | SHA256 261 | 262 | 263 | 264 | 265 | true 266 | trace.h 267 | true 268 | false 269 | 270 | 271 | 1.0.4 272 | 273 | 274 | SHA256 275 | 276 | 277 | 278 | 279 | true 280 | trace.h 281 | true 282 | false 283 | 284 | 285 | 1.0.4 286 | 287 | 288 | SHA256 289 | 290 | 291 | 292 | 293 | true 294 | trace.h 295 | true 296 | false 297 | 298 | 299 | 1.0.4 300 | 301 | 302 | SHA256 303 | 304 | 305 | 306 | 307 | true 308 | trace.h 309 | true 310 | false 311 | 312 | 313 | 1.0.4 314 | 315 | 316 | SHA256 317 | 318 | 319 | 320 | 321 | true 322 | trace.h 323 | true 324 | false 325 | 326 | 327 | 1.0.4 328 | 329 | 330 | SHA256 331 | 332 | 333 | 334 | 335 | true 336 | trace.h 337 | true 338 | false 339 | 340 | 341 | 1.0.4 342 | 343 | 344 | SHA256 345 | 346 | 347 | 348 | 349 | true 350 | trace.h 351 | true 352 | false 353 | 354 | 355 | 1.0.4 356 | 357 | 358 | SHA256 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | -------------------------------------------------------------------------------- /max98090/spb.cpp: -------------------------------------------------------------------------------- 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 "internal.h" 22 | #include "hiddevice.h" 23 | #include "spb.h" 24 | 25 | static ULONG ElanPrintDebugLevel = 100; 26 | static ULONG ElanPrintDebugCatagories = DBG_INIT || DBG_PNP || DBG_IOCTL; 27 | 28 | NTSTATUS 29 | SpbDoWriteDataSynchronously16( 30 | IN SPB_CONTEXT *SpbContext, 31 | IN UINT16 Address, 32 | IN PVOID Data, 33 | IN ULONG Length 34 | ) 35 | /*++ 36 | 37 | Routine Description: 38 | 39 | This helper routine abstracts creating and sending an I/O 40 | request (I2C Write) to the Spb I/O target. 41 | 42 | Arguments: 43 | 44 | SpbContext - Pointer to the current device context 45 | Address - The I2C register address to write to 46 | Data - A buffer to receive the data at at the above address 47 | Length - The amount of data to be read from the above address 48 | 49 | Return Value: 50 | 51 | NTSTATUS Status indicating success or failure 52 | 53 | --*/ 54 | { 55 | PUCHAR buffer; 56 | ULONG length; 57 | WDFMEMORY memory; 58 | WDF_MEMORY_DESCRIPTOR memoryDescriptor; 59 | NTSTATUS status; 60 | 61 | // 62 | // The address pointer and data buffer must be combined 63 | // into one contiguous buffer representing the write transaction. 64 | // 65 | length = Length + 2; 66 | memory = NULL; 67 | 68 | if (length > DEFAULT_SPB_BUFFER_SIZE) 69 | { 70 | status = WdfMemoryCreate( 71 | WDF_NO_OBJECT_ATTRIBUTES, 72 | NonPagedPool, 73 | CYAPA_POOL_TAG, 74 | length, 75 | &memory, 76 | (PVOID *)&buffer); 77 | 78 | if (!NT_SUCCESS(status)) 79 | { 80 | ElanPrint( 81 | DEBUG_LEVEL_ERROR, 82 | DBG_IOCTL, 83 | "Error allocating memory for Spb write - %!STATUS!", 84 | status); 85 | goto exit; 86 | } 87 | 88 | WDF_MEMORY_DESCRIPTOR_INIT_HANDLE( 89 | &memoryDescriptor, 90 | memory, 91 | NULL); 92 | } 93 | else 94 | { 95 | buffer = (PUCHAR)WdfMemoryGetBuffer(SpbContext->WriteMemory, NULL); 96 | 97 | WDF_MEMORY_DESCRIPTOR_INIT_BUFFER( 98 | &memoryDescriptor, 99 | (PVOID)buffer, 100 | length); 101 | } 102 | 103 | UINT16 AddressBuffer[] = { 104 | Address 105 | }; 106 | 107 | // 108 | // Transaction starts by specifying the address bytes 109 | // 110 | RtlCopyMemory(buffer, (UCHAR *)&AddressBuffer, sizeof(AddressBuffer)); 111 | 112 | // 113 | // Address is followed by the data payload 114 | // 115 | RtlCopyMemory((buffer + sizeof(AddressBuffer)), Data, length - sizeof(AddressBuffer)); 116 | 117 | status = WdfIoTargetSendWriteSynchronously( 118 | SpbContext->SpbIoTarget, 119 | NULL, 120 | &memoryDescriptor, 121 | NULL, 122 | NULL, 123 | NULL); 124 | 125 | if (!NT_SUCCESS(status)) 126 | { 127 | ElanPrint( 128 | DEBUG_LEVEL_ERROR, 129 | DBG_IOCTL, 130 | "Error writing to Spb - %!STATUS!", 131 | status); 132 | goto exit; 133 | } 134 | 135 | exit: 136 | 137 | if (NULL != memory) 138 | { 139 | WdfObjectDelete(memory); 140 | } 141 | 142 | return status; 143 | } 144 | 145 | NTSTATUS 146 | SpbDoWriteDataSynchronously( 147 | IN SPB_CONTEXT *SpbContext, 148 | IN UCHAR Address, 149 | IN PVOID Data, 150 | IN ULONG Length 151 | ) 152 | /*++ 153 | 154 | Routine Description: 155 | 156 | This helper routine abstracts creating and sending an I/O 157 | request (I2C Write) to the Spb I/O target. 158 | 159 | Arguments: 160 | 161 | SpbContext - Pointer to the current device context 162 | Address - The I2C register address to write to 163 | Data - A buffer to receive the data at at the above address 164 | Length - The amount of data to be read from the above address 165 | 166 | Return Value: 167 | 168 | NTSTATUS Status indicating success or failure 169 | 170 | --*/ 171 | { 172 | PUCHAR buffer; 173 | ULONG length; 174 | WDFMEMORY memory; 175 | WDF_MEMORY_DESCRIPTOR memoryDescriptor; 176 | NTSTATUS status; 177 | 178 | // 179 | // The address pointer and data buffer must be combined 180 | // into one contiguous buffer representing the write transaction. 181 | // 182 | length = Length + 1; 183 | memory = NULL; 184 | 185 | if (length > DEFAULT_SPB_BUFFER_SIZE) 186 | { 187 | status = WdfMemoryCreate( 188 | WDF_NO_OBJECT_ATTRIBUTES, 189 | NonPagedPool, 190 | CYAPA_POOL_TAG, 191 | length, 192 | &memory, 193 | (PVOID *)&buffer); 194 | 195 | if (!NT_SUCCESS(status)) 196 | { 197 | ElanPrint( 198 | DEBUG_LEVEL_ERROR, 199 | DBG_IOCTL, 200 | "Error allocating memory for Spb write - %!STATUS!", 201 | status); 202 | goto exit; 203 | } 204 | 205 | WDF_MEMORY_DESCRIPTOR_INIT_HANDLE( 206 | &memoryDescriptor, 207 | memory, 208 | NULL); 209 | } 210 | else 211 | { 212 | buffer = (PUCHAR)WdfMemoryGetBuffer(SpbContext->WriteMemory, NULL); 213 | 214 | WDF_MEMORY_DESCRIPTOR_INIT_BUFFER( 215 | &memoryDescriptor, 216 | (PVOID)buffer, 217 | length); 218 | } 219 | 220 | // 221 | // Transaction starts by specifying the address bytes 222 | // 223 | RtlCopyMemory(buffer, &Address, sizeof(Address)); 224 | 225 | // 226 | // Address is followed by the data payload 227 | // 228 | RtlCopyMemory((buffer + sizeof(Address)), Data, length - sizeof(Address)); 229 | 230 | status = WdfIoTargetSendWriteSynchronously( 231 | SpbContext->SpbIoTarget, 232 | NULL, 233 | &memoryDescriptor, 234 | NULL, 235 | NULL, 236 | NULL); 237 | 238 | if (!NT_SUCCESS(status)) 239 | { 240 | ElanPrint( 241 | DEBUG_LEVEL_ERROR, 242 | DBG_IOCTL, 243 | "Error writing to Spb - %!STATUS!", 244 | status); 245 | goto exit; 246 | } 247 | 248 | exit: 249 | 250 | if (NULL != memory) 251 | { 252 | WdfObjectDelete(memory); 253 | } 254 | 255 | return status; 256 | } 257 | 258 | NTSTATUS 259 | SpbWriteDataSynchronously( 260 | IN SPB_CONTEXT *SpbContext, 261 | IN UCHAR Address, 262 | IN PVOID Data, 263 | IN ULONG Length 264 | ) 265 | /*++ 266 | 267 | Routine Description: 268 | 269 | This routine abstracts creating and sending an I/O 270 | request (I2C Write) to the Spb I/O target and utilizes 271 | a helper routine to do work inside of locked code. 272 | 273 | Arguments: 274 | 275 | SpbContext - Pointer to the current device context 276 | Address - The I2C register address to write to 277 | Data - A buffer to receive the data at at the above address 278 | Length - The amount of data to be read from the above address 279 | 280 | Return Value: 281 | 282 | NTSTATUS Status indicating success or failure 283 | 284 | --*/ 285 | { 286 | NTSTATUS status; 287 | 288 | WdfWaitLockAcquire(SpbContext->SpbLock, NULL); 289 | 290 | status = SpbDoWriteDataSynchronously( 291 | SpbContext, 292 | Address, 293 | Data, 294 | Length); 295 | 296 | WdfWaitLockRelease(SpbContext->SpbLock); 297 | 298 | return status; 299 | } 300 | 301 | NTSTATUS 302 | SpbWriteDataSynchronously16( 303 | IN SPB_CONTEXT *SpbContext, 304 | IN UINT16 Address, 305 | IN PVOID Data, 306 | IN ULONG Length 307 | ) 308 | /*++ 309 | 310 | Routine Description: 311 | 312 | This routine abstracts creating and sending an I/O 313 | request (I2C Write) to the Spb I/O target and utilizes 314 | a helper routine to do work inside of locked code. 315 | 316 | Arguments: 317 | 318 | SpbContext - Pointer to the current device context 319 | Address - The I2C register address to write to 320 | Data - A buffer to receive the data at at the above address 321 | Length - The amount of data to be read from the above address 322 | 323 | Return Value: 324 | 325 | NTSTATUS Status indicating success or failure 326 | 327 | --*/ 328 | { 329 | NTSTATUS status; 330 | 331 | WdfWaitLockAcquire(SpbContext->SpbLock, NULL); 332 | 333 | status = SpbDoWriteDataSynchronously16( 334 | SpbContext, 335 | Address, 336 | Data, 337 | Length); 338 | 339 | WdfWaitLockRelease(SpbContext->SpbLock); 340 | 341 | return status; 342 | } 343 | 344 | NTSTATUS 345 | SpbReadDataSynchronously( 346 | _In_ SPB_CONTEXT *SpbContext, 347 | _In_ UCHAR Address, 348 | _In_reads_bytes_(Length) PVOID Data, 349 | _In_ ULONG Length 350 | ) 351 | /*++ 352 | 353 | Routine Description: 354 | 355 | This helper routine abstracts creating and sending an I/O 356 | request (I2C Read) to the Spb I/O target. 357 | 358 | Arguments: 359 | 360 | SpbContext - Pointer to the current device context 361 | Address - The I2C register address to read from 362 | Data - A buffer to receive the data at at the above address 363 | Length - The amount of data to be read from the above address 364 | 365 | Return Value: 366 | 367 | NTSTATUS Status indicating success or failure 368 | 369 | --*/ 370 | { 371 | PUCHAR buffer; 372 | WDFMEMORY memory; 373 | WDF_MEMORY_DESCRIPTOR memoryDescriptor; 374 | NTSTATUS status; 375 | ULONG_PTR bytesRead; 376 | 377 | WdfWaitLockAcquire(SpbContext->SpbLock, NULL); 378 | 379 | memory = NULL; 380 | status = STATUS_INVALID_PARAMETER; 381 | bytesRead = 0; 382 | 383 | // 384 | // Read transactions start by writing an address pointer 385 | // 386 | status = SpbDoWriteDataSynchronously( 387 | SpbContext, 388 | Address, 389 | NULL, 390 | 0); 391 | 392 | if (!NT_SUCCESS(status)) 393 | { 394 | ElanPrint( 395 | DEBUG_LEVEL_ERROR, 396 | DBG_IOCTL, 397 | "Error setting address pointer for Spb read - %!STATUS!", 398 | status); 399 | goto exit; 400 | } 401 | 402 | if (Length > DEFAULT_SPB_BUFFER_SIZE) 403 | { 404 | status = WdfMemoryCreate( 405 | WDF_NO_OBJECT_ATTRIBUTES, 406 | NonPagedPool, 407 | CYAPA_POOL_TAG, 408 | Length, 409 | &memory, 410 | (PVOID *)&buffer); 411 | 412 | if (!NT_SUCCESS(status)) 413 | { 414 | ElanPrint( 415 | DEBUG_LEVEL_ERROR, 416 | DBG_IOCTL, 417 | "Error allocating memory for Spb read - %!STATUS!", 418 | status); 419 | goto exit; 420 | } 421 | 422 | WDF_MEMORY_DESCRIPTOR_INIT_HANDLE( 423 | &memoryDescriptor, 424 | memory, 425 | NULL); 426 | } 427 | else 428 | { 429 | buffer = (PUCHAR)WdfMemoryGetBuffer(SpbContext->ReadMemory, NULL); 430 | 431 | WDF_MEMORY_DESCRIPTOR_INIT_BUFFER( 432 | &memoryDescriptor, 433 | (PVOID)buffer, 434 | Length); 435 | } 436 | 437 | 438 | status = WdfIoTargetSendReadSynchronously( 439 | SpbContext->SpbIoTarget, 440 | NULL, 441 | &memoryDescriptor, 442 | NULL, 443 | NULL, 444 | &bytesRead); 445 | 446 | if (!NT_SUCCESS(status) || 447 | bytesRead != Length) 448 | { 449 | ElanPrint( 450 | DEBUG_LEVEL_ERROR, 451 | DBG_IOCTL, 452 | "Error reading from Spb - %!STATUS!", 453 | status); 454 | goto exit; 455 | } 456 | 457 | // 458 | // Copy back to the caller's buffer 459 | // 460 | RtlCopyMemory(Data, buffer, Length); 461 | 462 | exit: 463 | if (NULL != memory) 464 | { 465 | WdfObjectDelete(memory); 466 | } 467 | 468 | WdfWaitLockRelease(SpbContext->SpbLock); 469 | 470 | return status; 471 | } 472 | 473 | NTSTATUS 474 | SpbReadDataSynchronously16( 475 | _In_ SPB_CONTEXT *SpbContext, 476 | _In_ UINT16 Address, 477 | _In_reads_bytes_(Length) PVOID Data, 478 | _In_ ULONG Length 479 | ) 480 | /*++ 481 | 482 | Routine Description: 483 | 484 | This helper routine abstracts creating and sending an I/O 485 | request (I2C Read) to the Spb I/O target. 486 | 487 | Arguments: 488 | 489 | SpbContext - Pointer to the current device context 490 | Address - The I2C register address to read from 491 | Data - A buffer to receive the data at at the above address 492 | Length - The amount of data to be read from the above address 493 | 494 | Return Value: 495 | 496 | NTSTATUS Status indicating success or failure 497 | 498 | --*/ 499 | { 500 | PUCHAR buffer; 501 | WDFMEMORY memory; 502 | WDF_MEMORY_DESCRIPTOR memoryDescriptor; 503 | NTSTATUS status; 504 | ULONG_PTR bytesRead; 505 | 506 | WdfWaitLockAcquire(SpbContext->SpbLock, NULL); 507 | 508 | memory = NULL; 509 | status = STATUS_INVALID_PARAMETER; 510 | bytesRead = 0; 511 | 512 | // 513 | // Read transactions start by writing an address pointer 514 | // 515 | status = SpbDoWriteDataSynchronously16( 516 | SpbContext, 517 | Address, 518 | NULL, 519 | 0); 520 | 521 | if (!NT_SUCCESS(status)) 522 | { 523 | ElanPrint( 524 | DEBUG_LEVEL_ERROR, 525 | DBG_IOCTL, 526 | "Error setting address pointer for Spb read - %!STATUS!", 527 | status); 528 | goto exit; 529 | } 530 | 531 | if (Length > DEFAULT_SPB_BUFFER_SIZE) 532 | { 533 | status = WdfMemoryCreate( 534 | WDF_NO_OBJECT_ATTRIBUTES, 535 | NonPagedPool, 536 | CYAPA_POOL_TAG, 537 | Length, 538 | &memory, 539 | (PVOID *)&buffer); 540 | 541 | if (!NT_SUCCESS(status)) 542 | { 543 | ElanPrint( 544 | DEBUG_LEVEL_ERROR, 545 | DBG_IOCTL, 546 | "Error allocating memory for Spb read - %!STATUS!", 547 | status); 548 | goto exit; 549 | } 550 | 551 | WDF_MEMORY_DESCRIPTOR_INIT_HANDLE( 552 | &memoryDescriptor, 553 | memory, 554 | NULL); 555 | } 556 | else 557 | { 558 | buffer = (PUCHAR)WdfMemoryGetBuffer(SpbContext->ReadMemory, NULL); 559 | 560 | WDF_MEMORY_DESCRIPTOR_INIT_BUFFER( 561 | &memoryDescriptor, 562 | (PVOID)buffer, 563 | Length); 564 | } 565 | 566 | 567 | status = WdfIoTargetSendReadSynchronously( 568 | SpbContext->SpbIoTarget, 569 | NULL, 570 | &memoryDescriptor, 571 | NULL, 572 | NULL, 573 | &bytesRead); 574 | 575 | if (!NT_SUCCESS(status) || 576 | bytesRead != Length) 577 | { 578 | ElanPrint( 579 | DEBUG_LEVEL_ERROR, 580 | DBG_IOCTL, 581 | "Error reading from Spb - %!STATUS!", 582 | status); 583 | goto exit; 584 | } 585 | 586 | // 587 | // Copy back to the caller's buffer 588 | // 589 | RtlCopyMemory(Data, buffer, Length); 590 | 591 | exit: 592 | if (NULL != memory) 593 | { 594 | WdfObjectDelete(memory); 595 | } 596 | 597 | WdfWaitLockRelease(SpbContext->SpbLock); 598 | 599 | return status; 600 | } 601 | 602 | VOID 603 | SpbTargetDeinitialize( 604 | IN WDFDEVICE FxDevice, 605 | IN SPB_CONTEXT *SpbContext 606 | ) 607 | /*++ 608 | 609 | Routine Description: 610 | 611 | This helper routine is used to free any members added to the SPB_CONTEXT, 612 | note the SPB I/O target is parented to the device and will be 613 | closed and free'd when the device is removed. 614 | 615 | Arguments: 616 | 617 | FxDevice - Handle to the framework device object 618 | SpbContext - Pointer to the current device context 619 | 620 | Return Value: 621 | 622 | NTSTATUS Status indicating success or failure 623 | 624 | --*/ 625 | { 626 | UNREFERENCED_PARAMETER(FxDevice); 627 | UNREFERENCED_PARAMETER(SpbContext); 628 | 629 | // 630 | // Free any SPB_CONTEXT allocations here 631 | // 632 | if (SpbContext->SpbLock != NULL) 633 | { 634 | WdfObjectDelete(SpbContext->SpbLock); 635 | } 636 | 637 | if (SpbContext->ReadMemory != NULL) 638 | { 639 | WdfObjectDelete(SpbContext->ReadMemory); 640 | } 641 | 642 | if (SpbContext->WriteMemory != NULL) 643 | { 644 | WdfObjectDelete(SpbContext->WriteMemory); 645 | } 646 | } 647 | 648 | NTSTATUS 649 | SpbTargetInitialize( 650 | IN WDFDEVICE FxDevice, 651 | IN SPB_CONTEXT *SpbContext 652 | ) 653 | /*++ 654 | 655 | Routine Description: 656 | 657 | This helper routine opens the Spb I/O target and 658 | initializes a request object used for the lifetime 659 | of communication between this driver and Spb. 660 | 661 | Arguments: 662 | 663 | FxDevice - Handle to the framework device object 664 | SpbContext - Pointer to the current device context 665 | 666 | Return Value: 667 | 668 | NTSTATUS Status indicating success or failure 669 | 670 | --*/ 671 | { 672 | WDF_OBJECT_ATTRIBUTES objectAttributes; 673 | WDF_IO_TARGET_OPEN_PARAMS openParams; 674 | UNICODE_STRING spbDeviceName; 675 | WCHAR spbDeviceNameBuffer[RESOURCE_HUB_PATH_SIZE]; 676 | NTSTATUS status; 677 | 678 | WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); 679 | objectAttributes.ParentObject = FxDevice; 680 | 681 | status = WdfIoTargetCreate( 682 | FxDevice, 683 | &objectAttributes, 684 | &SpbContext->SpbIoTarget); 685 | 686 | if (!NT_SUCCESS(status)) 687 | { 688 | ElanPrint( 689 | DEBUG_LEVEL_ERROR, 690 | DBG_IOCTL, 691 | "Error creating IoTarget object - %!STATUS!", 692 | status); 693 | 694 | WdfObjectDelete(SpbContext->SpbIoTarget); 695 | goto exit; 696 | } 697 | 698 | RtlInitEmptyUnicodeString( 699 | &spbDeviceName, 700 | spbDeviceNameBuffer, 701 | sizeof(spbDeviceNameBuffer)); 702 | 703 | status = RESOURCE_HUB_CREATE_PATH_FROM_ID( 704 | &spbDeviceName, 705 | SpbContext->I2cResHubId.LowPart, 706 | SpbContext->I2cResHubId.HighPart); 707 | 708 | if (!NT_SUCCESS(status)) 709 | { 710 | ElanPrint( 711 | DEBUG_LEVEL_ERROR, 712 | DBG_IOCTL, 713 | "Error creating Spb resource hub path string - %!STATUS!", 714 | status); 715 | goto exit; 716 | } 717 | 718 | WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( 719 | &openParams, 720 | &spbDeviceName, 721 | (GENERIC_READ | GENERIC_WRITE)); 722 | 723 | openParams.ShareAccess = 0; 724 | openParams.CreateDisposition = FILE_OPEN; 725 | openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL; 726 | 727 | status = WdfIoTargetOpen(SpbContext->SpbIoTarget, &openParams); 728 | 729 | if (!NT_SUCCESS(status)) 730 | { 731 | ElanPrint( 732 | DEBUG_LEVEL_ERROR, 733 | DBG_IOCTL, 734 | "Error opening Spb target for communication - %!STATUS!", 735 | status); 736 | goto exit; 737 | } 738 | 739 | // 740 | // Allocate some fixed-size buffers from NonPagedPool for typical 741 | // Spb transaction sizes to avoid pool fragmentation in most cases 742 | // 743 | status = WdfMemoryCreate( 744 | WDF_NO_OBJECT_ATTRIBUTES, 745 | NonPagedPool, 746 | CYAPA_POOL_TAG, 747 | DEFAULT_SPB_BUFFER_SIZE, 748 | &SpbContext->WriteMemory, 749 | NULL); 750 | 751 | if (!NT_SUCCESS(status)) 752 | { 753 | ElanPrint( 754 | DEBUG_LEVEL_ERROR, 755 | DBG_IOCTL, 756 | "Error allocating default memory for Spb write - %!STATUS!", 757 | status); 758 | goto exit; 759 | } 760 | 761 | status = WdfMemoryCreate( 762 | WDF_NO_OBJECT_ATTRIBUTES, 763 | NonPagedPool, 764 | CYAPA_POOL_TAG, 765 | DEFAULT_SPB_BUFFER_SIZE, 766 | &SpbContext->ReadMemory, 767 | NULL); 768 | 769 | if (!NT_SUCCESS(status)) 770 | { 771 | ElanPrint( 772 | DEBUG_LEVEL_ERROR, 773 | DBG_IOCTL, 774 | "Error allocating default memory for Spb read - %!STATUS!", 775 | status); 776 | goto exit; 777 | } 778 | 779 | // 780 | // Allocate a waitlock to guard access to the default buffers 781 | // 782 | status = WdfWaitLockCreate( 783 | WDF_NO_OBJECT_ATTRIBUTES, 784 | &SpbContext->SpbLock); 785 | 786 | if (!NT_SUCCESS(status)) 787 | { 788 | ElanPrint( 789 | DEBUG_LEVEL_ERROR, 790 | DBG_IOCTL, 791 | "Error creating Spb Waitlock - %!STATUS!", 792 | status); 793 | goto exit; 794 | } 795 | 796 | exit: 797 | 798 | if (!NT_SUCCESS(status)) 799 | { 800 | SpbTargetDeinitialize(FxDevice, SpbContext); 801 | } 802 | 803 | return status; 804 | } -------------------------------------------------------------------------------- /max98090 Package/max98090 Package.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Win8.1 Debug 6 | Win32 7 | 8 | 9 | Win8.1 Release 10 | Win32 11 | 12 | 13 | Win8 Debug 14 | Win32 15 | 16 | 17 | Win8 Release 18 | Win32 19 | 20 | 21 | Win7 Debug 22 | Win32 23 | 24 | 25 | Win7 Release 26 | Win32 27 | 28 | 29 | Win8.1 Debug 30 | x64 31 | 32 | 33 | Win8.1 Release 34 | x64 35 | 36 | 37 | Win8 Debug 38 | x64 39 | 40 | 41 | Win8 Release 42 | x64 43 | 44 | 45 | Win7 Debug 46 | x64 47 | 48 | 49 | Win7 Release 50 | x64 51 | 52 | 53 | 54 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B} 55 | {4605da2c-74a5-4865-98e1-152ef136825f} 56 | v4.5 57 | 11.0 58 | Win8.1 Debug 59 | Win32 60 | max98090_Package 61 | 10.0.22000.0 62 | max98090 Package 63 | 64 | 65 | 66 | WindowsV6.3 67 | true 68 | WindowsKernelModeDriver10.0 69 | Utility 70 | Package 71 | true 72 | 73 | 74 | WindowsV6.3 75 | false 76 | WindowsKernelModeDriver10.0 77 | Utility 78 | Package 79 | true 80 | 81 | 82 | Windows8 83 | true 84 | WindowsKernelModeDriver10.0 85 | Utility 86 | Package 87 | true 88 | 89 | 90 | Windows8 91 | false 92 | WindowsKernelModeDriver10.0 93 | Utility 94 | Package 95 | true 96 | 97 | 98 | Windows7 99 | true 100 | WindowsKernelModeDriver10.0 101 | Utility 102 | Package 103 | true 104 | 105 | 106 | Windows7 107 | false 108 | WindowsKernelModeDriver10.0 109 | Utility 110 | Package 111 | true 112 | 113 | 114 | WindowsV6.3 115 | true 116 | WindowsKernelModeDriver10.0 117 | Utility 118 | Package 119 | true 120 | 121 | 122 | WindowsV6.3 123 | false 124 | WindowsKernelModeDriver10.0 125 | Utility 126 | Package 127 | true 128 | 129 | 130 | Windows8 131 | true 132 | WindowsKernelModeDriver10.0 133 | Utility 134 | Package 135 | true 136 | 137 | 138 | Windows8 139 | false 140 | WindowsKernelModeDriver10.0 141 | Utility 142 | Package 143 | true 144 | 145 | 146 | Windows7 147 | true 148 | WindowsKernelModeDriver10.0 149 | Utility 150 | Package 151 | true 152 | 153 | 154 | Windows7 155 | false 156 | WindowsKernelModeDriver10.0 157 | Utility 158 | Package 159 | true 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | DbgengKernelDebugger 171 | False 172 | True 173 | 174 | 175 | 176 | False 177 | False 178 | True 179 | 180 | 133563 181 | 182 | 183 | DbgengKernelDebugger 184 | False 185 | True 186 | 187 | 188 | 189 | False 190 | False 191 | True 192 | 193 | 133563 194 | 195 | 196 | DbgengKernelDebugger 197 | False 198 | True 199 | 200 | 201 | 202 | False 203 | False 204 | True 205 | 206 | 133563 207 | 208 | 209 | DbgengKernelDebugger 210 | False 211 | True 212 | 213 | 214 | 215 | False 216 | False 217 | True 218 | 219 | 133563 220 | 221 | 222 | DbgengKernelDebugger 223 | False 224 | True 225 | 226 | 227 | 228 | False 229 | False 230 | True 231 | 232 | 133563 233 | 234 | 235 | DbgengKernelDebugger 236 | False 237 | True 238 | 239 | 240 | 241 | False 242 | False 243 | True 244 | 245 | 133563 246 | 247 | 248 | DbgengKernelDebugger 249 | False 250 | True 251 | 252 | 253 | 254 | False 255 | False 256 | True 257 | 258 | 133563 259 | 260 | 261 | DbgengKernelDebugger 262 | False 263 | True 264 | 265 | 266 | 267 | False 268 | False 269 | True 270 | 271 | 133563 272 | 273 | 274 | DbgengKernelDebugger 275 | False 276 | True 277 | 278 | 279 | 280 | False 281 | False 282 | True 283 | 284 | 133563 285 | 286 | 287 | DbgengKernelDebugger 288 | False 289 | True 290 | 291 | 292 | 293 | False 294 | False 295 | True 296 | 297 | 133563 298 | 299 | 300 | DbgengKernelDebugger 301 | False 302 | True 303 | 304 | 305 | 306 | False 307 | False 308 | True 309 | 310 | 133563 311 | 312 | 313 | DbgengKernelDebugger 314 | False 315 | True 316 | 317 | 318 | 319 | False 320 | False 321 | True 322 | 323 | 133563 324 | 325 | 326 | 327 | SHA256 328 | 329 | 330 | 331 | 332 | SHA256 333 | 334 | 335 | 336 | 337 | SHA256 338 | 339 | 340 | 341 | 342 | SHA256 343 | 344 | 345 | 346 | 347 | SHA256 348 | 349 | 350 | 351 | 352 | SHA256 353 | 354 | 355 | 356 | 357 | SHA256 358 | 359 | 360 | 361 | 362 | SHA256 363 | 364 | 365 | 366 | 367 | SHA256 368 | 369 | 370 | 371 | 372 | SHA256 373 | 374 | 375 | 376 | 377 | SHA256 378 | 379 | 380 | 381 | 382 | SHA256 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | {b3e71397-9be4-492b-aaed-4d056e59cb1f} 391 | 392 | 393 | 394 | 395 | 396 | -------------------------------------------------------------------------------- /max98090/max98090.c: -------------------------------------------------------------------------------- 1 | #define DESCRIPTOR_DEF 2 | #include "driver.h" 3 | #include "stdint.h" 4 | 5 | #define bool int 6 | #define MHz 1000000 7 | 8 | static ULONG MaxmDebugLevel = 100; 9 | static ULONG MaxmDebugCatagories = DBG_INIT || DBG_PNP || DBG_IOCTL; 10 | 11 | NTSTATUS 12 | DriverEntry( 13 | __in PDRIVER_OBJECT DriverObject, 14 | __in PUNICODE_STRING RegistryPath 15 | ) 16 | { 17 | NTSTATUS status = STATUS_SUCCESS; 18 | WDF_DRIVER_CONFIG config; 19 | WDF_OBJECT_ATTRIBUTES attributes; 20 | 21 | MaxmPrint(DEBUG_LEVEL_INFO, DBG_INIT, 22 | "Driver Entry\n"); 23 | 24 | WDF_DRIVER_CONFIG_INIT(&config, MaxmEvtDeviceAdd); 25 | 26 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 27 | 28 | // 29 | // Create a framework driver object to represent our driver. 30 | // 31 | 32 | status = WdfDriverCreate(DriverObject, 33 | RegistryPath, 34 | &attributes, 35 | &config, 36 | WDF_NO_HANDLE 37 | ); 38 | 39 | if (!NT_SUCCESS(status)) 40 | { 41 | MaxmPrint(DEBUG_LEVEL_ERROR, DBG_INIT, 42 | "WdfDriverCreate failed with status 0x%x\n", status); 43 | } 44 | 45 | return status; 46 | } 47 | 48 | static int max98090_i2c_write(PMAXM_CONTEXT pDevice, uint8_t reg, uint8_t data) 49 | { 50 | uint8_t rawdata[1]; 51 | rawdata[0] = data; 52 | SpbWriteDataSynchronously(&pDevice->I2CContext, reg, rawdata, 1); 53 | return 0; 54 | } 55 | 56 | static int max98090_i2c_read(PMAXM_CONTEXT pDevice, uint8_t reg, uint8_t *data) 57 | { 58 | uint8_t rawdata[256]; 59 | SpbReadDataSynchronously(&pDevice->I2CContext, 0, rawdata, 256); 60 | *data = rawdata[reg]; 61 | return 0; 62 | } 63 | 64 | static int max98090_update_bits(PMAXM_CONTEXT pDevice, uint8_t reg, 65 | uint8_t mask, uint8_t value) 66 | { 67 | uint8_t old; 68 | if (max98090_i2c_read(pDevice, reg, &old)) 69 | return 1; 70 | uint8_t new_value = (old & ~mask) | (value & mask); 71 | if (old != new_value && max98090_i2c_write(pDevice, reg, new_value)) 72 | return 1; 73 | 74 | return 0; 75 | } 76 | 77 | // Resets the audio codec. 78 | static int max98090_reset(PMAXM_CONTEXT codec) 79 | { 80 | // Gracefully reset the DSP core and the codec hardware in a proper 81 | // sequence. 82 | if (max98090_i2c_write(codec, M98090_REG_SOFTWARE_RESET, 83 | M98090_SWRESET_MASK)) 84 | return 1; 85 | return 0; 86 | } 87 | 88 | static int max98090_hw_params(PMAXM_CONTEXT codec) 89 | { 90 | unsigned char value; 91 | unsigned int sample_rate = 48000; 92 | unsigned int bits_per_sample = 24; 93 | 94 | switch (bits_per_sample) { 95 | case 16: 96 | max98090_i2c_read(codec, M98090_REG_INTERFACE_FORMAT, &value); 97 | if (max98090_update_bits(codec, M98090_REG_INTERFACE_FORMAT, 98 | M98090_WS_MASK, 0)) 99 | return 1; 100 | max98090_i2c_read(codec, M98090_REG_INTERFACE_FORMAT, &value); 101 | break; 102 | case 24: 103 | break; 104 | default: 105 | MaxmPrint(DEBUG_LEVEL_ERROR, 106 | DBG_IOCTL, "Illegal bits per sample %d.\n", 107 | bits_per_sample); 108 | break; 109 | } 110 | 111 | max98090_i2c_write(codec, M98090_REG_FILTER_CONFIG, M98090_MODE_MASK | M98090_AHPF_MASK | M98090_DHPF_MASK); 112 | return 0; 113 | } 114 | 115 | // Configures audio interface system clock for the selected frequency. 116 | static int max98090_set_sysclk(PMAXM_CONTEXT codec) 117 | { 118 | int freq = 512 /*256*/ /*lr_frame_size*/ * 48000 /* sample rate */; 119 | // TODO(hungte) Should we handle codec->master_clock? 120 | uint8_t mclksel = 0; 121 | 122 | /* 123 | * Setup clocks for slave mode, and using the PLL 124 | * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) 125 | * 0x02 (when master clk is 20MHz to 40MHz).. 126 | * 0x03 (when master clk is 40MHz to 60MHz).. 127 | */ 128 | if ((freq >= 10 * MHz) && (freq < 20 * MHz)) { 129 | mclksel |= M98090_PSCLK_DIV1; 130 | } 131 | else if ((freq >= 20 * MHz) && (freq < 40 * MHz)) { 132 | mclksel |= M98090_PSCLK_DIV2; 133 | } 134 | else if ((freq >= 40 * MHz) && (freq < 60 * MHz)) { 135 | mclksel |= M98090_PSCLK_DIV4; 136 | } 137 | else { 138 | MaxmPrint(DEBUG_LEVEL_ERROR, 139 | DBG_IOCTL, "Invalid master clock frequency\n"); 140 | return 1; 141 | } 142 | 143 | if (max98090_i2c_write(codec, M98090_REG_SYSTEM_CLOCK, mclksel)) 144 | return 1; 145 | 146 | MaxmPrint(DEBUG_LEVEL_ERROR, 147 | DBG_IOCTL, "Clock at %uHz\n", freq); 148 | return 0; 149 | } 150 | 151 | // Sets Max98090 I2S format. 152 | static int max98090_set_fmt(PMAXM_CONTEXT codec) 153 | { 154 | // Set to slave mode PLL - MAS mode off. 155 | if (max98090_i2c_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB, 0x00) || 156 | max98090_i2c_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB, 0x00)) 157 | return 1; 158 | 159 | if (max98090_update_bits(codec, M98090_REG_CLOCK_MODE, 160 | M98090_USE_M1_MASK, 0)) 161 | return 1; 162 | 163 | if (max98090_i2c_write(codec, M98090_REG_MASTER_MODE, 0)) 164 | return 1; 165 | 166 | // Format: I2S, IB_IF. 167 | if (max98090_i2c_write(codec, M98090_REG_INTERFACE_FORMAT, 168 | M98090_DLY_MASK)) 169 | return 1; 170 | 171 | return 0; 172 | } 173 | 174 | int max98090_device_init(PMAXM_CONTEXT codec) 175 | { 176 | // Reset the codec, the DSP core, and disable all interrupts. 177 | 178 | 179 | uint8_t id; 180 | if (max98090_i2c_read(codec, M98090_REG_REVISION_ID, &id)) 181 | return 1; 182 | DbgPrint("Maxim Hardware revision: 0x%x\n", id); 183 | 184 | max98090_i2c_write(codec, M98090_REG_DEVICE_SHUTDOWN, 185 | 0x00); //Shutdown the codec to allow setup 186 | 187 | /* Reading interrupt status to clear them */ 188 | int res = 0; 189 | res = max98090_i2c_read(codec, M98090_REG_DEVICE_STATUS, &id); 190 | 191 | res |= max98090_i2c_write(codec, M98090_REG_DAC_CONTROL, 192 | M98090_DACHP_MASK | M98090_PERFMODE_MASK); 193 | res |= max98090_i2c_write(codec, M98090_REG_ADC_CONTROL, M98090_ADCHP_MASK | M98090_ADCDITHER_MASK); 194 | res |= max98090_i2c_write(codec, M98090_REG_BIAS_CONTROL, 195 | M98090_VCM_MODE_MASK); 196 | 197 | res |= max98090_i2c_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB, 0x0); 198 | res |= max98090_i2c_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB, 0x0); 199 | res |= max98090_i2c_write(codec, M98090_REG_MASTER_MODE, 0x0); 200 | res |= max98090_i2c_write(codec, M98090_REG_INTERFACE_FORMAT, 0x0); 201 | res |= max98090_i2c_write(codec, M98090_REG_IO_CONFIGURATION, 202 | M98090_SDIEN_MASK); 203 | if (codec->HeadphonesConnected) { 204 | res |= max98090_i2c_write(codec, M98090_REG_OUTPUT_ENABLE, 205 | M98090_HPREN_MASK | M98090_HPLEN_MASK | 206 | M98090_DAREN_MASK | M98090_DALEN_MASK); 207 | } 208 | else { 209 | res |= max98090_i2c_write(codec, M98090_REG_OUTPUT_ENABLE, 210 | M98090_SPREN_MASK | M98090_SPLEN_MASK | 211 | M98090_DAREN_MASK | M98090_DALEN_MASK); 212 | } 213 | 214 | //IO Config 215 | 216 | res |= max98090_i2c_write(codec, M98090_REG_IO_CONFIGURATION, 217 | M98090_SDOEN_MASK | M98090_SDIEN_MASK); 218 | } 219 | 220 | int max98090_set_output(PMAXM_CONTEXT codec) { 221 | int res = 0; 222 | //Headphone Mixer Configuration 223 | res |= max98090_i2c_write(codec, M98090_REG_LEFT_HP_VOLUME, 0x1F); 224 | res |= max98090_i2c_write(codec, M98090_REG_RIGHT_HP_VOLUME, 0x1F); 225 | 226 | res |= max98090_i2c_write(codec, M98090_REG_LEFT_HP_MIXER, M98090_MIXHPL_DACL_MASK); 227 | res |= max98090_i2c_write(codec, M98090_REG_RIGHT_HP_MIXER, M98090_MIXHPR_DACR_MASK); 228 | 229 | res |= max98090_i2c_write(codec, M98090_REG_HP_CONTROL, 0x30); 230 | 231 | //Speaker Mixer Configuration 232 | res |= max98090_i2c_write(codec, M98090_REG_LEFT_SPK_VOLUME, 0x3F); 233 | res |= max98090_i2c_write(codec, M98090_REG_RIGHT_SPK_VOLUME, 0x3F); 234 | 235 | res |= max98090_i2c_write(codec, M98090_REG_LEFT_SPK_MIXER, M98090_MIXSPL_DACL_MASK); 236 | res |= max98090_i2c_write(codec, M98090_REG_RIGHT_SPK_MIXER, M98090_MIXSPR_DACR_MASK); 237 | 238 | res |= max98090_i2c_write(codec, M98090_REG_SPK_CONTROL, 0x0f); //gain config -12 db 239 | 240 | 241 | //Input config 242 | 243 | MaxmPrint(DEBUG_LEVEL_ERROR, 244 | DBG_IOCTL, "Input Configuration"); 245 | 246 | //Internal Mic 247 | 248 | res |= max98090_i2c_write(codec, M98090_REG_MIC1_INPUT_LEVEL, 0x14); 249 | 250 | res |= max98090_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE, M98090_MBVSEL_MASK, M98090_MBVSEL_2V8); 251 | 252 | res |= max98090_i2c_write(codec, M98090_REG_DIGITAL_MIC_CONFIG, 0x60); 253 | 254 | res |= max98090_i2c_write(codec, M98090_REG_LEFT_ADC_MIXER, M98090_MIXADL_MIC2_MASK); 255 | res |= max98090_i2c_write(codec, M98090_REG_RIGHT_ADC_MIXER, M98090_MIXADR_MIC2_MASK); 256 | 257 | res |= max98090_i2c_write(codec, M98090_REG_LEFT_ADC_LEVEL, (0x4 << M98090_AVLG_SHIFT) | 0x4); 258 | res |= max98090_i2c_write(codec, M98090_REG_RIGHT_ADC_LEVEL, (0x4 << M98090_AVLG_SHIFT) | 0x4); 259 | 260 | if (codec->HeadsetMicConnected) { 261 | max98090_i2c_write(codec, M98090_REG_MIC2_INPUT_LEVEL, (1 << M98090_MIC_PA2EN_SHIFT) | 0x00); //Set PA2EN to 1 for Headset, 0 for internal mic 262 | max98090_i2c_write(codec, M98090_REG_DIGITAL_MIC_ENABLE, (3 << M98090_MICCLK_SHIFT) | 0); 263 | max98090_i2c_write(codec, M98090_REG_INPUT_ENABLE, M98090_ADLEN_MASK | M98090_ADREN_MASK | M98090_MBEN_MASK); 264 | } 265 | else { 266 | max98090_i2c_write(codec, M98090_REG_MIC2_INPUT_LEVEL, (0 << M98090_MIC_PA2EN_SHIFT) | 0x00); //Set PA2EN to 1 for Headset, 0 for internal mic 267 | max98090_i2c_write(codec, M98090_REG_DIGITAL_MIC_ENABLE, (3 << M98090_MICCLK_SHIFT) | M98090_DIGMICR_MASK | M98090_DIGMICL_MASK); 268 | max98090_i2c_write(codec, M98090_REG_INPUT_ENABLE, 0x0); //Set to M98090_ADLEN_MASK | M98090_ADREN_MASK | M98090_MBEN_MASK for headset, 0 for internal mic 269 | } 270 | 271 | return res; 272 | } 273 | 274 | void max98090_jack_detect(PMAXM_CONTEXT codec) { 275 | max98090_i2c_write(codec, M98090_REG_DEVICE_SHUTDOWN, 276 | M98090_SHDNN_MASK); 277 | 278 | max98090_i2c_write(codec, M98090_REG_JACK_DETECT, 279 | M98090_JDETEN_MASK | M98090_JDEB_25MS | M98090_JDWK_MASK); 280 | } 281 | 282 | VOID 283 | MaxmBootWorkItem( 284 | IN WDFWORKITEM WorkItem 285 | ) 286 | { 287 | WDFDEVICE Device = (WDFDEVICE)WdfWorkItemGetParentObject(WorkItem); 288 | PMAXM_CONTEXT pDevice = GetDeviceContext(Device); 289 | 290 | max98090_device_init(pDevice); 291 | max98090_set_sysclk(pDevice); 292 | max98090_hw_params(pDevice); 293 | max98090_set_fmt(pDevice); 294 | max98090_set_output(pDevice); 295 | max98090_jack_detect(pDevice); 296 | 297 | pDevice->ConnectInterrupt = true; 298 | 299 | WdfObjectDelete(WorkItem); 300 | } 301 | 302 | void MaxmBootTimer(_In_ WDFTIMER hTimer) { 303 | WDFDEVICE Device = (WDFDEVICE)WdfTimerGetParentObject(hTimer); 304 | PMAXM_CONTEXT pDevice = GetDeviceContext(Device); 305 | 306 | WDF_OBJECT_ATTRIBUTES attributes; 307 | WDF_WORKITEM_CONFIG workitemConfig; 308 | WDFWORKITEM hWorkItem; 309 | 310 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 311 | WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attributes, MAXM_CONTEXT); 312 | attributes.ParentObject = Device; 313 | WDF_WORKITEM_CONFIG_INIT(&workitemConfig, MaxmBootWorkItem); 314 | 315 | WdfWorkItemCreate(&workitemConfig, 316 | &attributes, 317 | &hWorkItem); 318 | 319 | WdfWorkItemEnqueue(hWorkItem); 320 | WdfTimerStop(hTimer, FALSE); 321 | } 322 | 323 | NTSTATUS BOOTCODEC( 324 | _In_ PMAXM_CONTEXT pDevice 325 | ) 326 | { 327 | NTSTATUS status = 0; 328 | 329 | pDevice->ConnectInterrupt = false; 330 | 331 | pDevice->HeadphonesConnected = false; 332 | 333 | if (max98090_reset(pDevice)) 334 | return 1; 335 | 336 | WDF_TIMER_CONFIG timerConfig; 337 | WDFTIMER hTimer; 338 | WDF_OBJECT_ATTRIBUTES attributes; 339 | 340 | WDF_TIMER_CONFIG_INIT(&timerConfig, MaxmBootTimer); 341 | 342 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 343 | attributes.ParentObject = pDevice->FxDevice; 344 | status = WdfTimerCreate(&timerConfig, &attributes, &hTimer); 345 | 346 | WdfTimerStart(hTimer, WDF_REL_TIMEOUT_IN_MS(20)); 347 | 348 | return status; 349 | } 350 | 351 | NTSTATUS 352 | OnPrepareHardware( 353 | _In_ WDFDEVICE FxDevice, 354 | _In_ WDFCMRESLIST FxResourcesRaw, 355 | _In_ WDFCMRESLIST FxResourcesTranslated 356 | ) 357 | /*++ 358 | 359 | Routine Description: 360 | 361 | This routine caches the SPB resource connection ID. 362 | 363 | Arguments: 364 | 365 | FxDevice - a handle to the framework device object 366 | FxResourcesRaw - list of translated hardware resources that 367 | the PnP manager has assigned to the device 368 | FxResourcesTranslated - list of raw hardware resources that 369 | the PnP manager has assigned to the device 370 | 371 | Return Value: 372 | 373 | Status 374 | 375 | --*/ 376 | { 377 | PMAXM_CONTEXT pDevice = GetDeviceContext(FxDevice); 378 | BOOLEAN fSpbResourceFound = FALSE; 379 | BOOLEAN fJackDetectResourceFound = FALSE; 380 | BOOLEAN fMicDetectResourceFound = FALSE; 381 | NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES; 382 | 383 | UNREFERENCED_PARAMETER(FxResourcesRaw); 384 | 385 | // 386 | // Parse the peripheral's resources. 387 | // 388 | 389 | ULONG resourceCount = WdfCmResourceListGetCount(FxResourcesTranslated); 390 | 391 | for (ULONG i = 0; i < resourceCount; i++) 392 | { 393 | PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor; 394 | UCHAR Class; 395 | UCHAR Type; 396 | 397 | pDescriptor = WdfCmResourceListGetDescriptor( 398 | FxResourcesTranslated, i); 399 | 400 | switch (pDescriptor->Type) 401 | { 402 | case CmResourceTypeConnection: 403 | // 404 | // Look for I2C or SPI resource and save connection ID. 405 | // 406 | Class = pDescriptor->u.Connection.Class; 407 | Type = pDescriptor->u.Connection.Type; 408 | if (Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL && 409 | Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C) 410 | { 411 | if (fSpbResourceFound == FALSE) 412 | { 413 | status = STATUS_SUCCESS; 414 | pDevice->I2CContext.I2cResHubId.LowPart = pDescriptor->u.Connection.IdLowPart; 415 | pDevice->I2CContext.I2cResHubId.HighPart = pDescriptor->u.Connection.IdHighPart; 416 | fSpbResourceFound = TRUE; 417 | } 418 | else 419 | { 420 | } 421 | } 422 | if (Class == CM_RESOURCE_CONNECTION_CLASS_GPIO && Type == CM_RESOURCE_CONNECTION_TYPE_GPIO_IO) { 423 | if (fJackDetectResourceFound == FALSE) { 424 | pDevice->GpioContext.GpioResHubId.LowPart = pDescriptor->u.Connection.IdLowPart; 425 | pDevice->GpioContext.GpioResHubId.HighPart = pDescriptor->u.Connection.IdHighPart; 426 | fJackDetectResourceFound = TRUE; 427 | } 428 | else if (fMicDetectResourceFound == FALSE) { 429 | pDevice->MicGpioContext.GpioResHubId.LowPart = pDescriptor->u.Connection.IdLowPart; 430 | pDevice->MicGpioContext.GpioResHubId.HighPart = pDescriptor->u.Connection.IdHighPart; 431 | fMicDetectResourceFound = TRUE; 432 | } 433 | } 434 | break; 435 | default: 436 | // 437 | // Ignoring all other resource types. 438 | // 439 | break; 440 | } 441 | } 442 | 443 | // 444 | // An SPB resource is required. 445 | // 446 | 447 | if (fSpbResourceFound == FALSE || fJackDetectResourceFound == FALSE || fMicDetectResourceFound == FALSE) 448 | { 449 | status = STATUS_NOT_FOUND; 450 | return status; 451 | } 452 | 453 | status = GpioTargetInitialize(FxDevice, &pDevice->GpioContext); 454 | if (!NT_SUCCESS(status)) 455 | { 456 | return status; 457 | } 458 | 459 | status = GpioTargetInitialize(FxDevice, &pDevice->MicGpioContext); 460 | if (!NT_SUCCESS(status)) 461 | { 462 | return status; 463 | } 464 | 465 | status = SpbTargetInitialize(FxDevice, &pDevice->I2CContext); 466 | if (!NT_SUCCESS(status)) 467 | { 468 | return status; 469 | } 470 | 471 | return status; 472 | } 473 | 474 | NTSTATUS 475 | OnReleaseHardware( 476 | _In_ WDFDEVICE FxDevice, 477 | _In_ WDFCMRESLIST FxResourcesTranslated 478 | ) 479 | /*++ 480 | 481 | Routine Description: 482 | 483 | Arguments: 484 | 485 | FxDevice - a handle to the framework device object 486 | FxResourcesTranslated - list of raw hardware resources that 487 | the PnP manager has assigned to the device 488 | 489 | Return Value: 490 | 491 | Status 492 | 493 | --*/ 494 | { 495 | PMAXM_CONTEXT pDevice = GetDeviceContext(FxDevice); 496 | NTSTATUS status = STATUS_SUCCESS; 497 | 498 | UNREFERENCED_PARAMETER(FxResourcesTranslated); 499 | 500 | /*IO_DISCONNECT_INTERRUPT_PARAMETERS params; 501 | 502 | params.Version = CONNECT_FULLY_SPECIFIED; 503 | params.ConnectionContext.InterruptObject = pDevice->Interrupt; 504 | 505 | IoDisconnectInterruptEx(¶ms);*/ 506 | 507 | GpioTargetDeinitialize(FxDevice, &pDevice->GpioContext); 508 | GpioTargetDeinitialize(FxDevice, &pDevice->MicGpioContext); 509 | SpbTargetDeinitialize(FxDevice, &pDevice->I2CContext); 510 | 511 | return status; 512 | } 513 | 514 | NTSTATUS 515 | OnD0Entry( 516 | _In_ WDFDEVICE FxDevice, 517 | _In_ WDF_POWER_DEVICE_STATE FxPreviousState 518 | ) 519 | /*++ 520 | 521 | Routine Description: 522 | 523 | This routine allocates objects needed by the driver. 524 | 525 | Arguments: 526 | 527 | FxDevice - a handle to the framework device object 528 | FxPreviousState - previous power state 529 | 530 | Return Value: 531 | 532 | Status 533 | 534 | --*/ 535 | { 536 | UNREFERENCED_PARAMETER(FxPreviousState); 537 | 538 | PMAXM_CONTEXT pDevice = GetDeviceContext(FxDevice); 539 | NTSTATUS status = STATUS_SUCCESS; 540 | 541 | WdfTimerStart(pDevice->Timer, WDF_REL_TIMEOUT_IN_MS(10)); 542 | 543 | BOOTCODEC(pDevice); 544 | 545 | return status; 546 | } 547 | 548 | NTSTATUS 549 | OnD0Exit( 550 | _In_ WDFDEVICE FxDevice, 551 | _In_ WDF_POWER_DEVICE_STATE FxPreviousState 552 | ) 553 | /*++ 554 | 555 | Routine Description: 556 | 557 | This routine destroys objects needed by the driver. 558 | 559 | Arguments: 560 | 561 | FxDevice - a handle to the framework device object 562 | FxPreviousState - previous power state 563 | 564 | Return Value: 565 | 566 | Status 567 | 568 | --*/ 569 | { 570 | UNREFERENCED_PARAMETER(FxPreviousState); 571 | 572 | PMAXM_CONTEXT pDevice = GetDeviceContext(FxDevice); 573 | 574 | WdfTimerStop(pDevice->Timer, TRUE); 575 | 576 | pDevice->ConnectInterrupt = false; 577 | 578 | return STATUS_SUCCESS; 579 | } 580 | 581 | VOID 582 | CodecJackSwitchWorkItem( 583 | IN WDFWORKITEM WorkItem 584 | ) 585 | { 586 | WDFDEVICE Device = (WDFDEVICE)WdfWorkItemGetParentObject(WorkItem); 587 | PMAXM_CONTEXT pDevice = GetDeviceContext(Device); 588 | 589 | BYTE gpioState; 590 | GpioReadDataSynchronously(&pDevice->GpioContext, &gpioState, sizeof(BYTE)); 591 | if (pDevice->HeadphonesConnected != gpioState) { 592 | pDevice->HeadphonesConnected = gpioState; 593 | 594 | if (pDevice->HeadphonesConnected) { 595 | max98090_i2c_write(pDevice, M98090_REG_OUTPUT_ENABLE, 596 | M98090_HPREN_MASK | M98090_HPLEN_MASK | 597 | M98090_DAREN_MASK | M98090_DALEN_MASK); 598 | } 599 | else { 600 | max98090_i2c_write(pDevice, M98090_REG_OUTPUT_ENABLE, 601 | M98090_SPREN_MASK | M98090_SPLEN_MASK | 602 | M98090_DAREN_MASK | M98090_DALEN_MASK); 603 | } 604 | } 605 | 606 | BYTE micGpioState = 0; 607 | GpioReadDataSynchronously(&pDevice->MicGpioContext, &micGpioState, sizeof(BYTE)); 608 | micGpioState = !micGpioState; 609 | if (pDevice->HeadsetMicConnected != micGpioState) { 610 | pDevice->HeadsetMicConnected = micGpioState; 611 | 612 | if (pDevice->HeadsetMicConnected) { 613 | max98090_i2c_write(pDevice, M98090_REG_MIC2_INPUT_LEVEL, (1 << M98090_MIC_PA2EN_SHIFT) | 0x00); //Set PA2EN to 1 for Headset, 0 for internal mic 614 | max98090_i2c_write(pDevice, M98090_REG_DIGITAL_MIC_ENABLE, (3 << M98090_MICCLK_SHIFT) | 0); 615 | max98090_i2c_write(pDevice, M98090_REG_INPUT_ENABLE, M98090_ADLEN_MASK | M98090_ADREN_MASK | M98090_MBEN_MASK); 616 | } 617 | else { 618 | max98090_i2c_write(pDevice, M98090_REG_MIC2_INPUT_LEVEL, (0 << M98090_MIC_PA2EN_SHIFT) | 0x00); //Set PA2EN to 1 for Headset, 0 for internal mic 619 | max98090_i2c_write(pDevice, M98090_REG_DIGITAL_MIC_ENABLE, (3 << M98090_MICCLK_SHIFT) | M98090_DIGMICR_MASK | M98090_DIGMICL_MASK); 620 | max98090_i2c_write(pDevice, M98090_REG_INPUT_ENABLE, 0x0); //Set to M98090_ADLEN_MASK | M98090_ADREN_MASK | M98090_MBEN_MASK for headset, 0 for internal mic 621 | } 622 | } 623 | 624 | WdfObjectDelete(WorkItem); 625 | } 626 | 627 | void MaxmJDetTimer(_In_ WDFTIMER hTimer) { 628 | WDFDEVICE Device = (WDFDEVICE)WdfTimerGetParentObject(hTimer); 629 | PMAXM_CONTEXT pDevice = GetDeviceContext(Device); 630 | 631 | if (!pDevice->ConnectInterrupt) 632 | return; 633 | 634 | WDF_OBJECT_ATTRIBUTES attributes; 635 | WDF_WORKITEM_CONFIG workitemConfig; 636 | WDFWORKITEM hWorkItem; 637 | 638 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 639 | WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attributes, MAXM_CONTEXT); 640 | attributes.ParentObject = Device; 641 | WDF_WORKITEM_CONFIG_INIT(&workitemConfig, CodecJackSwitchWorkItem); 642 | 643 | WdfWorkItemCreate(&workitemConfig, 644 | &attributes, 645 | &hWorkItem); 646 | 647 | WdfWorkItemEnqueue(hWorkItem); 648 | } 649 | 650 | NTSTATUS 651 | MaxmEvtDeviceAdd( 652 | IN WDFDRIVER Driver, 653 | IN PWDFDEVICE_INIT DeviceInit 654 | ) 655 | { 656 | NTSTATUS status = STATUS_SUCCESS; 657 | WDF_IO_QUEUE_CONFIG queueConfig; 658 | WDF_OBJECT_ATTRIBUTES attributes; 659 | WDFDEVICE device; 660 | WDF_INTERRUPT_CONFIG interruptConfig; 661 | WDFQUEUE queue; 662 | UCHAR minorFunction; 663 | PMAXM_CONTEXT devContext; 664 | 665 | UNREFERENCED_PARAMETER(Driver); 666 | 667 | PAGED_CODE(); 668 | 669 | MaxmPrint(DEBUG_LEVEL_INFO, DBG_PNP, 670 | "MaxmEvtDeviceAdd called\n"); 671 | 672 | { 673 | WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks; 674 | WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks); 675 | 676 | pnpCallbacks.EvtDevicePrepareHardware = OnPrepareHardware; 677 | pnpCallbacks.EvtDeviceReleaseHardware = OnReleaseHardware; 678 | pnpCallbacks.EvtDeviceD0Entry = OnD0Entry; 679 | pnpCallbacks.EvtDeviceD0Exit = OnD0Exit; 680 | 681 | WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpCallbacks); 682 | } 683 | 684 | // 685 | // Because we are a virtual device the root enumerator would just put null values 686 | // in response to IRP_MN_QUERY_ID. Lets override that. 687 | // 688 | 689 | minorFunction = IRP_MN_QUERY_ID; 690 | 691 | status = WdfDeviceInitAssignWdmIrpPreprocessCallback( 692 | DeviceInit, 693 | MaxmEvtWdmPreprocessMnQueryId, 694 | IRP_MJ_PNP, 695 | &minorFunction, 696 | 1 697 | ); 698 | if (!NT_SUCCESS(status)) 699 | { 700 | MaxmPrint(DEBUG_LEVEL_ERROR, DBG_PNP, 701 | "WdfDeviceInitAssignWdmIrpPreprocessCallback failed Status 0x%x\n", status); 702 | 703 | return status; 704 | } 705 | 706 | // 707 | // Setup the device context 708 | // 709 | 710 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, MAXM_CONTEXT); 711 | 712 | // 713 | // Create a framework device object.This call will in turn create 714 | // a WDM device object, attach to the lower stack, and set the 715 | // appropriate flags and attributes. 716 | // 717 | 718 | status = WdfDeviceCreate(&DeviceInit, &attributes, &device); 719 | 720 | if (!NT_SUCCESS(status)) 721 | { 722 | MaxmPrint(DEBUG_LEVEL_ERROR, DBG_PNP, 723 | "WdfDeviceCreate failed with status code 0x%x\n", status); 724 | 725 | return status; 726 | } 727 | 728 | WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel); 729 | 730 | queueConfig.EvtIoInternalDeviceControl = MaxmEvtInternalDeviceControl; 731 | 732 | status = WdfIoQueueCreate(device, 733 | &queueConfig, 734 | WDF_NO_OBJECT_ATTRIBUTES, 735 | &queue 736 | ); 737 | 738 | if (!NT_SUCCESS(status)) 739 | { 740 | MaxmPrint(DEBUG_LEVEL_ERROR, DBG_PNP, 741 | "WdfIoQueueCreate failed 0x%x\n", status); 742 | 743 | return status; 744 | } 745 | 746 | // 747 | // Create manual I/O queue to take care of hid report read requests 748 | // 749 | 750 | devContext = GetDeviceContext(device); 751 | 752 | WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchManual); 753 | 754 | queueConfig.PowerManaged = WdfFalse; 755 | 756 | status = WdfIoQueueCreate(device, 757 | &queueConfig, 758 | WDF_NO_OBJECT_ATTRIBUTES, 759 | &devContext->ReportQueue 760 | ); 761 | 762 | if (!NT_SUCCESS(status)) 763 | { 764 | MaxmPrint(DEBUG_LEVEL_ERROR, DBG_PNP, 765 | "WdfIoQueueCreate failed 0x%x\n", status); 766 | 767 | return status; 768 | } 769 | 770 | WDF_TIMER_CONFIG timerConfig; 771 | WDFTIMER hTimer; 772 | 773 | WDF_TIMER_CONFIG_INIT_PERIODIC(&timerConfig, MaxmJDetTimer, 10); 774 | 775 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 776 | attributes.ParentObject = device; 777 | status = WdfTimerCreate(&timerConfig, &attributes, &hTimer); 778 | devContext->Timer = hTimer; 779 | if (!NT_SUCCESS(status)) 780 | { 781 | MaxmPrint(DEBUG_LEVEL_ERROR, DBG_PNP, "(%!FUNC!) WdfTimerCreate failed status:%!STATUS!\n", status); 782 | return status; 783 | } 784 | 785 | devContext->FxDevice = device; 786 | 787 | return status; 788 | } 789 | 790 | NTSTATUS 791 | MaxmEvtWdmPreprocessMnQueryId( 792 | WDFDEVICE Device, 793 | PIRP Irp 794 | ) 795 | { 796 | NTSTATUS status; 797 | PIO_STACK_LOCATION IrpStack, previousSp; 798 | PDEVICE_OBJECT DeviceObject; 799 | PWCHAR buffer; 800 | 801 | PAGED_CODE(); 802 | 803 | // 804 | // Get a pointer to the current location in the Irp 805 | // 806 | 807 | IrpStack = IoGetCurrentIrpStackLocation(Irp); 808 | 809 | // 810 | // Get the device object 811 | // 812 | DeviceObject = WdfDeviceWdmGetDeviceObject(Device); 813 | 814 | 815 | MaxmPrint(DEBUG_LEVEL_VERBOSE, DBG_PNP, 816 | "MaxmEvtWdmPreprocessMnQueryId Entry\n"); 817 | 818 | // 819 | // This check is required to filter out QUERY_IDs forwarded 820 | // by the HIDCLASS for the parent FDO. These IDs are sent 821 | // by PNP manager for the parent FDO if you root-enumerate this driver. 822 | // 823 | previousSp = ((PIO_STACK_LOCATION)((UCHAR *)(IrpStack)+ 824 | sizeof(IO_STACK_LOCATION))); 825 | 826 | if (previousSp->DeviceObject == DeviceObject) 827 | { 828 | // 829 | // Filtering out this basically prevents the Found New Hardware 830 | // popup for the root-enumerated Maxm on reboot. 831 | // 832 | status = Irp->IoStatus.Status; 833 | } 834 | else 835 | { 836 | switch (IrpStack->Parameters.QueryId.IdType) 837 | { 838 | case BusQueryDeviceID: 839 | case BusQueryHardwareIDs: 840 | // 841 | // HIDClass is asking for child deviceid & hardwareids. 842 | // Let us just make up some id for our child device. 843 | // 844 | buffer = (PWCHAR)ExAllocatePoolWithTag( 845 | NonPagedPool, 846 | MAXM_HARDWARE_IDS_LENGTH, 847 | MAXM_POOL_TAG 848 | ); 849 | 850 | if (buffer) 851 | { 852 | // 853 | // Do the copy, store the buffer in the Irp 854 | // 855 | RtlCopyMemory(buffer, 856 | MAXM_HARDWARE_IDS, 857 | MAXM_HARDWARE_IDS_LENGTH 858 | ); 859 | 860 | Irp->IoStatus.Information = (ULONG_PTR)buffer; 861 | status = STATUS_SUCCESS; 862 | } 863 | else 864 | { 865 | // 866 | // No memory 867 | // 868 | status = STATUS_INSUFFICIENT_RESOURCES; 869 | } 870 | 871 | Irp->IoStatus.Status = status; 872 | // 873 | // We don't need to forward this to our bus. This query 874 | // is for our child so we should complete it right here. 875 | // fallthru. 876 | // 877 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 878 | 879 | break; 880 | 881 | default: 882 | status = Irp->IoStatus.Status; 883 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 884 | break; 885 | } 886 | } 887 | 888 | MaxmPrint(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, 889 | "MaxmEvtWdmPreprocessMnQueryId Exit = 0x%x\n", status); 890 | 891 | return status; 892 | } 893 | 894 | VOID 895 | MaxmEvtInternalDeviceControl( 896 | IN WDFQUEUE Queue, 897 | IN WDFREQUEST Request, 898 | IN size_t OutputBufferLength, 899 | IN size_t InputBufferLength, 900 | IN ULONG IoControlCode 901 | ) 902 | { 903 | NTSTATUS status = STATUS_SUCCESS; 904 | WDFDEVICE device; 905 | PMAXM_CONTEXT devContext; 906 | 907 | UNREFERENCED_PARAMETER(OutputBufferLength); 908 | UNREFERENCED_PARAMETER(InputBufferLength); 909 | 910 | device = WdfIoQueueGetDevice(Queue); 911 | devContext = GetDeviceContext(device); 912 | 913 | switch (IoControlCode) 914 | { 915 | default: 916 | status = STATUS_NOT_SUPPORTED; 917 | break; 918 | } 919 | 920 | WdfRequestComplete(Request, status); 921 | 922 | return; 923 | } 924 | -------------------------------------------------------------------------------- /max98090/max98090.h: -------------------------------------------------------------------------------- 1 | /* 2 | * max98090.h -- MAX98090 ALSA SoC Audio driver 3 | * 4 | * Copyright 2011-2012 Maxim Integrated Products 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #ifndef _MAX98090_H 12 | #define _MAX98090_H 13 | 14 | /* 15 | * The default operating frequency for a DMIC attached to the codec. 16 | * This can be overridden by a device tree property. 17 | */ 18 | #define MAX98090_DEFAULT_DMIC_FREQ 2500000 19 | 20 | /* 21 | * MAX98090 Register Definitions 22 | */ 23 | 24 | #define M98090_REG_SOFTWARE_RESET 0x00 25 | #define M98090_REG_DEVICE_STATUS 0x01 26 | #define M98090_REG_JACK_STATUS 0x02 27 | #define M98090_REG_INTERRUPT_S 0x03 28 | #define M98090_REG_QUICK_SYSTEM_CLOCK 0x04 29 | #define M98090_REG_QUICK_SAMPLE_RATE 0x05 30 | #define M98090_REG_DAI_INTERFACE 0x06 31 | #define M98090_REG_DAC_PATH 0x07 32 | #define M98090_REG_MIC_DIRECT_TO_ADC 0x08 33 | #define M98090_REG_LINE_TO_ADC 0x09 34 | #define M98090_REG_ANALOG_MIC_LOOP 0x0A 35 | #define M98090_REG_ANALOG_LINE_LOOP 0x0B 36 | #define M98090_REG_RESERVED 0x0C 37 | #define M98090_REG_LINE_INPUT_CONFIG 0x0D 38 | #define M98090_REG_LINE_INPUT_LEVEL 0x0E 39 | #define M98090_REG_INPUT_MODE 0x0F 40 | #define M98090_REG_MIC1_INPUT_LEVEL 0x10 41 | #define M98090_REG_MIC2_INPUT_LEVEL 0x11 42 | #define M98090_REG_MIC_BIAS_VOLTAGE 0x12 43 | #define M98090_REG_DIGITAL_MIC_ENABLE 0x13 44 | #define M98090_REG_DIGITAL_MIC_CONFIG 0x14 45 | #define M98090_REG_LEFT_ADC_MIXER 0x15 46 | #define M98090_REG_RIGHT_ADC_MIXER 0x16 47 | #define M98090_REG_LEFT_ADC_LEVEL 0x17 48 | #define M98090_REG_RIGHT_ADC_LEVEL 0x18 49 | #define M98090_REG_ADC_BIQUAD_LEVEL 0x19 50 | #define M98090_REG_ADC_SIDETONE 0x1A 51 | #define M98090_REG_SYSTEM_CLOCK 0x1B 52 | #define M98090_REG_CLOCK_MODE 0x1C 53 | #define M98090_REG_CLOCK_RATIO_NI_MSB 0x1D 54 | #define M98090_REG_CLOCK_RATIO_NI_LSB 0x1E 55 | #define M98090_REG_CLOCK_RATIO_MI_MSB 0x1F 56 | #define M98090_REG_CLOCK_RATIO_MI_LSB 0x20 57 | #define M98090_REG_MASTER_MODE 0x21 58 | #define M98090_REG_INTERFACE_FORMAT 0x22 59 | #define M98090_REG_TDM_CONTROL 0x23 60 | #define M98090_REG_TDM_FORMAT 0x24 61 | #define M98090_REG_IO_CONFIGURATION 0x25 62 | #define M98090_REG_FILTER_CONFIG 0x26 63 | #define M98090_REG_DAI_PLAYBACK_LEVEL 0x27 64 | #define M98090_REG_DAI_PLAYBACK_LEVEL_EQ 0x28 65 | #define M98090_REG_LEFT_HP_MIXER 0x29 66 | #define M98090_REG_RIGHT_HP_MIXER 0x2A 67 | #define M98090_REG_HP_CONTROL 0x2B 68 | #define M98090_REG_LEFT_HP_VOLUME 0x2C 69 | #define M98090_REG_RIGHT_HP_VOLUME 0x2D 70 | #define M98090_REG_LEFT_SPK_MIXER 0x2E 71 | #define M98090_REG_RIGHT_SPK_MIXER 0x2F 72 | #define M98090_REG_SPK_CONTROL 0x30 73 | #define M98090_REG_LEFT_SPK_VOLUME 0x31 74 | #define M98090_REG_RIGHT_SPK_VOLUME 0x32 75 | #define M98090_REG_DRC_TIMING 0x33 76 | #define M98090_REG_DRC_COMPRESSOR 0x34 77 | #define M98090_REG_DRC_EXPANDER 0x35 78 | #define M98090_REG_DRC_GAIN 0x36 79 | #define M98090_REG_RCV_LOUTL_MIXER 0x37 80 | #define M98090_REG_RCV_LOUTL_CONTROL 0x38 81 | #define M98090_REG_RCV_LOUTL_VOLUME 0x39 82 | #define M98090_REG_LOUTR_MIXER 0x3A 83 | #define M98090_REG_LOUTR_CONTROL 0x3B 84 | #define M98090_REG_LOUTR_VOLUME 0x3C 85 | #define M98090_REG_JACK_DETECT 0x3D 86 | #define M98090_REG_INPUT_ENABLE 0x3E 87 | #define M98090_REG_OUTPUT_ENABLE 0x3F 88 | #define M98090_REG_LEVEL_CONTROL 0x40 89 | #define M98090_REG_DSP_FILTER_ENABLE 0x41 90 | #define M98090_REG_BIAS_CONTROL 0x42 91 | #define M98090_REG_DAC_CONTROL 0x43 92 | #define M98090_REG_ADC_CONTROL 0x44 93 | #define M98090_REG_DEVICE_SHUTDOWN 0x45 94 | #define M98090_REG_EQUALIZER_BASE 0x46 95 | #define M98090_REG_RECORD_BIQUAD_BASE 0xAF 96 | #define M98090_REG_DMIC3_VOLUME 0xBE 97 | #define M98090_REG_DMIC4_VOLUME 0xBF 98 | #define M98090_REG_DMIC34_BQ_PREATTEN 0xC0 99 | #define M98090_REG_RECORD_TDM_SLOT 0xC1 100 | #define M98090_REG_SAMPLE_RATE 0xC2 101 | #define M98090_REG_DMIC34_BIQUAD_BASE 0xC3 102 | #define M98090_REG_REVISION_ID 0xFF 103 | 104 | #define M98090_REG_CNT (0xFF+1) 105 | #define MAX98090_MAX_REGISTER 0xFF 106 | 107 | /* MAX98090 Register Bit Fields */ 108 | 109 | /* 110 | * M98090_REG_SOFTWARE_RESET 111 | */ 112 | #define M98090_SWRESET_MASK (1<<7) 113 | #define M98090_SWRESET_SHIFT 7 114 | #define M98090_SWRESET_WIDTH 1 115 | 116 | /* 117 | * M98090_REG_DEVICE_STATUS 118 | */ 119 | #define M98090_CLD_MASK (1<<7) 120 | #define M98090_CLD_SHIFT 7 121 | #define M98090_CLD_WIDTH 1 122 | #define M98090_SLD_MASK (1<<6) 123 | #define M98090_SLD_SHIFT 6 124 | #define M98090_SLD_WIDTH 1 125 | #define M98090_ULK_MASK (1<<5) 126 | #define M98090_ULK_SHIFT 5 127 | #define M98090_ULK_WIDTH 1 128 | #define M98090_JDET_MASK (1<<2) 129 | #define M98090_JDET_SHIFT 2 130 | #define M98090_JDET_WIDTH 1 131 | #define M98090_DRCACT_MASK (1<<1) 132 | #define M98090_DRCACT_SHIFT 1 133 | #define M98090_DRCACT_WIDTH 1 134 | #define M98090_DRCCLP_MASK (1<<0) 135 | #define M98090_DRCCLP_SHIFT 0 136 | #define M98090_DRCCLP_WIDTH 1 137 | 138 | /* 139 | * M98090_REG_JACK_STATUS 140 | */ 141 | #define M98090_LSNS_MASK (1<<2) 142 | #define M98090_LSNS_SHIFT 2 143 | #define M98090_LSNS_WIDTH 1 144 | #define M98090_JKSNS_MASK (1<<1) 145 | #define M98090_JKSNS_SHIFT 1 146 | #define M98090_JKSNS_WIDTH 1 147 | 148 | /* 149 | * M98090_REG_INTERRUPT_S 150 | */ 151 | #define M98090_ICLD_MASK (1<<7) 152 | #define M98090_ICLD_SHIFT 7 153 | #define M98090_ICLD_WIDTH 1 154 | #define M98090_ISLD_MASK (1<<6) 155 | #define M98090_ISLD_SHIFT 6 156 | #define M98090_ISLD_WIDTH 1 157 | #define M98090_IULK_MASK (1<<5) 158 | #define M98090_IULK_SHIFT 5 159 | #define M98090_IULK_WIDTH 1 160 | #define M98090_IJDET_MASK (1<<2) 161 | #define M98090_IJDET_SHIFT 2 162 | #define M98090_IJDET_WIDTH 1 163 | #define M98090_IDRCACT_MASK (1<<1) 164 | #define M98090_IDRCACT_SHIFT 1 165 | #define M98090_IDRCACT_WIDTH 1 166 | #define M98090_IDRCCLP_MASK (1<<0) 167 | #define M98090_IDRCCLP_SHIFT 0 168 | #define M98090_IDRCCLP_WIDTH 1 169 | 170 | /* 171 | * M98090_REG_QUICK_SYSTEM_CLOCK 172 | */ 173 | #define M98090_26M_MASK (1<<7) 174 | #define M98090_26M_SHIFT 7 175 | #define M98090_26M_WIDTH 1 176 | #define M98090_19P2M_MASK (1<<6) 177 | #define M98090_19P2M_SHIFT 6 178 | #define M98090_19P2M_WIDTH 1 179 | #define M98090_13M_MASK (1<<5) 180 | #define M98090_13M_SHIFT 5 181 | #define M98090_13M_WIDTH 1 182 | #define M98090_12P288M_MASK (1<<4) 183 | #define M98090_12P288M_SHIFT 4 184 | #define M98090_12P288M_WIDTH 1 185 | #define M98090_12M_MASK (1<<3) 186 | #define M98090_12M_SHIFT 3 187 | #define M98090_12M_WIDTH 1 188 | #define M98090_11P2896M_MASK (1<<2) 189 | #define M98090_11P2896M_SHIFT 2 190 | #define M98090_11P2896M_WIDTH 1 191 | #define M98090_256FS_MASK (1<<0) 192 | #define M98090_256FS_SHIFT 0 193 | #define M98090_256FS_WIDTH 1 194 | #define M98090_CLK_ALL_SHIFT 0 195 | #define M98090_CLK_ALL_WIDTH 8 196 | #define M98090_CLK_ALL_NUM (1<