FBDirectOnConsoleLCD

From HBMobile
Revision as of 20:36, 17 October 2007 by 69.181.111.57 (talk) (→‎Make)
Jump to navigationJump to search

Intro

Oh... one quick one... before we start here, I'm going to assume that you have a working Linux box with the latest GumStix build. You can find information about how to download and compile the canonical GumStix codebase by looking at the Getting The ConsoleLCD-VX Working page or the Buildroot page at GumStix' Wiki.

Specifically... I'm going to assume that you have a working environment for cross compiling. Once you've compiled gcc to be a cross-compiler, you setup the environment as described in the GumStix Programming page and use the command arm-linux-gcc to compile with. Actually... there's a cross-compiling version of all of the dev tools in the $(GUMROOT)/build_arm_nofpu/staging_dir/bin directory, so including it in your path is probably a good idea. Here's a brief shell script I have that sets up the environment... you should change the value of $(GUMROOT) to reflect where you installed the gumstix source.

#!/bin/bash
export GUMROOT=/home/msh/Projects/gumstix-buildroot
export PATH=$GUMROOT/build_arm_nofpu/staging_dir/bin:$PATH
export CC=arm-linux-gcc

If you source this from the command line after you login, this should setup your environment to allow you to use the arm-linux-gcc command.

Does the Frame Buffer Device Work?

Well... this is a pretty simple question to answer... Yes, the frame buffer works, otherwise we wouldn't get the GumStix logo on the screen after booting. But I figured it might be prudent to spend a little time verifying that it works the way I think it's supposed to work.

Getting Frame Buffer Info

You can get information about the frame buffer via the command line by looking at the pseudo-files available in the directory:

/sys/devices/virtual/graphics/fb0/

For instance, if you wanted to know the size of the screen and the bits per pixel, you could cat two of the files in the directory like this:

cat /sys/devices/virtual/graphics/fb0/bits_per_pixel /sys/devices/virtual/graphics/fb0/virtual_size

Drawing Stuff on the Screen By Writing to /dev/fb0

Here's some code I wrote to exercise the frame buffer device. You can see the results on episode 2 of the hbmobile vlog.

When you want to draw stuff on the screen, you take the following steps:

  1. Include the appropriate headers
  2. Open the "/dev/fb0" device file
  3. Use the ioctl syscall to get information about the device
  4. Use mmap to map the frame buffer into your process' memory space
  5. Poke pixels into screen memory

You can download a tar file with this code (which is released under a BSD style license) and a Makefile to help compile it. File:Fbtest.tgz

Include Headers, Create a main(), Yadda, Yadda, Yadda

So one of the things you can see that I do is I made an "App" data structure that's supposed to store the state of the application. You don't have to do this, I just thought it made things a little easier to deal with.

The most important part of the following code fragment is that it lists the headers you're supposed to use.

/* Macro Definitions */

/* File Inclusions */
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <linux/fb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <asm/param.h>
#include <string.h>
#include <sys/time.h>

/* Typedefs, Structs, Unions, Enums, Etc. */
typedef enum {
  FBT_E_NOERR = 0,
  FBT_E_USAGE,
  FBT_E_NULL,
  FBT_E_MALLOC,
  FBT_E_FILE,
  FBT_E_SCREENO,
  FBT_E_SCREENM
} FBTErr;

typedef struct {
  char *device;
  int screen_fd;
  unsigned char *screen_ptr;
  unsigned int size;
  int width;
  int height;
  int bpp;
  int left_margin;
  int right_margin;
  int top_margin;
  int bottom_margin;
} App;

/* Static Function Prototypes */
static FBTErr fbt_init( App **app, int argc, char *argv[] );
static FBTErr fbt_clear( App *app );
static FBTErr fbt_cleanup( App *app );
static FBTErr fbt_rainbow( App *app, unsigned int offset );
static FBTErr fbt_marching_rainbow( App *app );

/* Global Variable Declarations */

/* Function Definitions */

int main( int argc, char *argv[] ) {
  FBTErr err = FBT_E_NOERR;
  App *app = NULL;

  do {
    /* initialize the app structure */
    if( ( FBT_E_NOERR != ( err = fbt_init( &app, argc, argv ) ) ) ) {
      break;
    }

    /* clear the screen */
    if( ( FBT_E_NOERR != ( err = fbt_clear( app ) ) ) ) {
      break;
    }

    /* paint a rainbow */
    if( ( FBT_E_NOERR != ( err = fbt_marching_rainbow( app ) ) ) ) {
      break;
    }
    
  } while( 0 );

  if( ( FBT_E_FILE == err ) || ( FBT_E_SCREENO == err ) ||
      ( FBT_E_SCREENM == err ) ) {
    perror( NULL );
  }

  fbt_cleanup( app );

  return( err & 0xFF );
}

