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

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 xorg.conf 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)