Files
nv-xconfig/nvidia-xconfig-540.3.0/multiple_screens.c
svcmobrel-release b16ce48c94 Updating prebuilts and/or headers
6f6f68f976250f2646450e571f9ab2d7eee1ac80 - nvidia-xconfig-540.3.0/nvidia-cfg.h
46f2ea329459b0b16cb36cc14cec0de737d0963e - nvidia-xconfig-540.3.0/options.c
9b0c02164398be2795fea7cb13c9e1b062d24bfb - nvidia-xconfig-540.3.0/query_gpu_info.c
b1bc157844c5570f911b2bf45249bd26ce73efef - nvidia-xconfig-540.3.0/Makefile
10edc2c301fd331439b38d001c0df41669c238b2 - nvidia-xconfig-540.3.0/multiple_screens.c
f14e9577e537037d4778d490ef70f13b0e94a70a - nvidia-xconfig-540.3.0/option_table.h
1e39095238886dfc6a69f7ec750e10c05a93e7d1 - nvidia-xconfig-540.3.0/nvidia-xconfig.1.m4
913b088cb559f31e09a1baf89d85d75cb43079b4 - nvidia-xconfig-540.3.0/nvidia-xconfig.h
647e7896275a1113ec99352d573823a05001f57e - nvidia-xconfig-540.3.0/lscf.c
bfa37f78ba458f14a1865bbf3f67eb84987a7e1c - nvidia-xconfig-540.3.0/extract_edids.c
41bcac41393c9a465c30c07cf1ab386647a16687 - nvidia-xconfig-540.3.0/util.c
a5ad539267b73169480e7898b284823639c4db3a - nvidia-xconfig-540.3.0/tree.c
4cc77b90af91e615a64ae04893fdffa7939db84c - nvidia-xconfig-540.3.0/COPYING
c946eefad99cec9366d8abbd4e45c5d138d3b7be - nvidia-xconfig-540.3.0/make_usable.c
d37da5a118e0418b267a48d05fd144611316b42c - nvidia-xconfig-540.3.0/gen-manpage-opts.c
b3d0f9f27c4d9cb7940d04e1dd387d357a16024c - nvidia-xconfig-540.3.0/nvidia-xconfig.c
524990f5497f9bbeb3d148b5e7dc8d2267c3163d - nvidia-xconfig-540.3.0/common-utils/nvpci-utils.c
abb5c1b445d9353f2d2840bda848cd16109710a4 - nvidia-xconfig-540.3.0/common-utils/msg.h
1654638c567bc7f2bd70b54d807b498ab14c1061 - nvidia-xconfig-540.3.0/common-utils/nvpci-utils.h
8a346196b052cfb1e06dd83d2ad1fe71e928d2b3 - nvidia-xconfig-540.3.0/common-utils/common-utils.c
e572cac43202f6c2af1c80b9e3901215126093ed - nvidia-xconfig-540.3.0/common-utils/nvgetopt.h
8db48a58d1654ed69920366bef10f38b789a1a84 - nvidia-xconfig-540.3.0/common-utils/msg.c
8259a24058c714629f9819cc8c830ea9b202bb27 - nvidia-xconfig-540.3.0/common-utils/gen-manpage-opts-helper.h
5d55b94375c2055cfa1578e4c7f34c90e63a33f7 - nvidia-xconfig-540.3.0/common-utils/gen-manpage-opts-helper.c
dc2678d8a9d794a4b2b2718fce57ec087f21f54b - nvidia-xconfig-540.3.0/common-utils/common-utils.h
384e36102dcd08ed4c5de05b5e3b8a7cdb2e257d - nvidia-xconfig-540.3.0/common-utils/nvgetopt.c
33211ca0a10f50e2c87b9e8feef6c1ab381b57a1 - nvidia-xconfig-540.3.0/XF86Config-parser/DRI.c
5ffb2caa5077a2e6ec1c5ece807e71503fb2fbce - nvidia-xconfig-540.3.0/XF86Config-parser/Module.c
953b945f3b117d6fb44f1f738af17b6380a9ec72 - nvidia-xconfig-540.3.0/XF86Config-parser/Monitor.c
de1c758e29f217e1a99e4c076d54ac84bce98b18 - nvidia-xconfig-540.3.0/XF86Config-parser/Screen.c
f3d611bdbddfa64675a0810ef81dada57e224bcd - nvidia-xconfig-540.3.0/XF86Config-parser/Extensions.c
ec19d673a6a7d1d8f855f2d32d3e8f63046c3625 - nvidia-xconfig-540.3.0/XF86Config-parser/Files.c
e67d630ef396ab7d34524c333f3a77fc42ba8fc6 - nvidia-xconfig-540.3.0/XF86Config-parser/configProcs.h
c66a0a141e25e31b568fb9df41f17d7fb9e6d3b9 - nvidia-xconfig-540.3.0/XF86Config-parser/xf86Parser.h
4d9b03ea3badceb6bdfdf6589e6731140ec44079 - nvidia-xconfig-540.3.0/XF86Config-parser/Generate.c
cda3a4ab05bf48ba28af35b5c4c632e968afc7fa - nvidia-xconfig-540.3.0/XF86Config-parser/Util.c
83b0a8efd6a508db54995688ab353591bdb242a2 - nvidia-xconfig-540.3.0/XF86Config-parser/Flags.c
45ceb0129668346ae3e52d81bb6e2f97efadf9d0 - nvidia-xconfig-540.3.0/XF86Config-parser/Input.c
18711ff932af2202869a30f3b32d6d7fe7811c84 - nvidia-xconfig-540.3.0/XF86Config-parser/Layout.c
c7b8d8ca0f7c9dcc2cdca4f8e77d1122c71892ef - nvidia-xconfig-540.3.0/XF86Config-parser/Read.c
0a274c4bc54b6ae0f6d009e443bda0cb033d66b5 - nvidia-xconfig-540.3.0/XF86Config-parser/Pointer.c
04efe162cf6d97882f2bb87f3712d9a65c2320f3 - nvidia-xconfig-540.3.0/XF86Config-parser/Scan.c
7344be997921dec57959691e986763ee686888d5 - nvidia-xconfig-540.3.0/XF86Config-parser/Device.c
4407207cf890539dc604cff5b834c994b307729a - nvidia-xconfig-540.3.0/XF86Config-parser/Write.c
3f2238c88d737bd329a9bca1ac4b0bcf77bb9ac2 - nvidia-xconfig-540.3.0/XF86Config-parser/xf86tokens.h
a9bc06f33bf525c2b08be2dc3cd64a59c4c7946d - nvidia-xconfig-540.3.0/XF86Config-parser/Configint.h
9cbc29da282aa957f28b7fc83caf1e3b19ee2a52 - nvidia-xconfig-540.3.0/XF86Config-parser/Vendor.c
2022a4a3c2a1b23a67ae74f50a3849f5f3a45e4b - nvidia-xconfig-540.3.0/XF86Config-parser/Keyboard.c
6c210ad0eaee1db3fec48ff01746cb054b4f9aaa - nvidia-xconfig-540.3.0/XF86Config-parser/Video.c
ac878b26ded86a3c502a6a81fc4c4a96162afefb - nvidia-xconfig-540.3.0/XF86Config-parser/Merge.c

