XFree86 server 4.x Design (DRAFT) : Some notes about writing a driver
Previous: The vgahw module
Next: XFree86 server 4.x Design (DRAFT)

## 20. Some notes about writing a driver

NOTE: some parts of this are not up to date

The following is an outline for writing a basic unaccelerated driver for a PCI video card with a linear mapped framebuffer, and which has a VGA core. It is includes some general information that is relevant to most drivers (even those which don't fit that basic description).

The information here is based on the initial conversion of the Matrox Millennium driver to the ``new design''. For a fleshing out and sample implementation of some of the bits outlined here, refer to that driver. Note that this is an example only. The approach used here will not be appropriate for all drivers.

Each driver must reserve a unique driver name, and a string that is used to prefix all of its externally visible symbols. This is to avoid name space clashes when loading multiple drivers. The examples here are for the ``ZZZ'' driver, which uses the ``ZZZ'' or ``zzz'' prefix for its externally visible symbols.

### 20.1. Include files

All drivers normally include the following headers:

> `"xf86.h"
> "xf86_OSproc.h"
> "xf86_ansic.h"
> "xf86Resources.h"`

Wherever inb/outb (and related things) are used the following should be included:

> `"compiler.h"`

Note: in drivers, this must be included after `"xf86_ansic.h"`.

Drivers that need to access PCI vendor/device definitions need this:

> `"xf86PciInfo.h"`

Drivers that need to access the PCI config space need this:

> `"xf86Pci.h"`

Drivers using the mi banking wrapper need:

> `"mibank.h"`

Drivers that initialise a SW cursor need this:

> `"mipointer.h"`

All drivers implementing backing store need this:

> `"mibstore.h"`

All drivers using the mi colourmap code need this:

> `"micmap.h"`

If a driver uses the vgahw module, it needs this:

> `"vgaHW.h"`

Drivers supporting VGA or Hercules monochrome screens need:

> `"xf1bpp.h"`

Drivers supporting VGA or EGC 16-colour screens need:

> `"xf4bpp.h"`

Drivers using cfb need:

> `#define PSZ 8
> #include "cfb.h"
> #undef PSZ`

Drivers supporting bpp 16, 24 or 32 with cfb need one or more of:

> `"cfb16.h"
> "cfb24.h"
> "cfb32.h"`

The driver's own header file:

> `"zzz.h"`

Drivers must NOT include the following:

> `"xf86Priv.h"
> "xf86Privstr.h"
> "xf86_libc.h"
> "xf86_OSlib.h"
> "Xos.h"`
> any OS header

### 20.2. Data structures and initialisation

- The following macros should be defined:

      #define VERSION <version-as-an-int>
      #define ZZZ_NAME "ZZZ"         /* the name used to prefix messages */
      #define ZZZ_DRIVER_NAME "zzz"  /* the driver name as used in config file */
      #define ZZZ_MAJOR_VERSION <int>
      #define ZZZ_MINOR_VERSION <int>
      #define ZZZ_PATCHLEVEL    <int>

  NOTE: `ZZZ_DRIVER_NAME` should match the name of the driver module without things like the "lib" prefix, the "_drv" suffix or filename extensions.

- A DriverRec must be defined, which includes the functions required at the pre-probe phase. The name of this DriverRec must be an upper-case version of ZZZ_DRIVER_NAME (for the purposes of static linking).

      DriverRec ZZZ = {
          VERSION,
          ZZZ_DRIVER_NAME,
          ZZZIdentify,
          ZZZProbe,
          ZZZAvailableOptions,
          NULL,
          0
      };

- Define list of supported chips and their matching ID:

      static SymTabRec ZZZChipsets[] = {
          { PCI_CHIP_ZZZ1234, "zzz1234a" },
          { PCI_CHIP_ZZZ5678, "zzz5678a" },
          { -1,               NULL }
      };

  The token field may be any integer value that the driver may use to uniquely identify the supported chipsets. For drivers that support only PCI devices using the PCI device IDs might be a natural choice, but this isn't mandatory. For drivers that support both PCI and other devices (like ISA), some other ID should probably used. When other IDs are used as the tokens it is recommended that the names be defined as an `enum` type.

- If the driver uses the `xf86MatchPciInstances(`) helper (recommended for drivers that support PCI cards) a list that maps PCI IDs to chip IDs and fixed resources must be defined:

      static PciChipsets ZZZPciChipsets[] = {
          { PCI_CHIP_ZZZ1234, PCI_CHIP_ZZZ1234, RES_SHARED_VGA },
          { PCI_CHIP_ZZZ5678, PCI_CHIP_ZZZ5678, RES_SHARED_VGA },
          { -1,               -1,               RES_UNDEFINED }
      }

