Back to index

libdrm  2.4.37
linux.c
Go to the documentation of this file.
00001 /**************************************************************************
00002  *
00003  * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
00004  * All Rights Reserved.
00005  *
00006  * Permission is hereby granted, free of charge, to any person obtaining a
00007  * copy of this software and associated documentation files (the
00008  * "Software"), to deal in the Software without restriction, including
00009  * without limitation the rights to use, copy, modify, merge, publish,
00010  * distribute, sub license, and/or sell copies of the Software, and to
00011  * permit persons to whom the Software is furnished to do so, subject to
00012  * the following conditions:
00013  *
00014  * The above copyright notice and this permission notice (including the
00015  * next paragraph) shall be included in all copies or substantial portions
00016  * of the Software.
00017  *
00018  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00019  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00020  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
00021  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
00022  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
00023  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
00024  * USE OR OTHER DEALINGS IN THE SOFTWARE.
00025  *
00026  **************************************************************************/
00027 /*
00028  * Thanks to krh and jcristau for the tips on
00029  * going from fd to pci id via fstat and udev.
00030  */
00031 
00032 
00033 #include "config.h"
00034 #include <errno.h>
00035 #include <stdio.h>
00036 #include <stdlib.h>
00037 #include <xf86drm.h>
00038 #include <string.h>
00039 #include <unistd.h>
00040 
00041 #include <sys/stat.h>
00042 
00043 #include "internal.h"
00044 
00045 #define PATH_SIZE 512
00046 
00047 static int
00048 linux_name_from_sysfs(int fd, char **out)
00049 {
00050        char path[PATH_SIZE+1] = ""; /* initialize to please valgrind */
00051        char link[PATH_SIZE+1] = "";
00052        struct stat buffer;
00053        unsigned maj, min;
00054        char* slash_name;
00055        int ret;
00056 
00057        /* 
00058         * Inside the sysfs directory for the device there is a symlink
00059         * to the directory representing the driver module, that path
00060         * happens to hold the name of the driver.
00061         *
00062         * So lets get the symlink for the drm device. Then read the link
00063         * and filter out the last directory which happens to be the name
00064         * of the driver, which we can use to load the correct interface.
00065         *
00066         * Thanks to Ray Strode of Plymouth for the code.
00067         */
00068 
00069        ret = fstat(fd, &buffer);
00070        if (ret)
00071               return ret;
00072 
00073        if (!S_ISCHR(buffer.st_mode))
00074               return -EINVAL;
00075 
00076        maj = major(buffer.st_rdev);
00077        min = minor(buffer.st_rdev);
00078 
00079        snprintf(path, PATH_SIZE, "/sys/dev/char/%d:%d/device/driver", maj, min);
00080 
00081        if (readlink(path, link, PATH_SIZE) < 0)
00082               return -EINVAL;
00083 
00084        /* link looks something like this: ../../../bus/pci/drivers/intel */
00085        slash_name = strrchr(link, '/');
00086        if (!slash_name)
00087               return -EINVAL;
00088 
00089        /* copy name and at the same time remove the slash */
00090        *out = strdup(slash_name + 1);
00091        return 0;
00092 }
00093 
00094 static int
00095 linux_from_sysfs(int fd, struct kms_driver **out)
00096 {
00097        char *name;
00098        int ret;
00099 
00100        ret = linux_name_from_sysfs(fd, &name);
00101        if (ret)
00102               return ret;
00103 
00104        if (!strcmp(name, "intel"))
00105               ret = intel_create(fd, out);
00106 #ifdef HAVE_VMWGFX
00107        else if (!strcmp(name, "vmwgfx"))
00108               ret = vmwgfx_create(fd, out);
00109 #endif
00110 #ifdef HAVE_NOUVEAU
00111        else if (!strcmp(name, "nouveau"))
00112               ret = nouveau_create(fd, out);
00113 #endif
00114 #ifdef HAVE_RADEON
00115        else if (!strcmp(name, "radeon"))
00116               ret = radeon_create(fd, out);
00117 #endif
00118        else
00119               ret = -ENOSYS;
00120 
00121        free(name);
00122        return ret;
00123 }
00124 
00125 #if 0
00126 #define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
00127 #include <libudev.h>
00128 
00129 struct create_record
00130 {
00131        unsigned vendor;
00132        unsigned chip;
00133        int (*func)(int fd, struct kms_driver **out);
00134 };
00135 
00136 static struct create_record table[] = {
00137        { 0x8086, 0x2a42, intel_create }, /* i965 */
00138 #ifdef HAVE_VMWGFX
00139        { 0x15ad, 0x0405, vmwgfx_create }, /* VMware vGPU */
00140 #endif
00141        { 0, 0, NULL },
00142 };
00143 
00144 static int
00145 linux_get_pciid_from_fd(int fd, unsigned *vendor_id, unsigned *chip_id)
00146 {
00147        struct udev *udev;
00148        struct udev_device *device;
00149        struct udev_device *parent;
00150        const char *pci_id;
00151        struct stat buffer;
00152        int ret;
00153 
00154        ret = fstat(fd, &buffer);
00155        if (ret)
00156               return -EINVAL;
00157 
00158        if (!S_ISCHR(buffer.st_mode))
00159               return -EINVAL;
00160 
00161        udev = udev_new();
00162        if (!udev)
00163               return -ENOMEM;
00164 
00165        device = udev_device_new_from_devnum(udev, 'c', buffer.st_rdev);
00166        if (!device)
00167               goto err_free_udev;
00168 
00169        parent = udev_device_get_parent(device);
00170        if (!parent)
00171               goto err_free_device;
00172 
00173        pci_id = udev_device_get_property_value(parent, "PCI_ID");
00174        if (!pci_id)
00175               goto err_free_device;
00176 
00177        if (sscanf(pci_id, "%x:%x", vendor_id, chip_id) != 2)
00178               goto err_free_device;
00179 
00180        udev_device_unref(device);
00181        udev_unref(udev);
00182 
00183        return 0;
00184 
00185 err_free_device:
00186        udev_device_unref(device);
00187 err_free_udev:
00188        udev_unref(udev);
00189        return -EINVAL;
00190 }
00191 
00192 static int
00193 linux_from_udev(int fd, struct kms_driver **out)
00194 {
00195        unsigned vendor_id, chip_id;
00196        int ret, i;
00197 
00198        ret = linux_get_pciid_from_fd(fd, &vendor_id, &chip_id);
00199        if (ret)
00200               return ret;
00201 
00202        for (i = 0; table[i].func; i++)
00203               if (table[i].vendor == vendor_id && table[i].chip == chip_id)
00204                      return table[i].func(fd, out);
00205 
00206        return -ENOSYS;
00207 }
00208 #else
00209 static int
00210 linux_from_udev(int fd, struct kms_driver **out)
00211 {
00212        return -ENOSYS;
00213 }
00214 #endif
00215 
00216 int
00217 linux_create(int fd, struct kms_driver **out)
00218 {
00219        if (!dumb_create(fd, out))
00220               return 0;
00221 
00222        if (!linux_from_udev(fd, out))
00223               return 0;
00224 
00225        return linux_from_sysfs(fd, out);
00226 }