• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**************************************************************************
2  *
3  * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
22  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24  * USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27 /*
28  * Thanks to krh and jcristau for the tips on
29  * going from fd to pci id via fstat and udev.
30  */
31 
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 #include <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <xf86drm.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include <sys/stat.h>
44 
45 #include "internal.h"
46 
47 #define PATH_SIZE 512
48 
49 static int
linux_name_from_sysfs(int fd,char ** out)50 linux_name_from_sysfs(int fd, char **out)
51 {
52 	char path[PATH_SIZE+1] = ""; /* initialize to please valgrind */
53 	char link[PATH_SIZE+1] = "";
54 	struct stat buffer;
55 	unsigned maj, min;
56 	char* slash_name;
57 	int ret;
58 
59 	/*
60 	 * Inside the sysfs directory for the device there is a symlink
61 	 * to the directory representing the driver module, that path
62 	 * happens to hold the name of the driver.
63 	 *
64 	 * So lets get the symlink for the drm device. Then read the link
65 	 * and filter out the last directory which happens to be the name
66 	 * of the driver, which we can use to load the correct interface.
67 	 *
68 	 * Thanks to Ray Strode of Plymouth for the code.
69 	 */
70 
71 	ret = fstat(fd, &buffer);
72 	if (ret)
73 		return -EINVAL;
74 
75 	if (!S_ISCHR(buffer.st_mode))
76 		return -EINVAL;
77 
78 	maj = major(buffer.st_rdev);
79 	min = minor(buffer.st_rdev);
80 
81 	snprintf(path, PATH_SIZE, "/sys/dev/char/%d:%d/device/driver", maj, min);
82 
83 	if (readlink(path, link, PATH_SIZE) < 0)
84 		return -EINVAL;
85 
86 	/* link looks something like this: ../../../bus/pci/drivers/intel */
87 	slash_name = strrchr(link, '/');
88 	if (!slash_name)
89 		return -EINVAL;
90 
91 	/* copy name and at the same time remove the slash */
92 	*out = strdup(slash_name + 1);
93 	return 0;
94 }
95 
96 static int
linux_from_sysfs(int fd,struct kms_driver ** out)97 linux_from_sysfs(int fd, struct kms_driver **out)
98 {
99 	char *name;
100 	int ret;
101 
102 	ret = linux_name_from_sysfs(fd, &name);
103 	if (ret)
104 		return ret;
105 
106 #ifdef HAVE_INTEL
107 	if (!strcmp(name, "intel"))
108 		ret = intel_create(fd, out);
109 	else
110 #endif
111 #ifdef HAVE_VMWGFX
112 	if (!strcmp(name, "vmwgfx"))
113 		ret = vmwgfx_create(fd, out);
114 	else
115 #endif
116 #ifdef HAVE_NOUVEAU
117 	if (!strcmp(name, "nouveau"))
118 		ret = nouveau_create(fd, out);
119 	else
120 #endif
121 #ifdef HAVE_RADEON
122 	if (!strcmp(name, "radeon"))
123 		ret = radeon_create(fd, out);
124 	else
125 #endif
126 #ifdef HAVE_EXYNOS
127 	if (!strcmp(name, "exynos"))
128 		ret = exynos_create(fd, out);
129 	else
130 #endif
131 		ret = -ENOSYS;
132 
133 	free(name);
134 	return ret;
135 }
136 
137 #if 0
138 #define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
139 #include <libudev.h>
140 
141 struct create_record
142 {
143 	unsigned vendor;
144 	unsigned chip;
145 	int (*func)(int fd, struct kms_driver **out);
146 };
147 
148 static struct create_record table[] = {
149 	{ 0x8086, 0x2a42, intel_create }, /* i965 */
150 #ifdef HAVE_VMWGFX
151 	{ 0x15ad, 0x0405, vmwgfx_create }, /* VMware vGPU */
152 #endif
153 	{ 0, 0, NULL },
154 };
155 
156 static int
157 linux_get_pciid_from_fd(int fd, unsigned *vendor_id, unsigned *chip_id)
158 {
159 	struct udev *udev;
160 	struct udev_device *device;
161 	struct udev_device *parent;
162 	const char *pci_id;
163 	struct stat buffer;
164 	int ret;
165 
166 	ret = fstat(fd, &buffer);
167 	if (ret)
168 		return -EINVAL;
169 
170 	if (!S_ISCHR(buffer.st_mode))
171 		return -EINVAL;
172 
173 	udev = udev_new();
174 	if (!udev)
175 		return -ENOMEM;
176 
177 	device = udev_device_new_from_devnum(udev, 'c', buffer.st_rdev);
178 	if (!device)
179 		goto err_free_udev;
180 
181 	parent = udev_device_get_parent(device);
182 	if (!parent)
183 		goto err_free_device;
184 
185 	pci_id = udev_device_get_property_value(parent, "PCI_ID");
186 	if (!pci_id)
187 		goto err_free_device;
188 
189 	if (sscanf(pci_id, "%x:%x", vendor_id, chip_id) != 2)
190 		goto err_free_device;
191 
192 	udev_device_unref(device);
193 	udev_unref(udev);
194 
195 	return 0;
196 
197 err_free_device:
198 	udev_device_unref(device);
199 err_free_udev:
200 	udev_unref(udev);
201 	return -EINVAL;
202 }
203 
204 static int
205 linux_from_udev(int fd, struct kms_driver **out)
206 {
207 	unsigned vendor_id, chip_id;
208 	int ret, i;
209 
210 	ret = linux_get_pciid_from_fd(fd, &vendor_id, &chip_id);
211 	if (ret)
212 		return ret;
213 
214 	for (i = 0; table[i].func; i++)
215 		if (table[i].vendor == vendor_id && table[i].chip == chip_id)
216 			return table[i].func(fd, out);
217 
218 	return -ENOSYS;
219 }
220 #else
221 static int
linux_from_udev(int fd,struct kms_driver ** out)222 linux_from_udev(int fd, struct kms_driver **out)
223 {
224 	return -ENOSYS;
225 }
226 #endif
227 
228 int
linux_create(int fd,struct kms_driver ** out)229 linux_create(int fd, struct kms_driver **out)
230 {
231 	if (!dumb_create(fd, out))
232 		return 0;
233 
234 	if (!linux_from_udev(fd, out))
235 		return 0;
236 
237 	return linux_from_sysfs(fd, out);
238 }
239