- Define the `XF86ModuleVersionInfo` struct for the driver. This is required for the dynamically loaded version:

      #ifdef XFree86LOADER
      static XF86ModuleVersionInfo zzzVersRec =
      {
          "zzz",
          MODULEVENDORSTRING,
          MODINFOSTRING1,
          MODINFOSTRING2,
          XF86_VERSION_CURRENT,
          ZZZ_MAJOR_VERSION, ZZZ_MINOR_VERSION, ZZZ_PATCHLEVEL,
          ABI_CLASS_VIDEODRV,
          ABI_VIDEODRV_VERSION,
          MOD_CLASS_VIDEODRV,
          {0,0,0,0}
      };
      #endif

- Define a data structure to hold the driver's screen-specific data. This must be used instead of global variables. This would be defined in the `"zzz.h"` file, something like:

      typedef struct {
          type1  field1;
          type2  field2;
          int    fooHack;
          Bool   pciRetry;
          Bool   noAccel;
          Bool   hwCursor;
          CloseScreenProcPtr CloseScreen;
          OptionInfoPtr Options;
          ...
      } ZZZRec, *ZZZPtr;

- Define the list of config file Options that the driver accepts. For consistency between drivers those in the list of ``standard'' options should be used where appropriate before inventing new options.

      typedef enum {
          OPTION_FOO_HACK,
          OPTION_PCI_RETRY,
          OPTION_HW_CURSOR,
          OPTION_NOACCEL
      } ZZZOpts;

      static const OptionInfoRec ZZZOptions[] = {
        { OPTION_FOO_HACK,  "FooHack",   OPTV_INTEGER, {0}, FALSE },
        { OPTION_PCI_RETRY, "PciRetry",  OPTV_BOOLEAN, {0}, FALSE },
        { OPTION_HW_CURSOR, "HWcursor",  OPTV_BOOLEAN, {0}, FALSE },
        { OPTION_NOACCEL,   "NoAccel",   OPTV_BOOLEAN, {0}, FALSE },
        { -1,               NULL,        OPTV_NONE,    {0}, FALSE }
      };

### 20.3. Functions

#### 20.3.1. SetupProc

For dynamically loaded modules, a `ModuleData` variable is required. It is should be the name of the driver prepended to "ModuleData". A `Setup()` function is also required, which calls `xf86AddDriver()` to add the driver to the main list of drivers.

    #ifdef XFree86LOADER

    static MODULESETUPPROTO(mgaSetup);

    XF86ModuleData zzzModuleData = { &zzzVersRec, zzzSetup, NULL };

    static pointer
    zzzSetup(pointer module, pointer opts, int *errmaj, int *errmin)
    {
        static Bool setupDone = FALSE;

        /* This module should be loaded only once, but check to be sure. */

        if (!setupDone) {
            /*
             * Modules that this driver always requires may be loaded
             * here  by calling LoadSubModule().
             */

            setupDone = TRUE;
            xf86AddDriver(&MGA, module, 0);

            /*
             * The return value must be non-NULL on success even though
             * there is no TearDownProc.
             */
            return (pointer)1;
        } else {
            if (errmaj) *errmaj = LDR_ONCEONLY;
            return NULL;
        }
    }
    #endif

#### 20.3.2. GetRec, FreeRec

A function is usually required to allocate the driver's screen-specific data structure and hook it into the `ScrnInfoRec`'s `driverPrivate` field. The `ScrnInfoRec`'s `driverPrivate` is initialised to `NULL`, so it is easy to check if the initialisation has already been done. After allocating it, initialise the fields. By using `xnfcalloc()` to do the allocation it is zeroed, and if the allocation fails the server exits.