Change-Id: I3be9db5329476f82eb30f69e29fb957c39267004
2024-05-02 11:24:17 -07:00

1092 lines
31 KiB
C

/*
* nvidia-xconfig: A tool for manipulating X config files,
* specifically for use by the NVIDIA Linux graphics driver.
*
* Copyright (C) 2005 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses>.
*
*
* multiple_screens.c
*/
#include "nvidia-xconfig.h"
#include "xf86Parser.h"
#include "msg.h"
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
static int enable_separate_x_screens(Options *op, XConfigPtr config,
XConfigLayoutPtr layout);
static int disable_separate_x_screens(Options *op, XConfigPtr config,
XConfigLayoutPtr layout);
static int set_xinerama(int xinerama_enabled, XConfigLayoutPtr layout);
static XConfigDisplayPtr clone_display_list(XConfigDisplayPtr display0);
static XConfigDevicePtr clone_device(XConfigDevicePtr device0, int idx);
static XConfigScreenPtr clone_screen(XConfigScreenPtr screen0, int idx);
static void create_adjacencies(Options *op, XConfigPtr config,
XConfigLayoutPtr layout);
static int enable_all_gpus(Options *op, XConfigPtr config,
XConfigLayoutPtr layout);
static void free_unused_devices(Options *op, XConfigPtr config);
static void free_unused_monitors(Options *op, XConfigPtr config);
static int only_one_screen(Options *op, XConfigPtr config,
XConfigLayoutPtr layout);
/*
* get_screens_to_clone() - try to detect automatically how many heads has each
* device in order to use that number to create more than two separate X
* screens. If the user specifies the option --num-x-screens <quantity>, that
* value is used. If neither the user specifies the quantity or the number of
* heads can be detected automatically, it uses 2 heads (the standard
* behavior). This function returns an array of size nscreens with the number
* of screens to clone per screen candidate. The caller is responsible of
* freeing the memory of that array.
*/
static int* get_screens_to_clone(Options *op,
const XConfigScreenPtr *screen_candidates,
int nscreens)
{
DevicesPtr pDevices;
int *screens_to_clone, *supported_screens;
int i, j, devs_found;
screens_to_clone = nvalloc(nscreens * sizeof(int));
supported_screens = nvalloc(nscreens * sizeof(int));
/* Detect the number of supported screens per screen candidate */
devs_found = FALSE;
pDevices = find_devices(op);
if (pDevices) {
for (i = 0; i < nscreens; i++) {
int bus, slot, scratch;
if (!screen_candidates[i]) {
continue;
}
/* parse the bus id for this candidate screen */
if (!xconfigParsePciBusString(screen_candidates[i]->device->busid,
&bus, &slot, &scratch)) {
continue;
}
for (j = 0; j < pDevices->nDevices; j++) {
if ((pDevices->devices[j].dev.bus == bus) &&
(pDevices->devices[j].dev.slot == slot)) {
if (pDevices->devices[j].crtcs > 0) {
supported_screens[i] = pDevices->devices[j].crtcs;
}
break;
}
}
}
free_devices(pDevices);
devs_found = TRUE;
}
/* If user has defined a number of screens per GPU, use that value */
if (op->num_x_screens > 0) {
for (i = 0; i < nscreens; i++) {
if (!screen_candidates[i]) {
continue;
}
/* Print error when user specifies more X screens than supported */
if (devs_found && op->num_x_screens > supported_screens[i]) {
nv_warning_msg("Number of X screens specified is higher than the "
"supported quantity (%d)", supported_screens[i]);
}
screens_to_clone[i] = op->num_x_screens;
}
goto done;
}
for (i = 0; i < nscreens; i++) {
if (screen_candidates[i]) {
if (devs_found) {
/* If devices found, use the supported screens number */
screens_to_clone[i] = supported_screens[i];
}
else {
/* Default behavior (2 heads per GPU) */
screens_to_clone[i] = 2;
}
}
}
done:
nvfree(supported_screens);
return screens_to_clone;
}
/*
* clean_screen_list() - Used by enable_separate_x_screens and
* disable_separate_x_screens. Given the array screen_list and the config
* pointer, this function will leave only one screen per different busid in both
* screen_list array and config->screens list (i.e all the resulting screens in
* screen_list and config->screens will have an unique busid).
*/
static void clean_screen_list(XConfigScreenPtr *screen_list,
XConfigPtr config,
int nscreens)
{
int i, j;
int *bus, *slot, scratch;
/* trim out duplicates and get the bus ids*/
bus = nvalloc(sizeof(int) * nscreens);
slot = nvalloc(sizeof(int) * nscreens);
for (i = 0; i < nscreens; i++) {
bus[i] = -1;
}
for (i = 0; i < nscreens; i++) {
if (!screen_list[i] || (bus[i] == -1 &&
!xconfigParsePciBusString(screen_list[i]->device->busid,
&bus[i], &slot[i], &scratch))) {
continue;
}
for (j = i+1; j < nscreens; j++) {
if (!screen_list[j] || (bus[j] == -1 &&
!xconfigParsePciBusString(screen_list[j]->device->busid,
&bus[j], &slot[j], &scratch))) {
continue;
}
if ((bus[i] == bus[j]) && (slot[i] == slot[j])) {
screen_list[j] = NULL;
}
}
}
/*
* for every screen in the screen list, scan through all
* screens; if any screen, other than *this* screen has the same
* busid, remove it
*/
for (i = 0; i < nscreens; i++) {
XConfigScreenPtr screen, prev;
int bus0, slot0;
if (!screen_list[i]) {
continue;
}
screen = config->screens;
prev = NULL;
while (screen) {
if (screen_list[i] == screen) {
goto next_screen;
}
if (!screen->device) {
goto next_screen;
}
if (!screen->device->busid) {
goto next_screen;
}
if (!xconfigParsePciBusString(screen->device->busid,
&bus0, &slot0, &scratch)) {
goto next_screen;
}
if ((bus[i] == bus0) && (slot[i] == slot0)) {
XConfigScreenPtr next;
if (prev) {
prev->next = screen->next;
}
else {
config->screens = screen->next;
}
next = screen->next;
screen->next = NULL;
xconfigFreeScreenList(&screen);
screen = next;
}
else {
next_screen:
prev = screen;
screen = screen->next;
}
}
screen_list[i]->device->screen = -1;
}
}
/*
* apply_multi_screen_options() - there are 4 options that can affect
* multiple X screens:
*
* - add X screens for all GPUS in the system
* - separate X screens on one GPU (turned on or off)
* - only one X screen
* - Xinerama
*
* apply these options in that order
*/
int apply_multi_screen_options(Options *op, XConfigPtr config,
XConfigLayoutPtr layout)
{
if (op->enable_all_gpus) {
if (!enable_all_gpus(op, config, layout)) return FALSE;
}
if (GET_BOOL_OPTION(op->boolean_options,
SEPARATE_X_SCREENS_BOOL_OPTION)) {
if (GET_BOOL_OPTION(op->boolean_option_values,
SEPARATE_X_SCREENS_BOOL_OPTION)) {
if (!enable_separate_x_screens(op, config, layout)) return FALSE;
} else {
if (!disable_separate_x_screens(op, config, layout)) return FALSE;
}
}
if (GET_BOOL_OPTION(op->boolean_options,
XINERAMA_BOOL_OPTION)) {
if (!set_xinerama(GET_BOOL_OPTION(op->boolean_option_values,
XINERAMA_BOOL_OPTION),
layout)) return FALSE;
}
if (op->only_one_screen) {
if (!only_one_screen(op, config, layout)) return FALSE;
}
return TRUE;
} /* apply_multi_screen_options() */
/*
* find_devices() - dlopen the nvidia-cfg library and query the
* available information about the GPUs in the system.
*/
DevicesPtr find_devices(Options *op)
{
DevicesPtr pDevices = NULL;
DisplayDevicePtr pDisplayDevice;
int i, j, n, count = 0;
int swappedIndex;
unsigned int mask, bit;
DeviceRec tmpDevice;
NvCfgPciDevice *devs = NULL;
NvCfgBool is_primary_device;
NvCfgBool ret;
char *lib_path;
void *lib_handle;
NvCfgBool (*__getDevices)(int *n, NvCfgDevice **devs);
NvCfgBool (*__openDevice)(int bus, int slot, NvCfgDeviceHandle *handle);
NvCfgBool (*__getPciDevices)(int *n, NvCfgPciDevice **devs);
NvCfgBool (*__openPciDevice)(int domain, int bus, int slot, int function,
NvCfgDeviceHandle *handle);
NvCfgBool (*__getNumCRTCs)(NvCfgDeviceHandle handle, int *crtcs);
NvCfgBool (*__getProductName)(NvCfgDeviceHandle handle, char **name);
NvCfgBool (*__getDisplayDevices)(NvCfgDeviceHandle handle,
unsigned int *display_device_mask);
NvCfgBool (*__getEDID)(NvCfgDeviceHandle handle,
unsigned int display_device,
NvCfgDisplayDeviceInformation *info);
NvCfgBool (*__isPrimaryDevice)(NvCfgDeviceHandle handle,
NvCfgBool *is_primary_device);
NvCfgBool (*__closeDevice)(NvCfgDeviceHandle handle);
NvCfgBool (*__getDeviceUUID)(NvCfgDeviceHandle handle, char **uuid);
/* dlopen() the nvidia-cfg library */
#define __LIB_NAME "libnvidia-cfg.so.1"
if (op->nvidia_cfg_path) {
lib_path = nvstrcat(op->nvidia_cfg_path, "/", __LIB_NAME, NULL);
} else {
lib_path = strdup(__LIB_NAME);
}
lib_handle = dlopen(lib_path, RTLD_NOW);
nvfree(lib_path);
if (!lib_handle) {
nv_warning_msg("error opening %s: %s.", __LIB_NAME, dlerror());
return NULL;
}
#define __GET_FUNC(proc, name) \
(proc) = dlsym(lib_handle, (name)); \
if (!(proc)) { \
nv_warning_msg("error retrieving symbol %s from %s: %s", \
(name), __LIB_NAME, dlerror()); \
dlclose(lib_handle); \
return NULL; \
}
/* required functions */
__GET_FUNC(__getDevices, "nvCfgGetDevices");
__GET_FUNC(__openDevice, "nvCfgOpenDevice");
__GET_FUNC(__getPciDevices, "nvCfgGetPciDevices");
__GET_FUNC(__openPciDevice, "nvCfgOpenPciDevice");
__GET_FUNC(__getNumCRTCs, "nvCfgGetNumCRTCs");
__GET_FUNC(__getProductName, "nvCfgGetProductName");
__GET_FUNC(__getDisplayDevices, "nvCfgGetDisplayDevices");
__GET_FUNC(__getEDID, "nvCfgGetEDID");
__GET_FUNC(__closeDevice, "nvCfgCloseDevice");
__GET_FUNC(__getDeviceUUID, "nvCfgGetDeviceUUID");
__GET_FUNC(__isPrimaryDevice,"nvCfgIsPrimaryDevice");
if (__getPciDevices(&count, &devs) != NVCFG_TRUE) {
return NULL;
}
if (count == 0) return NULL;
pDevices = nvalloc(sizeof(DevicesRec));
pDevices->devices = nvalloc(sizeof(DeviceRec) * count);
pDevices->nDevices = count;
for (i = 0; i < count; i++) {
pDevices->devices[i].dev = devs[i];
if (__openPciDevice(devs[i].domain, devs[i].bus, devs[i].slot, 0,
&(pDevices->devices[i].handle)) != NVCFG_TRUE) {
goto fail;
}
if (__getNumCRTCs(pDevices->devices[i].handle,
&pDevices->devices[i].crtcs) != NVCFG_TRUE) {
goto fail;
}
if (__getProductName(pDevices->devices[i].handle,
&pDevices->devices[i].name) != NVCFG_TRUE) {
/* This call may fail with little impact to the Device section */
pDevices->devices[i].name = NULL;
}
if (__getDeviceUUID(pDevices->devices[i].handle,
&pDevices->devices[i].uuid) != NVCFG_TRUE) {
goto fail;
}
if (__getDisplayDevices(pDevices->devices[i].handle, &mask) !=
NVCFG_TRUE) {
goto fail;
}
pDevices->devices[i].displayDeviceMask = mask;
/* count the number of display devices */
for (n = j = 0; j < 32; j++) {
if (mask & (1 << j)) n++;
}
pDevices->devices[i].nDisplayDevices = n;
if (n) {
/* allocate the info array of the right size */
pDevices->devices[i].displayDevices =
nvalloc(sizeof(DisplayDeviceRec) * n);
/* fill in the info array */
for (n = j = 0; j < 32; j++) {
bit = 1 << j;
if (!(bit & mask)) continue;
pDisplayDevice = &pDevices->devices[i].displayDevices[n];
pDisplayDevice->mask = bit;
if (__getEDID(pDevices->devices[i].handle, bit,
&pDisplayDevice->info) != NVCFG_TRUE) {
pDisplayDevice->info_valid = FALSE;
} else {
pDisplayDevice->info_valid = TRUE;
}
n++;
}
} else {
pDevices->devices[i].displayDevices = NULL;
}
/* Use this index instead of i to close device after a possible swap */
swappedIndex = i;
if ((i != 0) &&
(__isPrimaryDevice(pDevices->devices[i].handle,
&is_primary_device) == NVCFG_TRUE) &&
(is_primary_device == NVCFG_TRUE)) {
memcpy(&tmpDevice, &pDevices->devices[0], sizeof(DeviceRec));
memcpy(&pDevices->devices[0], &pDevices->devices[i], sizeof(DeviceRec));
memcpy(&pDevices->devices[i], &tmpDevice, sizeof(DeviceRec));
swappedIndex = 0;
}
ret = __closeDevice(pDevices->devices[swappedIndex].handle);
pDevices->devices[swappedIndex].handle = NULL;
if (ret != NVCFG_TRUE) {
goto fail;
}
}
goto done;
fail:
nv_warning_msg("Unable to use the nvidia-cfg library to query NVIDIA "
"hardware.");
for (i = 0; i < pDevices->nDevices; i++) {
/* close the opened device */
if (pDevices->devices[i].handle) {
__closeDevice(pDevices->devices[i].handle);
}
}
free_devices(pDevices);
pDevices = NULL;
/* fall through */
done:
if (devs) free(devs);
return pDevices;
} /* find_devices() */
/*
* free_devices()
*/
void free_devices(DevicesPtr pDevices)
{
int i;
if (!pDevices) return;
for (i = 0; i < pDevices->nDevices; i++) {
if (pDevices->devices[i].displayDevices) {
nvfree(pDevices->devices[i].displayDevices);
}
}
if (pDevices->devices) {
nvfree(pDevices->devices);
}
nvfree(pDevices);
} /* free_devices() */
/*
* set_xinerama() - This makes sure there is a ServerLayout
* section and sets the "Xinerama" option
*/
static int set_xinerama(int xinerama_enabled, XConfigLayoutPtr layout)
{
xconfigAddNewOption(&(layout->options),
"Xinerama",
(xinerama_enabled ? "1" : "0"));
return TRUE;
} /* set_xinerama() */
/*
* enable_separate_x_screens() - this effectively clones each screen
* that is on a unique GPU.
*
* Algorithm:
*
* step 1: build a list of screens to be cloned
*
* step 2: assign a busID to every screen that is in the list (if
* BusIDs are not already assigned)
*
* step 3: clean the candidate list
*
* step 4: get the number of clones per screen candidate
*
* step 4: clone each eligible screen
*
* step 5: update adjacency list (just wipe the list and restart)
*/
static int enable_separate_x_screens(Options *op, XConfigPtr config,
XConfigLayoutPtr layout)
{
XConfigScreenPtr screen, *screenlist = NULL;
XConfigAdjacencyPtr adj;
int* screens_to_clone = NULL;
int i, nscreens = 0;
int have_busids;
/* step 1: build the list of screens that are candidate to be cloned */
if (op->screen) {
screen = xconfigFindScreen(op->screen, config->screens);
if (!screen) {
nv_error_msg("Unable to find screen '%s'.", op->screen);
return FALSE;
}
screenlist = nvalloc(sizeof(XConfigScreenPtr));
screenlist[0] = screen;
nscreens = 1;
} else {
for (adj = layout->adjacencies; adj; adj = adj->next) {
nscreens++;
screenlist = realloc(screenlist,
sizeof(XConfigScreenPtr) * nscreens);
screenlist[nscreens-1] = adj->screen;
}
}
if (!nscreens) return FALSE;
/* step 2: do all screens in the list have a bus ID? */
have_busids = TRUE;
for (i = 0; i < nscreens; i++) {
if (screenlist[i] &&
screenlist[i]->device &&
screenlist[i]->device->busid) {
// this screen has a busid
} else {
have_busids = FALSE;
break;
}
}
/*
* if not everyone has a bus id, then let's assign bus ids to all
* the screens; XXX what if _some_ already have busids? Too bad,
* we're going to reassign all of them.
*/
if (!have_busids) {
DevicesPtr pDevices;
pDevices = find_devices(op);
if (!pDevices) {
nv_error_msg("Unable to determine number or location of "
"GPUs in system; cannot "
"honor '--separate-x-screens' option.");
return FALSE;
}
for (i = 0; i < nscreens; i++) {
if (i >= pDevices->nDevices) {
/*
* we have more screens than GPUs, this screen is no
* longer a candidate
*/
screenlist[i] = NULL;
continue;
}
screenlist[i]->device->busid = nv_format_busid(op, i);
screenlist[i]->device->board = nvstrdup(pDevices->devices[i].name);
}
free_devices(pDevices);
}
/* step 3 */
clean_screen_list(screenlist, config, nscreens);
/* step 4 */
screens_to_clone = get_screens_to_clone(op, screenlist, nscreens);
/* step 5: clone each eligible screen */
for (i = 0; i < nscreens; i++) {
if (!screenlist[i]) continue;
while (--screens_to_clone[i] > 0) {
clone_screen(screenlist[i], screens_to_clone[i]);
}
}
nvfree(screens_to_clone);
/* step 6: wipe the existing adjacencies and recreate them */
xconfigFreeAdjacencyList(&layout->adjacencies);
create_adjacencies(op, config, layout);
/* free unused device and monitor sections */
free_unused_devices(op, config);
free_unused_monitors(op, config);
/* free stuff */
free(screenlist);
return TRUE;
} /* enable_separate_x_screens() */
/*
* disable_separate_x_screens() - remove multiple screens that are
* configured for the same GPU.
*
* Algorithm:
*
* step 1: find which screens need to be "de-cloned" (either
* op->screen or all screens in the layout)
*
* step 2: narrow that list down to screens that have a busid
* specified
*
* step 3: clean the candidate list
*
* step 4: recompute the adjacency list
*/
static int disable_separate_x_screens(Options *op, XConfigPtr config,
XConfigLayoutPtr layout)
{
XConfigScreenPtr screen, *screenlist = NULL;
XConfigAdjacencyPtr adj;
int i, nscreens = 0;
/* step 1: build the list of screens that are candidate to be de-cloned */
if (op->screen) {
screen = xconfigFindScreen(op->screen, config->screens);
if (!screen) {
nv_error_msg("Unable to find screen '%s'.", op->screen);
return FALSE;
}
screenlist = nvalloc(sizeof(XConfigScreenPtr));
screenlist[0] = screen;
nscreens = 1;
} else {
for (adj = layout->adjacencies; adj; adj = adj->next) {
nscreens++;
screenlist = realloc(screenlist,
sizeof(XConfigScreenPtr) * nscreens);
screenlist[nscreens-1] = adj->screen;
}
}
/*
* step 2: limit the list to screens that have a BusID; parse the busIDs
* while we're at it
*/
for (i = 0; i < nscreens; i++) {
int bus, slot, scratch;
if (screenlist[i] &&
screenlist[i]->device &&
screenlist[i]->device->busid &&
xconfigParsePciBusString(screenlist[i]->device->busid,
&bus, &slot, &scratch)) {
// this screen has a valid busid
} else {
screenlist[i] = NULL;
}
}
/* step 3 */
clean_screen_list(screenlist, config, nscreens);
/* step 4: wipe the existing adjacencies and recreate them */
xconfigFreeAdjacencyList(&layout->adjacencies);
create_adjacencies(op, config, layout);
/* free unused device and monitor sections */
free_unused_devices(op, config);
free_unused_monitors(op, config);
/* free stuff */
free(screenlist);
return TRUE;
} /* disable_separate_x_screens() */
/*
* clone_display_list() - create a duplicate of the specified display
* subsection.
*/
static XConfigDisplayPtr clone_display_list(XConfigDisplayPtr display0)
{
XConfigDisplayPtr d = NULL, prev = NULL, head = NULL;
while (display0) {
d = nvalloc(sizeof(XConfigDisplayRec));
memcpy(d, display0, sizeof(XConfigDisplayRec));
if (display0->visual) d->visual = nvstrdup(display0->visual);
if (display0->comment) d->comment = nvstrdup(display0->comment);
d->options = xconfigOptionListDup(display0->options);
d->next = NULL;
if (prev) prev->next = d;
if (!head) head = d;
prev = d;
display0 = display0->next;
}
return head;
} /* clone_display_list() */
/*
* clone_device() - duplicate the specified device section, updating
* the screen indices as approprate for multiple X screens on one GPU
*/
static XConfigDevicePtr clone_device(XConfigDevicePtr device0, int idx)
{
XConfigDevicePtr device;
device = nvalloc(sizeof(XConfigDeviceRec));
device->identifier = nvasprintf("%s (%d)", device0->identifier, idx);
if (device0->vendor) device->vendor = nvstrdup(device0->vendor);
if (device0->board) device->board = nvstrdup(device0->board);
if (device0->chipset) device->chipset = nvstrdup(device0->chipset);
if (device0->busid) device->busid = nvstrdup(device0->busid);
if (device0->card) device->card = nvstrdup(device0->card);
if (device0->driver) device->driver = nvstrdup(device0->driver);
if (device0->ramdac) device->ramdac = nvstrdup(device0->ramdac);
if (device0->comment) device->comment = nvstrdup(device0->comment);
/* these are needed for multiple X screens on one GPU */
device->screen = idx;
device0->screen = 0;
device->chipid = -1;
device->chiprev = -1;
device->irq = -1;
device->options = xconfigOptionListDup(device0->options);
/* insert the new device after the original device */
device->next = device0->next;
device0->next = device;
return device;
} /* clone_device() */
/*
* clone_screen() - duplicate the given screen, for use as the ith
* X screen on one GPU
*/
static XConfigScreenPtr clone_screen(XConfigScreenPtr screen0, int idx)
{
XConfigScreenPtr screen = nvalloc(sizeof(XConfigScreenRec));
screen->identifier = nvasprintf("%s (%d)", screen0->identifier, idx);
screen->device = clone_device(screen0->device, idx);
screen->device_name = nvstrdup(screen->device->identifier);
screen->monitor = screen0->monitor;
screen->monitor_name = nvstrdup(screen0->monitor_name);
screen->defaultdepth = screen0->defaultdepth;
screen->displays = clone_display_list(screen0->displays);
screen->options = xconfigOptionListDup(screen0->options);
if (screen0->comment) screen->comment = nvstrdup(screen0->comment);
/* insert the new screen after the original screen */
screen->next = screen0->next;
screen0->next = screen;
return screen;
} /* clone_screen() */
/*
* create_adjacencies() - loop through all the screens in the config,
* and add an adjacency section to the layout; this assumes that there
* are no existing adjacencies in the layout
*/
static void create_adjacencies(Options *op, XConfigPtr config,
XConfigLayoutPtr layout)
{
XConfigAdjacencyPtr adj, prev_adj;
XConfigScreenPtr screen;
int i;
i = 0;
prev_adj = NULL;
for (screen = config->screens; screen; screen = screen->next) {
adj = nvalloc(sizeof(XConfigAdjacencyRec));
adj->scrnum = i;
adj->screen_name = nvstrdup(screen->identifier);
adj->screen = screen;
if (prev_adj) {
prev_adj->next = adj;
} else {
layout->adjacencies = adj;
}
prev_adj = adj;
i++;
}
xconfigGenerateAssignScreenAdjacencies(layout);
} /* create_adjacencies() */
/*
* enable_all_gpus() - get information for every GPU in the system,
* and create a screen section for each
*
* XXX do we add new screens with reasonable defaults, or do we clone
* the first existing X screen N times? For now, we'll just add all
* new X screens
*/
static int enable_all_gpus(Options *op, XConfigPtr config,
XConfigLayoutPtr layout)
{
DevicesPtr pDevices;
int i;
pDevices = find_devices(op);
if (!pDevices) {
nv_error_msg("Unable to determine number of GPUs in system; cannot "
"honor '--enable-all-gpus' option.");
return FALSE;
}
/* free all existing X screens, monitors, devices, and adjacencies */
xconfigFreeScreenList(&config->screens);
xconfigFreeDeviceList(&config->devices);
xconfigFreeMonitorList(&config->monitors);
xconfigFreeAdjacencyList(&layout->adjacencies);
/* add N new screens; this will also add device and monitor sections */
for (i = 0; i < pDevices->nDevices; i++) {
xconfigGenerateAddScreen(config,
pDevices->devices[i].dev.bus,
pDevices->devices[i].dev.domain,
pDevices->devices[i].dev.slot,
pDevices->devices[i].name, i,
"nvidia", "NVIDIA Corporation");
}
free_devices(pDevices);
/* create adjacencies for the layout */
create_adjacencies(op, config, layout);
return TRUE;
} /* enable_all_gpus() */
/*
* free_unused_devices() - free unused device sections
*/
static void free_unused_devices(Options *op, XConfigPtr config)
{
XConfigDevicePtr device, prev, next;
XConfigScreenPtr screen;
int found;
/* free any unused device sections */
device = config->devices;
prev = NULL;
while (device) {
found = FALSE;
for (screen = config->screens; screen; screen = screen->next) {
if (device == screen->device) {
found = TRUE;
break;
}
}
if (!found) {
if (prev) {
prev->next = device->next;
} else {
config->devices = device->next;
}
next = device->next;
device->next = NULL;
xconfigFreeDeviceList(&device);
device = next;
} else {
prev = device;
device = device->next;
}
}
} /* free_unused_devices() */
/*
* free_unused_monitors() - free unused monitor sections
*/
static void free_unused_monitors(Options *op, XConfigPtr config)
{
XConfigMonitorPtr monitor, prev, next;
XConfigScreenPtr screen;
int found;
/* free any unused monitor sections */
monitor = config->monitors;
prev = NULL;
while (monitor) {
found = FALSE;
for (screen = config->screens; screen; screen = screen->next) {
if (monitor == screen->monitor) {
found = TRUE;
break;
}
}
if (!found) {
if (prev) {
prev->next = monitor->next;
} else {
config->monitors = monitor->next;
}
next = monitor->next;
monitor->next = NULL;
xconfigFreeMonitorList(&monitor);
monitor = next;
} else {
prev = monitor;
monitor = monitor->next;
}
}
} /* free_unused_monitors() */
/*
* only_one_screen() - delete all screens after the first one
*/
static int only_one_screen(Options *op, XConfigPtr config,
XConfigLayoutPtr layout)
{
if (!config->screens) return FALSE;
/* free all existing X screens after the first */
xconfigFreeScreenList(&config->screens->next);
/* free all adjacencies */
xconfigFreeAdjacencyList(&layout->adjacencies);
/* add new adjacency */
create_adjacencies(op, config, layout);
/* removed unused device and monitor sections */
free_unused_devices(op, config);
free_unused_monitors(op, config);
return TRUE;
} /* only_one_screen() */