diff --git a/FrameworkArgb/Device.c b/FrameworkArgb/Device.c index 356e992..0016adb 100644 --- a/FrameworkArgb/Device.c +++ b/FrameworkArgb/Device.c @@ -17,6 +17,8 @@ Module Name: #include "driver.h" #include "EcCommunication.h" #include "device.tmh" +#define _USE_MATH_DEFINES +#include // // This is the default report descriptor for the virtual Hid device returned @@ -200,6 +202,101 @@ HID_DESCRIPTOR G_DefaultHidDescriptor = { } }; +NTSTATUS +CalculateLampPositions( + PDEVICE_CONTEXT DeviceContext, + UINT16 LampCount, + UINT8 LedArrangement +) +{ + UINT8 Layers = 0; + if (LampCount > MAX_LAMPARRAY_LAMP_COUNT) { + TraceError("LampCount %d over %d", LampCount, MAX_LAMPARRAY_LAMP_COUNT); + return STATUS_INVALID_PARAMETER; + } + + switch (LedArrangement) { + // Circular, layers of 8 + case 0: + DeviceContext->Width = 80000; + DeviceContext->Height = 80000; + DeviceContext->Depth = 2000 * (LampCount / 8); + for (UINT8 i = 0; i <= LampCount / 8; i++) { + // 8 LEDs in a circle + // z is 0 for all LEDs, they're all in the same plane + // Bottom LED + DeviceContext->LampPositions[i+0].x = 40000; + DeviceContext->LampPositions[i+0].y = 0; + DeviceContext->LampPositions[i+0].z = 2000 * i; + DeviceContext->LampPositions[i+1].x = 60000; + DeviceContext->LampPositions[i+1].y = 20000; + DeviceContext->LampPositions[i+1].z = 2000 * i; + // Right LED + DeviceContext->LampPositions[i+2].x = 80000; + DeviceContext->LampPositions[i+2].y = 40000; + DeviceContext->LampPositions[i+2].z = 2000 * i; + DeviceContext->LampPositions[i+3].x = 60000; + DeviceContext->LampPositions[i+3].y = 60000; + DeviceContext->LampPositions[i+3].z = 2000 * i; + // Top LED + DeviceContext->LampPositions[i+4].x = 40000; + DeviceContext->LampPositions[i+4].y = 80000; + DeviceContext->LampPositions[i+1].z = 2000 * i; + DeviceContext->LampPositions[i+4].x = 20000; + DeviceContext->LampPositions[i+5].y = 60000; + DeviceContext->LampPositions[i+5].z = 2000 * i; + // Left LED + DeviceContext->LampPositions[i+6].x = 0; + DeviceContext->LampPositions[i+6].y = 40000; + DeviceContext->LampPositions[i+6].z = 2000 * i; + DeviceContext->LampPositions[i+7].x = 20000; + DeviceContext->LampPositions[i+7].y = 20000; + DeviceContext->LampPositions[i+7].z = 2000 * i; + } + break; + // LEDs arranged in a circle single layer, even distance from each other + case 1: + DeviceContext->Width = 80000; + DeviceContext->Height = 80000; + { + // Place LampCount LEDs evenly spaced around a circle of radius 40000 (centered at 40000,40000) + double centerX = 40000.0; + double centerY = 40000.0; + double radius = 40000.0; + for (UINT8 i = 0; i < LampCount; i++) { + double angle = (2.0 * M_PI * i) / LampCount; + DeviceContext->LampPositions[i].x = (UINT32)(centerX + radius * cos(angle)); + DeviceContext->LampPositions[i].y = (UINT32)(centerY + radius * sin(angle)); + } + } + break; + // Linear, LED strip with 5mm distance + case 2: + DeviceContext->Width = LampCount * 5000; + DeviceContext->Height = 0; + for (UINT8 i = 0; i <= LampCount / 8; i++) { + DeviceContext->LampPositions[i].x = i * 5000; + DeviceContext->LampPositions[i].y = 0; + } + break; + // Square Matrix with 5mm distance + case 3: + Layers = (UINT8) sqrt((double) LampCount); + DeviceContext->Width = Layers * 5000; + DeviceContext->Height = Layers * 5000; + for (UINT8 i = 0; i <= LampCount; i++) { + DeviceContext->LampPositions[i].x = (i % Layers) * 5000; + DeviceContext->LampPositions[i].y = (i / Layers) * 5000; + } + break; + default: + TraceError("LedArrangement %d invalid.", LedArrangement); + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + NTSTATUS FrameworkArgbCreateDevice( _Inout_ PWDFDEVICE_INIT DeviceInit @@ -227,6 +324,8 @@ Return Value: PHID_DEVICE_ATTRIBUTES hidAttributes; WDFDEVICE device; NTSTATUS status; + UINT8 LampCount; + UINT8 LedArrangement; TraceInformation("%!FUNC! Entry"); @@ -261,28 +360,39 @@ Return Value: deviceContext->Device = device; deviceContext->CurrentLampId = 0; deviceContext->AutonomousMode = TRUE; - // 8 LEDs in a circle - // z is 0 for all LEDs, they're all in the same plane - // Bottom LED - deviceContext->LampPositions[0].x = 40000; - deviceContext->LampPositions[0].y = 0; - deviceContext->LampPositions[1].x = 60000; - deviceContext->LampPositions[1].y = 20000; - // Right LED - deviceContext->LampPositions[2].x = 80000; - deviceContext->LampPositions[2].y = 40000; - deviceContext->LampPositions[3].x = 60000; - deviceContext->LampPositions[3].y = 60000; - // Top LED - deviceContext->LampPositions[4].x = 40000; - deviceContext->LampPositions[4].y = 80000; - deviceContext->LampPositions[5].x = 20000; - deviceContext->LampPositions[5].y = 60000; - // Left LED - deviceContext->LampPositions[6].x = 0; - deviceContext->LampPositions[6].y = 40000; - deviceContext->LampPositions[7].x = 20000; - deviceContext->LampPositions[7].y = 20000; + + LampCount = 0; + deviceContext->LampCount = 0; + deviceContext->Width = 0; + deviceContext->Height = 0; + deviceContext->Depth = 2000; + LedArrangement = 0; + status = CheckRegistryForLedConfig(device); + if (NT_SUCCESS(status)) { + // + // We need to read read descriptor from registry + // + status = ReadLedConfigFromRegistry(device, &LampCount, &LedArrangement); + if (!NT_SUCCESS(status)) { + TraceError("Failed to read LED config from registry\n"); + } + } + + status = CalculateLampPositions(deviceContext, LampCount, LedArrangement); + if (!NT_SUCCESS(status)) { + TraceError("Failed to calulcate lamp positions\n"); + deviceContext->LampCount = 0; + } + else { + deviceContext->LampCount = LampCount; + } + + // Default 8 LED fan + if (deviceContext->LampCount == 0) { + TraceError("No lamps set, falling back to default"); + deviceContext->LampCount = 8; + LedArrangement = 0; + } hidAttributes = &deviceContext->HidDeviceAttributes; RtlZeroMemory(hidAttributes, sizeof(HID_DEVICE_ATTRIBUTES)); @@ -526,3 +636,124 @@ Return Value: status = RequestCopyFromBuffer(Request, string, stringSizeCb); return status; } + +NTSTATUS +CheckRegistryForLedConfig( + WDFDEVICE Device +) +/*++ + +Routine Description: + + Read "ReadFromRegistry" key value from device parameters in the registry. + +Arguments: + + device - pointer to a device object. + +Return Value: + + NT status code. + +--*/ + +{ + WDFKEY hKey = NULL; + NTSTATUS status; + UNICODE_STRING valueName; + ULONG value; + + TraceInformation("%!FUNC! Entry"); + status = WdfDeviceOpenRegistryKey(Device, + PLUGPLAY_REGKEY_DEVICE, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &hKey); + if (NT_SUCCESS(status)) { + TraceInformation("%!FUNC! Found driver Registry key"); + RtlInitUnicodeString(&valueName, L"ReadFromRegistry"); + + status = WdfRegistryQueryULong(hKey, + &valueName, + &value); + + if (NT_SUCCESS(status)) { + TraceInformation("%!FUNC! ReadFromRegistry has value: %d", value); + if (value == 0) { + status = STATUS_UNSUCCESSFUL; + } + } + + WdfRegistryClose(hKey); + } + + TraceInformation("%!FUNC! Exiting with %!STATUS!", status); + return status; +} + +NTSTATUS +ReadLedConfigFromRegistry( + WDFDEVICE Device, + UINT8 *LampCount, + UINT8 *LedArrangement +) +/*++ + +Routine Description: + + Read LED config report descriptor from registry + +Arguments: + + device - pointer to a device object. + +Return Value: + + NT status code. + +--*/ +{ + WDFKEY hKey = NULL; + NTSTATUS status; + UNICODE_STRING valueName; + PDEVICE_CONTEXT deviceContext; + ULONG value; + + TraceInformation("%!FUNC! Entry"); + deviceContext = GetDeviceContext(Device); + + status = WdfDeviceOpenRegistryKey(Device, + PLUGPLAY_REGKEY_DEVICE, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &hKey); + + if (!NT_SUCCESS(status)) { + TraceError("%!FUNC! Failed to find driver registry key"); + return status; + } + + RtlInitUnicodeString(&valueName, L"LedCount"); + status = WdfRegistryQueryULong(hKey, &valueName, &value); + if (!NT_SUCCESS(status)) { + TraceError("%!FUNC! Failed to WdfRegistryQueryULong LedCount: %!STATUS!", status); + WdfRegistryClose(hKey); + return status; + } + TraceInformation("%!FUNC! LedCount has value: %d", value); + *LampCount = (UINT8) value; + + RtlInitUnicodeString(&valueName, L"LedArrangement"); + status = WdfRegistryQueryULong(hKey, &valueName, &value); + if (!NT_SUCCESS(status)) { + TraceError("%!FUNC! Failed to WdfRegistryQueryULong LedArrangement: %!STATUS!", status); + WdfRegistryClose(hKey); + return status; + } + TraceInformation("%!FUNC! LedArrangement has value: %d", value); + *LedArrangement = (UINT8) value; + + WdfRegistryClose(hKey); + + return status; +} diff --git a/FrameworkArgb/Device.h b/FrameworkArgb/Device.h index 188ae69..346421c 100644 --- a/FrameworkArgb/Device.h +++ b/FrameworkArgb/Device.h @@ -28,10 +28,10 @@ DRIVER_INITIALIZE DriverEntry; EVT_WDF_DRIVER_DEVICE_ADD EvtDeviceAdd; EVT_WDF_TIMER EvtTimerFunc; -#define LAMPARRAY_LAMP_COUNT 8 -#define LAMPARRAY_WIDTH 80000 // 80mm -#define LAMPARRAY_HEIGHT 80000 // 80mm -#define LAMPARRAY_DEPTH 20000 // 20mm +#define MAX_LAMPARRAY_LAMP_COUNT 256 +//#define LAMPARRAY_WIDTH 80000 // 80mm +//#define LAMPARRAY_HEIGHT 80000 // 80mm +//#define LAMPARRAY_DEPTH 20000 // 20mm #define LAMPARRAY_KIND 0x07 // LampArrayKindChassis #define LAMPARRAY_UPDATE_INTERVAL 100000 // 10ms @@ -48,10 +48,13 @@ typedef struct _DEVICE_CONTEXT HANDLE CrosEcHandle; UINT16 CurrentLampId; BOOLEAN AutonomousMode; - Position LampPositions[LAMPARRAY_LAMP_COUNT]; + UINT16 LampCount; + UINT32 Width; + UINT32 Height; + UINT32 Depth; + Position LampPositions[MAX_LAMPARRAY_LAMP_COUNT]; HID_DESCRIPTOR HidDescriptor; PHID_REPORT_DESCRIPTOR ReportDescriptor; - BOOLEAN ReadReportDescFromRegistry; } DEVICE_CONTEXT, * PDEVICE_CONTEXT; // @@ -168,6 +171,17 @@ RequestGetHidXferPacket_ToWriteToDevice( _Out_ HID_XFER_PACKET* Packet ); +NTSTATUS +CheckRegistryForLedConfig( + _In_ WDFDEVICE Device +); + +NTSTATUS +ReadLedConfigFromRegistry( + _In_ WDFDEVICE Device, + _Out_ UINT8 *LampCount, + _Out_ UINT8 *LedArrangement +); // // Misc definitions diff --git a/FrameworkArgb/FrameworkArgb.inf b/FrameworkArgb/FrameworkArgb.inf index 5299b3c..9222faf 100644 --- a/FrameworkArgb/FrameworkArgb.inf +++ b/FrameworkArgb/FrameworkArgb.inf @@ -10,7 +10,7 @@ ; INF file for installing HID minidriver (UMDF 2 version) ; ; Installation Notes: -; Using Devcon: Type "devcon install FrameworkArgb.inf root\FrameworkArgb" to install +; Using pnputil: Type "sudo pnputil /add-driver FrameworkArgb.inf /install" to install ; ; Important: ; This INF depends on MsHidUmdf.inf, which was introduced in build 22000 @@ -40,7 +40,7 @@ FrameworkArgb.dll = 1 %ManufacturerName% = Standard,NT$ARCH$.10.0...22000 ; wudfrd.inf introduced in build 22000 [Standard.NT$ARCH$.10.0...22000] -%DeviceName% = FrameworkArgb, Root\FrameworkArgb ; TODO: edit hw-id +%DeviceName% = FrameworkArgb, ACPI\FRMW0006 ; =================== UMDF Device ================================== diff --git a/FrameworkArgb/LampArray.cpp b/FrameworkArgb/LampArray.cpp index 47e1330..f7295d0 100644 --- a/FrameworkArgb/LampArray.cpp +++ b/FrameworkArgb/LampArray.cpp @@ -70,10 +70,10 @@ GetLampArrayAttributesReport( DeviceContext = DeviceContext; LampArrayAttributesReport report = { - LAMPARRAY_LAMP_COUNT, - LAMPARRAY_WIDTH, - LAMPARRAY_HEIGHT, - LAMPARRAY_DEPTH, + DeviceContext->LampCount, + DeviceContext->Width, + DeviceContext->Height, + DeviceContext->Depth, LAMPARRAY_KIND, LAMPARRAY_UPDATE_INTERVAL }; @@ -112,7 +112,7 @@ GetLampAttributesResponseReport( }; RtlCopyMemory(ReportBuffer, &report, sizeof(LampAttributesResponseReport)); - DeviceContext->CurrentLampId = CurrentLampId + 1 >= LAMPARRAY_LAMP_COUNT ? CurrentLampId : CurrentLampId + 1; + DeviceContext->CurrentLampId = CurrentLampId + 1 >= DeviceContext->LampCount ? CurrentLampId : CurrentLampId + 1; return Size; diff --git a/README.md b/README.md index fa02dd9..57830bf 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,43 @@ References: - [Windows.Devices.Enumeration](https://learn.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher?view=winrt-26100) - [Game Dev Documentation](https://learn.microsoft.com/en-us/gaming/gdk/docs/features/common/lighting/gc-lighting-toc) +## Configuration + +When the driver loads, the Windows Dynamic Lighting interface (or any HID +application) asks it about the LED configuration - how many and where in 3D +space. +This is so that they can know how to apply 2D or 3D animations to them. + +By default the driver loads with an 8 LED configuration arranged in a circle of +20mm diameter. This matches the ARGB fan that Framework offers for the +Framework Desktop. + +To customize the configuration, set/edit the following registry entries. +They are all under: `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\ACPI\FRMW0006\?\Device Parameters` + +This configuration must be done after the driver is installed, then you disable the device, set the registry entries and then re-enable the device. + +| Name | Type | Explanation | +|------------------|-------|---------------------------------| +| ReadFromRegistry | DWORD | If 1, the other values are read | +| LedCount | DWORD | How many LEDs in total | +| LedArrangement | DWORD | How the LEDs are arranged | + +LedArrangement can have the following values: + +- 0: Circular, layers of 8 (e.g. when 16 LEDs in total, two layers of 8 LEDs) +- 1: Circular, single layer +- 2: Linear (e.g. LED strip) +- 3: Square Matrix (works best with a square number of LEDs) + +For example to control just 4 LEDs on the fan: + +``` +sudo reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\ROOT\HIDCLASS\0000\Device Parameters" /v ReadFromRegistry /t REG_DWORD /d 1 +sudo reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\ROOT\HIDCLASS\0000\Device Parameters" /v LedCount /t REG_DWORD /d 8 +sudo reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\ROOT\HIDCLASS\0000\Device Parameters" /v LedArrangement /t REG_DWORD /d 0 +``` + ## Development ### Build @@ -42,10 +79,6 @@ Use Visual Studio 2022 Community Edition and build the project. ### Install ``` -# Software device right now -cp "C:\Program Files (x86)\Windows Kits\10\Tools\10.0.26100.0\x64\devcon.exe" . -sudo .\devcon install .\FrameworkArgb\x64\Debug\FrameworkArgb\FrameworkArgb.inf root\FrameworkArgb - # Soon with ACPI device in updated BIOS sudo pnputil /add-driver .\FrameworkArgb\x64\Debug\FrameworkArgb\FrameworkArgb.inf /install ```