use super::*;
use std::convert::TryFrom;
use std::collections::HashSet;
pub use global_packet_number::GlobalPacketNumber;
pub use joycon_features::{JoyConFeature, IMUConfig};
use std::sync::{Mutex, MutexGuard};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Rotation {
Portrait,
Landscape,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Rumble {
frequency: f32,
amplitude: f32,
}
impl Rumble {
pub fn frequency(&self) -> f32 {
self.frequency
}
pub fn amplitude(&self) -> f32 {
self.amplitude
}
pub fn new(freq: f32, amp: f32) -> Self {
let freq = if freq < 0.0 {
0.0
} else if freq > 1252.0 {
1252.0
} else {
freq
};
let amp = if amp < 0.0 {
0.0
} else if amp > 1.799 {
1.799
} else {
amp
};
Self {
frequency: freq,
amplitude: amp,
}
}
pub fn is_safe(&self) -> bool {
self.amplitude < 1.003
}
pub fn stop() -> Self {
Self {
frequency: 0.0,
amplitude: 0.0,
}
}
}
impl Into<[u8; 4]> for Rumble {
fn into(self) -> [u8; 4] {
let encoded_hex_freq = f32::round(f32::log2(self.frequency / 10.0) * 32.0) as u8;
let hf_freq: u16 = (encoded_hex_freq as u16).saturating_sub(0x60) * 4;
let lf_freq: u8 = encoded_hex_freq.saturating_sub(0x40);
let encoded_hex_amp = if self.amplitude > 0.23 {
f32::round(f32::log2(self.amplitude * 8.7) * 32.0) as u8
} else if self.amplitude > 0.12 {
f32::round(f32::log2(self.amplitude * 17.0) * 16.0) as u8
} else {
f32::round(f32::log2(self.amplitude * 17.0) * 16.0) as u8
};
let hf_amp: u16 = {
let hf_amp: u16 = (encoded_hex_freq as u16 - 0x60) * 4;
if hf_amp > 0x01FC {
0x01FC
} else { hf_amp }
};
let lf_amp: u8 = {
let lf_amp = encoded_hex_amp / 2 + 64;
if lf_amp > 0x7F {
0x7F
} else { lf_amp }
};
let mut buf = [0u8; 4];
buf[0] = (hf_freq & 0xFF) as u8;
buf[1] = (hf_amp + (hf_freq.wrapping_shr(8) & 0xFF)) as u8;
buf[2] = lf_freq.saturating_add(lf_amp.wrapping_shr(8) & 0xFF);
buf[3] = lf_amp & 0xFF;
buf
}
}
pub mod joycon_features {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum JoyConFeature {
IMUFeature(IMUConfig),
Vibration,
}
pub use imu_sensitivity::IMUConfig;
pub mod imu_sensitivity {
use std::hash::Hash;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GyroscopeSensitivity {
PM250dps = 0x00,
PM500dps = 0x01,
PM1000dps = 0x02,
PM2000dps = 0x03,
}
impl Default for GyroscopeSensitivity {
fn default() -> Self {
GyroscopeSensitivity::PM2000dps
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AccelerometerSensitivity {
PM8G = 0x00,
PM4G = 0x01,
PM2G = 0x02,
PM16G = 0x03,
}
impl Default for AccelerometerSensitivity {
fn default() -> Self {
AccelerometerSensitivity::PM8G
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GyroscopePerformanceRate {
F833Hz = 0x00,
F208Hz = 0x01,
}
impl Default for GyroscopePerformanceRate {
fn default() -> Self {
GyroscopePerformanceRate::F208Hz
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AccelerometerAntiAliasingFilterBandwidth {
F200Hz = 0x00,
F100Hz = 0x01,
}
impl Default for AccelerometerAntiAliasingFilterBandwidth {
fn default() -> Self {
AccelerometerAntiAliasingFilterBandwidth::F100Hz
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct IMUConfig {
pub gyroscope_sensitivity: GyroscopeSensitivity,
pub accelerometer_sensitivity: AccelerometerSensitivity,
pub gyroscope_performance_rate: GyroscopePerformanceRate,
pub accelerometer_anti_aliasing_filter_bandwidth: AccelerometerAntiAliasingFilterBandwidth,
}
impl Into<[u8; 4]> for IMUConfig {
fn into(self) -> [u8; 4] {
let IMUConfig {
gyroscope_sensitivity,
accelerometer_sensitivity,
gyroscope_performance_rate,
accelerometer_anti_aliasing_filter_bandwidth,
} = self;
[
gyroscope_sensitivity as u8,
accelerometer_sensitivity as u8,
gyroscope_performance_rate as u8,
accelerometer_anti_aliasing_filter_bandwidth as u8
]
}
}
impl Hash for IMUConfig {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
0.hash(state);
}
}
}
}
mod global_packet_number {
use std::ops::Add;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GlobalPacketNumber(pub u8);
impl GlobalPacketNumber {
pub fn next(self) -> GlobalPacketNumber {
self + GlobalPacketNumber(1)
}
}
impl Default for GlobalPacketNumber {
fn default() -> Self {
GlobalPacketNumber(0x0)
}
}
impl Add for GlobalPacketNumber {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
let (GlobalPacketNumber(raw), GlobalPacketNumber(raw_rhs))
= (self, rhs);
let res = raw.wrapping_add(raw_rhs);
GlobalPacketNumber(res)
}
}
impl Into<u8> for GlobalPacketNumber {
fn into(self) -> u8 {
self.0
}
}
}
#[derive(Debug)]
pub struct SimpleJoyConDriver {
pub joycon: Arc<Mutex<JoyConDevice>>,
pub rotation: Rotation,
rumble: (Option<Rumble>, Option<Rumble>),
enabled_features: HashSet<JoyConFeature>,
global_packet_number: GlobalPacketNumber,
}
impl SimpleJoyConDriver {
pub fn new(joycon: &Arc<Mutex<JoyConDevice>>) -> JoyConResult<Self> {
let mut driver = Self {
joycon: Arc::clone(joycon),
rotation: Rotation::Portrait,
rumble: (None, None),
enabled_features: HashSet::new(),
global_packet_number: GlobalPacketNumber::default(),
};
driver.reset()?;
Ok(driver)
}
pub fn joycon(&self) -> MutexGuard<JoyConDevice> {
match self.joycon.lock() {
Ok(joycon) => joycon,
Err(poisoned) => poisoned.into_inner(),
}
}
}
pub trait JoyConDriver {
const ACK_TRY: usize = 5;
fn write(&self, data: &[u8]) -> JoyConResult<usize>;
fn read(&self, buf: &mut [u8]) -> JoyConResult<usize>;
fn global_packet_number(&self) -> u8;
fn increase_global_packet_number(&mut self);
fn set_rumble_status(&mut self, rumble_l_r: (Option<Rumble>, Option<Rumble>));
fn rumble(&mut self, rumble_l_r: (Option<Rumble>, Option<Rumble>)) -> JoyConResult<usize> {
self.set_rumble_status(rumble_l_r);
self.send_command_raw(Command::Rumble as u8, 0, &[])
}
fn get_rumble_status(&self) -> (Option<Rumble>, Option<Rumble>);
fn send_command_raw(&mut self, command: u8, sub_command: u8, data: &[u8]) -> JoyConResult<usize> {
let mut buf = [0x0; 0x40];
buf[0] = command;
buf[1] = self.global_packet_number();
self.increase_global_packet_number();
let (rumble_l, rumble_r) = self.get_rumble_status();
if let Some(rumble_l) = rumble_l {
let rumble_left: [u8; 4] = rumble_l.into();
buf[2..6].copy_from_slice(&rumble_left);
}
if let Some(rumble_r) = rumble_r {
let rumble_right: [u8; 4] = rumble_r.into();
buf[6..10].copy_from_slice(&rumble_right);
}
buf[10] = sub_command;
buf[11..11 + data.len()].copy_from_slice(data);
self.write(&buf)
}
fn send_sub_command_raw(&mut self, sub_command: u8, data: &[u8]) -> JoyConResult<[u8; 362]> {
use input_report_mode::sub_command_mode::AckByte;
self.send_command_raw(1, sub_command, data)?;
std::iter::repeat(())
.take(Self::ACK_TRY)
.flat_map(|()| {
let mut buf = [0u8; 362];
self.read(&mut buf).ok()?;
let ack_byte = AckByte::from(buf[13]);
match ack_byte {
AckByte::Ack { .. } => Some(buf),
AckByte::Nack => None
}
})
.next()
.ok_or(JoyConError::SubCommandError(sub_command, Vec::new()))
}
fn send_command(&mut self, command: Command, sub_command: SubCommand, data: &[u8]) -> JoyConResult<usize> {
let command = command as u8;
let sub_command = sub_command as u8;
self.send_command_raw(command, sub_command, data)
}
fn send_sub_command(&mut self, sub_command: SubCommand, data: &[u8]) -> JoyConResult<[u8; 362]> {
self.send_sub_command_raw(sub_command as u8, data)
}
fn reset(&mut self) -> JoyConResult<()> {
self.send_sub_command(SubCommand::EnableIMU, &[0x00])?;
self.send_sub_command(SubCommand::EnableVibration, &[0x00])?;
Ok(())
}
fn enable_feature(&mut self, feature: JoyConFeature) -> JoyConResult<()>;
fn enabled_features(&self) -> &HashSet<JoyConFeature>;
fn devices(&self) -> Vec<Arc<Mutex<JoyConDevice>>>;
}
impl JoyConDriver for SimpleJoyConDriver {
fn write(&self, data: &[u8]) -> JoyConResult<usize> {
let joycon = self.joycon();
Ok(joycon.write(data)?)
}
fn read(&self, buf: &mut [u8]) -> JoyConResult<usize> {
Ok(self.joycon().read(buf)?)
}
fn global_packet_number(&self) -> u8 {
self.global_packet_number.into()
}
fn increase_global_packet_number(&mut self) {
self.global_packet_number = self.global_packet_number.next();
}
fn set_rumble_status(&mut self, rumble_l_r: (Option<Rumble>, Option<Rumble>)) {
self.rumble = rumble_l_r;
}
fn get_rumble_status(&self) -> (Option<Rumble>, Option<Rumble>) {
self.rumble
}
fn enable_feature(&mut self, feature: JoyConFeature) -> JoyConResult<()> {
match feature {
JoyConFeature::IMUFeature(feature) => {
let data: [u8; 4] = feature.into();
self.send_sub_command(SubCommand::EnableIMU, &[0x01])?;
self.send_sub_command(SubCommand::SetIMUSensitivity, &data)?;
}
JoyConFeature::Vibration => {
self.send_sub_command(SubCommand::EnableVibration, &[0x01])?;
}
}
self.enabled_features.insert(feature);
Ok(())
}
fn enabled_features(&self) -> &HashSet<JoyConFeature> {
&self.enabled_features
}
fn devices(&self) -> Vec<Arc<Mutex<JoyConDevice>>> {
vec![Arc::clone(&self.joycon)]
}
}
pub mod input_report_mode {
use super::*;
pub use common::*;
pub use self::{sub_command_mode::SubCommandMode, standard_full_mode::StandardFullMode, simple_hid_mode::SimpleHIDMode};
use std::convert::TryFrom;
use std::ops::{Deref, DerefMut};
use std::hash::Hash;
mod common {
use super::*;
use std::convert::TryFrom;
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum BatteryLevel {
Empty,
Critical,
Low,
Medium,
Full,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub struct Battery {
pub level: BatteryLevel,
pub is_charging: bool,
}
impl TryFrom<u8> for Battery {
type Error = JoyConError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
let is_charging = value % 2 == 1;
let value = if is_charging {
value - 1
} else { value };
let level = match value {
0 => BatteryLevel::Empty,
2 => BatteryLevel::Critical,
4 => BatteryLevel::Low,
6 => BatteryLevel::Medium,
8 => BatteryLevel::Full,
_ => Err(JoyConReportError::InvalidStandardInputReport(InvalidStandardInputReport::Battery(value)))?
};
Ok(Battery { level, is_charging })
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum Device {
JoyCon,
ProConOrChargingGrip,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub struct ConnectionInfo {
device: Device,
is_powered: bool,
}
impl TryFrom<u8> for ConnectionInfo {
type Error = JoyConError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
let device = match (value >> 1) & 3 {
3 => Device::JoyCon,
0 => Device::ProConOrChargingGrip,
_ => Err(InvalidStandardInputReport::ConnectionInfo(value))?
};
let is_powered = (value & 1) == 1;
Ok(ConnectionInfo {
device,
is_powered,
})
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct PushedButtons {
right: Vec<Buttons>,
shared: Vec<Buttons>,
left: Vec<Buttons>,
}
impl PushedButtons {
const RIGHT_BUTTONS: [Buttons; 8] = [
Buttons::Y,
Buttons::X,
Buttons::B,
Buttons::A,
Buttons::SR,
Buttons::SL,
Buttons::R,
Buttons::ZR,
];
const SHARED_BUTTONS: [Buttons; 8] = [
Buttons::Minus,
Buttons::Plus,
Buttons::RStick,
Buttons::LStick,
Buttons::Home,
Buttons::Capture,
Buttons::Capture,
Buttons::ChargingGrip,
];
const LEFT_BUTTONS: [Buttons; 8] = [
Buttons::Down,
Buttons::Up,
Buttons::Right,
Buttons::Left,
Buttons::SR,
Buttons::SL,
Buttons::L,
Buttons::ZL,
];
}
impl From<[u8; 3]> for PushedButtons {
fn from(value: [u8; 3]) -> Self {
let right_val = value[0];
let shared_val = value[1];
let left_val = value[2];
let right = PushedButtons::RIGHT_BUTTONS.iter()
.enumerate()
.filter(|(idx, _)| {
let idx = 2u8.pow(*idx as u32) as u8;
right_val & idx == idx
})
.map(|(_, b)| b.clone())
.collect();
let shared = PushedButtons::SHARED_BUTTONS.iter()
.enumerate()
.filter(|(idx, _)| {
let idx = 2u8.pow(*idx as u32) as u8;
shared_val & idx == idx
})
.map(|(_, b)| b.clone())
.collect();
let left = PushedButtons::LEFT_BUTTONS.iter()
.enumerate()
.filter(|(idx, _)| {
let idx = 2u8.pow(*idx as u32) as u8;
left_val & idx == idx
})
.map(|(_, b)| b.clone())
.collect();
PushedButtons {
right,
shared,
left,
}
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct AnalogStickData {
horizontal: u16,
vertical: u16,
}
impl From<[u8; 3]> for AnalogStickData {
fn from(value: [u8; 3]) -> Self {
let horizontal = value[0] as u16 | ((value[1] as u16 & 0xF) << 8);
let vertical = (value[1] as u16 >> 4) | ((value[2] as u16) << 4);
Self {
horizontal,
vertical,
}
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct CommonReport {
input_report_id: u8,
timer: u8,
battery: Battery,
connection_info: ConnectionInfo,
pushed_buttons: PushedButtons,
left_analog_stick_data: AnalogStickData,
right_analog_stick_data: AnalogStickData,
vibrator_input_report: u8,
}
impl TryFrom<[u8; 13]> for CommonReport {
type Error = JoyConError;
fn try_from(report: [u8; 13]) -> JoyConResult<CommonReport> {
let input_report_id = report[0];
let timer = report[1];
let (battery, connection_info) = {
let value = report[2];
let high_nibble = value / 16;
let low_nibble = value % 16;
(Battery::try_from(high_nibble)?, ConnectionInfo::try_from(low_nibble)?)
};
let pushed_buttons = {
let array = [report[3], report[4], report[5]];
PushedButtons::from(array)
};
let left_analog_stick_data = {
let array = [report[6], report[7], report[8]];
AnalogStickData::from(array)
};
let right_analog_stick_data = {
let array = [report[9], report[10], report[11]];
AnalogStickData::from(array)
};
let vibrator_input_report = report[12];
Ok(CommonReport {
input_report_id,
timer,
battery,
connection_info,
pushed_buttons,
left_analog_stick_data,
right_analog_stick_data,
vibrator_input_report,
})
}
}
}
pub trait InputReportMode<D: JoyConDriver>: Deref<Target=D> + DerefMut<Target=D> {
type Mode: InputReportMode<D>;
type Report: TryFrom<[u8; 362], Error=JoyConError>;
type ArgsType: AsRef<[u8]>;
const SUB_COMMAND: SubCommand;
const ARGS: Self::ArgsType;
fn setup(driver: D) -> JoyConResult<D>;
fn construct(driver: D) -> Self::Mode;
fn new(driver: D) -> JoyConResult<Self::Mode> {
let mut driver = Self::Mode::setup(driver)?;
driver.send_sub_command(Self::SUB_COMMAND, Self::ARGS.as_ref())?;
Ok(Self::construct(driver))
}
fn read_input_report(&self) -> JoyConResult<Self::Report> {
let mut buf = [0u8; 362];
self.read(&mut buf)?;
Self::Report::try_from(buf)
}
}
pub struct StandardInputReport<EX: TryFrom<[u8; 349], Error=JoyConError>> {
pub common: CommonReport,
pub extra: EX,
}
impl<EX> TryFrom<[u8; 362]> for StandardInputReport<EX>
where EX: TryFrom<[u8; 349], Error=JoyConError>
{
type Error = JoyConError;
fn try_from(value: [u8; 362]) -> Result<Self, Self::Error> {
let common = {
let mut data = [0x00; 13];
data.copy_from_slice(&value[0..13]);
CommonReport::try_from(data)?
};
let extra = {
let mut data = [0x00; 349];
data.copy_from_slice(&value[13..362]);
EX::try_from(data)?
};
Ok(StandardInputReport {
common,
extra,
})
}
}
impl<EX> Debug for StandardInputReport<EX>
where EX: TryFrom<[u8; 349], Error=JoyConError> + Debug
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"StandardInputReport {{ common: {:?}, extra: {:?} }}",
&self.common,
&self.extra,
)
}
}
impl<EX> Clone for StandardInputReport<EX>
where EX: TryFrom<[u8; 349], Error=JoyConError> + Clone
{
fn clone(&self) -> Self {
let common = self.common.clone();
let extra = self.extra.clone();
StandardInputReport { common, extra }
}
}
impl<EX> Hash for StandardInputReport<EX>
where EX: TryFrom<[u8; 349], Error=JoyConError> + Hash
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.common.hash(state);
self.extra.hash(state);
}
}
impl<EX> PartialEq for StandardInputReport<EX>
where EX: TryFrom<[u8; 349], Error=JoyConError> + PartialEq
{
fn eq(&self, other: &Self) -> bool {
self.common.eq(&other.common)
&& self.extra.eq(&other.extra)
}
}
impl<EX> Eq for StandardInputReport<EX>
where EX: TryFrom<[u8; 349], Error=JoyConError> + Eq
{}
pub mod sub_command_mode {
use super::*;
use std::marker::PhantomData;
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum AckByte {
Ack {
data_type: u8
},
Nack,
}
impl From<u8> for AckByte {
fn from(u: u8) -> Self {
if u >> 7 == 1 {
AckByte::Ack {
data_type: u & 0x7F
}
} else {
AckByte::Nack
}
}
}
pub trait SubCommandReplyData: TryFrom<[u8; 35], Error=JoyConError> {
type ArgsType: AsRef<[u8]>;
const SUB_COMMAND: SubCommand;
const ARGS: Self::ArgsType;
fn once<D>(driver: &mut D) -> JoyConResult<StandardInputReport<SubCommandReport<Self>>>
where Self: std::marker::Sized,
D: JoyConDriver
{
let reply = driver.send_sub_command(Self::SUB_COMMAND, Self::ARGS.as_ref())?;
StandardInputReport::try_from(reply)
}
}
#[derive(Clone)]
pub struct SubCommandReport<RD>
where RD: SubCommandReplyData
{
pub ack_byte: AckByte,
pub sub_command_id: u8,
pub reply: RD,
}
impl<RD> Debug for SubCommandReport<RD>
where RD: SubCommandReplyData + Debug
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f,
"SubCommandReport {{\
\n\t ack_byte: {:?},
\n\t sub_command_id: {},
\n\t reply: {:?},
}}",
self.ack_byte,
self.sub_command_id,
&self.reply
)
}
}
impl<RD> TryFrom<[u8; 349]> for SubCommandReport<RD>
where RD: SubCommandReplyData
{
type Error = JoyConError;
fn try_from(value: [u8; 349]) -> Result<Self, Self::Error> {
let ack_byte = AckByte::from(value[0]);
let sub_command_id = value[1];
let mut reply = [0x00; 35];
reply.copy_from_slice(&value[2..37]);
let reply = RD::try_from(reply)?;
Ok(SubCommandReport {
ack_byte,
sub_command_id,
reply,
})
}
}
pub struct SubCommandMode<D, RD>
where D: JoyConDriver, RD: SubCommandReplyData
{
driver: D,
_phantom: PhantomData<RD>,
}
impl<D, RD> Deref for SubCommandMode<D, RD>
where D: JoyConDriver,
RD: SubCommandReplyData
{
type Target = D;
fn deref(&self) -> &Self::Target {
&self.driver
}
}
impl<D, RD> DerefMut for SubCommandMode<D, RD>
where D: JoyConDriver,
RD: SubCommandReplyData
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.driver
}
}
impl<D, RD> InputReportMode<D> for SubCommandMode<D, RD>
where D: JoyConDriver,
RD: SubCommandReplyData
{
type Mode = SubCommandMode<D, RD>;
type Report = StandardInputReport<SubCommandReport<RD>>;
type ArgsType = RD::ArgsType;
const SUB_COMMAND: SubCommand = RD::SUB_COMMAND;
const ARGS: Self::ArgsType = RD::ARGS;
fn setup(driver: D) -> JoyConResult<D> {
Ok(driver)
}
fn construct(driver: D) -> Self::Mode {
SubCommandMode {
driver,
_phantom: PhantomData,
}
}
}
}
pub mod standard_full_mode {
use super::*;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct AxisData {
pub accel_x: i16,
pub accel_y: i16,
pub accel_z: i16,
pub gyro_1: i16,
pub gyro_2: i16,
pub gyro_3: i16,
}
impl From<[u8; 12]> for AxisData {
fn from(value: [u8; 12]) -> Self {
let accel_x = i16::from_le_bytes([value[0], value[1]]);
let accel_y = i16::from_le_bytes([value[2], value[3]]);
let accel_z = i16::from_le_bytes([value[4], value[5]]);
let gyro_1 = i16::from_le_bytes([value[6], value[7]]);
let gyro_2 = i16::from_le_bytes([value[8], value[9]]);
let gyro_3 = i16::from_le_bytes([value[10], value[11]]);
AxisData {
accel_x,
accel_y,
accel_z,
gyro_1,
gyro_2,
gyro_3,
}
}
}
#[derive(Debug, Clone)]
pub struct IMUData {
data: [AxisData; 3]
}
impl TryFrom<[u8; 349]> for IMUData {
type Error = JoyConError;
fn try_from(value: [u8; 349]) -> Result<Self, Self::Error> {
let latest = {
let mut latest = [0x00; 12];
latest.copy_from_slice(&value[0..12]);
latest
};
let a_5ms_older = {
let mut latest = [0x00; 12];
latest.copy_from_slice(&value[12..24]);
latest
};
let a_10ms_older = {
let mut latest = [0x00; 12];
latest.copy_from_slice(&value[24..36]);
latest
};
let axis_data = [
AxisData::from(latest),
AxisData::from(a_5ms_older),
AxisData::from(a_10ms_older)
];
Ok(IMUData {
data: axis_data,
})
}
}
pub struct StandardFullMode<D: JoyConDriver> {
driver: D,
}
impl<D> Deref for StandardFullMode<D>
where D: JoyConDriver
{
type Target = D;
fn deref(&self) -> &Self::Target {
&self.driver
}
}
impl<D> DerefMut for StandardFullMode<D>
where D: JoyConDriver
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.driver
}
}
impl<D> InputReportMode<D> for StandardFullMode<D>
where D: JoyConDriver
{
type Mode = StandardFullMode<D>;
type Report = StandardInputReport<IMUData>;
type ArgsType = [u8; 1];
const SUB_COMMAND: SubCommand = SubCommand::SetInputReportMode;
const ARGS: Self::ArgsType = [0x30];
fn setup(driver: D) -> JoyConResult<D> {
let mut driver = driver;
let imf_enabled = driver.enabled_features()
.iter()
.any(|jf| match jf {
JoyConFeature::IMUFeature(_) => true,
_ => false,
});
if !imf_enabled {
driver.enable_feature(JoyConFeature::IMUFeature(IMUConfig::default()))?;
}
Ok(driver)
}
fn construct(driver: D) -> Self::Mode {
Self::Mode {
driver
}
}
}
}
pub mod simple_hid_mode {
use super::*;
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
pub enum SimpleHIDButton {
Down,
Right,
Left,
Up,
SL,
SR,
Minus,
Plus,
LeftStick,
RightStick,
Home,
Capture,
L_R,
ZL_ZR,
}
const BUTTON1: [SimpleHIDButton; 6] = {
use SimpleHIDButton::*;
[Down, Right, Left, Up, SL, SR, ]
};
const BUTTON2: [SimpleHIDButton; 8] = {
use SimpleHIDButton::*;
[Minus, Plus, LeftStick, RightStick, Home, Capture, L_R, ZL_ZR, ]
};
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
pub enum StickDirection {
Up,
UpperRight,
Right,
BottomRight,
Bottom,
BottomLeft,
Left,
UpperLeft,
Neutral,
}
impl TryFrom<u8> for StickDirection {
type Error = JoyConError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use StickDirection::*;
let button = match value {
0 => Up,
1 => UpperRight,
2 => Right,
3 => BottomRight,
4 => Bottom,
5 => BottomLeft,
6 => Left,
7 => UpperLeft,
8 => Neutral,
v => Err(InvalidSimpleHIDReport::InvalidStickDirection(v))?,
};
Ok(button)
}
}
#[derive(Debug, Clone)]
pub struct SimpleHIDReport {
pub input_report_id: u8,
pub pushed_buttons: Vec<SimpleHIDButton>,
pub stick_direction: StickDirection,
pub filter_data: [u8; 8],
}
impl TryFrom<[u8; 12]> for SimpleHIDReport {
type Error = JoyConError;
fn try_from(value: [u8; 12]) -> Result<Self, Self::Error> {
let input_report_id = value[0];
let pushed_buttons = {
let byte_1 = value[1];
let byte_2 = value[2];
[].iter()
.chain(
BUTTON1.iter()
.enumerate()
.filter(|(idx, _)| {
let idx = 2u8.pow(*idx as u32) as u8;
byte_1 & idx == idx
})
.map(|(_, b)| b)
)
.chain(
BUTTON2.iter()
.enumerate()
.filter(|(idx, _)| {
let idx = 2u8.pow(*idx as u32) as u8;
byte_2 & idx == idx
})
.map(|(_, b)| b)
)
.cloned()
.collect()
};
let stick_direction = StickDirection::try_from(value[3])?;
let filter_data = {
let mut buf = [0u8; 8];
buf.copy_from_slice(&value[4..12]);
buf
};
Ok(SimpleHIDReport {
input_report_id,
pushed_buttons,
stick_direction,
filter_data,
})
}
}
impl TryFrom<[u8; 362]> for SimpleHIDReport {
type Error = JoyConError;
fn try_from(value: [u8; 362]) -> Result<Self, Self::Error> {
let mut buf = [0u8; 12];
buf.copy_from_slice(&value[0..12]);
Self::try_from(buf)
}
}
pub struct SimpleHIDMode<D: JoyConDriver> {
driver: D,
}
impl<D: JoyConDriver> Deref for SimpleHIDMode<D> {
type Target = D;
fn deref(&self) -> &Self::Target {
&self.driver
}
}
impl<D: JoyConDriver> DerefMut for SimpleHIDMode<D> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.driver
}
}
impl<D> InputReportMode<D> for SimpleHIDMode<D>
where D: JoyConDriver
{
type Mode = SimpleHIDMode<D>;
type Report = SimpleHIDReport;
type ArgsType = [u8; 1];
const SUB_COMMAND: SubCommand = SubCommand::SetInputReportMode;
const ARGS: Self::ArgsType = [0x3F];
fn setup(driver: D) -> JoyConResult<D> {
Ok(driver)
}
fn construct(driver: D) -> Self::Mode {
Self::Mode {
driver
}
}
}
}
}
pub mod lights {
use super::{*, input_report_mode::sub_command_mode::*};
use crate::joycon::driver::input_report_mode::StandardInputReport;
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum LightUp {
LED0 = 0x01,
LED1 = 0x02,
LED2 = 0x04,
LED3 = 0x08,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum Flash {
LED0 = 0x10,
LED1 = 0x20,
LED2 = 0x40,
LED3 = 0x80,
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct LightsStatus {
pub light_up: Vec<LightUp>,
pub flash: Vec<Flash>,
}
const LIGHT_UP: [LightUp; 4] =
[LightUp::LED0, LightUp::LED1, LightUp::LED2, LightUp::LED3];
const FLASH: [Flash; 4] =
[Flash::LED0, Flash::LED1, Flash::LED2, Flash::LED3];
impl TryFrom<[u8; 35]> for LightsStatus {
type Error = JoyConError;
fn try_from(value: [u8; 35]) -> Result<Self, Self::Error> {
let value = value[0];
let light_up = LIGHT_UP.iter()
.filter(|&&l| {
let light = l as u8;
value & light == light
})
.cloned()
.collect();
let flash = FLASH.iter()
.filter(|&&f| {
let flash = f as u8;
value & flash == flash
})
.cloned()
.collect();
Ok(LightsStatus { light_up, flash })
}
}
impl SubCommandReplyData for LightsStatus {
type ArgsType = [u8; 0];
const SUB_COMMAND: SubCommand = SubCommand::GetPlayerLights;
const ARGS: Self::ArgsType = [];
}
pub mod home_button {
use super::*;
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct u4(u8);
impl u4 {
const MAX: Self = u4(15);
}
impl From<u8> for u4 {
fn from(v: u8) -> Self {
let value = u4(v);
if value > Self::MAX {
Self::MAX
} else { value }
}
}
impl Into<u8> for u4 {
fn into(self) -> u8 {
self.0
}
}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct LightEmittingPhase {
pub led_intensity: u4,
pub fading_transition_duration: u4,
pub led_duration: u4,
}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct LightEmittingPattern {
phases_len: Option<u4>,
phases: Vec<LightEmittingPhase>,
global_mini_cycle_duration: u4,
led_start_intensity: u4,
repeat_count: u4,
}
impl LightEmittingPattern {
pub fn new(global_mini_cycle_duration: u8, led_start_intensity: u8, repeat_count: u4) -> Self {
let global_mini_cycle_duration = if global_mini_cycle_duration == 0 {
0.into()
} else {
((global_mini_cycle_duration - 7) / 12 + 1).into()
};
let led_start_intensity = {
let saturated = if 100 < led_start_intensity { 100 } else { led_start_intensity } as f32;
((saturated / 6.25) as u8).into()
};
LightEmittingPattern {
phases_len: None,
phases: Vec::with_capacity(15),
global_mini_cycle_duration,
led_start_intensity,
repeat_count,
}
}
pub fn push_phase(&mut self, phase: LightEmittingPhase) {
self.phases.push(phase);
}
pub fn add_phase(mut self, led_intensity: u8, fading_transition_duration: u16, led_duration: u16) -> Self {
let led_intensity = {
let saturated = if 100 < led_intensity { 100 } else { led_intensity } as f32;
((saturated / 6.25) as u8).into()
};
let fading_transition_duration: u4 = {
let gmcd: u8 = self.global_mini_cycle_duration.into();
(fading_transition_duration / gmcd as u16) as u8
}.into();
let led_duration = {
let gmcd: u8 = self.global_mini_cycle_duration.into();
(led_duration / gmcd as u16) as u8
}.into();
let phase = LightEmittingPhase {
led_intensity,
fading_transition_duration,
led_duration,
};
self.push_phase(phase);
self
}
pub fn emit_first_phase(&self) -> Self {
let mut pattern = self.clone();
pattern.phases_len = Some(0u8.into());
pattern.repeat_count = 0u8.into();
pattern
}
}
impl Into<[u8; 25]> for LightEmittingPattern {
fn into(self) -> [u8; 25] {
fn nibbles_to_u8(high: u4, low: u4) -> u8 {
let high = {
let high :u8 = high.into();
(high & 0x0F) << 4
};
let low = {
let low: u8 = low.into();
low & 0x0F
};
high | low
}
let mut buf = [0u8; 25];
let number_of_phases =
if let Some(p) = self.phases_len {
p
} else {
(self.phases.len() as u8).into()
};
buf[0] = nibbles_to_u8(number_of_phases, self.global_mini_cycle_duration);
buf[1] = nibbles_to_u8(self.led_start_intensity, self.repeat_count);
let mut even_phases = self.phases.iter()
.take(15)
.enumerate()
.filter(|(idx, _)| idx % 2 == 0)
.map(|e| e.1);
let mut odd_phases = self.phases.iter()
.take(15)
.enumerate()
.filter(|(idx, _)| idx % 2 == 1)
.map(|e| e.1);
let mut buf_index = 2;
while let (Some(even), odd) = (even_phases.next(), odd_phases.next()) {
{
let even_led_intensity = even.led_intensity;
let odd_led_intensity = odd.map(|odd| odd.led_intensity)
.unwrap_or(0u8.into());
buf[buf_index] = nibbles_to_u8(even_led_intensity, odd_led_intensity);
buf_index += 1;
}
{
let fading = even.fading_transition_duration;
let led = even.led_duration;
buf[buf_index] = nibbles_to_u8(fading, led);
buf_index += 1;
}
if let Some(odd) = odd {
let fading = odd.fading_transition_duration;
let led = odd.led_duration;
buf[buf_index] = nibbles_to_u8(fading, led);
buf_index += 1;
}
}
buf
}
}
}
pub trait Lights: JoyConDriver {
const LIGHT_UP: [LightUp; 4] = LIGHT_UP;
const FLASH: [Flash; 4] = FLASH;
fn set_player_lights(&mut self, light_up: &Vec<LightUp>, flash: &Vec<Flash>) -> JoyConResult<[u8; 362]> {
let arg = light_up.iter()
.map(|&lu| lu as u8)
.sum::<u8>()
+ flash.iter()
.map(|&f| f as u8)
.sum::<u8>();
let reply = self.send_sub_command(SubCommand::SetPlayerLights, &[arg])?;
Ok(reply)
}
fn get_player_lights(&mut self) -> JoyConResult<StandardInputReport<SubCommandReport<LightsStatus>>>
where Self: std::marker::Sized
{
LightsStatus::once(self)
}
fn set_home_light(&mut self, pattern: &home_button::LightEmittingPattern) -> JoyConResult<[u8; 362]> {
let arg: [u8;25] = pattern.clone().into();
self.send_sub_command(SubCommand::SetHOMELight, &arg)
}
}
impl<D> Lights for D where D: JoyConDriver {}
}
pub mod device_info {
use super::{*, input_report_mode::sub_command_mode::*};
impl TryFrom<u8> for JoyConDeviceType {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
let kind = match value {
0 => JoyConDeviceType::JoyConL,
1 => JoyConDeviceType::JoyConR,
2 => JoyConDeviceType::ProCon,
_ => Err(())?
};
Ok(kind)
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct JoyConMacAddress(pub [u8; 6]);
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct JoyConDeviceInfo {
pub firmware_version: u16,
pub device_type: JoyConDeviceType,
pub mac_address: JoyConMacAddress,
pub colors_in_spi: bool,
}
impl TryFrom<[u8; 35]> for JoyConDeviceInfo {
type Error = JoyConError;
fn try_from(value: [u8; 35]) -> Result<Self, Self::Error> {
let firmware_version = u16::from_be_bytes([value[0], value[1]]);
let device_kind = JoyConDeviceType::try_from(value[2])
.map_err(|()| {
JoyConError::SubCommandError(SubCommand::RequestDeviceInfo as u8, value.to_vec())
})?;
let mac_address = {
let mut buf = [0u8; 6];
buf.copy_from_slice(&value[4..10]);
JoyConMacAddress(buf)
};
let colors_in_spi = value[12] == 1;
Ok(JoyConDeviceInfo {
firmware_version,
device_type: device_kind,
mac_address,
colors_in_spi,
})
}
}
impl SubCommandReplyData for JoyConDeviceInfo {
type ArgsType = [u8; 0];
const SUB_COMMAND: SubCommand = SubCommand::RequestDeviceInfo;
const ARGS: Self::ArgsType = [];
}
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
pub enum Command {
RumbleAndSubCommand = 1,
NFC_IR_MCU_FW_Update = 3,
Rumble = 16,
RumbleAndRequestSpecificDataFromThe_NFC_IR_MCU = 17,
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
pub enum SubCommand {
GetOnlyControllerState = 0,
BluetoothManualPairing = 1,
RequestDeviceInfo = 2,
SetInputReportMode = 3,
TriggerButtonsElapsedTime = 4,
GetPageListState = 5,
SetHCIState = 6,
ResetPairingInfo = 7,
SetShipmentLowPowerState = 8,
SPIFlashRead = 10,
SPIFlashWrite = 11,
SPISectorErase = 12,
ResetNFC_IR_MCU = 32,
Set_NFC_IR_MCUConfiguration = 33,
Set_NFC_IR_MCUState = 34,
Get_x28_NFC_IR_MCUData = 41,
Set_GPIO_PinOutputValue = 42,
Get_x29_NFC_IR_MCUData = 43,
SetPlayerLights = 48,
GetPlayerLights = 49,
SetHOMELight = 56,
EnableIMU = 64,
SetIMUSensitivity = 65,
WriteToIMURegisters = 66,
ReadIMURegisters = 67,
EnableVibration = 72,
GetRegulatedVoltage = 80,
SetGPIOPinOutputValue = 81,
GetGPIOPinInput_OutputValue = 82,
}