Fix build with Microsoft Visual C++, Fix gfxhook and felica issue (#48)

I just wanna say that It is a SHAME that a Windows ONLY project was not able to build without MINGW
Also where's the missing `3mpxsc.h` in diva hook?

This also fixes the window size issue from hook_CreateWindowExA in gfxhook
And Fixes felica issue as described in #45

Reviewed-on: #48
Reviewed-by: Dniel97 <dniel97@noreply.gitea.tendokyu.moe>
Co-authored-by: GEEKiDoS <geek_ds@foxmail.com>
Co-committed-by: GEEKiDoS <geek_ds@foxmail.com>
This commit is contained in:
GEEKiDoS 2024-11-11 16:28:24 +00:00 committed by Dniel97
parent ceb2b63e8b
commit c80f903cf8
18 changed files with 432 additions and 58 deletions

View File

@ -88,7 +88,7 @@ struct led15093_req_reset {
struct led15093_req_set_timeout {
struct led15093_req_hdr hdr;
uint8_t cmd;
uint8_t count;
uint16_t count;
};
struct led15093_req_set_disable_response {

View File

@ -354,13 +354,13 @@ static HRESULT sg_nfc_poll_felica(
felica->type = 0x20;
felica->id_len = sizeof(felica->IDm) + sizeof(felica->PMm);
felica->IDm = _byteswap_uint64(IDm);
felica->PMm = _byteswap_uint64(felica_get_generic_PMm());
felica->PMm = _byteswap_uint64(felica_get_amusement_ic_PMm());
/* Initialize FeliCa IC emulator */
nfc->felica.IDm = IDm;
nfc->felica.PMm = felica_get_generic_PMm();
nfc->felica.system_code = 0x0000;
nfc->felica.PMm = felica_get_amusement_ic_PMm();
nfc->felica.system_code = 0x88b4;
return S_OK;
}

View File

@ -13,7 +13,6 @@
#include "platform/config.h"
#include "divahook/3mpxsc.h"
#include "divahook/diva-dll.h"
#include "divahook/slider.h"
@ -23,7 +22,6 @@ struct diva_hook_config {
struct aime_config aime;
struct dvd_config dvd;
struct gfx_config gfx;
struct touch3mpxsc_config touch3mpxsc;
struct touch_screen_config touch;
struct diva_dll_config dll;
struct slider_config slider;

View File

@ -116,7 +116,7 @@ static const struct hook_symbol reg_syms[] = {
}
};
#define device_fake_key 0xDEADBEEF
const size_t device_fake_key = 0xDEADBEEF;
static HANDLE ftdi_fd;
static char port_name[8];

View File

@ -109,8 +109,8 @@ HRESULT fgo_io_poll(void)
normalizedMagnitude = 0.0;
}
fgo_stick_x = normalizedLX * normalizedMagnitude * 32767;
fgo_stick_y = normalizedLY * normalizedMagnitude * 32767;
fgo_stick_x = (int16_t)(normalizedLX * normalizedMagnitude * 32767);
fgo_stick_y = (int16_t)(normalizedLY * normalizedMagnitude * 32767);
return S_OK;
}

View File

@ -12,7 +12,7 @@
/* Hook functions */
static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow);
static BOOL WINAPI hook_CreateWindowExA(
static HWND WINAPI hook_CreateWindowExA(
DWORD dwExStyle,
LPCSTR lpClassName,
LPCSTR lpWindowName,
@ -30,7 +30,7 @@ static BOOL WINAPI hook_CreateWindowExA(
/* Link pointers */
static BOOL (WINAPI *next_ShowWindow)(HWND hWnd, int nCmdShow);
static BOOL (WINAPI *next_CreateWindowExA)(
static HWND (WINAPI *next_CreateWindowExA)(
DWORD dwExStyle,
LPCSTR lpClassName,
LPCSTR lpWindowName,
@ -82,7 +82,7 @@ static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow)
return next_ShowWindow(hWnd, nCmdShow);
}
static BOOL WINAPI hook_CreateWindowExA(
static HWND WINAPI hook_CreateWindowExA(
DWORD dwExStyle,
LPCSTR lpClassName,
LPCSTR lpWindowName,
@ -97,18 +97,32 @@ static BOOL WINAPI hook_CreateWindowExA(
LPVOID lpParam
)
{
RECT rect;
dprintf("Gfx: CreateWindowExA hook hit\n");
// Set to WS_OVERLAPPEDWINDOW to enable a window with a border and windowed style
if (gfx_config.windowed) {
dwStyle = WS_OVERLAPPEDWINDOW;
if (!gfx_config.framed) {
if (gfx_config.windowed)
{
if (gfx_config.framed)
dwStyle |= WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;
else
dwStyle = WS_POPUP;
}
rect.left = ((X == CW_USEDEFAULT) ? 0 : X);
rect.top = ((Y == CW_USEDEFAULT) ? 0 : Y);
rect.right = rect.left + nWidth;
rect.bottom = rect.top + nHeight;
// Don't care if it's ok or not, since we are creating window and we can't just return a NULL
AdjustWindowRect(&rect, dwStyle, !!hMenu);
X = ((X == CW_USEDEFAULT) ? X : rect.left);
Y = ((Y == CW_USEDEFAULT) ? Y : rect.top);
nWidth = rect.right - rect.left;
nHeight = rect.bottom - rect.top;
}
return next_CreateWindowExA(
return next_CreateWindowExA(
dwExStyle,
lpClassName,
lpWindowName,

View File

@ -60,7 +60,7 @@ static void WINAPI hook_glutFullScreen(void)
return;
}
return next_glutFullScreen();
next_glutFullScreen();
}
static void WINAPI hook_glutInitDisplayMode(unsigned int mode)
@ -73,5 +73,5 @@ static void WINAPI hook_glutInitDisplayMode(unsigned int mode)
mode |= 0x0800;
}
return next_glutInitDisplayMode(mode);
next_glutInitDisplayMode(mode);
}

View File

@ -98,7 +98,7 @@ int WINAPI chcusb_imageformat_330(
uint16_t width,
uint16_t height,
uint16_t *rResult);
int __thiscall chcusb_setmtf(int32_t *mtf);
int __fastcall chcusb_setmtf(int32_t *mtf);
int WINAPI chcusb_makeGamma(uint16_t k, uint8_t *intoneR, uint8_t *intoneG, uint8_t *intoneB);
int WINAPI chcusb_setIcctable(
LPCSTR icc1,
@ -1634,7 +1634,7 @@ int WINAPI fwdlusb_open(uint16_t *rResult) {
return 1;
}
void fwdlusb_close() {}
void WINAPI fwdlusb_close() {}
int WINAPI fwdlusb_listupPrinter(uint8_t *rIdArray) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
@ -2115,7 +2115,7 @@ int WINAPI chcusb_open(uint16_t *rResult) {
return 1;
}
void chcusb_close() {
void WINAPI chcusb_close() {
dprintf("Printer: C3XXusb: %s\n", __func__);
}
@ -2334,7 +2334,7 @@ int WINAPI chcusb_imageformat_330(
return 1;
}
int __thiscall chcusb_setmtf(int32_t *mtf) {
int __fastcall chcusb_setmtf(int32_t *mtf) {
dprintf("Printer: C3XXusb: %s\n", __func__);
memcpy(MTF, mtf, sizeof(MTF));
@ -3351,4 +3351,4 @@ DWORD WriteArrayToFile(LPCSTR lpOutputFilePath, LPVOID lpDataTemp, DWORD nDataSi
void printer_set_dimensions(int width, int height){
WIDTH = width;
HEIGHT = height;
}
}

View File

@ -21,21 +21,27 @@ static HRESULT felica_cmd_get_system_code(
struct const_iobuf *req,
struct iobuf *res);
static HRESULT felica_cmd_nda_a4(
static HRESULT felica_cmd_active(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res);
uint64_t felica_get_generic_PMm(void)
static HRESULT felica_cmd_read_without_encryption(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res);
static HRESULT felica_cmd_write_without_encryption(
struct felica* f,
struct const_iobuf* req,
struct iobuf* res);
uint64_t felica_get_amusement_ic_PMm(void)
{
/* A FeliCa PMm contains low-level protocol timing information for
communicating with a particular IC card. The exact values are not
particularly important for our purposes, so we'll just return a hard-
coded PMm. This current value has been taken from an iPhone, emulating
a Suica pass via Apple Wallet, which seems to be one of the few
universally accepted FeliCa types for these games. Certain older
Suica passes and other payment and transportation cards
do not seem to be supported anymore. */
/*
* AIC Card PMm, if this is returned from the card,
* the aimelib will access the actual blocks for authentication.
*/
return 0x00F1000000014300;
}
@ -90,8 +96,14 @@ HRESULT felica_transact(
case FELICA_CMD_GET_SYSTEM_CODE:
return felica_cmd_get_system_code(f, req, res);
case FELICA_CMD_NDA_A4:
return felica_cmd_nda_a4(f, req, res);
case FELICA_READ_WITHOUT_ENCRYPTION:
return felica_cmd_read_without_encryption(f, req, res);
case FELICA_WRITE_WITHOUT_ENCRYPTION:
return felica_cmd_write_without_encryption(f, req, res);
case FELICA_CMD_ACTIVE:
return felica_cmd_active(f, req, res);
default:
dprintf("FeliCa: Unimplemented command %02x, payload:\n", code);
@ -184,7 +196,131 @@ static HRESULT felica_cmd_get_system_code(
return S_OK;
}
static HRESULT felica_cmd_nda_a4(
static HRESULT felica_cmd_read_without_encryption(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res)
{
HRESULT hr;
uint8_t system_code_count;
uint16_t* system_codes;
uint8_t read_block_count;
uint8_t* blocks;
size_t i;
hr = iobuf_read_8(req, &system_code_count);
if (FAILED(hr)) {
goto fail;
}
system_codes = malloc(sizeof(uint16_t) * system_code_count);
if (!system_codes) goto fail;
for (i = 0; i < system_code_count; i++) {
hr = iobuf_read_be16(req, system_codes + i);
if (FAILED(hr)) {
goto fail;
}
}
hr = iobuf_read_8(req, &read_block_count);
if (FAILED(hr)) {
goto fail;
}
blocks = malloc(read_block_count);
if (!system_codes) goto fail;
for (i = 0; i < read_block_count; i++) {
// 0x80
hr = iobuf_read_8(req, blocks + i);
if (FAILED(hr)) {
goto fail;
}
// actual block num
hr = iobuf_read_8(req, blocks + i);
if (FAILED(hr)) {
goto fail;
}
}
// status
hr = iobuf_write_be16(res, 0);
if (FAILED(hr)) {
goto fail;
}
// block count
hr = iobuf_write_8(res, read_block_count);
if (FAILED(hr)) {
goto fail;
}
// block data
for (i = 0; i < read_block_count; i++)
{
dprintf("FeliCa: Read block %x\n", blocks[i]);
switch (blocks[i]) {
case 0x82: {
hr = iobuf_write_be64(res, f->IDm);
if (FAILED(hr))
{
goto fail;
}
hr = iobuf_write_be64(res, 0x0078000000000000ull);
if (FAILED(hr))
{
goto fail;
}
}
default: {
hr = iobuf_write_be64(res, 0);
if (FAILED(hr))
{
goto fail;
}
hr = iobuf_write_be64(res, 0);
if (FAILED(hr))
{
goto fail;
}
}
}
}
hr = S_OK;
fail:
if (system_codes) free(system_codes);
if (blocks) free(blocks);
return hr;
}
static HRESULT felica_cmd_write_without_encryption(
struct felica* f,
struct const_iobuf* req,
struct iobuf* res)
{
return iobuf_write_be16(res, 0);
}
static HRESULT felica_cmd_active(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res)

View File

@ -8,9 +8,11 @@
#include "hook/iobuf.h"
enum {
FELICA_CMD_POLL = 0x00,
FELICA_CMD_GET_SYSTEM_CODE = 0x0c,
FELICA_CMD_NDA_A4 = 0xa4,
FELICA_CMD_POLL = 0x00,
FELICA_READ_WITHOUT_ENCRYPTION = 0x06,
FELICA_WRITE_WITHOUT_ENCRYPTION = 0x08,
FELICA_CMD_GET_SYSTEM_CODE = 0x0c,
FELICA_CMD_ACTIVE = 0xa4,
};
struct felica {
@ -24,4 +26,4 @@ HRESULT felica_transact(
struct const_iobuf *req,
struct iobuf *res);
uint64_t felica_get_generic_PMm(void);
uint64_t felica_get_amusement_ic_PMm(void);

View File

@ -184,10 +184,10 @@ static int16_t calculate_norm_steering(int16_t axis, uint16_t deadzone, bool lin
// apply non-linear transform to the axis
if (!linear_steering) {
return norm_axis * pow(norm_magnitude, 3.0) * max_wheel_value;
return (int16_t)(norm_axis * powf(norm_magnitude, 3.0f) * max_wheel_value);
}
return norm_axis * norm_magnitude * max_wheel_value;
return (int16_t)(norm_axis * norm_magnitude * max_wheel_value);
}
static void idac_xi_get_analogs(struct idac_io_analog_state *out) {

View File

@ -188,10 +188,10 @@ static int16_t calculate_norm_steering(int16_t axis, uint16_t deadzone, bool lin
// apply non-linear transform to the axis
if (!linear_steering) {
return norm_axis * pow(norm_magnitude, 3.0) * max_wheel_value;
return (int16_t)(norm_axis * powf(norm_magnitude, 3.0f) * max_wheel_value);
}
return norm_axis * norm_magnitude * max_wheel_value;
return (int16_t)(norm_axis * norm_magnitude * max_wheel_value);
}
static void idz_xi_jvs_read_analogs(struct idz_io_analog_state *out)

View File

@ -13,7 +13,6 @@ add_project_arguments(
'-DWIN32_LEAN_AND_MEAN',
'-D_WIN32_WINNT=_WIN32_WINNT_WIN7',
'-DMINGW_HAS_SECURE_API=1',
'-Wno-unused',
# '-ggdb', # Add debug information
language: 'c',
)
@ -24,6 +23,7 @@ if cc.get_id() != 'msvc'
add_project_arguments(
'-ffunction-sections',
'-fdata-sections',
'-Wno-unused',
language: 'c',
)

128
msvc-build.bat Normal file
View File

@ -0,0 +1,128 @@
@echo off
setlocal enabledelayedexpansion
set BUILD_DIR=build
set BUILD_DIR_32=%BUILD_DIR%\build32
set BUILD_DIR_64=%BUILD_DIR%\build64
set BUILD_DIR_ZIP=%BUILD_DIR%\zip
set DIST_DIR=dist
set DOC_DIR=doc
REM Set your Visual Studio install path if Visual Studio installation can not be detected
set VS_INSTALLATION=C:\Program Files\Microsoft Visual Studio\2022\Community
if /I "%1"=="build" (
call :detect-visual-studio
if ERRORLEVEL 2 exit /b
if ERRORLEVEL 1 (
echo Failed to detect Visual Studio installation path.
echo.
echo If Visual Studio is installed then edit VS_INSTALLATION in this file
echo to manually specify Visual Studio install path.
exit /b
)
call :detect-meson
if ERRORLEVEL 1 (
echo Meson is not installed.
exit /b
)
set VSVARSALL=!VSVARSALL!
set MESON=!MESON!
call :build
echo.
echo Build done!
exit /b
)
if /I "%1"=="zip" (
powershell -NoProfile -ExecutionPolicy Bypass -Command "& './package.ps1'"
exit /b
)
echo %~nx0 [action]
echo build: Build the for both x86 and x64
echo zip: Make zip file
exit /b
rem This should works for Visual Studio 2017+
:detect-visual-studio (
rem Who the hell on earth is still using a 32bit Windows in 2024
if "%ProgramFiles(x86)%"=="" (
set VSWHERE="%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe"
) else (
set VSWHERE="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
)
if exist %VSWHERE% (
REM get vcvarsall by using vswhere
set VSVARSALL=""
for /f "tokens=* usebackq" %%i in (`%VSWHERE% -find VC\Auxiliary\Build\vcvarsall.bat`) do set VSVARSALL="%%i"
) else (
REM fallback to old method
set VSVARSALL="%VS_INSTALLATION%\VC\Auxiliary\Build\vcvarsall.bat"
)
:check-vcvarsall
if /i %VSVARSALL%=="" (
echo Microsoft Visual C++ Component is not installed
echo Install it from Visual Studio Installer
exit /b 2
)
rem if a path is returned by vswhere, then it's sure that the result path exists
rem if path not exists than it was assumed from VS_INSTALLATION
if not exist %VSVARSALL% (
echo vsvarsall.bat not exists in VS_INSTALLATION,
echo either Visual C++ Component is not installed
echo or VS_INSTALLATION is wrong.
exit /b 1
)
exit /b 0
)
:detect-meson (
for /f "tokens=* usebackq" %%i in (`where meson`) do set MESON="%%i"
if not exist %MESON% (
exit /b 1
)
exit /b 0
)
:build (
:build_x64 (
call %VSVARSALL% x64
if exist %BUILD_DIR_64% (
%MESON% setup %BUILD_DIR_64% --backend vs --buildtype release --reconfigure
) else (
%MESON% setup %BUILD_DIR_64% --backend vs --buildtype release
)
pushd %BUILD_DIR_64%
msbuild /m /p:Configuration=release /p:Platform=x64 segatools.sln
popd
)
:build_x86 (
call %VSVARSALL% x86
if exist %BUILD_DIR_32% (
%MESON% setup %BUILD_DIR_32% --backend vs --buildtype release --reconfigure
) else (
%MESON% setup %BUILD_DIR_32% --backend vs --buildtype release
)
pushd %BUILD_DIR_32%
msbuild /m /p:Configuration=release /p:Platform=Win32 segatools.sln
popd
)
:end (
exit /b
)
)

96
package.ps1 Normal file
View File

@ -0,0 +1,96 @@
if ($null -eq $env:BUILD_DIR) {
$BUILD_DIR="build"
$BUILD_DIR_32="$BUILD_DIR\build32"
$BUILD_DIR_64="$BUILD_DIR\build64"
$BUILD_DIR_ZIP="$BUILD_DIR\zip"
$DIST_DIR="dist"
$DOC_DIR="doc"
} else {
$BUILD_DIR = $env:BUILD_DIR;
$BUILD_DIR_32 = $env:BUILD_DIR_32;
$BUILD_DIR_64 = $env:BUILD_DIR_64;
$BUILD_DIR_ZIP = $env:BUILD_DIR_ZIP;
$DIST_DIR = $env:DIST_DIR;
$DOC_DIR = $env:DOC_DIR;
}
$target = $null;
$line = '';
[System.Collections.ArrayList]$files = @();
$folder = Get-Location
cat .\Package.mk | % {
$trimmed = $_.TrimEnd('\').
TrimStart("`t ").
Replace('$(V)', '').
Replace('$(BUILD_DIR)', $BUILD_DIR).
Replace('$(BUILD_DIR_32)', $BUILD_DIR_32).
Replace('$(BUILD_DIR_64)', $BUILD_DIR_64).
Replace('$(BUILD_DIR_ZIP)', $BUILD_DIR_ZIP).
Replace('$(DIST_DIR)', $DIST_DIR).
Replace('$(DOC_DIR)', $DOC_DIR).
Replace('$@', $target).
Replace('/', '\');
if ($trimmed.EndsWith(': ') -or $trimmed.EndsWith(':')) {
$target = $trimmed.TrimEnd(': ');
$line = '';
$files.Clear();
cd $folder;
return;
}
if (-not $trimmed.StartsWith('|')) {
$line += $trimmed;
if ($_.EndsWith('\')) {
return;
}
}
$line.Split(';') | % {
$cmd = $_.Trim(' ');
if ($cmd.StartsWith('echo')) {
echo $cmd.TrimStart('echo ')
} elseif ($cmd.StartsWith('mkdir')) {
$cmd = $cmd.Replace('-p', '-Force')
echo " - $cmd"
Invoke-Expression $cmd | Out-Null
} elseif ($cmd.StartsWith('cp')) {
$tokens = $cmd.Replace('cp ', '').Split(' ');
$srcs = $tokens[0..($tokens.Count - 2)];
$dest = $tokens[$tokens.Count - 1];
echo " - cp -Path $srcs -Destination $dest";
cp -Path $srcs -Destination $dest
} elseif ($cmd.StartsWith('cd')) {
echo " - $cmd"
Invoke-Expression $cmd | Out-Null
} elseif ($cmd.StartsWith('zip ')) {
$tokens = $cmd.Substring(4, $cmd.Length - 4).Replace('-r ', '').Replace('-j ', '').Replace('*', '.\*');
$tokens = $tokens.Split(' ');
$target = $tokens[0]
if (-not $tokens.Contains('$^')) {
$files.AddRange($tokens[1..($tokens.Count - 1)]);
}
echo " - Compress-Archive -Path $files -DestinationPath $target -Force"
Compress-Archive -Path $files -DestinationPath $target -Force
} else {
$allExists = $true
$args = $cmd.Split(' ');
$args | ? { -not $_ -eq '' } | % {
if (-not (Test-Path $_)) {
$allExists = $false
}
}
if ($allExists) {
$files.AddRange($args);
}
}
}
$line = '';
}

View File

@ -34,11 +34,11 @@ static HRESULT vfs_path_hook_option(
static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes);
static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes);
static __thiscall wchar_t* hook_System_getAppRootPath();
static __thiscall wchar_t* (*next_System_getAppRootPath)();
static wchar_t* hook_System_getAppRootPath();
static wchar_t* (*next_System_getAppRootPath)();
static __thiscall wchar_t* hook_AppImage_getOptionMountRootPath();
static __thiscall wchar_t* (*next_AppImage_getOptionMountRootPath)();
static wchar_t* hook_AppImage_getOptionMountRootPath();
static wchar_t* (*next_AppImage_getOptionMountRootPath)();
static const struct hook_symbol amdaemon_syms[] = {
{
@ -524,7 +524,7 @@ static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes)
return reg_hook_read_wstr(bytes, nbytes, L"Y:\\");
}
static __thiscall wchar_t* hook_System_getAppRootPath()
static wchar_t* hook_System_getAppRootPath()
{
wchar_t *path = malloc(sizeof(wchar_t) * MAX_PATH);
wcscpy_s(path, MAX_PATH, vfs_config.appdata);
@ -534,7 +534,7 @@ static __thiscall wchar_t* hook_System_getAppRootPath()
return path;
}
static __thiscall wchar_t* hook_AppImage_getOptionMountRootPath()
static wchar_t* hook_AppImage_getOptionMountRootPath()
{
wchar_t *path = malloc(sizeof(wchar_t) * MAX_PATH);
wcscpy_s(path, MAX_PATH, vfs_config.option);

View File

@ -1,4 +1,4 @@
[wrap-git]
directory = capnhook
url = https://github.com/Hay1tsme/capnhook
revision = f060250e1b92dcb06946eb77742f0ab51755e158
revision = b595e4bf8a274ba3bdaf583e13a7ebc7efe0f48f

View File

@ -179,7 +179,7 @@ static int16_t calculate_norm_steering(int16_t axis, uint16_t deadzone, bool lin
// optionally normalize the magnitude with respect to its expected range
// giving a magnitude value of 0.0 to 1.0
norm_magnitude = magnitude / (max_stick_value - deadzone);
norm_magnitude = (int16_t)(magnitude / (max_stick_value - deadzone));
} else // if the controller is in the deadzone zero out the magnitude
{
magnitude = 0.0;
@ -188,10 +188,10 @@ static int16_t calculate_norm_steering(int16_t axis, uint16_t deadzone, bool lin
// apply non-linear transform to the axis
if (!linear_steering) {
return norm_axis * pow(norm_magnitude, 3.0) * max_wheel_value;
return (int16_t)(norm_axis * powf(norm_magnitude, 3.0f) * max_wheel_value);
}
return norm_axis * norm_magnitude * max_wheel_value;
return (int16_t)(norm_axis * norm_magnitude * max_wheel_value);
}
static void swdc_xi_get_analogs(struct swdc_io_analog_state *out)