Kernel interface

From Pandora Wiki
Revision as of 12:11, 3 July 2012 by Notaz (talk | contribs) (Nubs)
Jump to: navigation, search

In case you need to write low level code (you can't/don't want to use high level libs like SDL), you can use kernel interface. This is the recommended way to access hardware (as opposed to GP2X style of accessing chip registers by mmap'ing /dev/mem), because in case hardware changes are needed in future, they could be handled by kernel and all programs would still work. It also should allow several programs to work at the same time and should be more stable.


Buttons, keypad, touchscreen and nubs are all exposed through Linux event interface (EVDEV). All devices are represented by /dev/input/eventX files, which can be opened, read and queried (using ioctl calls). The reads can be synchronous (the read will only return when user does something, like presses the button), or asynchronous (the system will report what changed since the last time you asked).

For example, currently /dev/input/event2 represents game buttons, but in future it may become touchscreen. Scan input device names instead, example:

for (i = 0; 1; i++)
  sprintf(name, "/dev/input/event%i", i);
  fd = open(name, O_RDONLY);
  if (fd < 0) break; /* no more devices */
  ioctl(fd, EVIOCGNAME(sizeof(name)), name);
  if (strcmp(name, "gpio-keys") == 0)
    return fd; /* found the buttons! */
  close(fd); /* we don't need this device */

List of device names and events they send:

name description event.type event.code event.value
1 - pressed
2 - autorepeat event
gpio-keys game buttons EV_KEY KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_MENU (Pandora button), KEY_LEFTALT (Start), KEY_LEFTCTRL (Select), KEY_END (Y/North), KEY_HOME (A/East), KEY_PAGEDOWN (X/South), KEY_END (B/West), KEY_RIGHTSHIFT (Shoulder L), KEY_RIGHTCTRL (Shoulder R), KEY_KPPLUS (Shoulder L2), KEY_KPMINUS (Shoulder R2), KEY_COFFEE (Hold) 0 - released
1 - pressed
gpio-keys lid state EV_SW SW_LID 0 - closing
1 - opening
touchscreen touchscreen EV_ABS ABS_X, ABS_Y, ABS_PRESSURE varies, use calibration data
nub0 left nub EV_ABS ABS_X, ABS_Y -256 (Left/Up)
+256 (Right/Down)
nub1 right nub EV_ABS ABS_X, ABS_Y -256 (Left/Up))
+256 (Right/Down)

Sample code:
evtest.c op_test_inputs.c


Nubs have 3 modes that can be switched during runtime: absolute, mouse, mbuttons; this can be done by writing one of those 3 strings to /proc/pandora/nubX/mode . On mode change /dev/input/event* device reenumeration starts and files belonging to nubs are recreated and must be reopened.

Warning: it is not guaranteed that input files will finish recreating after a write to /proc/pandora/nubX/mode finishes, it is application's responsibility to wait until those files are available.

To solve the above problem, the OS provides a helper script that will wait for enumeration to complete:

/usr/pandora/scripts/ <left_num_mode> <right_nub_mode> is not available on HF6 or earlier firmwares, to support them, something like this could be used:

if [ -e /usr/pandora/scripts/ ]; then
  /usr/pandora/scripts/ absolute absolute
  echo absolute > /proc/pandora/nub0/mode
  echo absolute > /proc/pandora/nub1/mode


Event interface returns uncalibrated values directly from driver, so you need to use tslib or manage calibration yourself (using data from /etc/pointercal).


When using the direct kernel api for input, it is also important to stop X processing the events as well. To do this you can create an X window to eat the events. There is also a utility that comes with the Pandora to do this:

op_runfbapp ./yourapp


Pandora uses ALSA, but it has OSS emulation enabled too, so GP2X code should work.



Framebuffer device (/dev/fbX) is supported. There are 3 framebuffers available (/dev/fb0, /dev/fb1 and /dev/fb2), which represent 3 graphics/video layers on OMAP3 by default (but can be reconfigured). Only /dev/fb0 is enabled by default.

OMAP3 display subsystem is controlled by a driver known as DSS2, which has various controls available on /sys/devices/platform/omapdss/ (but they are not meant to be changed by programs, so they are root writable only). The driver exposes 3 layers (called overlays) and 2 displays. Overlays 1 and 2 can perform hardware scaling on the fly using 5-tap poly-phase filter, overlay0 can not. Displays 0 and 1 represent LCD and TV respectively. By default the 3 framebuffers (/dev/fbX) are redirected to 3 overlays, which all output to the LCD. This configuration is not meant to be changed by programs, only firmware should manage these.

Basic usage

framebuffer interface

Framebuffers can be accessed Linux fbdev interface:

