1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
//! Playstation Controller driver for Rust's [Embedded Hardware Abstraction Layer](https://github.com/japaric/embedded-hal)
//! ============================
//! 
//! The original PlayStation and most of its peripherals are 20+ years old at this point,
//! but they're easy to interface with for fun projects and wireless variants are easy
//! to come by while being [pretty cheap](https://www.amazon.ca/s/?ie=UTF8&keywords=ps2+wireless+controller)
//! ($28 Canadian for two as of this writing).
//! 
//! The current state of this library is such that it's pretty naïve to which controller
//! is plugged in, and it will make a guess based on the response to a poll request. The
//! response is broken into a header with an identification byte and an acknowledge byte and
//! we use the whole identification byte and just assume the data its sending is correct.
//! 
//! If you own something particularly interesting for the PS1 or PS2 that plugs into the
//! controller port and isn't supported here, feel free to reach out by creating an issue
//! and we can work out some creative way to get the device working with this library.
//! 
//! Efficiencies can be made here, and things will likely improve, but the darn thing is
//! useful now so let's start using it! If you find things to fix, please make an issue.
//! 
//! Hardware
//! -----------------------
//! 
//! Because the PlayStation can have up to four devices sharing the same SPI bus (two
//! controllers and two memory cards), they made the data out (MISO) pin open drain. That
//! means that it can only pull the line low and you'll need to add your own resistor
//! connected from that pin to +5v. In my testing with a voltage divider on a PlayStation 2,
//! the value is between 220 and 500 ohms.
//! 
//! Development and testing is recommended to by done on a Raspberry Pi as it has a reliable
//! SPI bus. Early testing on both a Next Thing Co. C.H.I.P. and an Odroid C1+ didn't go
//! very well. The C.H.I.P. added unexpected clock changes, and the C1+ had voltage pull
//! up/down problems with the data lines.
//! 
//! The controller itself is a 3.3v logic device and any force feedback is meant to be
//! driven by the 7.5v cd-rom voltage. Testing using the 5v line on of the Raspberry Pi
//! technically works, but the result is much weaker than the original console.
//! 
//! Bibliography
//! -----------------------
//! Here is the list of the great bits of documentation that helped get this project started
//! and continue to provide a good cross-reference to double check the work done here.
//! 
//! * [psxpad.html](http://domisan.sakura.ne.jp/article/psxpad/psxpad.html) - Wiring, testing and bootstrapping game pad on Linux with SPI
//! * [ps_eng.txt](http://kaele.com/~kashima/games/ps_eng.txt) - Controller / Memory Card Protocols (pre-DualShock)
//! * [Playstation 2 (Dual Shock) controller protocol notes](https://gist.github.com/scanlime/5042071) - Command protocols
//! * [psxpblib](http://www.debaser.force9.co.uk/psxpblib/) - Interfacing PlayStation controllers via the parallel port
//! * [Simulated PS2 Controller for Autonomously playing Guitar Hero](http://procrastineering.blogspot.ca/2010/12/simulated-ps2-controller-for.html) - SPI protocol captures


#![feature(untagged_unions)]
#![no_std]
#![deny(missing_docs)]
//#![deny(warnings)]
#![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;

/// The maximum length of a message from a controller
const MESSAGE_MAX_LENGTH: usize = 32;
/// Acknoweldgement byte for header commnad
//const ACK_BYTE: u8 = 0x5a;
/// Length of the command header
const HEADER_LEN: usize = 3;

/// Controller missing
const CONTROLLER_NOT_PRESENT: u8 = 0xff;
/// Original controller, SCPH-1080
const CONTROLLER_CLASSIC: u8 = 0xc1;
/// DualShock in Digital mode
const CONTROLLER_DUALSHOCK_DIGITAL: u8 = 0x41;
/// DualShock
const CONTROLLER_DUALSHOCK_ANALOG: u8 = 0x73;
/// DuakShock 2
const CONTROLLER_DUALSHOCK_PRESSURE: u8 = 0x79;
/// JogCon
const CONTROLLER_JOGCON: u8 = 0xe3;
/// NegCon
const CONTROLLER_NEGCON: u8 = 0x23;
/// NegCon
const CONTROLLER_GUNCON: u8 = 0x63;
/// Configuration Mode
const CONTROLLER_CONFIGURATION: u8 = 0xf3;

