mirror of
https://gitea.tendokyu.moe/beerpsi/chuniio-rs.git
synced 2026-10-08 06:27:55 +03:00
rewrite with structs
This commit is contained in:
+41
-12
@@ -47,6 +47,7 @@ use anyhow::Result;
|
||||
#[cfg(any(chuni, chusanapp))]
|
||||
use rusb::{DeviceHandle, UsbContext};
|
||||
|
||||
use crate::TIMEOUT;
|
||||
use super::ReadType;
|
||||
|
||||
pub const DEVICE_VID: u16 = 0x1973;
|
||||
@@ -54,8 +55,22 @@ pub const DEVICE_PID: u16 = 0x2001;
|
||||
pub const DEVICE_INTERFACE: u8 = 0;
|
||||
pub const READ_TYPE: ReadType = ReadType::Interrupt;
|
||||
pub const READ_ENDPOINT: u8 = 0x81;
|
||||
pub const INPUT_MEMORY_SIZE: usize = 35;
|
||||
pub const OUTPUT_MEMORY_SIZE: usize = 63;
|
||||
pub const INPUT_MEMORY_SIZE: usize = std::mem::size_of::<LaveritaV2Input>();
|
||||
pub const OUTPUT_MEMORY_SIZE: usize = std::mem::size_of::<LaveritaV2Output>();
|
||||
|
||||
#[repr(C)]
|
||||
struct LaveritaV2Input {
|
||||
pub report_id: u8,
|
||||
pub ir: u8,
|
||||
pub buttons: u8,
|
||||
pub slider_pressure: [u8; 32],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct LaveritaV2Output {
|
||||
pub report_id: u8,
|
||||
pub slider_led: [u8; 62],
|
||||
}
|
||||
|
||||
/// Poll JVS input.
|
||||
///
|
||||
@@ -69,17 +84,20 @@ pub const OUTPUT_MEMORY_SIZE: usize = 63;
|
||||
#[cfg(any(chuni, amdaemon))]
|
||||
#[inline(always)]
|
||||
pub fn jvs_poll(input: &[u8]) -> (u8, u8) {
|
||||
let mixed_beams = input[1] & 0x3F;
|
||||
let input = input.as_ptr() as *const LaveritaV2Input;
|
||||
let mixed_beams = unsafe { (*input).ir & 0x3F };
|
||||
let beams = ((mixed_beams & 0xAA) >> 1) | ((mixed_beams & 0x55) << 1);
|
||||
|
||||
(input[2] & 3, beams)
|
||||
(unsafe { (*input).buttons & 3 }, beams)
|
||||
}
|
||||
|
||||
/// Checks the current state of the coin button (if there is one).
|
||||
#[cfg(any(chuni, amdaemon))]
|
||||
#[inline(always)]
|
||||
pub fn is_coin_button_pressed(input: &[u8]) -> bool {
|
||||
(input[2] & 4) != 0
|
||||
let input = input.as_ptr() as *const LaveritaV2Input;
|
||||
|
||||
unsafe { ((*input).buttons & 4) != 0 }
|
||||
}
|
||||
|
||||
/// Reads slider pressure information from USB input.
|
||||
@@ -103,10 +121,11 @@ pub fn is_coin_button_pressed(input: &[u8]) -> bool {
|
||||
#[cfg(any(chuni, chusanapp))]
|
||||
#[inline(always)]
|
||||
pub fn read_pressure_data(input: &[u8]) -> [u8; 32] {
|
||||
let input = input.as_ptr() as *const LaveritaV2Input;
|
||||
let mut pressure = [0u8; 32];
|
||||
|
||||
for i in 0..32 {
|
||||
pressure[i] = input[3 + 31 - i];
|
||||
pressure[i] = unsafe { (*input).slider_pressure[31 - i] };
|
||||
}
|
||||
|
||||
pressure
|
||||
@@ -117,7 +136,11 @@ pub fn read_pressure_data(input: &[u8]) -> [u8; 32] {
|
||||
#[cfg(any(chuni, chusanapp))]
|
||||
#[inline(always)]
|
||||
pub fn init_output_buffer(output: &mut [u8]) {
|
||||
output[0] = 0;
|
||||
let output = output.as_mut_ptr() as *mut LaveritaV2Output;
|
||||
|
||||
unsafe {
|
||||
(*output).report_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the RGB lighting on the slider. A slice `rgb` is provided, alternating
|
||||
@@ -129,13 +152,19 @@ pub fn set_slider_leds<T: UsbContext>(
|
||||
output: &mut [u8],
|
||||
brg: &[u8],
|
||||
) -> Result<()> {
|
||||
use crate::TIMEOUT;
|
||||
unsafe {
|
||||
let output = output.as_mut_ptr() as *mut LaveritaV2Output;
|
||||
|
||||
for (buf_chunk, state_chunk) in output[1..].chunks_mut(2).take(31).zip(brg.chunks(3)) {
|
||||
buf_chunk[0] = (state_chunk[0] << 3 & 0xE0) | (state_chunk[2] >> 3);
|
||||
buf_chunk[1] = (state_chunk[1] & 0xF8) | (state_chunk[0] >> 5);
|
||||
for (buf_chunk, state_chunk) in (*output).slider_led
|
||||
.chunks_mut(2)
|
||||
.take(31)
|
||||
.zip(brg.chunks(3))
|
||||
{
|
||||
buf_chunk[0] = (state_chunk[0] << 3 & 0xE0) | (state_chunk[2] >> 3);
|
||||
buf_chunk[1] = (state_chunk[1] & 0xF8) | (state_chunk[0] >> 5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
device.write_interrupt(0x02, output, TIMEOUT)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
+53
-22
@@ -31,6 +31,7 @@ use anyhow::Result;
|
||||
#[cfg(any(chuni, chusanapp))]
|
||||
use rusb::{DeviceHandle, UsbContext};
|
||||
|
||||
use crate::TIMEOUT;
|
||||
use super::ReadType;
|
||||
|
||||
pub const DEVICE_VID: u16 = 0x0518;
|
||||
@@ -38,8 +39,25 @@ pub const DEVICE_PID: u16 = 0x2022;
|
||||
pub const DEVICE_INTERFACE: u8 = 3;
|
||||
pub const READ_TYPE: ReadType = ReadType::Interrupt;
|
||||
pub const READ_ENDPOINT: u8 = 0x83;
|
||||
pub const INPUT_MEMORY_SIZE: usize = 34;
|
||||
pub const OUTPUT_MEMORY_SIZE: usize = 122;
|
||||
pub const INPUT_MEMORY_SIZE: usize = std::mem::size_of::<LaveritaV3Input>();
|
||||
pub const OUTPUT_MEMORY_SIZE: usize = std::mem::size_of::<LaveritaV3Output>();
|
||||
|
||||
#[repr(C)]
|
||||
struct LaveritaV3Input {
|
||||
pub ir_buttons: u8,
|
||||
pub magic: u8,
|
||||
pub slider_pressure: [u8; 32],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct LaveritaV3Output {
|
||||
pub report_id_zero: u8,
|
||||
pub slider_led_zero: [u8; 60],
|
||||
|
||||
pub report_id_one: u8,
|
||||
pub slider_led_one: [u8; 33],
|
||||
pub padding: [u8; 27],
|
||||
}
|
||||
|
||||
/// Poll JVS input.
|
||||
///
|
||||
@@ -54,10 +72,11 @@ pub const OUTPUT_MEMORY_SIZE: usize = 122;
|
||||
#[inline(always)]
|
||||
pub fn jvs_poll(input: &[u8]) -> (u8, u8) {
|
||||
// Taken from laverita_v2
|
||||
let mixed_beams = input[0] & 0x3F;
|
||||
let input = input.as_ptr() as *const LaveritaV3Input;
|
||||
let mixed_beams = unsafe { (*input).ir_buttons & 0x3F };
|
||||
let beams = ((mixed_beams & 0xAA) >> 1) | ((mixed_beams & 0x55) << 1);
|
||||
|
||||
(input[0] >> 6, beams)
|
||||
(unsafe { (*input).ir_buttons >> 6 }, beams)
|
||||
}
|
||||
|
||||
/// Checks the current state of the coin button (if there is one).
|
||||
@@ -90,10 +109,13 @@ pub fn is_coin_button_pressed(_input: &[u8]) -> bool {
|
||||
#[cfg(any(chuni, chusanapp))]
|
||||
#[inline(always)]
|
||||
pub fn read_pressure_data(input: &[u8]) -> [u8; 32] {
|
||||
let input = input.as_ptr() as *const LaveritaV3Input;
|
||||
let mut pressure = [0u8; 32];
|
||||
|
||||
for (i, p) in input[2..34].iter().enumerate() {
|
||||
pressure[if i % 2 == 0 { 30 - i } else { 32 - i }] = *p;
|
||||
unsafe {
|
||||
for (i, p) in (*input).slider_pressure.iter().enumerate() {
|
||||
pressure[if i % 2 == 0 { 30 - i } else { 32 - i }] = *p;
|
||||
}
|
||||
}
|
||||
|
||||
pressure
|
||||
@@ -104,9 +126,13 @@ pub fn read_pressure_data(input: &[u8]) -> [u8; 32] {
|
||||
#[cfg(any(chuni, chusanapp))]
|
||||
#[inline(always)]
|
||||
pub fn init_output_buffer(output: &mut [u8]) {
|
||||
let output = output.as_mut_ptr() as *mut LaveritaV3Output;
|
||||
|
||||
// Packet indices
|
||||
output[0] = 0;
|
||||
output[61] = 1;
|
||||
unsafe {
|
||||
(*output).report_id_zero = 0;
|
||||
(*output).report_id_one = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the RGB lighting on the slider. A slice `rgb` is provided, alternating
|
||||
@@ -118,21 +144,26 @@ pub fn set_slider_leds<T: UsbContext>(
|
||||
output: &mut [u8],
|
||||
brg: &[u8],
|
||||
) -> Result<()> {
|
||||
use crate::TIMEOUT;
|
||||
let output_data = output.as_mut_ptr() as *mut LaveritaV3Output;
|
||||
|
||||
for (output_chunk, brg_chunk) in output[1..61].chunks_mut(3).zip(brg.chunks(3).take(20)) {
|
||||
output_chunk[0] = brg_chunk[1]; // R
|
||||
output_chunk[1] = brg_chunk[2]; // G
|
||||
output_chunk[2] = brg_chunk[0]; // B
|
||||
}
|
||||
|
||||
for (output_chunk, brg_chunk) in output[62..95]
|
||||
.chunks_mut(3)
|
||||
.zip(brg.chunks(3).skip(20).take(11))
|
||||
{
|
||||
output_chunk[0] = brg_chunk[1]; // R
|
||||
output_chunk[1] = brg_chunk[2]; // G
|
||||
output_chunk[2] = brg_chunk[0]; // B
|
||||
unsafe {
|
||||
for (output_chunk, brg_chunk) in (*output_data).slider_led_zero
|
||||
.chunks_mut(3)
|
||||
.zip(brg.chunks(3).take(20))
|
||||
{
|
||||
output_chunk[0] = brg_chunk[1]; // R
|
||||
output_chunk[1] = brg_chunk[2]; // G
|
||||
output_chunk[2] = brg_chunk[0]; // B
|
||||
}
|
||||
|
||||
for (output_chunk, brg_chunk) in (*output_data).slider_led_one
|
||||
.chunks_mut(3)
|
||||
.zip(brg.chunks(3).skip(20).take(11))
|
||||
{
|
||||
output_chunk[0] = brg_chunk[1]; // R
|
||||
output_chunk[1] = brg_chunk[2]; // G
|
||||
output_chunk[2] = brg_chunk[0]; // B
|
||||
}
|
||||
}
|
||||
|
||||
device.write_interrupt(0x03, &output[0..61], TIMEOUT)?;
|
||||
|
||||
@@ -35,8 +35,24 @@ pub const DEVICE_PID: u16 = 0x1231;
|
||||
pub const DEVICE_INTERFACE: u8 = 0;
|
||||
pub const READ_TYPE: ReadType = ReadType::Interrupt;
|
||||
pub const READ_ENDPOINT: u8 = 0x84;
|
||||
pub const INPUT_MEMORY_SIZE: usize = 36;
|
||||
pub const OUTPUT_MEMORY_SIZE: usize = 114;
|
||||
pub const INPUT_MEMORY_SIZE: usize = std::mem::size_of::<TasollerPlusInput>();
|
||||
pub const OUTPUT_MEMORY_SIZE: usize = std::mem::size_of::<TasollerPlusOutput>();
|
||||
|
||||
#[repr(C)]
|
||||
pub struct TasollerPlusInput {
|
||||
pub magic: [u8; 3],
|
||||
pub ir_buttons: u8,
|
||||
pub slider_pressure: [u8; 32],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct TasollerPlusOutput {
|
||||
pub magic: [u8; 2],
|
||||
pub protocol_version: u8,
|
||||
pub slider_led: [u8; 93],
|
||||
pub left_air_tower_led: [u8; 9],
|
||||
pub right_air_tower_led: [u8; 9],
|
||||
}
|
||||
|
||||
/// Poll JVS input.
|
||||
///
|
||||
@@ -50,12 +66,13 @@ pub const OUTPUT_MEMORY_SIZE: usize = 114;
|
||||
#[cfg(any(chuni, amdaemon))]
|
||||
#[inline(always)]
|
||||
pub fn jvs_poll(input: &[u8]) -> (u8, u8) {
|
||||
let input = input.as_ptr() as *const TasollerPlusInput;
|
||||
// The order of the bits from MSB to LSB is
|
||||
// FN1 FN2 IR1 IR2 IR3 IR4 IR5 IR6
|
||||
// so when reversed, it becomes
|
||||
// IR6 IR5 IR4 IR3 IR2 IR1 FN2 FN1
|
||||
// which is basically what segatools expect.
|
||||
let reversed = input[3].reverse_bits();
|
||||
let reversed = unsafe { (*input).ir_buttons.reverse_bits() };
|
||||
let opbtn = reversed & 3;
|
||||
let beams = reversed >> 2;
|
||||
|
||||
@@ -91,9 +108,10 @@ pub fn is_coin_button_pressed(_input: &[u8]) -> bool {
|
||||
#[cfg(any(chuni, chusanapp))]
|
||||
#[inline(always)]
|
||||
pub fn read_pressure_data(input: &[u8]) -> [u8; 32] {
|
||||
let input = input.as_ptr() as *const TasollerPlusInput;
|
||||
let mut pressure = [0u8; 32];
|
||||
|
||||
pressure.copy_from_slice(&input[4..36]);
|
||||
pressure.copy_from_slice(unsafe { (*input).slider_pressure.as_slice() });
|
||||
|
||||
pressure
|
||||
}
|
||||
@@ -103,9 +121,13 @@ pub fn read_pressure_data(input: &[u8]) -> [u8; 32] {
|
||||
#[cfg(any(chuni, chusanapp))]
|
||||
#[inline(always)]
|
||||
pub fn init_output_buffer(output: &mut [u8]) {
|
||||
output[0] = 0x44;
|
||||
output[1] = 0x4C;
|
||||
output[2] = 0x02;
|
||||
let output = output.as_mut_ptr() as *mut TasollerPlusOutput;
|
||||
|
||||
unsafe {
|
||||
(*output).magic[0] = 0x44;
|
||||
(*output).magic[1] = 0x4C;
|
||||
(*output).protocol_version = 0x02;
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the RGB lighting on the slider. A slice `rgb` is provided, alternating
|
||||
@@ -117,7 +139,13 @@ pub fn set_slider_leds<T: UsbContext>(
|
||||
output: &mut [u8],
|
||||
brg: &[u8],
|
||||
) -> Result<()> {
|
||||
for (output_chunk, brg_chunk) in output[3..96].chunks_mut(3).take(31).zip(brg.chunks(3)) {
|
||||
let output_data = output.as_mut_ptr() as *mut TasollerPlusOutput;
|
||||
|
||||
for (output_chunk, brg_chunk) in unsafe { (*output_data).slider_led }
|
||||
.chunks_mut(3)
|
||||
.take(31)
|
||||
.zip(brg.chunks(3))
|
||||
{
|
||||
output_chunk[0] = brg_chunk[1]; // R
|
||||
output_chunk[1] = brg_chunk[2]; // G
|
||||
output_chunk[2] = brg_chunk[0]; // B
|
||||
@@ -150,10 +178,16 @@ pub fn set_led_colors<T: UsbContext>(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let output_start = if board == 0 { 0x60 } else { 0x69 };
|
||||
let output_data = output.as_mut_ptr() as *mut TasollerPlusOutput;
|
||||
let air_rgb_start = if board == 0 { 0x96 } else { 0xB4 };
|
||||
|
||||
output[output_start..output_start + 9].copy_from_slice(&rgb[air_rgb_start..air_rgb_start + 9]);
|
||||
unsafe {
|
||||
if board == 0 {
|
||||
(*output_data).left_air_tower_led.copy_from_slice(&rgb[air_rgb_start..air_rgb_start + 9]);
|
||||
} else {
|
||||
(*output_data).right_air_tower_led.copy_from_slice(&rgb[air_rgb_start..air_rgb_start + 9]);
|
||||
}
|
||||
}
|
||||
|
||||
device.write_bulk(0x03, output, TIMEOUT)?;
|
||||
|
||||
|
||||
+52
-13
@@ -34,21 +34,39 @@ pub const DEVICE_PID: u16 = 0x2333;
|
||||
pub const DEVICE_INTERFACE: u8 = 0;
|
||||
pub const READ_TYPE: ReadType = ReadType::Interrupt;
|
||||
pub const READ_ENDPOINT: u8 = 0x84;
|
||||
pub const INPUT_MEMORY_SIZE: usize = 36;
|
||||
pub const OUTPUT_MEMORY_SIZE: usize = 240;
|
||||
pub const INPUT_MEMORY_SIZE: usize = std::mem::size_of::<TasollerV2Input>();
|
||||
pub const OUTPUT_MEMORY_SIZE: usize = std::mem::size_of::<TasollerV2Output>();
|
||||
|
||||
#[repr(C)]
|
||||
struct TasollerV2Input {
|
||||
pub magic: [u8; 3],
|
||||
pub ir_buttons: u8,
|
||||
pub slider_pressure: [u8; 32],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct TasollerV2Output {
|
||||
pub magic: [u8; 3],
|
||||
pub slider_led: [u8; 93],
|
||||
pub left_air_tower_led: [u8; 72],
|
||||
pub right_air_tower_led: [u8; 72],
|
||||
}
|
||||
|
||||
#[cfg(any(chuni, amdaemon))]
|
||||
#[inline(always)]
|
||||
pub fn jvs_poll(input: &[u8]) -> (u8, u8) {
|
||||
let input = input.as_ptr() as *const TasollerV2Input;
|
||||
let ir_buttons = unsafe { (*input).ir_buttons };
|
||||
|
||||
// Drop the IR bits from the input...
|
||||
let opbtn = input[3] >> 6;
|
||||
let opbtn = ir_buttons >> 6;
|
||||
|
||||
// Swap bit position of FN1 and FN2 so that FN1 maps to Test (bit 0)
|
||||
// and FN2 maps to service (bit 1)
|
||||
let opbtn = (opbtn & 1) << 1 | (opbtn & 2) >> 1;
|
||||
|
||||
// Mask for last 6 bits to get the IR beams
|
||||
let beams = input[3] & 63;
|
||||
let beams = ir_buttons & 63;
|
||||
|
||||
(opbtn, beams)
|
||||
}
|
||||
@@ -63,9 +81,12 @@ pub fn is_coin_button_pressed(_input: &[u8]) -> bool {
|
||||
#[cfg(any(chuni, chusanapp))]
|
||||
#[inline(always)]
|
||||
pub fn read_pressure_data(input: &[u8]) -> [u8; 32] {
|
||||
let input = input.as_ptr() as *const TasollerV2Input;
|
||||
let slider_pressure = unsafe { (*input).slider_pressure.as_slice() };
|
||||
|
||||
let mut pressure = [0u8; 32];
|
||||
|
||||
for (i, p) in input.iter().skip(4).take(32).enumerate() {
|
||||
for (i, p) in slider_pressure.iter().enumerate() {
|
||||
pressure[if i % 2 == 0 { 30 - i } else { 32 - i }] = *p;
|
||||
}
|
||||
|
||||
@@ -75,9 +96,13 @@ pub fn read_pressure_data(input: &[u8]) -> [u8; 32] {
|
||||
#[cfg(any(chuni, chusanapp))]
|
||||
#[inline(always)]
|
||||
pub fn init_output_buffer(output: &mut [u8]) {
|
||||
output[0] = 0x42;
|
||||
output[1] = 0x4C;
|
||||
output[2] = 0;
|
||||
let output = output.as_mut_ptr() as *mut TasollerV2Output;
|
||||
|
||||
unsafe {
|
||||
(*output).magic[0] = 0x42;
|
||||
(*output).magic[1] = 0x4C;
|
||||
(*output).magic[2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(chuni, chusanapp))]
|
||||
@@ -87,7 +112,13 @@ pub fn set_slider_leds<T: UsbContext>(
|
||||
output: &mut [u8],
|
||||
brg: &[u8],
|
||||
) -> Result<()> {
|
||||
for (output_chunk, brg_chunk) in output[3..96].chunks_mut(3).take(31).zip(brg.chunks(3)) {
|
||||
let output_data = output.as_mut_ptr() as *mut TasollerV2Output;
|
||||
|
||||
for (output_chunk, brg_chunk) in unsafe { (*output_data).slider_led }
|
||||
.chunks_mut(3)
|
||||
.take(31)
|
||||
.zip(brg.chunks(3))
|
||||
{
|
||||
output_chunk[0] = brg_chunk[2]; // G
|
||||
output_chunk[1] = brg_chunk[1]; // R
|
||||
output_chunk[2] = brg_chunk[0]; // B
|
||||
@@ -110,13 +141,21 @@ pub fn set_led_colors<T: UsbContext>(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let air_rgb = if board == 0 { 0x96 } else { 0xB4 };
|
||||
let output_start = if board == 0 { 96 } else { 168 };
|
||||
let output_data = output.as_mut_ptr() as *mut TasollerV2Output;
|
||||
let air_rgb_start = if board == 0 { 0x96 } else { 0xB4 };
|
||||
let mut air_tower_buf = unsafe {
|
||||
if board == 0 {
|
||||
(*output_data).left_air_tower_led
|
||||
} else {
|
||||
(*output_data).right_air_tower_led
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
for (buf_chunk, state_chunk) in output[output_start..output_start + 72]
|
||||
for (buf_chunk, state_chunk) in air_tower_buf
|
||||
.chunks_mut(3)
|
||||
.take(24)
|
||||
.zip(rgb[air_rgb..air_rgb + 9].chunks(3).cycle().take(24))
|
||||
.zip(rgb[air_rgb_start..air_rgb_start + 9].chunks(3).cycle().take(24))
|
||||
{
|
||||
buf_chunk[0] = state_chunk[1]; // R
|
||||
buf_chunk[1] = state_chunk[0]; // B
|
||||
|
||||
Reference in New Issue
Block a user