Hands-on Projects for the Linux Graphics Subsystem
|
We continue here with InitOutput() at the point we left the 'Module Loading' paragraph. At this point xf86DriverList[] is filled with entries for each of the drivers and xf86NumDrivers has the number of drivers.
The comment in InitOutput() indicates the next step that follows:
* Call each of the Identify functions and call the driverFunc to check * if HW access is required. The Identify functions print out some * identifying information, and anything else that might be * needed at this early stage. |
The source code that implements this is the following:
for (i = 0; i < xf86NumDrivers; i++) { xorgHWFlags flags; /* The Identify function is mandatory, but if it isn't there continue */ if (xf86DriverList[i]->Identify != NULL) xf86DriverList[i]->Identify(0); else { xf86Msg(X_WARNING, "Driver `%s' has no Identify function\n", xf86DriverList[i]->driverName ? xf86DriverList[i]->driverName : "noname"); } if (!xorgHWAccess && (!xf86DriverList[i]->driverFunc || !xf86DriverList[i]->driverFunc(NULL, GET_REQUIRED_HW_INTERFACES, &flags) || NEED_IO_ENABLED(flags))) xorgHWAccess = TRUE; } |
If we continue with the example of the previous paragraph, the mga driver the identify routine that is used is MGAIdentify(), which is a member of the driver's DriverRec struct that was added previously in xf86DriverList[].
_X_EXPORT DriverRec MGA_C_NAME = { MGA_VERSION, MGA_DRIVER_NAME, MGAIdentify, MGAProbe, MGAAvailableOptions, NULL, 0 }; |
MGAIdentify() is implemented as:
static void MGAIdentify(int flags) { xf86PrintChipsets(MGA_NAME, "driver for Matrox chipsets", MGAChipsets); } |
xf86PrintChipsets() prints out to stderr or to a log file, via xf86Msg(), the driver name, message passed in the second argument and the names of the supported chipsets. Those chipsets as found in mga_driver.c are:
/* Supported chipsets */ static SymTabRec MGAChipsets[] = { { PCI_CHIP_MGA2064, "mga2064w" }, { PCI_CHIP_MGA1064, "mga1064sg" }, { PCI_CHIP_MGA2164, "mga2164w" }, { PCI_CHIP_MGA2164_AGP, "mga2164w AGP" }, { PCI_CHIP_MGAG100, "mgag100" }, { PCI_CHIP_MGAG100_PCI, "mgag100 PCI" }, { PCI_CHIP_MGAG200, "mgag200" }, { PCI_CHIP_MGAG200_PCI, "mgag200 PCI" }, { PCI_CHIP_MGAG200_SE_A_PCI, "mgag200 SE A PCI" }, { PCI_CHIP_MGAG200_SE_B_PCI, "mgag200 SE B PCI" }, { PCI_CHIP_MGAG400, "mgag400" }, { PCI_CHIP_MGAG550, "mgag550" }, {-1, NULL } }; |
SymTabRec is defined in xf86str.h.
The next step is shown by the comment in InitOutput():
* Now call each of the Probe functions. Each successful probe will * result in an extra entry added to the xf86Screens[] list for each * instance of the hardware found. |
The source code that follows it is:
for (i = 0; i < xf86NumDrivers; i++) { xorgHWFlags flags; if (!xorgHWAccess) { if (!xf86DriverList[i]->driverFunc || !xf86DriverList[i]->driverFunc(NULL, GET_REQUIRED_HW_INTERFACES, &flags) || NEED_IO_ENABLED(flags)) continue; } if (xf86DriverList[i]->Probe != NULL) xf86DriverList[i]->Probe(xf86DriverList[i], PROBE_DEFAULT); else { xf86MsgVerb(X_WARNING, 0, "Driver `%s' has no Probe function (ignoring)\n", xf86DriverList[i]->driverName ? xf86DriverList[i]->driverName : "noname"); } xf86SetPciVideo(NULL,NONE); } |
For the mga driver the probe function is called MGAProbe()
We read at the MGAProbe() comment:
* The aim here is to find all cards that this driver can handle, * and for the ones not already claimed by another driver, claim the * slot, and allocate a ScrnInfoRec. |
MGAProbe() calls xf86MatchDevice() as:
if ((numDevSections = xf86MatchDevice(MGA_DRIVER_NAME, &devSections)) <= 0) { |
In a NutshellMGAProbe() calls xf86MatchDevice(), which is used fill devSections (given as pgdp[] in xf86MatchDevice) a list of pointers to GDevRec. This includes the devices supported by the drivers that the server loads at run time that also appear in the config file. devSections is then passed to xf86MatchPciInstances() where the xf86PciVideoInfo[], obtained from Parsing the xorg.conf file (Part 2)", is used to find the PCI devices that match the given vendor ID, which in our example is PCI_VENDOR_MATROX. The matching fill instances[]. The devices from instances[] are compared with devSections (devList[]).
|
xf86MatchDevice()Its comment explains its usage:
As the previous comment explained the drivers found in the Layout section of the config file:
are compared with the drivers the server loads at run time in our case MGA_DRIVER_NAME and generally drivername, i.e the first argument of xf86MatchDevice. Any matching udpdates the second argument of xf86MatchDevice(). MGA_DRIVER_NAME is defined as mga in mga.h:
Also two GDevRec as seen in the example of the previous section have device values "mga". Their GDevPtr are included in the devSections.
For each screen found at the layout section (see "Parsing the xorg.conf file (Part 2)") as xf86ConfigLayout.screens[j].screen (a confScreenRec struct) xf86NameCmp() is used to find a drivername hit in the layout drivers.
The device is then added to pgdp[] an array of GDevPtr. GDevPtr is defined in xf86str.h as:
As most arrays in Xserver source pgdp[] becomes also NULL terminated. pgdp[] updates the second argument passed to xf86MatchDevice().
|
Next we examine the following part of MGAProbe() code:
* All of the cards this driver supports are PCI, so the "probing" just * amounts to checking the PCI data that the server has already collected. */ if (xf86GetPciVideoInfo() == NULL) { /* * We won't let anything in the config file override finding no * PCI video cards at all. This seems reasonable now, but we'll see. */ return FALSE; } numUsed = xf86MatchPciInstances(MGA_NAME, PCI_VENDOR_MATROX, MGAChipsets, MGAPciChipsets, devSections, numDevSections, drv, &usedChips); |
xf86GetPciVideoInfo() is implemented as:
/* * Get xf86PciVideoInfo for a driver. */ pciVideoPtr * xf86GetPciVideoInfo(void) { return xf86PciVideoInfo; } |
xf86PciVideoInfo[] was filled previously as we saw at the General Bus Probe section. It is returned now, to pciVideoPtr data and will be used in the code that follows. The devSections, filled at xf86MatchDevice() will be passed as the fifth parameter to the next routine called, xf86MatchPciInstances():
xf86MatchPciInstances() [called by MGAProbe()]For xf86MatchPciInstances() we read at its comment (at another version of xf86Helper.c though):
Step 1In the MGAProbe() function xf86MatchPciInstances() was called with vendorID PCI_VENDOR_MATROX and the following code executes:
The xf86PciVideoInfo[] items are checked for the given vendorID, i.e. PCI_VENDOR_MATROX, and if a match is found an instances[] element is filled. Recall that xf86PciVideoInfo[] was filled by FindPCIVideoInfo() at the Make a General bus probe section. At this point the instances[] are filled if a Matrox ID exist in the PCI devices included in xf86PciVideoInfo[]. That means that the previous PCI probe returned a Matrox device. The code looks after the device ID. If the chiptype from xf86PciVideoInfo[] matches one in MGAPciChipsets[] (provided by the MGA driver) the foundHW field of instances is set to TRUE. instances[] represent therefore PCI devices found from the PCI probe that have the given device driver's vendor and chip ID. The elements of this array include also a 'screen' field that will be explained latter. instances[] is defined inside the routine as:
where pci is of type pciVideoPtr, defined in xf86str.h as:
PCIchipsets, the fourth argument was MGAPciChipsets[] passed to xf86MatchPciInstances() from MGAProbe() as MGAPciChipsets[]. It is found in mga_driver.c as:
PciChipsets is defined as:
Step 2The next part of code does what its comment says:
devList is actually the devSections filled by xf86MatchDevice(). If a device is found with screen number > 0 and a PCI busID that matches one in the instances, then instances[] obtain a new element that includes the current device. This is the case with the example of a previous section.
The 'screen' field of the instances[] elements, mentioned previously, is to be assigned next. The PCI characteristics (bus, device, function) from the devList[], which is actually the device list from xorg.conf that xf86MatchDevice() updated previously, are compared with the ones of the instances[] elements. Is a match is found a new instsance[] element is created with the 'screen' given from the devList[].
Step 3The following part of code executes next:
xf86ComparePciBusString calls xf86ParsePciBusString(), where a BUS ID string with the format "bus[@domain]:device[:func]" is analysed to bus, device, function and those are compared with the second, third and fourth argument of xf86ComparePciBusString(). Therefore the bus, device and function PCI values that are compared are from the instances[] and the devList[]. Recall that devList[] is the devSections filled by xf86MatchDevice() and instances[] resulted from the General PCI scanning. The screen values are also compared: devList[j]->screen == instances[i].screen On success and if the bus is not occupied the found bus becomes the one at the current devSections item: devBus = devList[j]; Next the retEntries[] should be returned to the caller routine.
The list xf86MatchPciInstances() returns (as the last argument though) is actually foundEntities (or retEntities). This is the list of entity indicies associated with the driver. xf86ClaimPciSlot() claims a slot in xf86Entities[] and fills it. It is implemented as:
xf86MatchPciInstances() [called by xf86MatchPciInstances()]
xf86Entities[] is defined in xf86Bus.c as:
where EntityPtr points to EntityRec, as found in xf86Bus.h:
xf86AllocateEntity() is implemented as:
If we consider for example that we have one entity, xf86AllocateEntity() returns 0. xf86CheckPciSlot checks if the slot requested is free.
xf86AddDevToEntity [called by xf86MatchPciInstances()]xf86AddDevToEntity() is implemented as:
xf86AllocateEntity() returned 0 to entityIndex, which is the first argument here. Notice that since xf86AddDevToEntity() for the current example runs twice (the first time embedded in xf86ClaimPciSlot) the numInstances becomes 2. Also pEnt->devices[0] and pEnt->devices[1] that are created in the first and the second xf86AddDevToEntity() call respectively take the GDevPtr values used in this example.
From this routine becomes clear the relationship between instance and entity, two notions we met frequently at the most recently routines: An entity has many instances (entity instances) represented by numInstances field from EntityRec. This is the result of xf86AddDevToEntity(). The instance refers to a HARDWARE instance (PCI device) that match the given vendor ID, also their chip type is listed in the chipsets table of the certain vendor and also takes the screen number from the device that match it at the PCI characteristics. An instance has a screen number starting from 0. To each entity is assigned a driver, a chipset, a bus, device and func (taken from the instance). Therefore the entity claims PCI slot for each instance of the hardware found. This prevents other drivers from claiming the same hardware. The same entity can be used more than once for devices with multiple screens per entity. This assumes implicitly that there will be a screen == 0 instance. Both heads (screens) will be driven by different instances of the same driver. Each driver runs on a different entity of the same device. Since there is a different copy of the screen structures and driver structures for each driver for each entity this doesn't differ much from the same driver running two distinct chipsets. For each active instance of the hardware found, a ScrenInfoRec is allocated. multiple screens implies including multiple instances of drivers What is an entity? From the DESIGN document we read:
|
MGAProbe() allocates a new ScrnInfoRec:
ScrnInfoPtr pScrn; . . . /* Allocate a ScrnInfoRec and claim the slot */ pScrn = NULL; |
ScrnInfoPtr is defined in xf86str.h as:
typedef struct _ScrnInfoRec *ScrnInfoPtr; |
where _ScrnInfoRec is a big struct, defined in the same file as:
typedef struct _ScrnInfoRec { int driverVersion; char * driverName; /* canonical name used in */ /* the config file */ ScreenPtr pScreen; /* Pointer to the ScreenRec */ int scrnIndex; /* Number of this screen */ Bool configured; /* Is this screen valid */ int origIndex; /* initial number assigned to * this screen before * finalising the number of * available screens */ /* Display-wide screenInfo values needed by this screen */ int imageByteOrder; int bitmapScanlineUnit; int bitmapScanlinePad; int bitmapBitOrder; int numFormats; PixmapFormatRec formats[MAXFORMATS]; PixmapFormatRec fbFormat; int bitsPerPixel; /* fb bpp */ Pix24Flags pixmap24; /* pixmap pref for depth 24 */ int depth; /* depth of default visual */ MessageType depthFrom; /* set from config? */ MessageType bitsPerPixelFrom; /* set from config? */ rgb weight; /* r/g/b weights */ rgb mask; /* rgb masks */ rgb offset; /* rgb offsets */ int rgbBits; /* Number of bits in r/g/b */ Gamma gamma; /* Gamma of the monitor */ int defaultVisual; /* default visual class */ int maxHValue; /* max horizontal timing */ int maxVValue; /* max vertical timing value */ int virtualX; /* Virtual width */ int virtualY; /* Virtual height */ int xInc; /* Horizontal timing increment */ MessageType virtualFrom; /* set from config? */ int displayWidth; /* memory pitch */ int frameX0; /* viewport position */ int frameY0; int frameX1; int frameY1; int zoomLocked; /* Disallow mode changes */ DisplayModePtr modePool; /* list of compatible modes */ DisplayModePtr modes; /* list of actual modes */ DisplayModePtr currentMode; /* current mode * This was previously * overloaded with the modes * field, which is a pointer * into a circular list */ confScreenPtr confScreen; /* Screen config info */ MonPtr monitor; /* Monitor information */ DispPtr display; /* Display information */ int * entityList; /* List of device entities */ int numEntities; int widthmm; /* physical display dimensions * in mm */ int heightmm; int xDpi; /* width DPI */ int yDpi; /* height DPI */ char * name; /* Name to prefix messages */ pointer driverPrivate; /* Driver private area */ DevUnion * privates; /* Other privates can hook in * here */ DriverPtr drv; /* xf86DriverList[] entry */ pointer module; /* Pointer to module head */ int colorKey; int overlayFlags; /* Some of these may be moved out of here into the driver private area */ char * chipset; /* chipset name */ char * ramdac; /* ramdac name */ char * clockchip; /* clock name */ Bool progClock; /* clock is programmable */ int numClocks; /* number of clocks */ int clock[MAXCLOCKS]; /* list of clock frequencies */ int videoRam; /* amount of video ram (kb) */ unsigned long biosBase; /* Base address of video BIOS */ unsigned long memPhysBase; /* Physical address of FB */ unsigned long fbOffset; /* Offset of FB in the above */ IOADDRESS domainIOBase; /* Domain I/O base address */ int memClk; /* memory clock */ int textClockFreq; /* clock of text mode */ Bool flipPixels; /* swap default black/white */ pointer options; int chipID; int chipRev; int racMemFlags; int racIoFlags; pointer access; xf86CurrentAccessPtr CurrentAccess; resType resourceType; pointer busAccess; /* Allow screens to be enabled/disabled individually */ Bool vtSema; DevUnion pixmapPrivate; /* saved devPrivate from pixmap */ /* hw cursor moves at SIGIO time */ Bool silkenMouse; /* Storage for clockRanges and adjustFlags for use with the VidMode ext */ ClockRangesPtr clockRanges; int adjustFlags; /* * These can be used when the minor ABI version is incremented. * The NUM_* parameters must be reduced appropriately to keep the * structure size and alignment unchanged. */ int reservedInt[NUM_RESERVED_INTS]; int * entityInstanceList; pointer reservedPtr[NUM_RESERVED_POINTERS]; /* * Driver entry points. * */ xf86ProbeProc *Probe; xf86PreInitProc *PreInit; xf86ScreenInitProc *ScreenInit; xf86SwitchModeProc *SwitchMode; xf86AdjustFrameProc *AdjustFrame; xf86EnterVTProc *EnterVT; xf86LeaveVTProc *LeaveVT; xf86FreeScreenProc *FreeScreen; xf86ValidModeProc *ValidMode; xf86EnableDisableFBAccessProc *EnableDisableFBAccess; xf86SetDGAModeProc *SetDGAMode; xf86ChangeGammaProc *ChangeGamma; xf86PointerMovedProc *PointerMoved; xf86PMEventProc *PMEvent; xf86HandleMessageProc *HandleMessage; xf86DPMSSetProc *DPMSSet; xf86LoadPaletteProc *LoadPalette; xf86SetOverscanProc *SetOverscan; xorgDriverFuncProc *DriverFunc; /* * This can be used when the minor ABI version is incremented. * The NUM_* parameter must be reduced appropriately to keep the * structure size and alignment unchanged. */ funcPointer reservedFuncs[NUM_RESERVED_FUNCS]; } ScrnInfoRec; |
then MGAProbe() calls xf86ConfigPciEntity() to return a ScrnInfoPtr as pScrn. It is called as:
else for (i = 0; i < numUsed; i++) { ScrnInfoPtr pScrn; EntityInfoPtr pEnt; #ifdef DISABLE_VGA_IO MgaSavePtr smga; #endif /* Allocate a ScrnInfoRec and claim the slot */ pScrn = NULL; #ifndef DISABLE_VGA_IO if ((pScrn = xf86ConfigPciEntity(pScrn, 0,usedChips[i], MGAPciChipsets, NULL, NULL, NULL, NULL, NULL))) #else smga = xnfalloc(sizeof(MgaSave)); smga->pvp = xf86GetPciInfoForEntity(usedChips[i]); if ((pScrn = xf86ConfigPciEntity(pScrn, 0,usedChips[i], MGAPciChipsets, NULL,VgaIOSave, VgaIOSave, VgaIORestore,smga))) #endif |
Example (continued)For the current example (based in this xorg.conf) numUsed, the variable xf86MatchPciInstances() returned is 2 and therefore the previous 'for' loop allocates and fills two ScrnInfoRec. Also the code that follows in this section is executed twice.
|
xf86ConfigPciEntity() [called by MGAProbe()]xf86ConfigPciEntity's comment says:
From the DESIGN document we read:
xf86GetEntityInfo() [called from xf86ConfigPciEntity()]xf86ConfigPciEntity calls xf86GetEntityInfo() as:
entityIndex was passed to xf86ConfigPciEntity as the third argument. This was the usedChips[] elements. usedChips[] was previously filled by xf86MatchPciInstances(). It was the last argument, which was corresponding to the "Returned list of entity indicies associated with the driver." xf86GetEntityInfo is implemented as:
The current info is found at xf86Entities[entityIndex]. As entityIndex is passed from MGAProbe() to xf86ConfigPciEntity() the usedChips[i] value. This was filled by xf86MatchPciInstances() and returned to the caller routine (MGAProbe) as foundEntities or retEntities in the code since:
We read from the DESIGN document for xf86AllocateScreen():
xf86ConfigPciEntity [called by xf86ConfigPciEntity()]xf86AddEntityToScreen() as the comment of the DESIGN document says:
It is implemented as:
For xf86SetEntityFuncs() the DESIGN document refers:
It is implemented as:
|
MGAProbe() fills then some fields for the ScrnInfoRec:
/* Fill in what we can of the ScrnInfoRec */ pScrn->driverVersion = MGA_VERSION; pScrn->driverName = MGA_DRIVER_NAME; pScrn->name = MGA_NAME; pScrn->Probe = MGAProbe; pScrn->PreInit = MGAPreInit; pScrn->ScreenInit = MGAScreenInit; pScrn->SwitchMode = MGASwitchMode; pScrn->AdjustFrame = MGAAdjustFrame; pScrn->EnterVT = MGAEnterVT; pScrn->LeaveVT = MGALeaveVT; pScrn->FreeScreen = MGAFreeScreen; pScrn->ValidMode = MGAValidMode; foundScreen = TRUE; |
Example (continued)The following are the two ScrnInfoRec structs that are resulted from the current example:
|
/* * For cards that can do dual head per entity, mark the entity * as sharable. */ pEnt = xf86GetEntityInfo(usedChips[i]); if ((pEnt->chipset == PCI_CHIP_MGAG400 || pEnt->chipset == PCI_CHIP_MGAG550)) { MGAEntPtr pMgaEnt = NULL; DevUnion *pPriv; xf86SetEntitySharable(usedChips[i]); /* Allocate an entity private if necessary */ if (MGAEntityIndex < 0) MGAEntityIndex = xf86AllocateEntityPrivateIndex(); pPriv = xf86GetEntityPrivate(pScrn->entityList[0], MGAEntityIndex); if (!pPriv->ptr) { pPriv->ptr = xnfcalloc(sizeof(MGAEntRec), 1); pMgaEnt = pPriv->ptr; pMgaEnt->lastInstance = -1; } else { pMgaEnt = pPriv->ptr; } /* * Set the entity instance for this instance of the driver. For * dual head per card, instance 0 is the "master" instance, driving * the primary head, and instance 1 is the "slave". */ pMgaEnt->lastInstance++; xf86SetEntityInstanceForScreen(pScrn, pScrn->entityList[0], pMgaEnt->lastInstance); } } |
NOTES:
Blue colored code indicates that a value is assigned to a ScrnInfoRec field.
REFERENCES:
Graphic Stack Overview
DESIGN Document
Driver Development
Nouveau open source driver