Control Flow in the Server and Mandatory Driver Functions

XFree86 X server ``New Design'' (DRAFT) : Control Flow in the Server and Mandatory Driver Functions
Previous: Resource Access Control Introduction
Next: Optional Driver Functions

## 5. Control Flow in the Server and Mandatory Driver Functions

At the start of each server generation, `main()` (`dix/main.c`) calls the DDX function `InitOutput()`. This is the first place that the DDX gets control. `InitOutput()` is expected to fill in the global `screenInfo` struct, and one `screenInfo.screen[]` entry for each screen present. Here is what `InitOutput()` does:

### 5.1. Parse the XF86Config file

This is done at the start of the first server generation only.

The XF86Config file is read in full, and the resulting information stored in data structures. None of the parsed information is processed at this point. The parser data structures are opaque to the video drivers and to most of the common layer code.

The entire file is parsed first to remove any section ordering requirements.

### 5.2. Initial processing of parsed information and command line options

This is done at the start of the first server generation only.

The initial processing is to determine paths like the ModulePath, etc, and to determine which ServerLayout, Screen and Device sections are active.

### 5.3. Enable port I/O access

Port I/O access is controlled from the XFree86 common layer, and is ``all or nothing''. It is enabled prior to calling driver probes, at the start of subsequent server generations, and when VT switching back to the Xserver. It is disabled at the end of server generations, and when VT switching away from the Xserver.

The implementation details of this may vary on different platforms.

### 5.4. General bus probe

This is done at the start of the first server generation only.

In the case of ix86 machines, this will be a general PCI probe. The full information obtained here will be available to the drivers. This information persists for the life of the Xserver. In the PCI case, the PCI information for all video cards found is available by calling `xf86GetPciVideoInfo()`.

> `pciVideoPtr *xf86GetPciVideoInfo(void)`

> > returns a pointer to a list of pointers to `pciVideoRec` entries, of which there is one for each detected PCI video card. The list is terminated with a `NULL` pointer. If no PCI video cards were detected, the return value is `NULL`.

After the bus probe, the resource broker is initialised.

### 5.5. Load initial set of modules

This is done at the start of the first server generation only.

The core server contains a list of mandatory modules. These are loaded first. Currently the only module on this list is the bitmap font module.

The next set of modules loaded are those specified explicitly in the Module section of the config file.

The final set of initial modules are the driver modules referenced by the active Device and InputDevice sections in the config file. Each of these modules is loaded exactly once.

### 5.6. Register Video and Input Drivers

This is done at the start of the first server generation only.

When a driver module is loaded, the loader calls its `Setup` function. For video drivers, this function calls `xf86AddDriver()` to register the driver's `DriverRec`, which contains a small set of essential details and driver entry points required during the early phase of `InitOutput()`. `xf86AddDriver()` adds it to the global `xf86DriverList[]` array.

The `DriverRec` contains the driver canonical name, the `Identify()`, `Probe()` and `AvailableOptions()` function entry points as well as a pointer to the driver's module (as returned from the loader when the driver was loaded) and a reference count which keeps track of how many screens are using the driver. The entry driver entry points are those required prior to the driver allocating and filling in its `ScrnInfoRec`.

For a static server, the `xf86DriverList[]` array is initialised at build time, and the loading of modules is not done.

A similar procedure is used for input drivers. The input driver's `Setup` function calls `xf86AddInputDriver()` to register the driver's `InputDriverRec`, which contains a small set of essential details and driver entry points required during the early phase of `InitInput()`. `xf86AddInputDriver()` adds it to the global `xf86InputDriverList[]` array. For a static server, the `xf86InputDriverList[]` array is initialised at build time.

Both the `xf86DriverList[]` and `xf86InputDriverList[]` arrays have been initialised by the end of this stage.

Once all the drivers are registered, their `ChipIdentify()` functions are called.

> `void ChipIdentify(int flags)`

