mirror of
git://nv-tegra.nvidia.com/tegra/gfx-src/nv-xconfig.git
synced 2025-12-22 09:22:53 +03:00
22a5c807291792a264b5acca7ff6d199468890b8 - nvidia-xconfig-35.6.2/Makefile 4cc77b90af91e615a64ae04893fdffa7939db84c - nvidia-xconfig-35.6.2/COPYING f14e9577e537037d4778d490ef70f13b0e94a70a - nvidia-xconfig-35.6.2/option_table.h 647e7896275a1113ec99352d573823a05001f57e - nvidia-xconfig-35.6.2/lscf.c 1e39095238886dfc6a69f7ec750e10c05a93e7d1 - nvidia-xconfig-35.6.2/nvidia-xconfig.1.m4 a5ad539267b73169480e7898b284823639c4db3a - nvidia-xconfig-35.6.2/tree.c b3d0f9f27c4d9cb7940d04e1dd387d357a16024c - nvidia-xconfig-35.6.2/nvidia-xconfig.c 46f2ea329459b0b16cb36cc14cec0de737d0963e - nvidia-xconfig-35.6.2/options.c 6f6f68f976250f2646450e571f9ab2d7eee1ac80 - nvidia-xconfig-35.6.2/nvidia-cfg.h 913b088cb559f31e09a1baf89d85d75cb43079b4 - nvidia-xconfig-35.6.2/nvidia-xconfig.h bfa37f78ba458f14a1865bbf3f67eb84987a7e1c - nvidia-xconfig-35.6.2/extract_edids.c d37da5a118e0418b267a48d05fd144611316b42c - nvidia-xconfig-35.6.2/gen-manpage-opts.c 41bcac41393c9a465c30c07cf1ab386647a16687 - nvidia-xconfig-35.6.2/util.c 9b0c02164398be2795fea7cb13c9e1b062d24bfb - nvidia-xconfig-35.6.2/query_gpu_info.c c946eefad99cec9366d8abbd4e45c5d138d3b7be - nvidia-xconfig-35.6.2/make_usable.c 10edc2c301fd331439b38d001c0df41669c238b2 - nvidia-xconfig-35.6.2/multiple_screens.c c7b8d8ca0f7c9dcc2cdca4f8e77d1122c71892ef - nvidia-xconfig-35.6.2/XF86Config-parser/Read.c a9bc06f33bf525c2b08be2dc3cd64a59c4c7946d - nvidia-xconfig-35.6.2/XF86Config-parser/Configint.h 2022a4a3c2a1b23a67ae74f50a3849f5f3a45e4b - nvidia-xconfig-35.6.2/XF86Config-parser/Keyboard.c 83b0a8efd6a508db54995688ab353591bdb242a2 - nvidia-xconfig-35.6.2/XF86Config-parser/Flags.c e67d630ef396ab7d34524c333f3a77fc42ba8fc6 - nvidia-xconfig-35.6.2/XF86Config-parser/configProcs.h f3d611bdbddfa64675a0810ef81dada57e224bcd - nvidia-xconfig-35.6.2/XF86Config-parser/Extensions.c 5ffb2caa5077a2e6ec1c5ece807e71503fb2fbce - nvidia-xconfig-35.6.2/XF86Config-parser/Module.c 7344be997921dec57959691e986763ee686888d5 - nvidia-xconfig-35.6.2/XF86Config-parser/Device.c 33211ca0a10f50e2c87b9e8feef6c1ab381b57a1 - nvidia-xconfig-35.6.2/XF86Config-parser/DRI.c 953b945f3b117d6fb44f1f738af17b6380a9ec72 - nvidia-xconfig-35.6.2/XF86Config-parser/Monitor.c 6c210ad0eaee1db3fec48ff01746cb054b4f9aaa - nvidia-xconfig-35.6.2/XF86Config-parser/Video.c 4407207cf890539dc604cff5b834c994b307729a - nvidia-xconfig-35.6.2/XF86Config-parser/Write.c de1c758e29f217e1a99e4c076d54ac84bce98b18 - nvidia-xconfig-35.6.2/XF86Config-parser/Screen.c c66a0a141e25e31b568fb9df41f17d7fb9e6d3b9 - nvidia-xconfig-35.6.2/XF86Config-parser/xf86Parser.h 9cbc29da282aa957f28b7fc83caf1e3b19ee2a52 - nvidia-xconfig-35.6.2/XF86Config-parser/Vendor.c 18711ff932af2202869a30f3b32d6d7fe7811c84 - nvidia-xconfig-35.6.2/XF86Config-parser/Layout.c cda3a4ab05bf48ba28af35b5c4c632e968afc7fa - nvidia-xconfig-35.6.2/XF86Config-parser/Util.c ec19d673a6a7d1d8f855f2d32d3e8f63046c3625 - nvidia-xconfig-35.6.2/XF86Config-parser/Files.c ac878b26ded86a3c502a6a81fc4c4a96162afefb - nvidia-xconfig-35.6.2/XF86Config-parser/Merge.c 0a274c4bc54b6ae0f6d009e443bda0cb033d66b5 - nvidia-xconfig-35.6.2/XF86Config-parser/Pointer.c 3f2238c88d737bd329a9bca1ac4b0bcf77bb9ac2 - nvidia-xconfig-35.6.2/XF86Config-parser/xf86tokens.h 45ceb0129668346ae3e52d81bb6e2f97efadf9d0 - nvidia-xconfig-35.6.2/XF86Config-parser/Input.c 4d9b03ea3badceb6bdfdf6589e6731140ec44079 - nvidia-xconfig-35.6.2/XF86Config-parser/Generate.c 04efe162cf6d97882f2bb87f3712d9a65c2320f3 - nvidia-xconfig-35.6.2/XF86Config-parser/Scan.c 5d55b94375c2055cfa1578e4c7f34c90e63a33f7 - nvidia-xconfig-35.6.2/common-utils/gen-manpage-opts-helper.c 384e36102dcd08ed4c5de05b5e3b8a7cdb2e257d - nvidia-xconfig-35.6.2/common-utils/nvgetopt.c dc2678d8a9d794a4b2b2718fce57ec087f21f54b - nvidia-xconfig-35.6.2/common-utils/common-utils.h aac5cbf0e68e1da4e646c316a7c0e620304ee16b - nvidia-xconfig-35.6.2/common-utils/msg.c e572cac43202f6c2af1c80b9e3901215126093ed - nvidia-xconfig-35.6.2/common-utils/nvgetopt.h 8a346196b052cfb1e06dd83d2ad1fe71e928d2b3 - nvidia-xconfig-35.6.2/common-utils/common-utils.c 1654638c567bc7f2bd70b54d807b498ab14c1061 - nvidia-xconfig-35.6.2/common-utils/nvpci-utils.h 8259a24058c714629f9819cc8c830ea9b202bb27 - nvidia-xconfig-35.6.2/common-utils/gen-manpage-opts-helper.h 524990f5497f9bbeb3d148b5e7dc8d2267c3163d - nvidia-xconfig-35.6.2/common-utils/nvpci-utils.c abb5c1b445d9353f2d2840bda848cd16109710a4 - nvidia-xconfig-35.6.2/common-utils/msg.h Change-Id: I99682b72c725a16fef77ba060c57fd78b05ae428
1092 lines
31 KiB
C
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() */
|
|
|