Compare commits

...

4 Commits
trunk ... trunk

3 changed files with 49 additions and 4 deletions

View File

@ -1,6 +1,8 @@
# chuniio-yubideck
ChuniIO driver for YubiDeck FW 3.0.
This fork adds support for the RPunithm, an HID device that is compatible with the YubiDeck protocol.
Thanks to:
- [hlcm0](https://github.com/hlcm0/yubideck-io-firmware/blob/main/code/pico_yubideck_emu/report.h) and [4yn](https://github.com/4yn/slidershim/blob/main/src-slider_io/src/device/hid.rs#L257)
for YubiDeck protocol information
@ -18,6 +20,9 @@ path64=chuniio_yubideck_amdaemon.dll
;; For CHUNITHM PARADISE and older
path=chuniio_yubideck.dll
;; For RPunithm
rpunithm=1
[io3]
test=0x31
service=0x32
@ -26,8 +31,9 @@ coin=0x33
## USB Protocol
USB device: `1973:2001`, interface 0
Note: On the RPunithm, the slider is exposed on interface 2 as the device is reported as a composite device with multiple interfaces
- Endpoint IN Interrupt (0x81)
- Endpoint IN Interrupt (0x81) (Note: Endpoint is 0x83 on the RPunithm)
- Data length: 45 bytes
- `data[0]`: bits 0-5: beam {2, 1, 4, 3, 6, 5}
- `data[1]`: bits 0-2 for the 3 buttons (1 = pressed)

View File

@ -4,3 +4,8 @@ pub struct Configuration {
pub service_key: u32,
pub coin_key: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct DeviceConfiguration {
pub rpunithm: u32
}

View File

@ -31,6 +31,11 @@ cfg_if::cfg_if! {
if #[cfg(any(chuni, chusanapp))] {
use std::{sync::{atomic::AtomicBool, RwLock}, thread};
use lazy_static::lazy_static;
use winapi::um::{winbase::GetPrivateProfileIntA};
use create::configuration::DeviceConfiguration;
use anyhow::Result;
use ::log::info;
use rusb::{DeviceHandle, GlobalContext};
@ -41,6 +46,26 @@ cfg_if::cfg_if! {
static DEVICE: RwLock<Option<DeviceHandle<GlobalContext>>> = RwLock::new(None);
static SLIDER_ACTIVE: AtomicBool = AtomicBool::new(false);
lazy_static! {
static ref CONFIGURATION: DeviceConfiguration = {
let chuniio = CString::new("chuniio").unwrap();
let rpunithm = CString::new("rpunithm").unwrap();
let cfg_file = CString::new(".\\segatools.ini").unwrap();
unsafe {
DeviceConfiguration {
rpunithm: GetPrivateProfileIntA(
chuniio.as_ptr(),
rpunithm.as_ptr(),
// default always 0, just in-case a regular yubideck user uses this chuniio and has no idea what a rpunithm is
0,
cfg_file.as_ptr(),
)
}
}
};
}
}
}
@ -376,8 +401,15 @@ fn yubideck_init() -> Result<()> {
continue;
};
device.set_active_configuration(1)?;
device.claim_interface(0)?;
// the rpunithm exposes the slider interface on interface 2, as opposed to interface 0 on the yubideck
if CONFIGURATION.rpunithm == 1 {
device.set_active_configuration(1)?;
device.claim_interface(2)?;
}
else {
device.set_active_configuration(1)?;
device.claim_interface(0)?;
};
let mut global_device = DEVICE.write().unwrap();
*global_device = Some(device);
@ -413,8 +445,10 @@ fn input_thread_proc() {
};
let usb_out = unsafe { output_shmem.as_slice_mut() };
let endpoint = if CONFIGURATION.rpunithm == 1 {0x83} else {0x81};
loop {
if let Err(e) = device.read_interrupt(0x81, usb_in, TIMEOUT) {
if let Err(e) = device.read_interrupt(endpoint, usb_in, TIMEOUT) {
match e {
rusb::Error::NoDevice | rusb::Error::Io => {
error!("Controller disconnected.");