> > This is expected to print a message indicating the driver name, a short summary of what it supports, and a list of the chipset names that it supports. It may use the xf86PrintChipsets() helper to do this.

> `void xf86PrintChipsets(const char *drvname, const char *drvmsg,
> SymTabPtr chips)`

> > This function provides an easy way for a driver's ChipIdentify function to format the identification message.

### 5.7. Initialise Access Control

This is done at the start of the first server generation only.

The Resource Access Control (RAC) subsystem is initialised before calling any driver functions that may access hardware. All generic bus information is probed and saved (for restoration later). All (shared resource) video devices are disabled at the generic bus level, and a probe is done to find the ``primary'' video device. These devices remain disabled for the next step.

### 5.8. Video Driver Probe

This is done at the start of the first server generation only. The `ChipProbe()` function of each registered video driver is called.

> `Bool ChipProbe(DriverPtr drv, int flags)`

> > The purpose of this is to identify all instances of hardware supported by the driver. The flags value is currently either 0, `PROBE_DEFAULT` or `PROBE_DETECT`. `PROBE_DETECT` is used if "-configure" or "-probe" command line arguments are given and indicates to the `Probe()` function that it should not configure the bus entities and that no XF86Config information is available.

> > The probe must find the active device sections that match the driver by calling `xf86MatchDevice()`. The number of matches found limits the maximum number of instances for this driver. If no matches are found, the function should return `FALSE` immediately.

> > Devices that cannot be identified by using device-independent methods should be probed at this stage (keeping in mind that access to all resources that can be disabled in a device-independent way are disabled during this phase). The probe must be a minimal probe. It should just determine if there is a card present that the driver can drive. It should use the least intrusive probe methods possible. It must not do anything that is not essential, like probing for other details such as the amount of memory installed, etc. It is recommended that the `xf86MatchPciInstances()` helper function be used for identifying matching PCI devices, and similarly the `xf86MatchIsaInstances()` for ISA (non-PCI) devices (see the RAC section). These helpers also checks and claims the appropriate entity. When not using the helper, that should be done with `xf86CheckPciSlot()` and `xf86ClaimPciSlot()` for PCI devices and `xf86ClaimIsaSlot()` for ISA devices (see the RAC section).

> > The probe must register all non-relocatable resources at this stage. If a resource conflict is found between exclusive resources the driver will fail immediately. This is usually best done with the `xf86ConfigPciEntity()` helper function for PCI and `xf86ConfigIsaEntity()` for ISA (see the RAC section). It is possible to register some entity specific functions with those helpers. When not using the helpers, the `xf86AddEntityToScreen()` `xf86ClaimFixedResources()` and `xf86SetEntityFuncs()` should be used instead (see the RAC section).

> > If a chipset is specified in an active device section which the driver considers relevant (ie it has no driver specified, or the driver specified matches the driver doing the probe), the Probe must return `FALSE` if the chipset doesn't match one supported by the driver.

> > If there are no active device sections that the driver considers relevant, it must return `FALSE`.

> > Allocate a `ScrnInfoRec` for each active instance of the hardware found, and fill in the basic information, including the other driver entry points. This is best done with the `xf86ConfigIsaEntity()` helper function for ISA instances or `xf86ConfigPciEntity()` for PCI instances. These functions allocate a `ScrnInfoRec` for active entities. Optionally `xf86AllocateScreen()` function may also be used to allocate the `ScrnInfoRec`. Any of these functions take care of initialising fields to defined ``unused'' values.

> > Claim the entities for each instance of the hardware found. This prevents other drivers from claiming the same hardware.

> > Must leave hardware in the same state it found it in, and must not do any hardware initialisation.

> > All detection can be overridden via the config file, and that parsed information is available to the driver at this stage.

> > Returns `TRUE` if one or more instances are found, and `FALSE` otherwise.

> `int xf86MatchDevice(const char *drivername,
> GDevPtr **driversectlist)`