NOTE: When allocating structures from inside the driver which are defined on the common level it is important to initialize the structure to zero. Only this guarantees that the server remains source compatible to future changes in common level structures.

    static Bool
    ZZZGetRec(ScrnInfoPtr pScrn)
    {
        if (pScrn->driverPrivate != NULL)
            return TRUE;
        pScrn->driverPrivate = xnfcalloc(sizeof(ZZZRec), 1);
        /* Initialise as required */
        ...
        return TRUE;
    }

Define a macro in `"zzz.h"` which gets a pointer to the `ZZZRec` when given `pScrn`:

    #define ZZZPTR(p) ((ZZZPtr)((p)->driverPrivate))

Define a function to free the above, setting it to `NULL` once it has been freed:

    static void
    ZZZFreeRec(ScrnInfoPtr pScrn)
    {
        if (pScrn->driverPrivate == NULL)
            return;
        xfree(pScrn->driverPrivate);
        pScrn->driverPrivate = NULL;
    }

#### 20.3.3. Identify

Define the `Identify()` function. It is run before the Probe, and typically prints out an identifying message, which might include the chipsets it supports. This function is mandatory:

    static void
    ZZZIdentify(int flags)
    {
        xf86PrintChipsets(ZZZ_NAME, "driver for ZZZ Tech chipsets",
                          ZZZChipsets);
    }

#### 20.3.4. Probe

Define the `Probe()` function. The purpose of this is to find all instances of the hardware that the driver supports, and for the ones not already claimed by another driver, claim the slot, and allocate a `ScrnInfoRec`. This should be a minimal probe, and it should under no circumstances leave the state of the hardware changed. Because a device is found, don't assume that it will be used. Don't do any initialisations other than the required `ScrnInfoRec` initialisations. Don't allocate any new data structures.

This function is mandatory.

