PS2 Linux Programming



Using the PS2 Controllers




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:





Unused? This always seems to be zero.


Controller type and pad status (type is top 4 bits)


Digital button presses.


Right stick, x-axis


Right stick, y-axis


Left stick, x-axis


Left stick, y-axis


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;



                ioctl (pad0_fd, PS2PAD_IOCGETSTAT, &res);

      } while (res == PS2PAD_STAT_BUSY);





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




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 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)




A combination of PAD_? flags (PAD_0 and PAD_1) ORd together


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


Nonzero on success, zero on error


This starts up the pad system. It must be called before any of the other functions.


To initialise pad 0 in locked analogue mode, without button pressure sensitivity enabled, call:




void pad_update (int pads)




A combination of PAD_? flags (PAD_0 and PAD_1) ORd together


This updates the contents of the "pad" global variable for the pads specified. This should be called once per frame


To update the contents of pad[0], call:

pad_update (PAD_0);



void pad_cleanup (int pads)




A combination of PAD_? flags (PAD_0 and PAD_1) ORd together


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.


To release the resources held by pad 0, call:

pad_cleanup (PAD_0);



void enable_actuator(int padnum, int enable_small, int enable_big);




0 for pad0 or 1 for PAD1.


to enable 1 to disable.


to enable 1 to disable.


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.


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);




0 for pad0 or 1 for PAD1.


The small intensity is either on or off. 0 for off and 1 for on.


An intensity value between 0 and 255


This controls the intensity level of the actuators.


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;






used to initialise the pad state.


set to 1 if actuator supported otherwise 0


a bitfield describing which buttons are currently pressed


these values are mapped from -1 -> +1


these values are mapped from 0 -> 1


like buttons, but contains buttons pressed since the last update


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