/// Command to poll buttons
const CMD_POLL: &[u8] = &[0x00, 0x42, 0x00];
/// Command to enter escape mode
const CMD_ENTER_ESCAPE_MODE: &[u8] = &[0x00, 0x43, 0x00, 0x01, 0x00];
/// Command to exit escape mode
const CMD_EXIT_ESCAPE_MODE: &[u8] = &[0x00, 0x43, 0x00, 0x00, 0x00];
/// Command to set response format. Right now asks for all data
const CMD_RESPONSE_FORMAT: &[u8] = &[0x00, 0x4F, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00];
/// Command to initialize / customize pressure
const CMD_INIT_PRESSURE: &[u8] = &[0x00, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00];
/// Command to set major mode (DualShock = 1 / Digital = 0)
const CMD_SET_MODE: &[u8] = &[0x00, 0x44, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
/// Command to read extended status
const CMD_READ_STATUS: &[u8] = &[0x00, 0x45, 0x00, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a];
/// Command to read constant 1 at address 00
const CMD_READ_CONST1A: &[u8] = &[0x00, 0x46, 0x00, 0x00, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a];
/// Command to read constant 1 at address 01
const CMD_READ_CONST1B: &[u8] = &[0x00, 0x46, 0x00, 0x01, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a];
/// Command to read constant 2 at address 00
const CMD_READ_CONST2: &[u8] = &[0x00, 0x47, 0x00, 0x00, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a];
/// Command to read constant 3 at address 00
const CMD_READ_CONST3A: &[u8] = &[0x00, 0x4C, 0x00, 0x00, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a];
/// Command to read constant 3 at address 01
const CMD_READ_CONST3B: &[u8] = &[0x00, 0x4C, 0x00, 0x01, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a];
/// Command to enable DualShock motors
const CMD_MOTOR_DUALSHOCK: &[u8] = &[0x00, 0x4D, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff];
/// Command to enable JogCon motor
const CMD_MOTOR_JOGCON: &[u8] = &[0x00, 0x4D, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff];

#[repr(C)]
/// The poll command returns a series of bytes. This union allows us to interact with
/// those bytes with some functions. It's nice sugar that allows us to use the data
/// in an obvious way without needing to copy the bytes around
pub union ControllerData {
    /// The raw data representing the buttons. Kind of unwieldy
    pub data: [u8; MESSAGE_MAX_LENGTH],
    /// Maps the underlying bytes to buttons and the whammy bar of the Guitar Hero controller
    pub gh: GuitarHero,
    /// Maps the bytes to the Mad Maestro baton
    pub b: Baton,
    classic: Classic,
    ds: DualShock,
    ds2: DualShock2,
    jc: JogCon,
    nc: NegCon,
    gc: GunCon,
}

/// The active port to set on the Multitap
#[derive(Clone, PartialEq)]
pub enum MultitapPort {
    /// The first port on the multi-tap and also the port when no tap
    /// is present
    A = 0x01,
    /// The second port on the multi-top
    B = 0x02,
    /// The third port on the multi-top
    C = 0x03,
    /// The fourth port on the multi-top
    D = 0x04,
    /// Some unknown id when booting the PS2. Memory card?
    M = 0x61,
    /// This may be for the multitap itself
    X = 0xff,
}

/// Errors that can arrise from trying to communicate with the controller
pub enum Error<E> {
    /// Late collision
    LateCollision,
    /// Something responded badly
    BadResponse,
    /// SPI error
    Spi(E),
}

impl<E> From<E> for Error<E> {
    fn from(e: E) -> Self {
        Error::Spi(e)
    }
}

/// Commands to send off with a poll request.
pub trait PollCommand {
    /// Re-write the provided slice starting from index 0. This command
    /// is called by read_input() which will provide a sub-slice of the
    /// controller's command bytes.
    fn set_command(&self, &mut [u8]);
}

/// Many controllers have the same set of buttons (Square, Circle, L3, R1, etc).
/// The devices that do have these buttons implement this trait. Depite the original
/// Controller not having L3 and R3, they are brought out regardless and just considered
/// unpressable.
pub trait HasStandardButtons {
    /// This does require a clone operation of the bytes inside the controller.
    /// To save yourself the copy, you can access the button data directly via `buttons`
    fn buttons(&self) -> GamepadButtons;
}

/// Holds information about the controller's configuration and constants
#[derive(Default)]
pub struct ControllerConfiguration {
    /// The controller's current status and *perhaps* its generation
    pub status: [u8; 6],
    /// Unknown constant
    pub const1a: [u8; 5],
    /// Unknown constant
    pub const1b: [u8; 5],
    /// Unknown constant
    pub const2: [u8; 5],
    /// Unknown constant
    pub const3a: [u8; 5],
    /// Unknown constant
    pub const3b: [u8; 5],
}

/// Possible devices that can be returned by the poll command to the controller.
/// Currently, we're relying both on the device type (high nybble) and the number
/// of 16bit words its returning (low nybble) to guess the device type.
/// 
/// While that's not ideal, I haven't found a better way to do this yet. It seems
/// as though the constants to define devices rarely change so for example, the
/// Guitar Hero controller reports in every way that it is a DualShock 1 controller.
/// Other devices, like the DVD remote don't even support escape mode so this
/// is the best I can do until we find a better way to get creative.
pub enum Device {
    /// If pulling the device type didn't work
    None,
    /// A new controller type we haven't seen before
    Unknown,
    /// The controller is waiting for configuration data. Users of the library should
    /// never need to see this state.
    ConfigurationMode,
    /// Original controller that shipped with the PlayStation. Only contains regular
    /// buttons. DualShock 1 and 2 can emulate this mode
    Classic(Classic),
    /// Controller with two analog sticks. This was the final controller style shipped with
    /// the original PlayStation
    DualShock(DualShock),
    /// Controller that shipped with the PlayStation 2. Has dual analog sticks but also pressure
    /// senstive buttons
    DualShock2(DualShock2),
    /// Controller that shipped with Guitar Hero
    GuitarHero(GuitarHero),
    /// The Namco JonCon
    JogCon(JogCon),
    /// The Namco NegCon
    NegCon(NegCon),
    /// The Namco GunCon
    GunCon(GunCon),
    /// The Mad Maestro Baton
    Baton(Baton),
}

/// The main event! Create a port using an SPI bus and start commanding
/// controllers!
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 {

    /// Create a new device to talk over the PlayStation's controller
    /// port
    pub fn new(spi: SPI, mut select: Option<CS>) -> Self {
        if let Some(ref mut x) = select {
            x.set_high(); // Disable controller for now
        }

        Self {
            dev: spi,
            select,
            multitap_port: MultitapPort::A,
        }
    }

    fn flip(bytes: &mut [u8]) {
        for byte in bytes.iter_mut() {
		    *byte = byte.swap_bits();
	    }
    }

    /// Set the active port on the multi-tap. If no tap is being used, anything
    /// other than `A` will fail to return anything. Or so I assume! Setting this
    /// will mean any commands send will be directed towards that port indefinitely.
    pub fn set_multitap_port(&mut self, port: MultitapPort) {
        self.multitap_port = port;
    }

    /// Sends commands to the underlying hardware and provides responses
    pub fn send_command(&mut self, command: &[u8], result: &mut [u8]) -> Result<(), E> {
        // Pack in bytes for the command we'll be sending
        result[..command.len()].copy_from_slice(command);
        result[0] = self.multitap_port.clone() as u8;

        // Because not all hardware supports LSB mode for SPI, we flip
        // the bits ourselves
        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(())
    }

    /// Configure the controller to set it to DualShock2 mode. This will also
    /// enable analog mode on DualShock1 controllers.
    pub fn enable_pressure(&mut self) -> Result<(), E> {
        // TODO: Redefine this to allow input parameters. Right now they're are hard coded
        // TODO: Detect and return actual protocol errors

        let mut buffer = [0u8; MESSAGE_MAX_LENGTH];

        // Wake up the controller if needed
        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(())
    }

    /// Configure the JogCon for wheel control.
    /// 
    /// If no digital buttons are pressed in this mode for 60 seconds, the
    /// JogCon will go to sleep until buttons are pressed. If no polling is
    /// done for 10 seconds, it will drop out of this mode and revert to
    /// the standard Controller mode
    pub fn enable_jogcon(&mut self) -> Result<(), E> {
        let mut buffer = [0u8; MESSAGE_MAX_LENGTH];

        // Wake up the controller if needed
        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(())
    }

    /// Read various parameters from the controller including its current
    /// status.
    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);

        // Overlay the command to send with the poll...
        if let Some(x) = command {
            x.set_command(&mut data[HEADER_LEN..]);
        }

        self.send_command(&data, &mut buffer)?;

        // Device polling will return `ACK_BYTE` in the third byte if the command
        // was properly understood
        /*
        match device {
            Device::None => {},
            _ => {
                if buffer[2] != ACK_BYTE {
                    return Err(Error::BadResponse);
                }
            }
        }
        */

        Ok(buffer)
    }

    /// Get the raw data from polling for a controller. You can use this to cooerce the data into
    /// some controller that can't be safely identified by `read_input`, but you should rely on that
    /// function if you can.
    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)?;

        // Shift the controller data over because we don't need the header anymore
        buffer[0 .. MESSAGE_MAX_LENGTH - 3].copy_from_slice(&data[HEADER_LEN..]);

        Ok(ControllerData { data: buffer })
    }

    /// Ask the controller for input states. Different contoller types will be returned automatically
    /// for you. If you'd like to cooerce a controller yourself, use `read_raw`.
    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)?;

        // Shift the controller data over because we don't need the header anymore
        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() {
        // Again, buttons are active low, hence 'fe' and '7f'
        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);
        }
    }
}