NOTE: The `xf86DrvMsg()` functions cannot be used from the Probe.

    static Bool
    ZZZProbe(DriverPtr drv, int flags)
    {
        Bool foundScreen = FALSE;
        int numDevSections, numUsed;
        GDevPtr *devSections;
        int *usedChips;
        int i;

        /*
         * Find the config file Device sections that match this
         * driver, and return if there are none.
         */
        if ((numDevSections = xf86MatchDevice(ZZZ_DRIVER_NAME,
                                              &devSections)) <= 0) {
            return FALSE;
        }

        /*
         * Since this is a PCI card, "probing" just amounts to checking
         * the PCI data that the server has already collected.  If there
         * is none, return.
         *
         * Although the config file is allowed to override things, it
         * is reasonable to not allow it to override the detection
         * of no PCI video cards.
         *
         * The provided xf86MatchPciInstances() helper takes care of
         * the details.
         */
        /* test if PCI bus present */
        if (xf86GetPciVideoInfo()) {

            numUsed = xf86MatchPciInstances(ZZZ_NAME, PCI_VENDOR_ZZZ,
                                ZZZChipsets, ZZZPciChipsets, devSections,
                                numDevSections, drv, &usedChips);

            for (i = 0; i < numUsed; i++) {
                ScrnInfoPtr pScrn = NULL;
                if ((pScrn = xf86ConfigPciEntity(pScrn, flags, usedChips[i],
                                                 ZZZPciChipsets, NULL, NULL,
                                                 NULL, NULL, NULL))) {
                   /* Allocate a ScrnInfoRec */
                   pScrn->driverVersion = VERSION;
                   pScrn->driverName    = ZZZ_DRIVER_NAME;
                   pScrn->name          = ZZZ_NAME;
                   pScrn->Probe         = ZZZProbe;
                   pScrn->PreInit       = ZZZPreInit;
                   pScrn->ScreenInit    = ZZZScreenInit;
                   pScrn->SwitchMode    = ZZZSwitchMode;
                   pScrn->AdjustFrame   = ZZZAdjustFrame;
                   pScrn->EnterVT       = ZZZEnterVT;
                   pScrn->LeaveVT       = ZZZLeaveVT;
                   pScrn->FreeScreen    = ZZZFreeScreen;
                   pScrn->ValidMode     = ZZZValidMode;
                   foundScreen = TRUE;
                   /* add screen to entity */
               }
            }
            xfree(usedChips);
        }

    #ifdef HAS_ISA_DEVS
        /*
         * If the driver supports ISA hardware, the following block
         * can be included too.
         */
        numUsed = xf86MatchIsaInstances(ZZZ_NAME, ZZZChipsets,
                                 ZZZIsaChipsets, drv, ZZZFindIsaDevice,
                                 devSections, numDevSections, &usedChips);
        for (i = 0; i < numUsed; i++) {
            ScrnInfoPtr pScrn = NULL;
    	if ((pScrn = xf86ConfigIsaEntity(pScrn, flags, usedChips[i],
    					 ZZZIsaChipsets, NULL, NULL, NULL,
    					 NULL, NULL))) {
                pScrn->driverVersion = VERSION;
                pScrn->driverName    = ZZZ_DRIVER_NAME;
                pScrn->name          = ZZZ_NAME;
                pScrn->Probe         = ZZZProbe;
                pScrn->PreInit       = ZZZPreInit;
                pScrn->ScreenInit    = ZZZScreenInit;
                pScrn->SwitchMode    = ZZZSwitchMode;
                pScrn->AdjustFrame   = ZZZAdjustFrame;
                pScrn->EnterVT       = ZZZEnterVT;
                pScrn->LeaveVT       = ZZZLeaveVT;
                pScrn->FreeScreen    = ZZZFreeScreen;
                pScrn->ValidMode     = ZZZValidMode;
                foundScreen = TRUE;
            }
        }
        xfree(usedChips);
    #endif /* HAS_ISA_DEVS */

        xfree(devSections);
        return foundScreen;

#### 20.3.5. AvailableOptions

Define the `AvailableOptions()` function. The purpose of this is to return the available driver options back to the -configure option, so that an XF86Config file can be built and the user can see which options are available for them to use.

#### 20.3.6. PreInit

Define the `PreInit()` function. The purpose of this is to find all the information required to determine if the configuration is usable, and to initialise those parts of the `ScrnInfoRec` that can be set once at the beginning of the first server generation. The information should be found in the least intrusive way possible.

This function is mandatory.

NOTES:

1. The `PreInit()` function is only called once during the life of the X server (at the start of the first generation).
2. Data allocated here must be of the type that persists for the life of the X server. This means that data that hooks into the `ScrnInfoRec`'s `privates` field should be allocated here, but data that hooks into the `ScreenRec`'s `devPrivates` field should not be allocated here. The `driverPrivate` field should also be allocated here.
3. Although the `ScrnInfoRec` has been allocated before this function is called, the `ScreenRec` has not been allocated. That means that things requiring it cannot be used in this function.
4. Very little of the `ScrnInfoRec` has been initialised when this function is called. It is important to get the order of doing things right in this function.

    static Bool
    ZZZPreInit(ScrnInfoPtr pScrn, int flags)
    {
        /* Fill in the monitor field */
        pScrn->monitor = pScrn->confScreen->monitor;

        /*
         * If using the vgahw module, it will typically be loaded
         * here by calling xf86LoadSubModule(pScrn, "vgahw");
         */

        /*
         * Set the depth/bpp.  Use the globally preferred depth/bpp.  If the
         * driver has special default depth/bpp requirements, the defaults should
         * be specified here explicitly.
         * We support both 24bpp and 32bpp framebuffer layouts.
         * This sets pScrn->display also.
         */
        if (!xf86SetDepthBpp(pScrn, 0, 0, 0,
                             Support24bppFb | Support32bppFb)) {
            return FALSE;
        } else {
            if (depth/bpp isn't one we support) {
                print error message;
                return FALSE;
            }
        }
        /* Print out the depth/bpp that was set */
        xf86PrintDepthBpp(pScrn);

        /* Set bits per RGB for 8bpp */
        if (pScrn->depth <= 8) {
            /* Take into account a dac_6_bit option here */
            pScrn->rgbBits = 6 or 8;
        }

        /*
         * xf86SetWeight() and xf86SetDefaultVisual() must be called
         * after pScrn->display is initialised.
         */

        /* Set weight/mask/offset for depth > 8 */
        if (pScrn->depth > 8) {
            if (!xf86SetWeight(pScrn, defaultWeight, defaultMask)) {
                return FALSE;
            } else {
                if (weight isn't one we support) {
                    print error message;
                    return FALSE;
                }
            }
        }

        /* Set the default visual. */
        if (!xf86SetDefaultVisual(pScrn, -1)) {
            return FALSE;
        } else {
            if (visual isn't one we support) {
                print error message;
                return FALSE;
            }
        }

        /* If the driver supports gamma correction, set the gamma. */
        if (!xf86SetGamma(pScrn, default_gamma)) {
            return FALSE;
        }

        /* This driver uses a programmable clock */
        pScrn->progClock = TRUE;

        /* Allocate the ZZZRec driverPrivate */
        if (!ZZZGetRec(pScrn)) {
            return FALSE;
        }

        pZzz = ZZZPTR(pScrn);

        /* Collect all of the option flags (fill in pScrn->options) */
        xf86CollectOptions(pScrn, NULL);

        /*
         * Process the options based on the information in ZZZOptions.
         * The results are written to pZzz->Options.  If all of the options
         * processing is done within this function a local variable "options"
         * can be used instead of pZzz->Options.
         */
        if (!(pZzz->Options = xalloc(sizeof(ZZZOptions))))
            return FALSE;
        (void)memcpy(pZzz->Options, ZZZOptions, sizeof(ZZZOptions));
        xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, pZzz->Options);

        /*
         * Set various fields of ScrnInfoRec and/or ZZZRec based on
         * the options found.
         */
        from = X_DEFAULT;
        pZzz->hwCursor = FALSE;
        if (xf86IsOptionSet(pZzz->Options, OPTION_HW_CURSOR)) {
            from = X_CONFIG;
            pZzz->hwCursor = TRUE;
        }
        xf86DrvMsg(pScrn->scrnIndex, from, "Using %s cursor\n",
                   pZzz->hwCursor ? "HW" : "SW");
        if (xf86IsOptionSet(pZzz->Options, OPTION_NOACCEL)) {
            pZzz->noAccel = TRUE;
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                       "Acceleration disabled\n");
        } else {
            pZzz->noAccel = FALSE;
        }
        if (xf86IsOptionSet(pZzz->Options, OPTION_PCI_RETRY)) {
            pZzz->UsePCIRetry = TRUE;
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry enabled\n");
        }
        pZzz->fooHack = 0;
        if (xf86GetOptValInteger(pZzz->Options, OPTION_FOO_HACK,
                                 &pZzz->fooHack)) {
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Foo Hack set to %d\n",
                       pZzz->fooHack);
        }

        /*
         * Find the PCI slot(s) that this screen claimed in the probe.
         * In this case, exactly one is expected, so complain otherwise.
         * Note in this case we're not interested in the card types so
         * that parameter is set to NULL.
         */
        if ((i = xf86GetPciInfoForScreen(pScrn->scrnIndex, &pciList, NULL))
            != 1) {
            print error message;
            ZZZFreeRec(pScrn);
            if (i > 0)
                xfree(pciList);
            return FALSE;
        }
        /* Note that pciList should be freed below when no longer needed */

        /*
         * Determine the chipset, allowing config file chipset and
         * chipid values to override the probed information.  The config
         * chipset value has precedence over its chipid value if both
         * are present.
         *
         * It isn't necessary to fill in pScrn->chipset if the driver
         * keeps track of the chipset in its ZZZRec.
         */

        ...

        /*
         * Determine video memory, fb base address, I/O addresses, etc,
         * allowing the config file to override probed values.
         *
         * Set the appropriate pScrn fields (videoRam is probably the
         * most important one that other code might require), and
         * print out the settings.
         */

        ...

        /* Initialise a clockRanges list. */

        ...

        /* Set any other chipset specific things in the ZZZRec */

        ...

        /* Select valid modes from those available */

        i = xf86ValidateModes(pScrn, pScrn->monitor->Modes,
                              pScrn->display->modes, clockRanges,
                              NULL, minPitch, maxPitch, rounding,
                              minHeight, maxHeight,
                              pScrn->display->virtualX,
                              pScrn->display->virtualY,
                              pScrn->videoRam * 1024,
                              LOOKUP_BEST_REFRESH);
        if (i == -1) {
            ZZZFreeRec(pScrn);
            return FALSE;
        }

        /* Prune the modes marked as invalid */

        xf86PruneDriverModes(pScrn);

        /* If no valid modes, return */

        if (i == 0 || pScrn->modes == NULL) {
            print error message;
            ZZZFreeRec(pScrn);
            return FALSE;
        }

        /*
         * Initialise the CRTC fields for the modes.  This driver expects
         * vertical values to be halved for interlaced modes.
         */
        xf86SetCrtcForModes(pScrn, INTERLACE_HALVE_V);

        /* Set the current mode to the first in the list. */
        pScrn->currentMode = pScrn->modes;

        /* Print the list of modes being used. */
        xf86PrintModes(pScrn);

        /* Set the DPI */
        xf86SetDpi(pScrn, 0, 0);

        /* Load bpp-specific modules */
        switch (pScrn->bitsPerPixel) {
        case 1:
            mod = "xf1bpp";
            break;
        case 4:
            mod = "xf4bpp";
            break;
        case 8:
            mod = "cfb";
            break;
        case 16:
            mod = "cfb16";
            break;
        case 24:
            mod = "cfb24";
            break;
        case 32:
            mod = "cfb32";
            break;
        }
        if (mod && !xf86LoadSubModule(pScrn, mod))
            ZZZFreeRec(pScrn);
            return FALSE;

        /* Load XAA if needed */
        if (!pZzz->noAccel || pZzz->hwCursor)
            if (!xf86LoadSubModule(pScrn, "xaa")) {
                ZZZFreeRec(pScrn);
                return FALSE;
            }

        /* Done */
        return TRUE;
    }