> > This function takes the name of the driver and returns via `driversectlist` a list of device sections that match the driver name. The function return value is the number of matches found. If a fatal error is encountered the return value is `-1`.

> > The caller should use `xfree()` to free `*driversectlist` when it is no longer needed.

> `ScrnInfoPtr xf86AllocateScreen(DriverPtr drv, int flags)`

> > This function allocates a new `ScrnInfoRec` in the `xf86Screens[]` array. This function is normally called by the video driver `ChipProbe()` functions. The return value is a pointer to the newly allocated `ScrnInfoRec`. The `scrnIndex`, `origIndex`, `module` and `drv` fields are initialised. The reference count in `drv` is incremented. The storage for any currently allocated ``privates'' pointers is also allocated and the `privates` field initialised (the privates data is of course not allocated or initialised). This function never returns on failure. If the allocation fails, the server exits with a fatal error. The flags value is not currently used, and should be set to zero.

At the completion of this, a list of `ScrnInfoRecs` have been allocated in the `xf86Screens[]` array, and the associated entities and fixed resources have been claimed. The following `ScrnInfoRec` fields must be initialised at this point:

>               driverVersion
>               driverName
>               scrnIndex(*)
>               origIndex(*)
>               drv(*)
>               module(*)
>               name
>               Probe
>               PreInit
>               ScreenInit
>               EnterVT
>               LeaveVT
>               numEntities
>               entityList
>               access

`(*)` These are initialised when the `ScrnInfoRec` is allocated, and not explicitly by the driver.

The following `ScrnInfoRec` fields must be initialised if the driver is going to use them:

>               SwitchMode
>               AdjustFrame
>               FreeScreen
>               ValidMode

### 5.9. Matching Screens

This is done at the start of the first server generation only.

After the Probe phase is finished, there will be some number of `ScrnInfoRecs`. These are then matched with the active Screen sections in the XF86Config, and those not having an active Screen section are deleted. If the number of remaining screens is 0, `InitOutput()` sets `screenInfo.numScreens` to `0` and returns.

At this point the following fields of the `ScrnInfoRecs` must be initialised:

>               confScreen

### 5.10. Allocate non-conflicting resources

This is done at the start of the first server generation only.

Before calling the drivers again, the resource information collected from the Probe phase is processed. This includes checking the extent of PCI resources for the probed devices, and resolving any conflicts in the relocatable PCI resources. It also reports conflicts, checks bus routing issues, and anything else that is needed to enable the entities for the next phase.

If any drivers registered an `EntityInit()` function during the Probe phase, then they are called here.

### 5.11. Sort the Screens and pre-check Monitor Information

This is done at the start of the first server generation only.

The list of screens is sorted to match the ordering requested in the config file.

The list of modes for each active monitor is checked against the monitor's parameters. Invalid modes are pruned.

### 5.12. PreInit

This is done at the start of the first server generation only.

For each `ScrnInfoRec`, enable access to the screens entities and call the `ChipPreInit()` function.

> `Bool ChipPreInit(ScrnInfoRec screen, int flags)`

> > The purpose of this function is to find out 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 number of entities registered for the screen should be checked against the expected number (most drivers expect only one). The entity information for each of them should be retrieved (with `xf86GetEntityInfo()`) and checked for the correct bus type and that none of the sharable resources registered during the Probe phase was rejected.

> > Access to resources for the entities that can be controlled in a device-independent way are enabled before this function is called. If the driver needs to access any resources that it has disabled in an `EntityInit()` function that it registered, then it may enable them here providing that it disables them before this function returns.

> > This includes probing for video memory, clocks, ramdac, and all other HW info that is needed. It includes determining the depth/bpp/visual and related info. It includes validating and determining the set of video modes that will be used (and anything that is required to determine that).