Initializing Things

For my money, this is where the most interesting stuff relating to frame buffer access occurs.. and it's not really that interesting. It's just boilerplate code.

/* fbt_init() - initialize the app structure
** if *app is null, the function will allocate a new app structure for you.
** otherwise it assumes you've done it yourself. If app is null... well, that's
** an error.
*/

FBTErr fbt_init( App **app, int argc, char *argv[] ) {
  FBTErr err = FBT_E_NOERR;
  unsigned int unalloc = 0;
  struct fb_var_screeninfo screeninfo;
  unsigned int bpp;

  do {
    if( NULL == app ) {
      err = FBT_E_NULL;
      break;
    }

    if( NULL == *app ) {
      *app = (App *) malloc( sizeof( App ) );
      unalloc = 1;
      if( NULL == *app ) {
	unalloc = 0;
	err = FBT_E_MALLOC;
	break;
      }
    }

    memset( *app, 0, sizeof( App ) );

    (*app)->screen_fd = -1;
    (*app)->device = "/dev/fb0";
    printf( "opening %s\n", (*app)->device );
    if( ( (*app)->screen_fd = open( (*app)->device, O_RDWR ) ) < 0 ) {
      err = FBT_E_FILE;
      break;
    }
    printf( "device %s is file descriptor %d\n", (*app)->device, (*app)->screen_fd );

    if( ( ioctl( (*app)->screen_fd, FBIOGET_VSCREENINFO, &screeninfo ) ) > 0 ) {
      err = FBT_E_SCREENO;
      break;
    }

    if( screeninfo.bits_per_pixel > 24 ) {
      bpp = 32;
    } else if( screeninfo.bits_per_pixel > 16 ) {
      bpp = 24;
    } else if( screeninfo.bits_per_pixel > 8 ) {
      bpp = 16;
    } else {
      bpp = 8;
    }

    (*app)->width = screeninfo.xres_virtual;
    (*app)->height = screeninfo.yres_virtual;
    (*app)->bpp = screeninfo.bits_per_pixel;
    (*app)->size = (*app)->height * (*app)->width * ( bpp / 8 );
    (*app)->left_margin = screeninfo.left_margin;
    (*app)->right_margin = screeninfo.right_margin;
    (*app)->top_margin = screeninfo.upper_margin;
    (*app)->bottom_margin = screeninfo.lower_margin;

    printf( "got device info: %d x %d (%d bits per pixels)\nmargin is (%d,%d,%d,%d)\n",
	    (*app)->width, (*app)->height, (*app)->bpp, (*app)->left_margin,
	    (*app)->right_margin, (*app)->top_margin, (*app)->bottom_margin );

    (*app)->screen_ptr = (unsigned char *) mmap( 0, (*app)->size,
	    ( PROT_READ | PROT_WRITE ), MAP_SHARED, (*app)->screen_fd, 0);

    if( MAP_FAILED == (*app)->screen_ptr ) {
      err = FBT_E_SCREENM;
      break;
    }
      
    printf( "screen pointer is %d bytes at %08X\n", (*app)->size, (*app)->screen_ptr );
    unalloc = 0;
  } while( 0 );

  if( 0 != unalloc ) {
    free( *app );
    *app = NULL;
  }

  return( err );
}

Clearing the Screen

This code demonstrates simple access to the screen. We put a 0xFF in every byte in the frame buffer. It's pretty straight forward...

static FBTErr fbt_clear( App *app ) {
  FBTErr err = FBT_E_NOERR;
  unsigned int i, size;

  do {
    if( NULL == app ) {
      err = FBT_E_NULL;
      break;
    }

    for( i = 0; i < app->size; i++ ) {
      app->screen_ptr[i] = 0xFF;
    }
  } while( 0 );

  return( err );
  
}

Drawing a Rainbow

This code shows how I draw a rainbow pattern.

static FBTErr fbt_rainbow( App *app, unsigned int offset ) {
  unsigned int i,c,x;

  for( i = 0; i < app->size; i++ ) {
    switch( i % 3 ) {
    case 0:
      c = ( offset + i ) % 0x40000;
      x = c;
      break;
    case 1:
      x = c >> 9;
      break;
    case 2:
      x = c >> 18;
    }
    app->screen_ptr[i] = (unsigned char) ( x & 0xFF );
  }
}

