blob: 02182d3923fb50e4ccec54bf53b181ae6ef59b72 [file] [log] [blame]
Jakob Bornecrantz9042d722010-01-08 02:51:04 +00001/**************************************************************************
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
Jakob Bornecrantzd920fa92010-01-12 17:53:49 +000033#include "config.h"
Jakob Bornecrantz9042d722010-01-08 02:51:04 +000034#include <errno.h>
35#include <stdio.h>
Jakob Bornecrantzd920fa92010-01-12 17:53:49 +000036#include <stdlib.h>
Jakob Bornecrantz9042d722010-01-08 02:51:04 +000037#include <xf86drm.h>
Jakob Bornecrantzd920fa92010-01-12 17:53:49 +000038#include <string.h>
39#include <unistd.h>
40
Jakob Bornecrantz9042d722010-01-08 02:51:04 +000041#include <sys/stat.h>
42
43#include "internal.h"
44
Jakob Bornecrantzd920fa92010-01-12 17:53:49 +000045#define PATH_SIZE 512
46
47static int
48linux_name_from_sysfs(int fd, char **out)
49{
50 char path[PATH_SIZE+1] = ""; /* initialize to please valgrind */
51 char link[PATH_SIZE+1] = "";
52 struct stat buffer;
53 unsigned maj, min;
54 char* slash_name;
55 int ret;
56
57 /*
58 * Inside the sysfs directory for the device there is a symlink
59 * to the directory representing the driver module, that path
60 * happens to hold the name of the driver.
61 *
62 * So lets get the symlink for the drm device. Then read the link
63 * and filter out the last directory which happens to be the name
64 * of the driver, which we can use to load the correct interface.
65 *
66 * Thanks to Ray Strode of Plymouth for the code.
67 */
68
69 ret = fstat(fd, &buffer);
70 if (ret)
71 return ret;
72
73 if (!S_ISCHR(buffer.st_mode))
74 return -EINVAL;
75
76 maj = major(buffer.st_rdev);
77 min = minor(buffer.st_rdev);
78
79 snprintf(path, PATH_SIZE, "/sys/dev/char/%d:%d/device/driver", maj, min);
80
81 if (readlink(path, link, PATH_SIZE) < 0)
82 return -EINVAL;
83
84 /* link looks something like this: ../../../bus/pci/drivers/intel */
85 slash_name = strrchr(link, '/');
86 if (!slash_name)
87 return -EINVAL;
88
89 /* copy name and at the same time remove the slash */
90 *out = strdup(slash_name + 1);
91 return 0;
92}
93
94static int
95linux_from_sysfs(int fd, struct kms_driver **out)
96{
97 char *name;
98 int ret;
99
100 ret = linux_name_from_sysfs(fd, &name);
101 if (ret)
102 return ret;
103
104 if (!strcmp(name, "intel"))
105 ret = intel_create(fd, out);
106#ifdef HAVE_VMWGFX
107 else if (!strcmp(name, "vmwgfx"))
108 ret = vmwgfx_create(fd, out);
109#endif
Marcin Kościelnickid5a2e772010-02-27 16:02:25 +0000110#ifdef HAVE_NOUVEAU
111 else if (!strcmp(name, "nouveau"))
112 ret = nouveau_create(fd, out);
113#endif
Jakob Bornecrantzd920fa92010-01-12 17:53:49 +0000114 else
115 ret = -ENOSYS;
116
117 free(name);
118 return ret;
119}
120
121#if 0
Jakob Bornecrantz9042d722010-01-08 02:51:04 +0000122#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
123#include <libudev.h>
124
Jakob Bornecrantzd920fa92010-01-12 17:53:49 +0000125struct create_record
126{
127 unsigned vendor;
128 unsigned chip;
129 int (*func)(int fd, struct kms_driver **out);
130};
131
132static struct create_record table[] = {
133 { 0x8086, 0x2a42, intel_create }, /* i965 */
134#ifdef HAVE_VMWGFX
135 { 0x15ad, 0x0405, vmwgfx_create }, /* VMware vGPU */
136#endif
137 { 0, 0, NULL },
138};
139
140static int
141linux_get_pciid_from_fd(int fd, unsigned *vendor_id, unsigned *chip_id)
Jakob Bornecrantz9042d722010-01-08 02:51:04 +0000142{
143 struct udev *udev;
144 struct udev_device *device;
145 struct udev_device *parent;
146 const char *pci_id;
147 struct stat buffer;
148 int ret;
149
150 ret = fstat(fd, &buffer);
151 if (ret)
152 return -EINVAL;
153
154 if (!S_ISCHR(buffer.st_mode))
155 return -EINVAL;
156
157 udev = udev_new();
158 if (!udev)
159 return -ENOMEM;
160
161 device = udev_device_new_from_devnum(udev, 'c', buffer.st_rdev);
162 if (!device)
163 goto err_free_udev;
164
165 parent = udev_device_get_parent(device);
166 if (!parent)
167 goto err_free_device;
168
169 pci_id = udev_device_get_property_value(parent, "PCI_ID");
170 if (!pci_id)
171 goto err_free_device;
172
173 if (sscanf(pci_id, "%x:%x", vendor_id, chip_id) != 2)
174 goto err_free_device;
175
176 udev_device_unref(device);
177 udev_unref(udev);
178
179 return 0;
180
181err_free_device:
182 udev_device_unref(device);
183err_free_udev:
184 udev_unref(udev);
185 return -EINVAL;
186}
Jakob Bornecrantzd920fa92010-01-12 17:53:49 +0000187
188static int
189linux_from_udev(int fd, struct kms_driver **out)
190{
191 unsigned vendor_id, chip_id;
192 int ret, i;
193
194 ret = linux_get_pciid_from_fd(fd, &vendor_id, &chip_id);
195 if (ret)
196 return ret;
197
198 for (i = 0; table[i].func; i++)
199 if (table[i].vendor == vendor_id && table[i].chip == chip_id)
200 return table[i].func(fd, out);
201
202 return -ENOSYS;
203}
204#else
205static int
206linux_from_udev(int fd, struct kms_driver **out)
207{
208 return -ENOSYS;
209}
210#endif
211
212int
213linux_create(int fd, struct kms_driver **out)
214{
215 if (!linux_from_udev(fd, out))
216 return 0;
217
218 return linux_from_sysfs(fd, out);
219}