#![feature(untagged_unions)]
#![no_std]
#![deny(missing_docs)]
#![feature(unsize)]
pub mod classic;
pub mod dualshock;
pub mod negcon;
pub mod jogcon;
pub mod guncon;
pub mod guitarhero;
pub mod baton;
extern crate bit_reverse;
extern crate bitflags;
extern crate byteorder;
extern crate embedded_hal as hal;
use bit_reverse::ParallelReverse;
use hal::blocking::spi;
use hal::digital::OutputPin;
use classic::{Classic, GamepadButtons};
use dualshock::{DualShock, DualShock2};
use negcon::NegCon;
use jogcon::JogCon;
use guncon::GunCon;
use guitarhero::GuitarHero;
use baton::Baton;
const MESSAGE_MAX_LENGTH: usize = 32;
const HEADER_LEN: usize = 3;
const CONTROLLER_NOT_PRESENT: u8 = 0xff;
const CONTROLLER_CLASSIC: u8 = 0xc1;
const CONTROLLER_DUALSHOCK_DIGITAL: u8 = 0x41;
const CONTROLLER_DUALSHOCK_ANALOG: u8 = 0x73;
const CONTROLLER_DUALSHOCK_PRESSURE: u8 = 0x79;
const CONTROLLER_JOGCON: u8 = 0xe3;
const CONTROLLER_NEGCON: u8 = 0x23;
const CONTROLLER_GUNCON: u8 = 0x63;
const CONTROLLER_CONFIGURATION: u8 = 0xf3;
const CMD_POLL: &[u8] = &[0x00, 0x42, 0x00];
const CMD_ENTER_ESCAPE_MODE: &[u8] = &[0x00, 0x43, 0x00, 0x01, 0x00];
const CMD_EXIT_ESCAPE_MODE: &[u8] = &[0x00, 0x43, 0x00, 0x00, 0x00];
const CMD_RESPONSE_FORMAT: &[u8] = &[0x00, 0x4F, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00];
const CMD_INIT_PRESSURE: &[u8] = &[0x00, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00];
const CMD_SET_MODE: &[u8] = &[0x00, 0x44, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
const CMD_READ_STATUS: &[u8] = &[0x00, 0x45, 0x00, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a];
const CMD_READ_CONST1A: &[u8] = &[0x00, 0x46, 0x00, 0x00, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a];
const CMD_READ_CONST1B: &[u8] = &[0x00, 0x46, 0x00, 0x01, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a];
const CMD_READ_CONST2: &[u8] = &[0x00, 0x47, 0x00, 0x00, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a];
const CMD_READ_CONST3A: &[u8] = &[0x00, 0x4C, 0x00, 0x00, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a];
const CMD_READ_CONST3B: &[u8] = &[0x00, 0x4C, 0x00, 0x01, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a];
const CMD_MOTOR_DUALSHOCK: &[u8] = &[0x00, 0x4D, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff];
const CMD_MOTOR_JOGCON: &[u8] = &[0x00, 0x4D, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff];
#[repr(C)]
pub union ControllerData {
pub data: [u8; MESSAGE_MAX_LENGTH],
pub gh: GuitarHero,
pub b: Baton,
classic: Classic,
ds: DualShock,
ds2: DualShock2,
jc: JogCon,
nc: NegCon,
gc: GunCon,
}
#[derive(Clone, PartialEq)]
pub enum MultitapPort {
A = 0x01,
B = 0x02,
C = 0x03,
D = 0x04,
M = 0x61,
X = 0xff,
}
pub enum Error<E> {
LateCollision,
BadResponse,
Spi(E),
}
impl<E> From<E> for Error<E> {
fn from(e: E) -> Self {
Error::Spi(e)
}
}
pub trait PollCommand {
fn set_command(&self, &mut [u8]);
}
pub trait HasStandardButtons {
fn buttons(&self) -> GamepadButtons;
}
#[derive(Default)]
pub struct ControllerConfiguration {
pub status: [u8; 6],
pub const1a: [u8; 5],
pub const1b: [u8; 5],
pub const2: [u8; 5],
pub const3a: [u8; 5],
pub const3b: [u8; 5],
}
pub enum Device {
None,
Unknown,
ConfigurationMode,
Classic(Classic),
DualShock(DualShock),
DualShock2(DualShock2),
GuitarHero(GuitarHero),
JogCon(JogCon),
NegCon(NegCon),
GunCon(GunCon),
Baton(Baton),
}
pub struct PlayStationPort<SPI, CS> {
dev: SPI,
select: Option<CS>,
multitap_port: MultitapPort,
}
impl<E, SPI, CS> PlayStationPort<SPI, CS>
where
SPI: spi::Transfer<u8, Error = E>,
CS: OutputPin {
pub fn new(spi: SPI, mut select: Option<CS>) -> Self {
if let Some(ref mut x) = select {
x.set_high(); }
Self {
dev: spi,
select,
multitap_port: MultitapPort::A,
}
}
fn flip(bytes: &mut [u8]) {
for byte in bytes.iter_mut() {
*byte = byte.swap_bits();
}
}
pub fn set_multitap_port(&mut self, port: MultitapPort) {
self.multitap_port = port;
}
pub fn send_command(&mut self, command: &[u8], result: &mut [u8]) -> Result<(), E> {
result[..command.len()].copy_from_slice(command);
result[0] = self.multitap_port.clone() as u8;
Self::flip(result);
if let Some(ref mut x) = self.select {
x.set_low();
}
self.dev.transfer(result)?;
if let Some(ref mut x) = self.select {
x.set_high();
}
Self::flip(result);
Ok(())
}
pub fn enable_pressure(&mut self) -> Result<(), E> {
let mut buffer = [0u8; MESSAGE_MAX_LENGTH];
self.send_command(CMD_POLL, &mut buffer)?;
self.send_command(CMD_ENTER_ESCAPE_MODE, &mut buffer)?;
self.send_command(CMD_SET_MODE, &mut buffer)?;
self.send_command(CMD_MOTOR_DUALSHOCK, &mut buffer)?;
self.send_command(CMD_INIT_PRESSURE, &mut buffer)?;
self.send_command(CMD_RESPONSE_FORMAT, &mut buffer)?;
self.send_command(CMD_EXIT_ESCAPE_MODE, &mut buffer)?;
Ok(())
}
pub fn enable_jogcon(&mut self) -> Result<(), E> {
let mut buffer = [0u8; MESSAGE_MAX_LENGTH];
self.send_command(CMD_POLL, &mut buffer)?;
self.send_command(CMD_ENTER_ESCAPE_MODE, &mut buffer)?;
self.send_command(CMD_SET_MODE, &mut buffer)?;
self.send_command(CMD_MOTOR_JOGCON, &mut buffer)?;
self.send_command(CMD_EXIT_ESCAPE_MODE, &mut buffer)?;
Ok(())
}
pub fn read_config(&mut self) -> Result<ControllerConfiguration, E> {
let mut config: ControllerConfiguration = Default::default();
let mut buffer = [0u8; MESSAGE_MAX_LENGTH];
self.send_command(CMD_ENTER_ESCAPE_MODE, &mut buffer)?;
self.send_command(CMD_READ_STATUS, &mut buffer)?;
config.status.copy_from_slice(&buffer[HEADER_LEN..9]);
self.send_command(CMD_READ_CONST1A, &mut buffer)?;
config.const1a.copy_from_slice(&buffer[4..9]);
self.send_command(CMD_READ_CONST1B, &mut buffer)?;
config.const1b.copy_from_slice(&buffer[4..9]);
self.send_command(CMD_READ_CONST2, &mut buffer)?;
config.const2.copy_from_slice(&buffer[4..9]);
self.send_command(CMD_READ_CONST3A, &mut buffer)?;
config.const3a.copy_from_slice(&buffer[4..9]);
self.send_command(CMD_READ_CONST3B, &mut buffer)?;
config.const3b.copy_from_slice(&buffer[4..9]);
self.send_command(CMD_EXIT_ESCAPE_MODE, &mut buffer)?;
Ok(config)
}
fn read_port(&mut self, command: Option<&PollCommand>) -> Result<[u8; MESSAGE_MAX_LENGTH], Error<E>> {
let mut buffer = [0u8; MESSAGE_MAX_LENGTH];
let mut data = [0u8; MESSAGE_MAX_LENGTH];
data[..CMD_POLL.len()].copy_from_slice(CMD_POLL);
if let Some(x) = command {
x.set_command(&mut data[HEADER_LEN..]);
}
self.send_command(&data, &mut buffer)?;
Ok(buffer)
}
pub fn read_raw(&mut self, command: Option<&PollCommand>) -> Result<ControllerData, Error<E>> {
let mut buffer = [0u8; MESSAGE_MAX_LENGTH];
let data = self.read_port(command)?;
buffer[0 .. MESSAGE_MAX_LENGTH - 3].copy_from_slice(&data[HEADER_LEN..]);
Ok(ControllerData { data: buffer })
}
pub fn read_input(&mut self, command: Option<&PollCommand>) -> Result<Device, Error<E>> {
let mut buffer = [0u8; MESSAGE_MAX_LENGTH];
let data = self.read_port(command)?;
buffer[0 .. MESSAGE_MAX_LENGTH - 3].copy_from_slice(&data[HEADER_LEN..]);
let controller = ControllerData { data: buffer };
let device;
unsafe {
device = match data[1] {
CONTROLLER_NOT_PRESENT => Device::None,
CONTROLLER_CONFIGURATION => Device::ConfigurationMode,
CONTROLLER_CLASSIC => Device::Classic(controller.classic),
CONTROLLER_DUALSHOCK_DIGITAL => Device::Classic(controller.classic),
CONTROLLER_DUALSHOCK_ANALOG => Device::DualShock(controller.ds),
CONTROLLER_DUALSHOCK_PRESSURE => Device::DualShock2(controller.ds2),
CONTROLLER_JOGCON => Device::JogCon(controller.jc),
CONTROLLER_NEGCON => Device::NegCon(controller.nc),
CONTROLLER_GUNCON => Device::GunCon(controller.gc),
_ => Device::Unknown,
}
}
Ok(device)
}
}
mod tests {
#[test]
fn union_test() {
let controller = ControllerData {
data: [
0xfe,
0x7f,
0x00,
0x00,
0x00,
0xff
],
};
unsafe {
assert!(controller.ds.buttons.select() == true);
assert!(controller.ds.buttons.square() == true);
assert!(controller.ds.lx == 0);
assert!(controller.ds.ly == 255);
}
}
}