PS2 Linux Programming
Using the PS2 Controllers
Introduction
With the PS2 Linux kit,
Sony has provided a full driver for the pads. The driver allows access to all
the features of the DualShock2 controller which are:
·
Button presses
detection;
·
Analogue sticks;
·
Button
pressure sensitivity;
·
Vibration;
·
Ability to
control the red "ANALOG" light;
The interface to this
driver is through the Linux /dev/ps2pad00 and /dev/ps2pad10 devices.
The devices
Both of the devices behave
in the same way and are referred to as controllers 0 and 1. At any time 32
bytes of information can be read from the device, these providing the current
state of the controller.
In addition to reading the
pad data, through the Linux driver interface it is possible to set up various
controller options. This interface is through the ioctl
function. There are a number of ioctl functions
(described in /usr/doc/PlayStation2/ps2pad_en.txt) which control the various
aspects of the pads.
Reading from the Pad
Device
First it is necessary to open
the device. In Unix-type environments, everything is treated as a file, so the
device is opened as follows:
#include
<unistd.h>
#include
<fcntl.h>
int pad0_fd = open
("/dev/ps2pad00", O_RDONLY);
Once the device is open,
data can be read from it. Up to 32 bytes at a time can be read from the device,
although depending upon the mode that is being used,
only 8 bytes may be needed. To read the current pad state into a buffer:
char padbuf[32];
read (pad0_fd, padbuf, 32);
This provides a buffer full
of raw pad data. The pad buffer can be refreshed as often is required. As a
matter of good practice, the pad device should be closed at the end of the
program with:
close (pad0_fd);
Turning the Pad Data
into Something Useful
The data returned by the
DualShock2 driver is arranged in the following manner:
byte |
data |
0 |
Unused?
This always seems to be zero. |
1 |
Controller
type and pad status (type is top 4 bits) |
2&3 |
Digital
button presses. |
4 |
Right
stick, x-axis |
5 |
Right
stick, y-axis |
6 |
Left
stick, x-axis |
7 |
Left
stick, y-axis |
8-19 |
Analogue
button pressures |
Note that if the
controller is in digital mode, only the first 4 bytes are useful.
Controlling the
Controller
By default, the pad driver
will return only the digital button press information. To read the state of the
analogue sticks or the button pressure the required features must be requested
via ioctls.
These ioctls
are described in the file mentioned above
(/usr/doc/PlayStation2/ps2pad_en.txt), but unfortunately this file does not
explain the logic of how to use them.
The pad has two modes -
digital and analogue (when the pad is in analogue mode, the little red light is
on). The mode can be switched by pressing the “ANALOGU” button on the pad, but
it can also be switched with the PS2PAD_IOCSETMODE ioctl.
The ioctl interface is designed to work for any
controller, not just Dualshock2 pads and as such, it knows nothing about
"Analogue" and "Digital" modes- it just has a list of modes
which it knows is available, and the ioctl sets the
current offset into this list. In the case of the Dualshock2, the offset for
digital mode is 0 and for analogue mode is 1. The offsets for other peripherals
may be different but these different peripherals have not been invertigated.
It is also possible to
lock the controller into a given mode. If the game needs the analogue
functionality, there is no point letting the user switch back to digital mode
(in fact, the game would probably fail Sony's QA if it did...). So, at the same
time as setting the mode of the controller, a locking setting can be specified
(i.e. locked or not locked). The value specified to lock the controller is 3,
and to unlock it is 2. The caveat with locking the pad is that it doesn't
automatically get unlocked when the program ends; unlocking needs to be done in
the program before exiting.
Once the pad is in
analogue mode, it is possible to get button pressure readings (for the dpad, symbols and shoulder buttons). This is done with the
PS2PAD_IOCENTERPRESSMODE ioctl. When the program
exits, this mode should be un-set with the PS2PAD_IOCEXITPRESSMODE ioctl.
The Pad Status
One thing to note is that
the pad doesn't respond instantly to requests. Some requests, (including
opening the device) take some time before the pad is ready to do anything.
At any point, the
PS2PAD_GETSTAT ioctl can be used to return the status of the
pads. If this returns PS2PAD_STAT_BUSY, the program should wait until it
returns a not busy signal before anything is done with the pad. A routine like
the following would do the job:
int res
= 0;
do
{
ioctl (pad0_fd, PS2PAD_IOCGETSTAT,
&res);
} while
(res == PS2PAD_STAT_BUSY);
Conclusion
The
functionality described above, plus much more, is implemented in the pad.c and pad.h files
accompanying this tutorial. Details on using pad.c
and pad.h are given below.
Using the PS2 Pad
Library
Introduction
The data returned from the
PS2 Pad driver can require some effort to make useful. This is a small library
to simplify the interface.
It supports all the
functionality of the DualShock2.
The API
The API of this library
consists of five functions It creates a global variable, "pad[2]" (one element for each controller), which
contains current information about the pads. The functions are as follows:
int pad_init (int pads, int initflags)
Arguments
pads: |
A
combination of PAD_? flags (PAD_0 and PAD_1) ORd
together |
initflags: |
A
combination of the PAD_INIT_* flags ORd together. PAD_INIT_DIGITAL:
request digital mode PAD_INIT_ANALOGUE:
request analogue mode PAD_INIT_LOCK:
lock the pad to the specified mode PAD_INIT_UNLOCK:do not lock the pad mode PAD_INIT_PRESSURE:
request that button pressures are returned |
Returns |
Nonzero
on success, zero on error |
Description |
This
starts up the pad system. It must be called before any of the other
functions. |
Example |
To
initialise pad 0 in locked analogue mode, without button pressure sensitivity
enabled, call: pad_init (PAD_0, PAD_INIT_ANALOGUE | PAD_INIT_LOCK); |
void pad_update (int pads)
Arguments
pads:
|
A
combination of PAD_? flags (PAD_0 and PAD_1) ORd
together |
Description |
This
updates the contents of the "pad" global variable for the pads
specified. This should be called once per frame |
Example |
To
update the contents of pad[0], call: pad_update (PAD_0); |
void pad_cleanup (int pads)
Arguments
pads: |
A
combination of PAD_? flags (PAD_0 and PAD_1) ORd
together |
Description |
This
releases the resources held by the pad system. If the pad mode is locked,
this function must be called before the program exits or the pad will remain
locked. pad_update() will not work after this call. |
Example |
To
release the resources held by pad 0, call: pad_cleanup (PAD_0); |
void enable_actuator(int padnum, int
enable_small, int enable_big);
Arguments
padnum: |
0
for pad0 or 1 for PAD1. |
enable_small |
to
enable 1 to disable. |
enable_big |
to
enable 1 to disable. |
Description |
This
enables the big and/or small actuator within the controller specified. The
controller must support the actuator facility and this can be verified by
checking the state of the “actuator” member of the pad structure, this being
set when the pad is initialised. |
Example |
To
enable both actuators for pad 0; enable_actuator(0, 1, 1); |
void set_actuator(int padnum, unsigned char small_intensity, unsigned char big_intensity);
Arguments
padnum: |
0
for pad0 or 1 for PAD1. |
Small_intensity |
The
small intensity is either on or off. 0 for off and 1 for on. |
Big_intensity |
An
intensity value between 0 and 255 |
Description |
This
controls the intensity level of the actuators. |
Example |
To
set the small actuator on and the large actuator on at an intensity of 255. set_actuator(0, 1, 255); |
The Pad Data Structure
In addition to providing
these functions, the API creates a global variable, "pad". This is an
array of two padinfo_t structures, which look like
this:
typedef struct
{
int initflags;
int actuator;
int buttons;
float axes[4];
float pressures[12];
int pressed;
int released;
} padinfo_t;
Description:
initflags |
used
to initialise the pad state. |
actuator
|
set
to 1 if actuator supported otherwise 0 |
buttons
|
a
bitfield describing which buttons are currently
pressed |
axes[4]
|
these
values are mapped from -1 -> +1 |
pressures[12] |
these
values are mapped from 0 -> 1 |
pressed
|
like
buttons, but contains buttons pressed since the last update |
released
|
like
buttons, but contains buttons released since the last update |
Some examples
To check if the start button on pad 0 is being
pressed:
if (pad[0].buttons & PAD_START) //it's being pressed
To check if circle has been pressed since the last
frame:
if (pad[0].pressed & PAD_CIRCLE) //it has been pressed
To read the x-axis value
from the left stick on pad 1 (assuming that pad_init
was called with the PAD_INIT_ANALOGUE flag set):
float xaxisval =
pad[1].axes[PAD_AXIS_LX];
To read how hard the L1
button on pad 0 is being pressed (assuming that pad_init
was called with the PAD_INIT_ANALOGUE and PAD_INIT_PRESSURE flags set):
float cross_pressure =
pad[0].pressures[PAD_PCROSS];
Dr Henry S Fortuna