XFree86 server 4.x 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:
- 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).
- A resource marked ``disabled'' during OPERATING state will be
ignored entirely.
- 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.
- 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 server 4.x Design (DRAFT)
: Control Flow in the Server and Mandatory Driver Functions
Previous: Resource Access Control Introduction
Next: Optional Driver Functions
|