#### 20.3.7. MapMem, UnmapMem

Define functions to map and unmap the video memory and any other memory apertures required. These functions are not mandatory, but it is often useful to have such functions.

    static Bool
    ZZZMapMem(ScrnInfoPtr pScrn)
    {
        /* Call xf86MapPciMem() to map each PCI memory area */
        ...
        return TRUE or FALSE;
    }

    static Bool
    ZZZUnmapMem(ScrnInfoPtr pScrn)
    {
        /* Call xf86UnMapVidMem() to unmap each memory area */
        ...
        return TRUE or FALSE;
    }

#### 20.3.8. Save, Restore

Define functions to save and restore the original video state. These functions are not mandatory, but are often useful.

    static void
    ZZZSave(ScrnInfoPtr pScrn)
    {
        /*
         * Save state into per-screen data structures.
         * If using the vgahw module, vgaHWSave will typically be
         * called here.
         */
        ...
    }

    static void
    ZZZRestore(ScrnInfoPtr pScrn)
    {
        /*
         * Restore state from per-screen data structures.
         * If using the vgahw module, vgaHWRestore will typically be
         * called here.
         */
        ...
    }

#### 20.3.9. ModeInit

Define a function to initialise a new video mode. This function isn't mandatory, but is often useful.

    static Bool
    ZZZModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode)
    {
        /*
         * Program a video mode.  If using the vgahw module,
         * vgaHWInit and vgaRestore will typically be called here.
         * Once up to the point where there can't be a failure
         * set pScrn->vtSema to TRUE.
         */
        ...
    }