> > This information should be determined in the least intrusive way possible. The state of the HW must remain unchanged by this function. Although video memory (including MMIO) may be mapped within this function, it must be unmapped before returning. Driver specific information should be stored in a structure hooked into the `ScrnInfoRec`'s `driverPrivate` field. Any other modules which require persistent data (ie data that persists across server generations) should be initialised in this function, and they should allocate a ``privates'' index to hook their data into by calling `xf86AllocateScrnInfoPrivateIndex().` The ``privates'' data is persistent.

> > Helper functions for some of these things are provided at the XFree86 common level, and the driver can choose to make use of them.

> > All additional resources that the screen needs must be registered here. This should be done with `xf86RegisterResources()`. If some of the fixed resources registered in the Probe phase are not needed or not decoded by the hardware when in the OPERATING server state, their status should be updated with `xf86SetOperatingState()`.

> > Modules may be loaded at any point in this function, and all modules that the driver will need must be loaded before the end of this function. Either the `xf86LoadSubModule()` or the `xf86LoadDrvSubModule()` function should be used to load modules depending on whether a `ScrnInfoRec` has been set up. A driver may unload a module within this function if it was only needed temporarily, and the `xf86UnloadSubModule()` function should be used to do that. Otherwise there is no need to explicitly unload modules because the loader takes care of module dependencies and will unload submodules automatically if/when the driver module is unloaded.

> > The bulk of the `ScrnInfoRec` fields should be filled out in this function.

> > `ChipPreInit()` returns `FALSE` when the configuration is unusable in some way (unsupported depth, no valid modes, not enough video memory, etc), and `TRUE` if it is usable.

> > It is expected that if the `ChipPreInit()` function returns `TRUE`, then the only reasons that subsequent stages in the driver might fail are lack or resources (like xalloc failures). All other possible reasons for failure should be determined by the `ChipPreInit()` function.

The `ScrnInfoRecs` for screens where the `ChipPreInit()` fails are removed. If none remain, `InitOutput()` sets `screenInfo.numScreens` to `0` and returns.

At this point, further fields of the `ScrnInfoRecs` would normally be filled in. Most are not strictly mandatory, but many are required by other layers and/or helper functions that the driver may choose to use. The documentation for those layers and helper functions indicates which they require.

The following fields of the `ScrnInfoRecs` should be filled in if the driver is going to use them:

>               monitor
>               display
>               depth
>               pixmapBPP
>               bitsPerPixel
>               weight                (>8bpp only)
>               mask                  (>8bpp only)
>               offset                (>8bpp only)
>               rgbBits               (8bpp only)
>               gamma
>               defaultVisual
>               maxHValue
>               maxVValue
>               virtualX
>               virtualY
>               displayWidth
>               frameX0
>               frameY0
>               frameX1
>               frameY1
>               zoomLocked
>               modePool
>               modes
>               currentMode
>               progClock             (TRUE if clock is programmable)
>               chipset
>               ramdac
>               clockchip
>               numClocks             (if not programmable)
>               clock[]               (if not programmable)
>               videoRam
>               biosBase
>               memBase
>               memClk
>               driverPrivate
>               chipID
>               chipRev

> `pointer xf86LoadSubModule(ScrnInfoPtr pScrn, const char *name)`: and `pointer xf86LoadDrvSubModule(DriverPtr drv, const char *name)`:

> > Load a module that a driver depends on. This function loads the module `name` as a sub module of the driver. The return value is a handle identifying the new module. If the load fails, the return value will be `NULL`. If a driver needs to explicitly unload a module it has loaded in this way, the return value must be saved and passed to `xf86UnloadSubModule()` when unloading.

> `void xf86UnloadSubModule(pointer module)`

> > Unloads the module referenced by `module`. `module` should be a pointer returned previously by `xf86LoadSubModule()` or `xf86LoadDrvSubModule()` .

### 5.13. Cleaning up Unused Drivers

At this point it is known which screens will be in use, and which drivers are being used. Unreferenced drivers (and modules they may have loaded) are unloaded here.