static FBTErr fbt_marching_rainbow( App *app ) {
  unsigned int i;
  fd_set foo;
  struct timeval time;

  FD_ZERO( &foo );
  memset( &time, 0, sizeof( struct timeval ) );

  time.tv_sec = 0;
  time.tv_usec = 150000;

  for( i = 0; i < 130560; i++ ) {
    fbt_rainbow( app, i * 3 );
    select( 0, &foo, &foo, &foo, &time );
  }

  return( FBT_E_NOERR );
}

Cleaning Up

Before we quit, we unmap the screen and close the device file. I think that Linux is smart enough to do this automagically when your process quits, but hey, it's always polite to explicitly tell the system your intentions.

static FBTErr fbt_cleanup( App *app ) {
  FBTErr err = FBT_E_NOERR;

  do {
    if( NULL == app ) {
      err = FBT_E_NULL;
      break;
    }

    if( ( NULL != app->screen_ptr ) && ( MAP_FAILED != app->screen_ptr ) ) {
      munmap( app->screen_ptr, app->size );
    }

    if( app->screen_fd >= 0 ) {
      close( app->screen_fd );
    }
  } while( 0 );

  return( err );
}

Does DirectFB Work on your Desktop Linux?

After spending a bit of time trying to get DirectFB to work on the GumStix platform (and getting strange results,) I thought I would make sure I understand how it's supposed to work on a Desktop x86 system. So, to get DirectFB to work with the image we created on the VMWareImage page, we follow these steps.

Install PNG, JPEG, and FreeType2 Libraries

Execute the command:

sudo apt-get install libpng12-dev libjpeg62-dev libfreetype6-dev pkg-config fbset

Download, Configure and Compile the DirectFB Source

Download version 1.1.0 of the DirectFB source by going to the web site and downloading it through your browser, or by executing the following commands:

mkdir -p ~/Library/Download
cd ~/Library/Download
wget http://directfb.org/downloads/Core/DirectFB-1.1.0.tar.gz
cd ~/Projects
tar xzvf ~/Library/download/DirectFB-1.1.0-tar.gz

Now change directories to the DirectFB source directory and configure, make and make install it. I chose to install it in /usr/local and encountered some problems with getting X11 support to compile. So.. here's how I did it:

cd ~/Projects/DirectFB-1.1.0
./configure --prefix=/usr/local --disable-x11
make
sudo make install

Download, Configure and Compile the DirectFB Examples Source

Download version 1.0.0 of the DirectFB examples by going to the web site and downloading it through your browser, or by executing the following commands:

cd ~/Libraru/Download
wget http://directfb.org/downloads/Extras/DirectFB-examples-1.0.0.tar.gz
cd ~Projects
tar xzvf ~/Library/download/DirectFB-examples-1.0.0.tar.gz

Now change directories to the DirectFB examples directory and configure, make and make install it. Again, I chose to install the binaries into /usr/local</a> and prefixed the binaries with "x86" to insure there's no confusion when we start making the ARM based binaries. Here are the commands I used:

cd ~/Projects/DirectFB-examples-1.0.0
./configure --prefix=/usr/local --program-prefix=x86
make
sudo make install

Modify your Grub Settings and Reboot

In the file /boot/grub/menu.lst, find the line that begins with:

kernel /boot/vmlinuz-2.6.20-15-generic

and append

video=vesafb:ywrap,mtrr vga=788

to it.

Now reboot your machine.

Change To Virtual Terminal 1

I tried changing the virtual terminal with the Ctrl-Alt-F1 using VMWare, but it didn't work especially well. Ctrl-Alt is key sequence that escapes the mouse from moving the virtual pointer, so I guess VMWare is gobbling that key sequence. I wonder if there's a way to tell VMWare to use a different key sequence?

You can, however, use the chvt command to change virtual terminals from the command line. So this command should do the trick:

sudo chvt 1

Login, Set Some Variables, Test the DirectFB Example Code

You should see a login screen. Go ahead and login with the username mobo and the password hbmobile.

You'll need to set some environment variables:

export FRAMEBUFFER=/dev/fb0
export LD_LIBRARY_PATH=/usr/local/lib:/usr/lib:/lib

And now you should be able to execute the example DirectFB applications in /usr/local/bin. My favorite is x86df_dok, it spits a lot of stuff on the screen and performs some simple benchmarking. w00t!

Add DirectFB, FreeType, libjpeg and libpng

