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

University of Abertay Dundee

h.s.fortuna@abertay.ac.uk