### 5.14. Consistency Checks

The parameters that must be global to the server, like pixmap formats, bitmap bit order, bitmap scanline unit and image byte order are compared for each of the screens. If a mismatch is found, the server exits with an appropriate message.

### 5.15. Check if Resource Control is Needed

Determine if resource access control is needed. This is the case if more than one screen is used. If necessary the RAC wrapper module is loaded.

### 5.16. AddScreen (ScreenInit)

At this point, the valid screens are known. `AddScreen()` is called for each of them, passing `ChipScreenInit()` as the argument. `AddScreen()` is a DIX function that allocates a new `screenInfo.screen[]` entry (aka `pScreen`), and does some basic initialisation of it. It then calls the `ChipScreenInit()` function, with `pScreen` as one of its arguments. If `ChipScreenInit()` returns `FALSE`, `AddScreen()` returns `-1`. Otherwise it returns the index of the screen. `AddScreen()` should only fail because of programming errors or failure to allocate resources (like memory). All configuration problems should be detected BEFORE this point.

> `Bool ChipScreenInit(int index, ScreenPtr pScreen,
> int argc, char **argv)`

> > This is called at the start of each server generation.

> > Fill in all of `pScreen`, possibly doing some of this by calling ScreenInit functions from other layers like mi, framebuffers (cfb, etc), and extensions.

> > Decide which operations need to be placed under resource access control. The classes of operations are the frame buffer operations (`RAC_FB`), the pointer operations (`RAC_CURSOR`), the viewport change operations (`RAC_VIEWPORT`) and the colormap operations (`RAC_COLORMAP`). Any operation that requires resources which might be disabled during OPERATING state should be set to use RAC. This can be specified separately for memory and IO resources (the `racMemFlags` and `racIoFlags` fields of the `ScrnInfoRec` respectively).

> > Map any video memory or other memory regions.

> > Save the video card state. Enough state must be saved so that the original state can later be restored.

> > Initialise the initial video mode. The `ScrnInfoRec`'s `vtSema` field should be set to `TRUE` just prior to changing the video hardware's state.

The `ChipScreenInit()` function (or functions from other layers that it calls) should allocate entries in the `ScreenRec`'s `devPrivates` area by calling `AllocateScreenPrivateIndex()` if it needs per-generation storage. Since the `ScreenRec`'s `devPrivates` information is cleared for each server generation, this is the correct place to initialise it.

After `AddScreen()` has successfully returned, the following `ScrnInfoRec` fields are initialised:

>               pScreen
>               racMemFlags
>               racIoFlags

The `ChipScreenInit()` function should initialise the `CloseScreen` and `SaveScreen` fields of `pScreen`. The old value of `pScreen->CloseScreen` should be saved as part of the driver's per-screen private data, allowing it to be called from `ChipCloseScreen()`. This means that the existing `CloseScreen()` function is wrapped.

### 5.17. Finalising RAC Initialisation

After all the `ChipScreenInit()` functions have been called, each screen has registered its RAC requirements. This information is used to determine which shared resources are requested by more than one driver and set the access functions accordingly. This is done following these rules:

1. The sharable resources registered by each entity are compared. If a resource is registered by more than one entity the entity will be marked to indicate that it needs to share this resources type (IO or MEM).
2. A resource marked ``disabled'' during OPERATING state will be ignored entirely.
3. A resource marked ``unused'' will only conflict with an overlapping resource of an other entity if the second is actually in use during OPERATING state.
4. If an ``unused'' resource was found to conflict but the entity does not use any other resource of this type the entire resource type will be disabled for that entity.

### 5.18. Finishing InitOutput()

At this point `InitOutput()` is finished, and all the screens have been setup in their initial video mode.

### 5.19. Mode Switching

When a SwitchMode event is received, `ChipSwitchMode()` is called (when it exists):