#### 20.3.10. ScreenInit

Define the `ScreenInit()` function. This is called at the start of each server generation, and should fill in as much of the `ScreenRec` as possible as well as any other data that is initialised once per generation. It should initialise the framebuffer layers it is using, and initialise the initial video mode.

This function is mandatory.

NOTE: The `ScreenRec` (`pScreen`) is passed to this driver, but it and the `ScrnInfoRecs` are not yet hooked into each other. This means that in this function, and functions it calls, one cannot be found from the other.

    static Bool
    ZZZScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv)
    {
        /* Get the ScrnInfoRec */
        pScrn = xf86Screens[pScreen->myNum];

        /*
         * If using the vgahw module, its data structures and related
         * things are typically initialised/mapped here.
         */

        /* Save the current video state */
        ZZZSave(pScrn);

        /* Initialise the first mode */
        ZZZModeInit(pScrn, pScrn->currentMode);

        /* Set the viewport if supported */

        ZZZAdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);

        /*
         * Setup the screen's visuals, and initialise the framebuffer
         * code.
         */

        /* Reset the visual list */
        miClearVisualTypes();

        /*
         * Setup the visuals supported.  This driver only supports
         * TrueColor for bpp > 8, so the default set of visuals isn't
         * acceptable.  To deal with this, call miSetVisualTypes with
         * the appropriate visual mask.
         */

        if (pScrn->bitsPerPixel > 8) {
            if (!miSetVisualTypes(pScrn->depth, TrueColorMask,
                                  pScrn->rgbBits, pScrn->defaultVisual))
                return FALSE;
        } else {
            if (!miSetVisualTypes(pScrn->depth,
                                  miGetDefaultVisualMask(pScrn->depth),
                                  pScrn->rgbBits, pScrn->defaultVisual))
                return FALSE;
        }

        /*
         * Initialise the framebuffer.
         */

        switch (pScrn->bitsPerPixel) {
        case 1:
            ret = xf1bppScreenInit(pScreen, FbBase,
                                   pScrn->virtualX, pScrn->virtualY,
                                   pScrn->xDpi, pScrn->yDpi,
                                   pScrn->displayWidth);
            break;
        case 4:
            ret = xf4bppScreenInit(pScreen, FbBase,
                                   pScrn->virtualX, pScrn->virtualY,
                                   pScrn->xDpi, pScrn->yDpi,
                                   pScrn->displayWidth);
            break;
        case 8:
            ret = cfbScreenInit(pScreen, FbBase,
                                pScrn->virtualX, pScrn->virtualY,
                                pScrn->xDpi, pScrn->yDpi,
                                pScrn->displayWidth);
            break;
        case 16:
            ret = cfb16ScreenInit(pScreen, FbBase,
                                  pScrn->virtualX, pScrn->virtualY,
                                  pScrn->xDpi, pScrn->yDpi,
                                  pScrn->displayWidth);
            break;
        case 24:
            ret = cfb24ScreenInit(pScreen, FbBase,
                                  pScrn->virtualX, pScrn->virtualY,
                                  pScrn->xDpi, pScrn->yDpi,
                                  pScrn->displayWidth);
            break;
        case 32:
            ret = cfb32ScreenInit(pScreen, FbBase,
                                  pScrn->virtualX, pScrn->virtualY,
                                  pScrn->xDpi, pScrn->yDpi,
                                  pScrn->displayWidth);
            break;
        default:
            print a message about an internal error;
            ret = FALSE;
            break;
        }

        if (!ret)
            return FALSE;

        /* Override the default mask/offset settings */
        if (pScrn->bitsPerPixel > 8) {
            for (i = 0, visual = pScreen->visuals;
                 i < pScreen->numVisuals; i++, visual++) {
                if ((visual->class | DynamicClass) == DirectColor) {
                    visual->offsetRed = pScrn->offset.red;
                    visual->offsetGreen = pScrn->offset.green;
                    visual->offsetBlue = pScrn->offset.blue;
                    visual->redMask = pScrn->mask.red;
                    visual->greenMask = pScrn->mask.green;
                    visual->blueMask = pScrn->mask.blue;
                }
            }
        }

        /*
         * If banking is needed, initialise an miBankInfoRec (defined in
         * "mibank.h"), and call miInitializeBanking().
         */
        if (!miInitializeBanking(pScreen, pScrn->virtualX, pScrn->virtualY,
                                         pScrn->displayWidth, pBankInfo))
            return FALSE;

        /*
         * If backing store is to be supported (as is usually the case),
         * initialise it.
         */
        miInitializeBackingStore(pScreen);

        /*
         * Set initial black & white colourmap indices.
         */
        xf86SetBlackWhitePixels(pScreen);

        /*
         * Install colourmap functions.  If using the vgahw module,
         * vgaHandleColormaps would usually be called here.
         */

        ...

        /*
         * Initialise cursor functions.  This example is for the mi
         * software cursor.
         */
        miDCInitialize(pScreen, xf86GetPointerScreenFuncs());

        /* Initialise the default colourmap */
        switch (pScrn->depth) {
        case 1:
            if (!xf1bppCreateDefColormap(pScreen))
                return FALSE;
            break;
        case 4:
            if (!xf4bppCreateDefColormap(pScreen))
                return FALSE;
            break;
        default:
            if (!cfbCreateDefColormap(pScreen))
                return FALSE;
            break;
        }

        /*
         * Wrap the CloseScreen vector and set SaveScreen.
         */
        ZZZPTR(pScrn)->CloseScreen = pScreen->CloseScreen;
        pScreen->CloseScreen = ZZZCloseScreen;
        pScreen->SaveScreen = ZZZSaveScreen;

        /* Report any unused options (only for the first generation) */
        if (serverGeneration == 1) {
            xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);
        }

        /* Done */
        return TRUE;
    }

