  GGI and KGI
  Josh Yelon, jyelon@cs.uiuc.edu
  $Date: 1998/06/29 17:07:23 $

  This document provides detailed information about GGI and it's kernel
  part KGI.
  ______________________________________________________________________

  Table of Contents


  1. Introduction

  2. The TTY Devices

  3. The GRAPH Devices

  4. Other Files in /dev

  5. Virtual Mice

  6. Merging Event Streams

  7. Supporting LibGGI

  8. Writing KGI Display Drivers

  9. Summary



  ______________________________________________________________________

  1.  Introduction

  Linux is rock solid, it's almost crash-proof.  But for a long time,
  there's been one thing that can consistently bring Linux down: console
  graphics.  The GGI project wishes to close that gaping vulnerability,
  making it possible to do console graphics without fear of crashing
  Linux.

  The centerpiece of the GGI project is "libggi", a shared library for
  drawing graphics.  It is not unlike svgalib, though it has several
  differences.  It supports accelerated graphics.  It can draw on an X
  window.  You don't have to be root to use it.  But most importantly,
  you cannot crash Linux with it.  Libggi requires a small amount of
  kernel support to achieve this goal.  This kernel support is provided
  by a set of loadable kernel modules, collectively called KGI.  KGI
  stands for Kernel Graphics Interface.  KGI has two basic goals:

  1. to add support for multiple monitors and keyboards

  2. to add support for the needs of libggi

  Though it is sometimes said that KGI puts graphics in the kernel, that
  is not strictly correct.  Libggi is what contains the drawing code.
  KGI, the kernel part, only contains a few support functions for
  libggi.  That's usually a tiny stub of code, and it rarely involves
  any actual drawing in the kernel.

  KGI consists of four kinds of loadable kernel modules:

  o  The base KGI module.

  o  The display driver (a different one for each kind of video card)

  o  The keyboard driver (modified to support multiple keyboards)

  o  The mouse driver/virtualizer (will be explained later)

  Because we've kept the drawing code out of the kernel, the display
  driver is only about 20 kbytes.  This makes it one of the smallest,
  simplest modules in the Linux kernel.  By way of comparison, it's 1/2
  the size of the ppp driver, and less than than 1/5 the size of the
  sound driver.  The total size of a typical set of KGI modules is less
  than 40 kbytes.

  We'll start with a discussion of how KGI supports multiple monitors
  and keyboards.  Later, we'll discuss its functions for supporting
  libggi.


  2.  The TTY Devices

  To support multiple keyboards and multiple monitors, KGI needs to add
  more tty devices to the dev directory.  UNIX tradition says that a tty
  device should consist of one keyboard and one monitor, paired
  together.  KGI has to bend this tradition a little bit to support
  systems with mismatched numbers of monitors and keyboards.  To achieve
  this with minimal alterations to the accepted way of doing things, KGI
  represents extra monitors as ttys which are missing their keyboards,
  and extra keyboards as ttys which are missing their monitors.

  Bear in mind that while the tty devices seem to pair the monitors with
  the keyboards, there's no reason you actually have to use them in
  pairs.  If an application program needs two monitors and three
  keyboards, it need merely open two ttys for writing, and three ttys
  for reading.

  Traditional Linux provides 64 "virtual consoles".  To put it
  differently, the keyboard emulates 64 virtual keyboards, the monitor
  emulates 64 virtual monitors, and when joined pairwise, these devices
  become 64 ttys.  KGI allows more keyboards and more monitors, and it
  virtualizes them all.  With KGI, each physical keyboard is treated as
  16 virtual keyboards, and each physical monitor is treated as 16
  virtual monitors.  A set of 16 virtual keyboards and 16 virtual
  monitors can be combined pairwise into a set of 16 tty devices.  As
  stated earlier, the pairing does not have to be perfect --- it is
  legal to make a set of 16 tty devices without keyboards, or 16 tty
  devices without monitors.

  Because each physical device emulates 16 virtual devices, tty device
  files always occur in sets of 16.  We've arranged the dev directory
  into subdirectories to make this grouping clear (notice that the files
  are named "vty" for the moment, but they're basically ttys):

  /dev/display/head0/vty1
  /dev/display/head0/vty2
  /dev/display/head0/...
  /dev/display/head0/vty16

  /dev/display/head1/vty1
  /dev/display/head1/vty2
  /dev/display/head1/...
  /dev/display/head1/vty16

  With KGI, the display drivers can be loadable modules.  When you load
  the module, you specify (by means of insmod parameters) which set of
  16 ttys the display should attach itself to.  Likewise, when you load
  a keyboard driver, you specify (by means of insmod parameters) which
  set of 16 ttys the keyboard should attach itself to.  There are also
  configuration calls that can change these settings later.
  It would be traditional to attach the main keyboard to the first set
  of 16 ttys.  And it would also be traditional to attach the main
  monitor to the same 16 ttys.  Then, you'd run "getty" on the first
  several ttys.  This would give you the old-fashioned Linux behavior.

  But, KGI doesn't enforce anything like that.  If you have one monitor
  and one keyboard, you could associate the monitor with 3rd set of 16
  ttys, and the keyboard with the 8th set of 16 ttys. If you did that,
  you'd have to open /dev/display/head2/tty1 for writing to get to the
  monitor, and you'd have to open /dev/display/head7/tty1 for reading to
  get the keyboard.  This would break almost every user program, since
  almost every program expects a tty to have both a keyboard and a
  monitor.  But, there's nothing in KGI stopping you from doing it.

  When a physical keyboard and physical display are caused to share a
  set of 16 ttys, "echo" from the 16 virtual keyboards goes to the 16
  virtual displays, as you would expect.  And, if you hit ALT-Fn on the
  keyboard, the display will flip as well, as expected.

  Of course, you can compile a keyboard and a display driver into the
  kernel, and by default, they automatically attach themselves to the
  first 16 ttys.  And, as always, these settings can be reconfigured on
  the fly.


  3.  The GRAPH Devices

  In addition to the "tty" devices, KGI adds the "graph" devices.  These
  are in a one-to-one relationship with tty devices: for every tty,
  there's one graph device.  So the graph devices are named:

  /dev/display/head0/graph1
  /dev/display/head0/graph2
  /dev/display/head0/...
  /dev/display/head0/graph16

  /dev/display/head1/graph1
  /dev/display/head1/graph2
  /dev/display/head1/...
  /dev/display/head1/graph16

  The graph device represents the same monitor as the matching tty
  device, except that you can use the graph device to draw graphics on
  the monitor.  Libggi opens the graph device, and uses ioctl calls and
  mmap calls to control the video card.  The details are described
  later.


  4.  Other Files in /dev

  In addition to the tty and graph devices, there are a few other
  entries in the dev directory.

  Opening /dev/tty has the same effect it has always had.  Opening
  /dev/tty is equivalent to opening the tty device that is the process's
  controlling terminal.  We have added a new file, /dev/graphic, which
  is entirely analogous to /dev/tty.  Opening /dev/graphic is equivalent
  to opening the graph device that corresponds to the process's
  controlling terminal.

  Linux traditionally has several devices /dev/tty1, /dev/tty2, etc.
  When using GGI, one would normally create these devices and give them
  the same device numbers as /dev/display/head0/tty1,
  /dev/display/head0/tty2, etc.


  5.  Virtual Mice

  In traditional Linux, if you run two mouse-utilizing programs on two
  different virtual consoles, they will both try to open the mouse
  device.  One will succeed, one will fail.  Usually, this results in
  the failure of one of the two programs.  Clearly, it would be better
  if the two programs could coexist and share the mouse, in the same way
  that they coexist and share the keyboard.

  The problem stems from the fact that the keyboard is virtualized, but
  the mouse is not.  This is easily fixed.  We've created a simple
  mouse-driver module that can virtualize a mouse.  It opens one
  physical mouse device, and creates 16 virtual mouse devices.

  In the same way that a "tty" device represents both a virtual monitor
  and a virtual keyboard, we decided that a "graph" device could
  represent both a virtual monitor and a virtual mouse.  Opening a graph
  device for writing gives you a virtual monitor.  Opening one for
  reading gives you a virtual mouse.  As with ttys, this isn't a strict
  pairing --- a graph device can be missing a monitor, or missing a
  mouse.  When you load a mouse-driver module, you specify (via insmod
  parameters) which set of 16 graph devices the mouse should attach
  itself to.

  While graph devices seem to "pair" the mice with the monitors, there
  is again no requirement that you actually use them in pairs.  If an
  application program needs three mice, four graphics displays, and five
  keyboards, it need merely open three graph devices for reading, four
  graph devices for writing, and five tty devices for reading.

  The mouse driver not only virtualizes the mouse, it also standardizes
  its protocol.  Real physical mice speak mouse systems protocol,
  microsoft mouse protocol, or any number of other protocols.  But no
  matter what protocol the physical device uses, the virtual mice all
  use the standard protocol.


  6.  Merging Event Streams

  In normal UNIX, a tty device pairs one keyboard with one monitor.  KGI
  bends that rule a little bit.  As we already mentioned, you can omit
  the keyboard or the monitor.  But, there's a second way you can bend
  the rules.

  Suppose you have two keyboards: a standard keyboard, and a braille
  keyboard.  You load the driver for the standard one, and tell it to
  attach to the first 16 ttys.  You then load the driver for the braille
  keyboard, and you also tell it to use the same 16 ttys.  You've
  created an overlap --- two keyboards, sharing the same dev files.  In
  these cases, KGI merges the stream of characters coming from the two
  keyboards.  This allows the sighted and the blind user to share the
  machine without reconfiguring the keyboard.

  This also works with mice.  Suppose you like to use the mouse for your
  word processing, but you like to use a graphics tablet for CAD work.
  You can load your mouse and your tablet driver into the same "dev"
  files.  KGI merges the streams of events from the two mice into a
  single comprehensible stream.  If you run the X server, you can move
  the pointer by dragging the mouse, or by using the tablet.  This
  allows you to use your word processor and your CAD program, without
  restarting the X server in between.

  [Technically, I know it's still possible to tell the events from the
  merged items apart.  I think that's too much detail for a paper of
  this level.]

  7.  Supporting LibGGI

  When libggi opens one of the graph devices for writing, it gets a file
  descriptor.  KGI implements a few standard "ioctl" operations on the
  file descriptor:

  o  Reset display

  o  Identify video card type

  o  Change mode (text, 640x480, 1024x768, etc)

  o  Copy pixels to display

  o  A few others

  One might immediately wonder: Just some mode setting, some memory copy
  operations, and that's it?  Where's the draw circle, the flood fill,
  the shade polygon?  But remember: the kernel code isn't supposed to do
  drawing.  Its purpose is to support libggi.  Adding more drawing
  primitives would increase the complexity of the kernel --- and libggi
  just doesn't need them.  The operations listed above are plenty for
  libggi to implement unaccelerated graphics.

  For accelerated graphics, libggi needs a little more.  To make
  acceleration possible, display drivers are allowed to supplement the
  basic set of operations with additional ones.  The various video card
  drivers are not required to standardize on these supplemental
  operations.  For example, the trident 9640 driver could provide a
  "bitblit" ioctl.  The Matrox Millenium driver could provide a "render
  array of 3D shapes" ioctl.  Most video drivers let libggi draw
  directly into video RAM, by providing a "mmap the frame buffer"
  function.  Many drivers provide functions that bypass the abstraction
  layer.  For example, some display drivers provide a "mmap the video
  registers" function.  If the author of the display driver can think of
  some fast way for libggi to control the video card, then the author
  should implement it, by all means.  These extra functions, provided
  for speed, are called the "acceleration functions".

  We chose not to standardize the acceleration functions.  Doing so
  would shoehorn video cards into a framework that might not fit them.
  Worse, it would require display drivers to emulate all the drawing
  primitives not supported by the card, easily increasing the size of
  the kernel driver by an order of magnitude.

  Most importantly, though, libggi just doesn't need standardization in
  the kernel display driver.  Libggi was designed to be able to draw on
  many different kinds of devices.  It can draw on virtual consoles, but
  it can also draw on X windows, on 3Dfx devices, even on an ascii art
  renderer!  Libggi dynamically loads device-specific libraries to help
  it deal with a variety of different display devices.  One can add
  support for a new device by merely adding a device-specific library to
  libggi's repository.  When a KGI display driver adds some
  unstandardized acceleration functions, it is merely creating a variant
  of an existing display device.  The author of the display driver is
  expected to write a device-specific library for libggi's repository.
  This is a simple procedure.  In this way, libggi easily deals with the
  variations in the display drivers created by acceleration functions.

  Of course, this all happens transparently to the user of libggi.  To
  the user, libggi just works faster when it's being used on an
  accelerated card.




  8.  Writing KGI Display Drivers

  Though the display drivers are allowed to export almost any
  acceleration function they like, there is one restriction: a display
  driver may not provide an "unsafe" operation that could crash or
  damage the system.  Recall that any user can open the graph devices
  and start performing ioctls.  We do not want to allow such users to
  crash the system.

  Suppose you're writing a driver for a poorly designed video card which
  can be "locked up" by writing illegal values to its registers.  In
  that case, it would not be legal for the driver to export the video
  registers directly.  Similarly, if exporting the registers would give
  the user the power to fry the monitor, then it is also not allowed.

  There is a second consideration: kernel complexity should be kept
  minimal.  The writer of a display driver shouldn't provide a hundred
  acceleration functions for drawing patterned ellipses, dotted lines,
  filled polygons, shaded 3D polygons...  that's just too much to put in
  the kernel.  A driver providing no more than ten acceleration
  functions would make us happy.  One providing twenty would makes us
  nervous.  One providing a hundred would probably be rejected.  We like
  to keep the acceleration functions to about 4 kbytes.

  We like it when drivers provide "bulk" operations like "draw array of
  shapes".  Bulk operations are more efficient, because they can do a
  lot of drawing in just one ioctl.  We also like it when drivers
  provide operations like "mmap the frame buffer".  That's a nice one,
  because libggi only needs one mmap to set things up, and the rest can
  be done in libggi.  This is efficient, and it keeps the kernel simple.
  And when "mmap the video registers" can be done safely, that neatly
  transfers all the smarts out of the kernel, and into libggi.


  9.  Summary

  KGI, the kernel graphics interface, is a set of loadable kernel
  modules with these goals:

  1. to add support for multiple monitors and keyboards

  2. to add support for the needs of libggi

  KGI supports the multiple monitors and keyboards by creating groups of
  16 ttys and 16 graph devices.  One can load a kernel driver for a
  keyboard, which is then virtualized into 16 virtual keyboards.  One
  can assign these keyboards to a set of 16 tty devices.  One can load a
  kernel driver for a video card, which is virtualized into 16 virtual
  displays.  One can then assign these displays to a set of 16 tty and
  16 graph devices.  One can also load a mouse driver module, which
  virtualizes a mouse into a set of 16 virtual mice, which one can
  assign to a set of 16 graph devices.  If you load two independent
  keyboard drivers into the same set of 16 ttys, the characters from the
  two will be merged.  Likewise if you load two mouse drivers into the
  same 16 graph devices.

  KGI allows libggi to draw in the following manner. Libggi opens one of
  the graph devices, yielding a file descriptor.  The file descriptor
  implements a few ioctls that perform simple drawing operations.  There
  aren't many of these operations, but they are enough to do
  unaccelerated graphics.  In addition, the file descriptor may support
  a half dozen device-specific "acceleration" functions.  These are
  usually powerful, general operations like "bitblit" or "mmap the video
  registers".  Libggi uses these when they're available.  Libggi
  abstracts over the variation in the kernel display drivers, presenting
  a consistent interface to the user.