I haven't made "official" patches for DirectFB and BuildRoot, so we're going to be using the BuildRoot build scripts in a somewhat non-standard way. Specifically, we're going to short-circuit it's "automagic" downloading behavior by manually downloading packages into the downloads directory and untarring them ourselves. Once we do that, we'll overwrite some of the source and configuration files with the contents of a "patch" archive I have here. So let's take it step by step.

Download the Files

In order to get DirectFB working, you need to have (at least) FreeType, libjpeg and libpng installed. Okay.. technically you don't really need these libraries, but if you want to have fonts, jpeg or png images, you're going to have to install these packages. Here's a table of the packages, revision numbers and download URLs that I used:

Package Revision URL
DirectFB 1.1.0 http://directfb.org/downloads/Core/DirectFB-1.1.0.tar.gz
FreeType 2.3.5 http://download.savannah.gnu.org/releases/freetype/freetype-2.3.5.tar.gz
libjpeg 0.6b ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz
libpng 1.2.22 http://prdownloads.sourceforge.net/libpng/libpng-1.2.22.tar.gz?download

And here's the script I used to download them. It assumes that you have the gumstix_buildroot in the ~/Projects directory.

cd ~/Projects/gumstix-buildroot/dl
wget 'http://directfb.org/downloads/Core/DirectFB-1.1.0.tar.gz'
wget 'http://download.savannah.gnu.org/releases/freetype/freetype-2.3.5.tar.gz'
wget 'ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz'
wget 'http://prdownloads.sourceforge.net/libpng/libpng-1.2.22.tar.gz?download'

Untar the Files

There are no great tricks to untarring these files, just make sure you untar them in the ~/Projects/gumstix-buildroot/build_arm_nofpu directory. Here's the script I used:

cd ~/Projects/gumstix-buildroot/build_arm_nofpu
tar xzvf ~/Projects/gumstix-buildroot/dl/DirectFB-1.1.0.tar.gz
tar xzvf ~/Projects/gumstix-buildroot/dl/freetype-2.3.5.tar.gz
tar xzvf ~/Projects/gumstix-buildroot/dl/jpegsrc.v6b.tar.gz
tar xzvf ~/Projects/gumstix-buildroot/dl/libpng-1.2.22.tar.gz

Apply Patches

And I use the term "patches" loosely. I've yet to produce "real" patch files, I just have a tar file with files that overwrite "sub-optimal" files in the build tree. When I get around to producing a real patch file, I'll put a link to it here and update this description. Applying and downloading the patch file is pretty straight-forward. You download the foo file, cd to the ~/Projects/gumstix_buildroot directory and untar the "patch" archive. Here's a script:

cd ~/Projects/gumstix_buildroot
wget 'http://hbmobile.org/downloads/directfb_patches.tar.gz'
tar xzvf directfb_patches.tar.gz

Rebuildify

Modify /opt

We setup our packages to install into /opt. I did this just to make sure that it was easy to tell what bits were from this DirectFB enabling projects and which bits weren't. But... since we're not running our build scripts as root, we need to modify the permissions on /opt to allow us to write into this directory. Please note that this is the type of thing one would NEVER do on a production machine. So I re-iterate that in order to make the build process easier, I'm ignoring security best-practice. Please make sure the machine you do this on is behind a firewall..

So here's what I did:

sudo chmod 777 /opt

Remove the old root filesystem, uImage and u-boot.bin

cd ~/Projects/gumstix-buildroot
rm rootfs.arm_nofpu.jffs2 u-boot.bin uImage

Make menuconfig

What can I say, I'm more comfortable with the menuconfig system. Execute the following line and it should eventually pop up a curses based menu where you'll have the opportunity to select directfb, freetype, jpeg and libpng:

make menuconfig

Once you're in this menu:

  1. arrow down to the "Package Selection for Target" menu item and hit <enter>
  2. arrow down until you see the "directfb" entry and hit the space bar (you should see an asterisk appear beside this item)
  3. hit the right arrow so the <Exit> "button" at the bottom of the screen is selected, then hit <enter>
  4. hit the right arrow so the <Exit> "button" is again selected, then hit <enter>
  5. when the system asks if you want to save your configuration, hit <enter> to confirm that you do want to save changes (this should drop you back out to the main menu)

Make

Enter the command

cd ~/Projects/gumstix-buildroot
make
rm rootfs.arm_nofpu.jffs2 u-boot.bin uImage
tar xzvf directfb_patches.tar.gz
make

and go get some coffee.

Download and Compile Examples

Reflash with the new Root image and Kernel