#### 20.3.11. SwitchMode

Define the `SwitchMode()` function if mode switching is supported by the driver.

    static Bool
    ZZZSwitchMode(int scrnIndex, DisplayModePtr mode, int flags)
    {
        return ZZZModeInit(xf86Screens[scrnIndex], mode);
    }

#### 20.3.12. AdjustFrame

Define the `AdjustFrame()` function if the driver supports this.

    static void
    ZZZAdjustFrame(int scrnIndex, int x, int y, int flags)
    {
        /* Adjust the viewport */
    }

#### 20.3.13. EnterVT, LeaveVT

Define the `EnterVT()` and `LeaveVT()` functions.

These functions are mandatory.

    static Bool
    ZZZEnterVT(int scrnIndex, int flags)
    {
        ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
        return ZZZModeInit(pScrn, pScrn->currentMode);
    }

    static void
    ZZZLeaveVT(int scrnIndex, int flags)
    {
        ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
        ZZZRestore(pScrn);
    }

#### 20.3.14. CloseScreen

Define the `CloseScreen()` function:

This function is mandatory. Note that it unwraps the previously wrapped `pScreen->CloseScreen`, and finishes by calling it.

    static Bool
    ZZZCloseScreen(int scrnIndex, ScreenPtr pScreen)
    {
        ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
        if (pScrn->vtSema) {
            ZZZRestore(pScrn);
            ZZZUnmapMem(pScrn);
        }
        pScrn->vtSema = FALSE;
        pScreen->CloseScreen = ZZZPTR(pScrn)->CloseScreen;
        return (*pScreen->CloseScreen)(scrnIndex, pScreen);
    }

#### 20.3.15. SaveScreen

Define the `SaveScreen()` function (the screen blanking function). When using the vgahw module, this will typically be:

    static Bool
    ZZZSaveScreen(ScreenPtr pScreen, int mode)
    {
        return vgaHWSaveScreen(pScreen, mode);
    }

This function is mandatory. Before modifying any hardware register directly this function needs to make sure that the Xserver is active by checking if `pScrn` is non-NULL and for `pScrn->vtSema == TRUE`.

#### 20.3.16. FreeScreen

Define the `FreeScreen()` function. This function is optional. It should be defined if the `ScrnInfoRec` `driverPrivate` field is used so that it can be freed when a screen is deleted by the common layer for reasons possibly beyond the driver's control. This function is not used in during normal (error free) operation. The per-generation data is freed by the `CloseScreen()` function.

    static void
    ZZZFreeScreen(int scrnIndex, int flags)
    {
        /*
         * If the vgahw module is used vgaHWFreeHWRec() would be called
         * here.
         */
        ZZZFreeRec(xf86Screens[scrnIndex]);
    }

XFree86 server 4.x Design (DRAFT) : Some notes about writing a driver
Previous: The vgahw module
Next: XFree86 server 4.x Design (DRAFT)