fbdev = open("/dev/fb0", O_RDONLY);
buffer = mmap(0, 800*480*2, PROT_WRITE, MAP_SHARED, fbdev, 0);

(this is basic example, no error checks)

the returned pointer can be used to draw on the screen.

Be sure to #include <linux/fb.h> to get access to the FB device ioctl interface, and <sys/ioctl.h> for access to ioctl itself.

double buffering

This can be achieved using FBIOPAN_DISPLAY ioctl system call. For this you need to mmap framebuffer of double size

buffer1 = mmap(0, 800*480*2 * 2, PROT_WRITE, MAP_SHARED, fbdev, 0);
buffer2 = (char *)mem + 800*480*2;

Then to display buffer2 you would call:

struct fb_var_screeninfo fbvar;
ioctl(fbdev, FBIOGET_VSCREENINFO, &fbvar);
fbvar.yoffset = 480;
ioctl(fbdev, FBIOPAN_DISPLAY, &fbvar);

going back to buffer1 would be repeating above with fbvar.yoffset = 0. Tripple or quad buffering can be implemented using the same technique.

vertical sync

Linux has standard FBIO_WAITFORVSYNC for this:

int arg = 0;
ioctl(fbdev, FBIO_WAITFORVSYNC, &arg);

be sure to pass argument value 0 or it will not work.

Currently FBIO_WAITFORVSYNC is not defined in <linux/fb.h>, although this is in the process of modification. For now, define in the following manner:

  #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)

hardware scaling

Overlay1 (/dev/fb1) can be used to achieve hardware scaling. Technically overlay2 (fb2) can be used for this too, but it is planned to be used by the system for TV-out functionality, so don't use it. The overlay is configured using series of standard and OMAP specific ioctl calls, but the system ships with some tools to achieve this from scripts too. This way the framebuffer can be set up for some arbitrary size (say 320x240) and can output to LCD as 800x480 with hardware scaling. Here is an example script:

ofbset -fb /dev/fb1 -pos 0 0 -size 800 480 -mem 307200 -en 1
fbset -fb /dev/fb1 -g 320 240 320 480 16


ofbset -fb /dev/fb1 -pos 0 0 -size 0 0 -mem 0 -en 0

What it does:

  • allocates OMAP DSS layer, asks video output to be 800x480 at position 0,0 (could set it to 640x480 at 80,0 instead to get centered 2x scaling of 320x240). 307200 bytes of video memory are allocated for 2 320x240 16bpp screens (for doublebuffering).
  • sets video mode to 320x240@16bpp, virtual resolution 320x480 for doublebuffering.
  • runs your app
  • cleans the video layer on exit

Now the program can act as if it works with 320x240 16bpp screen.

LCD refresh rate

The OS has a dedicated script which can change the LCD refresh rate. It must be ran with sudo:

sudo -n /usr/pandora/scripts/ 50

From code, system() function can be used for this.

Note: this doesn't mean your program has to run as root to use this (it never should!), just run the script as shown above.

Hardware scaling filter control

The hardware scaler in pandora uses poly-phase 5-tap 8-phase filter. It is described in and sections of TRM. There are 40 coefficients programmable for both horizontal and vertical resampling (vertical resampling might also use 3 taps/24 coefficients, it depends on available pixel clock).

using predefined filters

Just run 'sudo /usr/pandora/scripts/ <name>', where name currently can be:

  • default - the default filter from OMAP manual
  • none - nearest neighbor

using custom filters

run 'sudo /usr/pandora/scripts/ <basename>', where basename* files contains coefficient table. The script will look for <basename>_h, <basename>_v3 and <basename>_v5 files for 5-tap horizontal, 3-tap vertical and 5-tap vertical configurations respectively. Pass absolute or relative path for basename with './', like './something'. The coefficients should be in a form of table like described in TRM, for example:

  0   0 128   0   0
 -1  13 124  -8   0
 -2  30 112 -11  -1
 -5  51  95 -11  -2
  0  -9  73  73  -9
 -2 -11  95  51  -5
 -1 -11 112  30  -2
  0  -8 124  13  -1

For 3-tap table, first and last columns should contain zeros.

LEDs and Display backlight

The LEDs can be controlled via /sys/class/leds/, and then a file [1]:

  • pandora::sd1
  • pandora::sd2
  • pandora::charger
  • pandora::power
  • pandora::bluetooth
  • pandora::wifi
  • pandora::keypad_bl

Backlight can be controlled via /sys/class/backlight/.


Some things can be controlled through files in /proc/pandora/:

  • /proc/pandora/cpu_mhz_max - if cpufreq is enabled, sets maximum allowed cpu clock, if not, just sets CPU clock to value supplied (echo 600 > /proc/pandora/cpu_mhz_max). Might also just use cpufreq parameters themselves.

more to come..