Writing a Video Driver

On startup ...

  1. Make a module
  2. Claim a major number
  3. Register a status file in /proc
  4. Detect the presence or absence of a video card.
  5. Find the current location of displayed video RAM
  6. Clear the screen
  7. Set the cursor to column=0, row=0

On Write

  1. For each charactor
    1. If it's an <ESC>c, clear the screen
    2. If it's an <ESC>l, clear the current line
    3. If it's an <ESC>r, set the reverse video flag
    4. If it's <ESC>g
      1. read the next byte.  Set row to the ASCII value of that charactor
      2. read the next byte.  Set column to the ASCII value of that charactor
      3. If the row and column are in error,
        1. reset their values
        2. Ignore the rest of the write
        3. return EYOUSTINK
    5. Else
      1. Print the charactor using the reverse video flag
      2. Make column = column+1
      3. If column = 81
        1. Column = 0
        2. Row = Row+1
        3. if Row == 26
          1. Scroll the screen one line by doing a memory copy
          2. Row = 25

On Open

  1. If they are not root, return EPERM
  2. Set Row = 0, column = 0;

On Read


Return bytes starting from the current (row, column) in typwriter order until you have met the request or there are no more bytes on the screen.  Successive reads should read from successive locations.  The column and row are to be kept by file descriptor, not by process ID or globally.
 

The Status File

Should tell the following information ...
 
Point Task
3 It's a module
3 Claims a major number
5 Can tell if there is a video card
3 Can tell where video ram is
3 Can tell the current location of the displayed memory
2 Can clear the screen on startup
4 Can clear the screen on <ESC>c
6 Can clear the current line on <ESC>l
4 Writing to the device changes the screen
4 Device keeps track of (row,column) for writes.  Successive writes write to successive locations
4 Screen scrolls
4 Reading gives data from the screen
4 Device keeps track of (row,column) for reads.  Successive reads read from successive locations
8 Row and column are kept by file descriptor, not globally or some other way.
1-6 The Status file (one point for each item)
-4 I can crash the module
-6 I can crash the kernel
3 Done before the due date
-3 Done more than one day after the due date
6 Implements the <ESC>g sequence
4 Implements the <ESC>r sequence

Legal Note

Specifications are often in error.  You are responsible to create what I meant to say, not what I *did* say.

Some useful hints

You can find the location of the video card by looking in /proc/iomem.  But that's the whole card, and not just the text mode ram.

The Linux Kernel Code contains these lines

static unsigned long vga_vram_base = 0xa0000;
static unsigned long vga_vram_end  = 0xbffff;
You must always map a virtual address to a physical address.  To change location 1234 in ram to a 99, use this:
unsigned long pos = 1234;
char *ptr = (char *)(__va(pos));
*ptr = 99;
  --  OR --
*((char *)(__va(1234))) = 99;
The kernel contains the following code, lifted from drivers/video/vgacon.c,modified by me
 /*
  *      Find out if there is a graphics card present.
  *      Are there smarter methods around?
  */
  volatile u16 *p = (volatile u16 *)__va(vga_vram_base);
  u16 saved1 = scr_readw(p);
  u16 saved2 = scr_readw(p + 1);
  scr_writew(0xAA55, p);
  scr_writew(0x55AA, p + 1);
  if (scr_readw(p) != 0xAA55 || scr_readw(p + 1) != 0x55AA) {
           scr_writew(saved1, p);
           scr_writew(saved2, p + 1);
           return 0;
   }
  scr_writew(0x55AA, p);
  scr_writew(0xAA55, p + 1);
  if (scr_readw(p) != 0x55AA || scr_readw(p + 1) != 0xAA55) {
          scr_writew(saved1, p);
          scr_writew(saved2, p + 1);
          return 0;
  }
  scr_writew(saved1, p);
  scr_writew(saved2, p + 1);
  return 1;
Stephan has some code that says
static void put_char( char color, char ch )
{
        /* _UGLY_ hack to set vga text window to start of vram
         *
         * Check /usr/src/linux/drivers/video/vgacon.c lines:
         * 149: void write_vga( int, int )
         * 351: void vga_set_mem_top( struct vc_data* )
         * 928: int vgacon_scrolldelta( struct vc_data*, int )
         *
         * These functions demonstrate the proper way to do it. ;)
         */
 
    outw( 12, 0x3d4 );  /* FUGLY */
    outw( 13, 0x3d4 );  /* HACK! */
 
And the kernel has some code that says
static inline void vga_set_mem_top(struct vc_data *c)
{
        write_vga(12, (c->vc_visible_origin-vga_vram_base)/2);
}
Or
To save where you _were_:

        int a;
        outb( 12, 0x3d4 );
        a = inb( 0x3d5 );
        outb( 13, 0x3d4 );
        a = (a<<8) | (inb( 0x3d5 ) & 0xff);

'a' now holds the position we were at, and:

        outw( 12 + (a&0xff00), 0x3d4 );
        outw( 13 + ((a&0x00ff)<<8), 0x3d4 );

will restore that position.

If you didn't already know this... thank Michael Abrash for writing his
'Zen of Graphics Programming' book... a great read anyday!

Oh, 0x3d4 is CRT Control Index Register, 0x3d5 is CRT Control Data
register, 12 is register for HIGH byte of display position, and 13 is
register for LOW byte of display position. :)