> `Bool ChipSwitchMode(int index, DisplayModePtr mode, int flags)`

> > Initialises the new mode for the screen identified by `index;`. The viewport may need to be adjusted also.

### 5.20. Changing Viewport

When a Change Viewport event is received, `ChipAdjustFrame()` is called (when it exists):

> `void ChipAdjustFrame(int index, int x, int y, int flags)`

> > Changes the viewport for the screen identified by `index;`.

> > It should be noted that many chipsets impose restrictions on where the viewport may be placed in the virtual resolution, either for alignment reasons, or to prevent the start of the viewport from being positioned within a pixel (as can happen in a 24bpp mode). After calculating the value the chipset's panning registers need to be set to for non-DGA modes, this function should recalculate the ScrnInfoRec's `frameX0`, `frameY0`, `frameX1` and `frameY1` fields to correspond to that value. If this is not done, switching to another mode might cause the position of a hardware cursor to change.

### 5.21. VT Switching

When a VT switch event is received, `xf86VTSwitch()` is called. `xf86VTSwitch()` does the following:

On ENTER:

  - enable port I/O access
  - save and initialise the bus/resource state
  - enter the SETUP server state
  - calls `ChipEnterVT()` for each screen
  - enter the OPERATING server state
  - validate GCs
  - Restore fb from saved pixmap for each screen
  - Enable all input devices
On LEAVE:

  - Save fb to pixmap for each screen
  - validate GCs
  - enter the SETUP server state
  - calls `ChipLeaveVT()` for each screen
  - disable all input devices
  - restore bus/resource state
  - disables port I/O access

> `Bool ChipEnterVT(int index, int flags)`

> > This function should initialise the current video mode and initialise the viewport, turn on the HW cursor if appropriate, etc.

> > Should it re-save the video state before initialising the video mode?

> `void ChipLeaveVT(int index, int flags)`

> > This function should restore the saved video state. If appropriate it should also turn off the HW cursor, and invalidate any pixmap/font caches.

> Optionally, `ChipLeaveVT()` may also unmap memory regions. If so, `ChipEnterVT()` will need to remap them. Additionally, if an aperture used to access video memory is unmapped and remapped in this fashion, `ChipEnterVT()` will also need to notify the framebuffer layers of the aperture's new location in virtual memory. This is done with a call to the screen's `ModifyPixmapHeader()` function, as follows

> > `(*pScreen->ModifyPixmapHeader)(pScrn->ppix,
> > -1, -1, -1, -1, -1, NewApertureAddress);`

> > > where the ```ppix''` field in a ScrnInfoRec points to the pixmap used by the screen's `SaveRestoreImage()` function to hold the screen's contents while switched out.

> Currently, aperture remapping, as described here, should not be attempted if the driver uses the `xf8_16bpp` or `xf8_32bpp` framebuffer layers. A pending restructuring of VT switching will address this restriction in the near future.

Other layers may wrap the `ChipEnterVT()` and `ChipLeaveVT()` functions if they need to take some action when these events are received.

### 5.22. End of server generation

At the end of each server generation, the DIX layer calls `ChipCloseScreen()` for each screen:

> `Bool ChipCloseScreen(int index, ScreenPtr pScreen)`

> > This function should restore the saved video state and unmap the memory regions.

> > It should also free per-screen data structures allocated by the driver. Note that the persistent data held in the `ScrnInfoRec`'s `driverPrivate` field should not be freed here because it is needed by subsequent server generations.

> > The `ScrnInfoRec`'s `vtSema` field should be set to `FALSE` once the video HW state has been restored.

> > Before freeing the per-screen driver data the saved `CloseScreen` value should be restored to `pScreen->CloseScreen`, and that function should be called after freeing the data.

XFree86 X server ``New Design'' (DRAFT) : Control Flow in the Server and Mandatory Driver Functions
Previous: Resource Access Control Introduction
Next: Optional Driver Functions