Compare commits

...

12 Commits
1.6.1 ... main

Author SHA1 Message Date
scrib-bler
c5f6496adf Changed default dll behaviour to be compatiable with chusanApp games 2024-08-06 18:37:51 +01:00
8078821d7c Attempt to make configuration instructions clearer. 2024-07-26 17:21:37 +00:00
1bc0bc65e3 Update README.md
Removed reference to chuniio branch, as all versions of Dniel97 segatools now support the LED change
2024-06-09 21:39:35 +00:00
da041efcec LUMINOUS support
added LUMINOUS support, kek
2024-03-22 05:51:09 +00:00
a67e50866e removed [io3] section
removed [io3] section from readme as it was confusing for some people
2024-03-22 05:49:01 +00:00
8ace7dace5 clean up alignment
probably still bad
2024-01-09 19:49:08 +00:00
84489e8fae Make tower LEDs more arcade accurate
Now using all the tower LED data sent by the game instead of just using 1 and repeating it across all LEDs.

This will have no effect in game as the game simply sets them all to the same colour anyway :(
2024-01-09 19:36:56 +00:00
1cfbf74bc6 API version bumped to 0x0102 in line wtih Dniel97 segatools
0x0102 is to signify the Tower LED support, which has already been implemented. This is so that we are reporting the correct API version.
2023-12-25 23:25:48 +00:00
dbd35c2c8c Merge pull request 'Added support for zig v0.11' (#1) from Dniel97/chuniio-tasoller:main into main
Reviewed-on: #1
Tested with zig 0.11 and zig 0.12dev. Tested resulted dlls in game.
2023-12-21 19:24:18 +00:00
cd59aca9e5
Added support for zig v0.11 2023-12-21 02:55:11 +01:00
aef4fa5c56 change chuni_io_led_init from c_int to HRESULT to better match segatools implementation 2023-12-21 00:09:48 +00:00
15be6e1a2c Update README.md 2023-12-19 23:58:13 +00:00
5 changed files with 155 additions and 134 deletions

View File

@ -1,39 +1,38 @@
## chuniio-tasoller *with air tower LEDs* ## chuniio-tasoller *with air tower LEDs*
chuniio driver for tasoller custom 2.0 firmware chuniio driver for tasoller custom 2.0 firmware
now with working air tower LEDs when using [Dniel97 segatools](https://gitea.tendokyu.moe/Dniel97/segatools) now with working air tower LEDs when using [Dniel97 segatools](https://gitea.tendokyu.moe/Dniel97/segatools/src/branch/chuniio)
thanks to: thanks to:
**akiroz** for the original code and USB Protocol information - [**akiroz**](https://dev.s-ul.net/akiroz/chuniio-tasoller) for the original code and USB Protocol information
[**Dniel97**](https://gitea.tendokyu.moe/Dniel97) for rewritting my failed attempts at making this work - [**Dniel97**](https://gitea.tendokyu.moe/Dniel97) for rewritting my failed attempts at making this work
the current implementation may not be fully arcade accurate, but should be visually acceptable during gameplay
this should hopefully be addressed in the future
Supported titles: Supported titles:
- Chunithm - CHUNITHM
- Chunithm NEW - CHUNITHM NEW
- Chunithn SUN - CHUNITHM NEW PLUS
- Chunithm SUN PLUS - CHUNITHM SUN
- CHUNITHM SUN PLUS
- CHUNITHM LUMINOUS
## Configuration ## Configuration
- For modern CHUNITHM (NEW and above)
segatools.ini
```ini
[chuniio]
path32=chuniio_tasoller.dll
path64=chuniio_tasoller_x64.dll
```
- For older CHUNITHM (PARADISE LOST and older)
segatools.ini segatools.ini
```ini ```ini
[chuniio] [chuniio]
;; For Chunithm or Chunithm NEW (segatools_32.ini)
path=chuniio_tasoller.dll path=chuniio_tasoller.dll
;; For Chunithm NEW (segatools_64.ini) chusan=0
; path=chuniio_tasoller_x64.dll
;; Uncomment for Chunithm NEW
; chusan=1
[io3]
test=0x31
service=0x32
coin=0x33
``` ```
## USB Protocol ## USB Protocol
@ -59,6 +58,6 @@ Custom firmware USB device: 1CCF:2333
``` ```
$ git clone ... $ git clone ...
$ git submodule update --init $ git submodule update --init
$ zig build -Drelease-safe=true $ zig build -Doptimize=ReleaseSafe
$ ls zig-out/lib/chuniio_tasoller.dll $ ls zig-out/lib/chuniio_tasoller.dll
``` ```

View File

@ -1,20 +1,24 @@
const std = @import("std"); const std = @import("std");
const CrossTarget = std.zig.CrossTarget; const CrossTarget = std.zig.CrossTarget;
const Build = std.build.Builder;
const Step = Build.Step;
pub fn build(b: *std.build.Builder) void { pub fn build(b: *Build) void {
const mode = b.standardOptimizeOption(.{});
const lib86 = b.addSharedLibrary("chuniio_tasoller", "src/main.zig", .unversioned); const lib86 = b.addSharedLibrary(Build.SharedLibraryOptions{
lib86.setBuildMode(b.standardReleaseOptions()); .name = "chuniio_tasoller",
lib86.setTarget(CrossTarget{ .os_tag = .windows, .cpu_arch = .i386, .abi = .msvc }); .root_source_file = .{.path = "src/main.zig"},
lib86.install(); .target = CrossTarget{ .os_tag = .windows, .cpu_arch = .x86, .abi = .msvc },
.optimize = mode,
});
b.installArtifact(lib86);
const lib64 = b.addSharedLibrary("chuniio_tasoller_x64", "src/main.zig", .unversioned); const lib64 = b.addSharedLibrary(Build.SharedLibraryOptions{
lib64.setBuildMode(b.standardReleaseOptions()); .name = "chuniio_tasoller_x64",
lib64.setTarget(CrossTarget{ .os_tag = .windows, .cpu_arch = .x86_64, .abi = .msvc }); .root_source_file = .{.path = "src/main.zig"},
lib64.install(); .target = CrossTarget{ .os_tag = .windows, .cpu_arch = .x86_64, .abi = .msvc },
.optimize = mode,
// const exe = b.addExecutable("tasoller_test", "src/main.zig"); });
// lib.setBuildMode(b.standardReleaseOptions()); b.installArtifact(lib64);
// exe.setTarget(target);
// exe.install();
} }

View File

@ -1,4 +1,11 @@
[chuniio] [chuniio]
;; For Chunithm
;path=chuniio_tasoller.dll
;; For Chunithm NEW or newer
path32=chuniio_tasoller.dll
path64=chuniio_tasoller_x64.dll
;; Uncomment for Chunithm NEW or newer
chusan=1 chusan=1
[io3] [io3]

View File

@ -58,7 +58,7 @@ var input_thread: ?std.Thread = null;
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var usb_out_op = std.Thread.Mutex{}; var usb_out_op = std.Thread.Mutex{};
var usb_out = std.mem.zeroes([80*3]u8); var usb_out = std.mem.zeroes([80 * 3]u8);
var usb_in: ?[]u8 = null; var usb_in: ?[]u8 = null;
var tasoller: ?*anyopaque = null; var tasoller: ?*anyopaque = null;
@ -67,11 +67,11 @@ pub fn main() !void {
usb_in = try gpa.allocator().alloc(u8, 0x24); usb_in = try gpa.allocator().alloc(u8, 0x24);
var i: u32 = 0; var i: u32 = 0;
try tasoller_init(); try tasoller_init();
while(true) { while (true) {
if(WinUsb_WritePipe(tasoller, 0x03, @ptrCast(*u8, &usb_out), usb_out.len, &i, null) == 0) { if (WinUsb_WritePipe(tasoller, 0x03, @as(*u8, @ptrCast(&usb_out)), usb_out.len, &i, null) == 0) {
std.log.warn("[chuniio] WinUsb_WritePipe: {any}", .{GetLastError()}); std.log.warn("[chuniio] WinUsb_WritePipe: {any}", .{GetLastError()});
} }
if(WinUsb_ReadPipe(tasoller, 0x84, usb_in.ptr, usb_in.len, &i, null) == 0) { if (WinUsb_ReadPipe(tasoller, 0x84, usb_in.ptr, usb_in.len, &i, null) == 0) {
std.log.warn("[chuniio] WinUsb_ReadPipe: {any}", .{GetLastError()}); std.log.warn("[chuniio] WinUsb_ReadPipe: {any}", .{GetLastError()});
} }
std.time.sleep(100_000_000); std.time.sleep(100_000_000);
@ -81,8 +81,7 @@ pub fn main() !void {
// MAIN DRIVER ====================================================================================================== // MAIN DRIVER ======================================================================================================
fn tasoller_init() !void { fn tasoller_init() !void {
if (cfg.?.chusan == 1) {
if(cfg.?.chusan == 1) {
std.log.info("[chuniio] Initializing mode: chusan", .{}); std.log.info("[chuniio] Initializing mode: chusan", .{});
const szName = W("Local\\ChuniioTasoller"); const szName = W("Local\\ChuniioTasoller");
const hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, null, PAGE_READWRITE, 0, 0x24, szName) orelse { const hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, null, PAGE_READWRITE, 0, 0x24, szName) orelse {
@ -93,8 +92,8 @@ fn tasoller_init() !void {
std.log.err("[chuniio] MapViewOfFile: {any}", .{GetLastError()}); std.log.err("[chuniio] MapViewOfFile: {any}", .{GetLastError()});
return error.AccessError; return error.AccessError;
}; };
usb_in = @ptrCast([*]u8, pBuf)[0..0x24]; usb_in = @as([*]u8, @ptrCast(pBuf))[0..0x24];
if(builtin.cpu.arch == .x86_64) return; if (builtin.cpu.arch == .x86_64) return;
} else { } else {
std.log.info("[chuniio] Initializing mode: chuni", .{}); std.log.info("[chuniio] Initializing mode: chuni", .{});
usb_in = gpa.allocator().alloc(u8, 0x24) catch |err| { usb_in = gpa.allocator().alloc(u8, 0x24) catch |err| {
@ -104,7 +103,7 @@ fn tasoller_init() !void {
} }
const hDevInfo = SetupDiGetClassDevsW(&GUID_DEVINTERFACE_USB_DEVICE, null, null, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); const hDevInfo = SetupDiGetClassDevsW(&GUID_DEVINTERFACE_USB_DEVICE, null, null, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if(hDevInfo == INVALID_HANDLE_VALUE) { if (hDevInfo == INVALID_HANDLE_VALUE) {
std.log.err("[chuniio] SetupDiGetClassDevs: {any}", .{GetLastError()}); std.log.err("[chuniio] SetupDiGetClassDevs: {any}", .{GetLastError()});
return error.AccessError; return error.AccessError;
} }
@ -114,55 +113,47 @@ fn tasoller_init() !void {
var devIf = std.mem.zeroes(SP_DEVICE_INTERFACE_DATA); var devIf = std.mem.zeroes(SP_DEVICE_INTERFACE_DATA);
devIf.cbSize = @sizeOf(SP_DEVICE_INTERFACE_DATA); devIf.cbSize = @sizeOf(SP_DEVICE_INTERFACE_DATA);
var devicePath: ?[*:0]const u8 = null; var devicePath: ?[*:0]const u8 = null;
while(SetupDiEnumDeviceInterfaces(hDevInfo, null, &GUID_DEVINTERFACE_USB_DEVICE, ifIdx, &devIf) != 0) : (ifIdx += 1) { while (SetupDiEnumDeviceInterfaces(hDevInfo, null, &GUID_DEVINTERFACE_USB_DEVICE, ifIdx, &devIf) != 0) : (ifIdx += 1) {
var requiredSize: u32 = 0; var requiredSize: u32 = 0;
var detailBuf align(4) = std.mem.zeroes([512]u8); var detailBuf align(4) = std.mem.zeroes([512]u8);
var devIfDetail = @ptrCast(*SP_DEVICE_INTERFACE_DETAIL_DATA_A, &detailBuf); var devIfDetail = @as(*SP_DEVICE_INTERFACE_DETAIL_DATA_A, @ptrCast(&detailBuf));
devIfDetail.cbSize = @sizeOf(SP_DEVICE_INTERFACE_DETAIL_DATA_A); devIfDetail.cbSize = @sizeOf(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
if(SetupDiGetDeviceInterfaceDetailA(hDevInfo, &devIf, devIfDetail, 263, &requiredSize, null) == 0) { if (SetupDiGetDeviceInterfaceDetailA(hDevInfo, &devIf, devIfDetail, 263, &requiredSize, null) == 0) {
std.log.err("[chuniio] SetupDiGetDeviceInterfaceDetailA: {any}", .{GetLastError()}); std.log.err("[chuniio] SetupDiGetDeviceInterfaceDetailA: {any}", .{GetLastError()});
continue; continue;
} }
if(requiredSize >= 263) { if (requiredSize >= 263) {
std.log.err("[chuniio] SetupDiGetDeviceInterfaceDetailA: Path too long", .{}); std.log.err("[chuniio] SetupDiGetDeviceInterfaceDetailA: Path too long", .{});
continue; continue;
} }
const path = detailBuf[@offsetOf(SP_DEVICE_INTERFACE_DETAIL_DATA_A, "DevicePath") .. requiredSize :0]; const path = detailBuf[@offsetOf(SP_DEVICE_INTERFACE_DETAIL_DATA_A, "DevicePath")..requiredSize :0];
// std.log.info("[chuniio] devPath: {s}", .{path}); // std.log.info("[chuniio] devPath: {s}", .{path});
if(std.mem.indexOf(u8, path, "vid_1ccf") == null) continue; if (std.mem.indexOf(u8, path, "vid_1ccf") == null) continue;
if(std.mem.indexOf(u8, path, "pid_2333") == null) continue; if (std.mem.indexOf(u8, path, "pid_2333") == null) continue;
devicePath = path; devicePath = path;
break; break;
} }
if(devicePath == null) { if (devicePath == null) {
std.log.err("[chuniio] Tasoller not found", .{}); std.log.err("[chuniio] Tasoller not found", .{});
return error.AccessError; return error.AccessError;
} }
const hDeviceHandle = CreateFileA( const hDeviceHandle = CreateFileA(devicePath, @as(FILE_ACCESS_FLAGS, @enumFromInt(GENERIC_READ | GENERIC_WRITE)), @as(FILE_SHARE_MODE, @enumFromInt(@intFromEnum(FILE_SHARE_READ) | @intFromEnum(FILE_SHARE_WRITE))), null, // Security Attributes
devicePath, OPEN_EXISTING, .FILE_FLAG_OVERLAPPED, null // Template File
@intToEnum(FILE_ACCESS_FLAGS, GENERIC_READ | GENERIC_WRITE), );
@intToEnum(FILE_SHARE_MODE, @enumToInt(FILE_SHARE_READ) | @enumToInt(FILE_SHARE_WRITE)),
null, // Security Attributes if (hDeviceHandle == INVALID_HANDLE_VALUE) {
OPEN_EXISTING, std.log.err("[chuniio] CreateFileA {any}: {any}\n", .{ devicePath, GetLastError() });
.FILE_FLAG_OVERLAPPED,
null // Template File
) orelse {
std.log.err("[chuniio] CreateFileA {s}: {any}", .{devicePath, GetLastError()});
return error.AccessError;
};
if(hDeviceHandle == INVALID_HANDLE_VALUE) {
std.log.err("[chuniio] CreateFileA {s}: {any}", .{devicePath, GetLastError()});
return error.AccessError; return error.AccessError;
} }
if(WinUsb_Initialize(hDeviceHandle, &tasoller) == 0) { if (WinUsb_Initialize(hDeviceHandle, &tasoller) == 0) {
std.log.err("[chuniio] WinUsb_Initialize: {any}", .{GetLastError()}); std.log.err("[chuniio] WinUsb_Initialize: {any}\n", .{ GetLastError() });
return error.AccessError; return error.AccessError;
} }
// Init magic bytes // Init magic bytes
std.mem.copy(u8, usb_out[0..3], &[_]u8{0x42, 0x4C, 0x00}); std.mem.copy(u8, usb_out[0..3], &[_]u8{ 0x42, 0x4C, 0x00 });
input_thread = std.Thread.spawn(.{}, input_thread_proc, .{}) catch |err| { input_thread = std.Thread.spawn(.{}, input_thread_proc, .{}) catch |err| {
std.log.err("[chuniio] Spawn input thread: {any}", .{err}); std.log.err("[chuniio] Spawn input thread: {any}", .{err});
@ -173,23 +164,23 @@ fn tasoller_init() !void {
// Poll input regardless of slider start/stop // Poll input regardless of slider start/stop
fn input_thread_proc() void { fn input_thread_proc() void {
std.log.info("[chuniio] Input thread started", .{}); std.log.info("[chuniio] Input thread started", .{});
while(true) { while (true) {
var len: u32 = 0; var len: u32 = 0;
if(WinUsb_ReadPipe(tasoller, 0x84, @ptrCast(*u8, usb_in.?.ptr), @intCast(u32, usb_in.?.len), &len, null) == 0) { if (WinUsb_ReadPipe(tasoller, 0x84, @as(*u8, @ptrCast(usb_in.?.ptr)), @as(u32, @intCast(usb_in.?.len)), &len, null) == 0) {
std.log.warn("[chuniio] WinUsb_ReadPipe: {any}", .{GetLastError()}); std.log.warn("[chuniio] WinUsb_ReadPipe: {any}", .{GetLastError()});
} }
} }
} }
const chuni_io_slider_callback_t = ?fn ([*c]const u8) callconv(.C) void; const chuni_io_slider_callback_t = ?*fn ([*c]const u8) callconv(.C) void;
fn slider_thread_proc(callback: chuni_io_slider_callback_t) void { fn slider_thread_proc(callback: chuni_io_slider_callback_t) void {
var pressure = std.mem.zeroes([32]u8); var pressure = std.mem.zeroes([32]u8);
while(slider_active) { while (slider_active) {
for(usb_in.?[4..]) |byte, i| { for (usb_in.?[4..], 0..) |byte, i| {
// Tasoller order: top->bottom, left->right // Tasoller order: top->bottom, left->right
// Chunithm order: top->bottom, right->left // Chunithm order: top->bottom, right->left
pressure[if(i%2 == 0) 30-i else 32-i] = byte; pressure[if (i % 2 == 0) 30 - i else 32 - i] = byte;
} }
callback.?(&pressure); callback.?(&pressure);
std.time.sleep(1_000_000); // 1ms, limit reporting to 1kHz max std.time.sleep(1_000_000); // 1ms, limit reporting to 1kHz max
@ -199,65 +190,65 @@ fn slider_thread_proc(callback: chuni_io_slider_callback_t) void {
// DLL EXPORTS ====================================================================================================== // DLL EXPORTS ======================================================================================================
export fn chuni_io_get_api_version() c_ushort { export fn chuni_io_get_api_version() c_ushort {
return 0x0101; return 0x0102;
} }
pub export fn DllMain(hDllHandle: HANDLE, dwReason: DWORD, lpreserved: LPVOID) BOOL { pub export fn DllMain(hDllHandle: HANDLE, dwReason: DWORD, lpreserved: LPVOID) BOOL {
_ = hDllHandle; _ = hDllHandle;
_ = lpreserved; _ = lpreserved;
if(dwReason != DLL_PROCESS_ATTACH) return win32.zig.TRUE; if (dwReason != DLL_PROCESS_ATTACH) return win32.zig.TRUE;
const cfg_file = ".\\segatools.ini"; const cfg_file = ".\\segatools.ini";
std.log.info("[chuniio] Loading config from {s}", .{cfg_file}); std.log.info("[chuniio] Loading config from {s}", .{cfg_file});
cfg = .{ cfg = .{
.test_key = @intCast(i32, GetPrivateProfileIntA("io3", "test", 0x31, cfg_file)), .test_key = @as(i32, @intCast(GetPrivateProfileIntA("io3", "test", 0x70, cfg_file))),
.serv_key = @intCast(i32, GetPrivateProfileIntA("io3", "service", 0x32, cfg_file)), .serv_key = @as(i32, @intCast(GetPrivateProfileIntA("io3", "service", 0x71, cfg_file))),
.coin_key = @intCast(i32, GetPrivateProfileIntA("io3", "coin", 0x33, cfg_file)), .coin_key = @as(i32, @intCast(GetPrivateProfileIntA("io3", "coin", 0x72, cfg_file))),
.chusan = @intCast(i32, GetPrivateProfileIntA("chuniio", "chusan", 0, cfg_file)), .chusan = @as(i32, @intCast(GetPrivateProfileIntA("chuniio", "chusan", 1, cfg_file))),
}; };
return win32.zig.TRUE; return win32.zig.TRUE;
} }
export fn chuni_io_jvs_init() HRESULT { export fn chuni_io_jvs_init() HRESULT {
if(cfg.?.chusan == 1 and builtin.cpu.arch != .x86_64) return S_OK; if (cfg.?.chusan == 1 and builtin.cpu.arch != .x86_64) return S_OK;
tasoller_init() catch return E_FAIL; tasoller_init() catch return E_FAIL;
return S_OK; return S_OK;
} }
export fn chuni_io_jvs_poll(opbtn: ?[*]u8, beams: ?[*]u8) void { export fn chuni_io_jvs_poll(opbtn: ?[*]u8, beams: ?[*]u8) void {
if(cfg.?.chusan == 1 and builtin.cpu.arch != .x86_64) return; if (cfg.?.chusan == 1 and builtin.cpu.arch != .x86_64) return;
if(opbtn == null or beams == null) return; if (opbtn == null or beams == null) return;
if(GetAsyncKeyState(cfg.?.test_key) != 0 or (usb_in.?[3] & (1 << 7)) != 0) opbtn.?.* |= (1 << 0); if (GetAsyncKeyState(cfg.?.test_key) != 0 or (usb_in.?[3] & (1 << 7)) != 0) opbtn.?[0] |= (1 << 0);
if(GetAsyncKeyState(cfg.?.serv_key) != 0 or (usb_in.?[3] & (1 << 6)) != 0) opbtn.?.* |= (1 << 1); if (GetAsyncKeyState(cfg.?.serv_key) != 0 or (usb_in.?[3] & (1 << 6)) != 0) opbtn.?[0] |= (1 << 1);
beams.?.* |= usb_in.?[3] & 0b111111; beams.?[0] |= usb_in.?[3] & 0b111111;
} }
var coin_conter: c_ushort = 0; var coin_conter: c_ushort = 0;
var coin_prev_depressed = false; var coin_prev_depressed = false;
export fn chuni_io_jvs_read_coin_counter(total: ?*c_ushort) void { export fn chuni_io_jvs_read_coin_counter(total: ?*c_ushort) void {
if(cfg.?.chusan == 1 and builtin.cpu.arch != .x86_64) return; if (cfg.?.chusan == 1 and builtin.cpu.arch != .x86_64) return;
if(total == null) return; if (total == null) return;
const coin_depressed = GetAsyncKeyState(cfg.?.coin_key) != 0; const coin_depressed = GetAsyncKeyState(cfg.?.coin_key) != 0;
if(coin_depressed and !coin_prev_depressed) coin_conter += 1; if (coin_depressed and !coin_prev_depressed) coin_conter += 1;
coin_prev_depressed = coin_depressed; coin_prev_depressed = coin_depressed;
total.?.* = coin_conter; total.?.* = coin_conter;
} }
export fn chuni_io_slider_init() HRESULT { export fn chuni_io_slider_init() HRESULT {
if(cfg.?.chusan == 0) return S_OK; if (cfg.?.chusan == 0) return S_OK;
tasoller_init() catch return E_FAIL; tasoller_init() catch return E_FAIL;
return S_OK; return S_OK;
} }
export fn chuni_io_slider_start(callback: chuni_io_slider_callback_t) void { export fn chuni_io_slider_start(callback: chuni_io_slider_callback_t) void {
if(cfg.?.chusan == 1 and builtin.cpu.arch != .i386) return; if (cfg.?.chusan == 1 and builtin.cpu.arch != .x86) return;
if(callback == null) return; if (callback == null) return;
thread_op.lock(); thread_op.lock();
defer thread_op.unlock(); defer thread_op.unlock();
if(slider_thread == null) { if (slider_thread == null) {
slider_active = true; slider_active = true;
slider_thread = std.Thread.spawn(.{}, slider_thread_proc, .{callback}) catch |err| { slider_thread = std.Thread.spawn(.{}, slider_thread_proc, .{callback}) catch |err| {
std.log.err("[chuniio] Spawn slider thread: {any}", .{err}); std.log.err("[chuniio] Spawn slider thread: {any}", .{err});
@ -267,11 +258,11 @@ export fn chuni_io_slider_start(callback: chuni_io_slider_callback_t) void {
} }
export fn chuni_io_slider_stop() void { export fn chuni_io_slider_stop() void {
if(cfg.?.chusan == 1 and builtin.cpu.arch != .i386) return; if (cfg.?.chusan == 1 and builtin.cpu.arch != .x86) return;
thread_op.lock(); thread_op.lock();
defer thread_op.unlock(); defer thread_op.unlock();
if(slider_thread != null) { if (slider_thread != null) {
slider_active = false; slider_active = false;
slider_thread.?.join(); slider_thread.?.join();
slider_thread = null; slider_thread = null;
@ -279,52 +270,72 @@ export fn chuni_io_slider_stop() void {
} }
export fn chuni_io_slider_set_leds(rgb: ?[*]u8) void { export fn chuni_io_slider_set_leds(rgb: ?[*]u8) void {
if(cfg.?.chusan == 1 and builtin.cpu.arch != .i386) return; if (cfg.?.chusan == 1 and builtin.cpu.arch != .x86) return;
if(rgb == null) return; if (rgb == null) return;
var n: u32 = 0; var n: u32 = 0;
const out = usb_out[3..96]; const out = usb_out[3..96];
while(n < 31) : (n += 1) { while (n < 31) : (n += 1) {
out[n*3+0] = rgb.?[n*3+2]; out[n * 3 + 0] = rgb.?[n * 3 + 2];
out[n*3+1] = rgb.?[n*3+1]; out[n * 3 + 1] = rgb.?[n * 3 + 1];
out[n*3+2] = rgb.?[n*3+0]; out[n * 3 + 2] = rgb.?[n * 3 + 0];
} }
usb_out_op.lock(); usb_out_op.lock();
defer usb_out_op.unlock(); defer usb_out_op.unlock();
if(WinUsb_WritePipe(tasoller, 0x03, @ptrCast(*u8, &usb_out), usb_out.len, &n, null) == 0) { if (WinUsb_WritePipe(tasoller, 0x03, @as(*u8, @ptrCast(&usb_out)), usb_out.len, &n, null) == 0) {
std.log.warn("[chuniio] WinUsb_WritePipe: {any}", .{GetLastError()}); std.log.warn("[chuniio] WinUsb_WritePipe: {any}", .{GetLastError()});
} }
} }
export fn chuni_io_led_init() c_int { export fn chuni_io_led_init() HRESULT {
return 0; return S_OK;
} }
export fn chuni_io_led_set_colors(board: u8, rgb: ?[*]u8) void { export fn chuni_io_led_set_colors(board: u8, rgb: ?[*]u8) void {
if(cfg.?.chusan == 1 and builtin.cpu.arch != .i386) return; if (cfg.?.chusan == 1 and builtin.cpu.arch != .x86) return;
if(rgb == null) return; if (rgb == null) return;
var n: u32 = 0; var n: u32 = 0;
if(board == 0) { if (board == 0) {
const out = usb_out[96..168]; const out = usb_out[96..168];
while(n < 24) : (n += 1) { const led_limit: u32 = 3;
out[n*3+1] = rgb.?[0x96]; const i_limit: u32 = 8;
out[n*3+0] = rgb.?[0x97];
out[n*3+2] = rgb.?[0x98]; var led: u32 = 0;
} while (led < led_limit) {
} else if(board == 1) { var i: u32 = 0;
const out = usb_out[168..240]; while (i < i_limit) {
while(n < 24) : (n += 1) { n = (8 * led) + i;
out[n*3+1] = rgb.?[0xb4]; out[n * 3 + 1] = rgb.?[0x96 + (led * 3)];
out[n*3+0] = rgb.?[0xb5]; out[n * 3 + 0] = rgb.?[0x97 + (led * 3)];
out[n*3+2] = rgb.?[0xb6]; out[n * 3 + 2] = rgb.?[0x98 + (led * 3)];
i += 1;
} }
led += 1;
} }
} else if (board == 1) {
const out = usb_out[168..240];
const led_limit: u32 = 3;
const i_limit: u32 = 8;
var led: u32 = 0;
while (led < led_limit) {
var i: u32 = 0;
while (i < i_limit) {
n = (8 * led) + i;
out[n * 3 + 1] = rgb.?[0xb4 + (led * 3)];
out[n * 3 + 0] = rgb.?[0xb5 + (led * 3)];
out[n * 3 + 2] = rgb.?[0xb6 + (led * 3)];
i += 1;
}
led += 1;
}
}
usb_out_op.lock(); usb_out_op.lock();
defer usb_out_op.unlock(); defer usb_out_op.unlock();
if(WinUsb_WritePipe(tasoller, 0x03, @ptrCast(*u8, &usb_out), usb_out.len, &n, null) == 0) { if (WinUsb_WritePipe(tasoller, 0x03, @as(*u8, @ptrCast(&usb_out)), usb_out.len, &n, null) == 0) {
std.log.warn("[chuniio] WinUsb_WritePipe: {any}", .{GetLastError()}); std.log.warn("[chuniio] WinUsb_WritePipe: {any}", .{GetLastError()});
} }
} }

@ -1 +1 @@
Subproject commit a74c9dae6a1ccd361eb9a1d146a09c08d22f02b0 Subproject commit 6777f1db221d0cb50322842f558f03e3c3a4099f