1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25 /*
26 * Copyright 2017 Gordon W. Ross
27 */
28
29 /*
30 * Helper functions for sun_xf86drm.c to find DRM device nodes
31 * given a pair of device major/minor numbers.
32 *
33 * Some of this code was cribbed from the "prtconf" command.
34 */
35
36 /*
37 * Device major numbers on illumos and Solaris are dynamic.
38 *
39 * Device node naming is also different. See:
40 * uts/common/io/drm/drm_stub.c drm_get_minor()
41 * uts/common/io/drm/drm_sysfs.c drm_sysfs_device_add()
42 *
43 * Device "types" (encoded in some of the minor bits)
44 * Typical device setup with intel graphics:
45 *
46 * DRM_MINOR_LEGACY /devices/pci@0,0/display@2:drm0
47 * DRM_MINOR_CONTROL /devices/pci@0,0/display@2:controlD0
48 * DRM_MINOR_RENDER /devices/pci@0,0/display@2:renderD0
49 * DRM_MINOR_VGATEXT /devices/pci@0,0/display@2:gfx0
50 * DRM_MINOR_AGPMASTER /devices/pci@0,0/display@2:agpmaster0
51 *
52 * The "devlinks" system also makes links under /dev
53 * which normally look something like this:
54 *
55 * /dev/fbs/gfx0 -> .../display@2:gfx0
56 * /dev/fb -> .../display@2:gfx0
57 * /dev/fb0 -> fbs/text-0
58 * /dev/fb1 -> fbs/gfx0
59 * /dev/agp/agpmaster0 -> .../display@2:agpmaster0
60 * /dev/dri/card1 -> .../display@2:controlD0
61 *
62 * (i916 driver) /devices/pci@0,0/pci1179,1@0:agptarget
63 * /dev/agp/agptarget0 -> /devices/pci@0,0/pci1179,1@0:agptarget
64 *
65 * (agpgart driver) /devices/agpgart:agpgart
66 * /dev/agpgart -> /devices/agpgart:agpgart
67 */
68
69 #include <string.h>
70 #include <fcntl.h>
71 #include <fnmatch.h>
72 #include <libdevinfo.h>
73 #include <sys/stat.h>
74 #include <sys/ioctl.h>
75 #include <sys/mkdev.h>
76
77 #include "xf86drm.h"
78
79 struct search_args {
80 char *s_path;
81 int s_minor;
82 };
83
84 static int _sun_drm_major; /* cache */
85
86 /*
87 * Search callback function called for each minor node under
88 * some device that was found to be of possible interest.
89 * Return non-zero if match found.
90 */
91 static int
find_minor(di_node_t node,di_minor_t minor,struct search_args * sargs)92 find_minor(di_node_t node, di_minor_t minor, struct search_args *sargs)
93 {
94 char *path;
95 dev_t devt;
96 int ret;
97
98 devt = di_minor_devt(minor);
99
100 /* Does the caller want a specific minor number? */
101 if (sargs->s_minor >= 0 &&
102 sargs->s_minor != minor(devt))
103 return (0);
104
105 /*
106 * get device minor node path
107 * Note: allocates path
108 */
109 if ((path = di_devfs_minor_path(minor)) == NULL)
110 return (0);
111 ret = asprintf(&sargs->s_path, "/devices%s", path);
112 di_devfs_path_free(path);
113
114 if (ret < 0) {
115 free(sargs->s_path);
116 return (0);
117 }
118
119 return (1);
120 }
121
122 /*
123 * Call back function for di_walk_node, called for every device node.
124 */
125 static int
find_dev(di_node_t node,void * vargs)126 find_dev(di_node_t node, void *vargs)
127 {
128 struct search_args *sargs = vargs;
129 const char *node_name;
130 di_minor_t minor_node;
131
132 node_name = di_node_name(node);
133
134 if (strcmp(node_name, "pseudo") == 0)
135 return (DI_WALK_PRUNECHILD);
136
137 /*
138 * Had: udev_enumerate_add_match_subsystem(e, "drm");
139 * so skip anything outside "drm".
140 * For illumos or Solaris, I think we want to skip
141 * anything that's not named "display".
142 */
143 if (strcmp(node_name, "display") != 0)
144 return (DI_WALK_CONTINUE);
145
146 /*
147 * Walk the minor node paths searching...
148 */
149 minor_node = DI_MINOR_NIL;
150 while ((minor_node = di_minor_next(node, minor_node)) != DI_MINOR_NIL) {
151 if (find_minor(node, minor_node, sargs)) {
152 /* Found it! */
153 return (DI_WALK_TERMINATE);
154 }
155 }
156
157 return (DI_WALK_CONTINUE);
158 }
159
160 /*
161 * Helper function for xf86drm.c
162 * drmGetMinorNameForFD()
163 * drmParseSubsystemType()
164 * drmParsePciBusInfo()
165 * drmParsePciDeviceInfo()
166 *
167 * Given a device minor number, find the /devices path.
168 * Returns malloc'ed memory at *pathp, caller frees.
169 */
170 int
_sun_drm_find_device(int min,char ** pathp)171 _sun_drm_find_device(int min, char **pathp)
172 {
173 struct search_args sargs;
174 di_node_t root_node;
175
176 root_node = di_init("/", DINFOCPYALL);
177 if (root_node == DI_NODE_NIL)
178 return (-errno);
179
180 memset(&sargs, 0, sizeof (sargs));
181
182 di_walk_node(root_node, DI_WALK_CLDFIRST, &sargs, find_dev);
183 di_fini(root_node);
184
185 if (sargs.s_path == NULL)
186 return (-ENOENT);
187
188 if (pathp != NULL)
189 *pathp = sargs.s_path;
190 else
191 free(sargs.s_path);
192
193 return (0);
194 }
195
196 /*
197 * Helper function for DRM_MAJOR in xf86drm.c
198 * Return the major number assigned to the drm driver.
199 */
200 int
_sun_drm_get_major(void)201 _sun_drm_get_major(void)
202 {
203 struct stat sbuf;
204 dev_t dev = 0;
205 char *path;
206 int i, ret;
207
208 if (_sun_drm_major != 0)
209 return (_sun_drm_major);
210
211 for (i = 0; i < DRM_MAX_MINOR; i++) {
212 ret = _sun_drm_find_device(i, &path);
213 if (ret != 0)
214 continue;
215 ret = stat(path, &sbuf);
216 free(path);
217 if (ret != 0)
218 continue;
219 if (!S_ISCHR(sbuf.st_mode))
220 continue;
221 dev = major(sbuf.st_rdev);
222 if (dev != 0) {
223 _sun_drm_major = dev;
224 return (dev);
225 }
226 }
227
228 /*
229 * No devices found? No way to return errors here,
230 * so just return an impossible value, and let
231 * later calls like open fail.
232 */
233 return (MAXMAJ32);
234 }
235
236 /*
237 * Helper function for drmParseSubsystemType()
238 * Returns one of: DRM_BUS_PCI, ... or -EINVAL.
239 * Our only driver implementations currently
240 * are on PCI. Others todo.
241 */
242 int
_sun_drm_get_subsystem(char * path)243 _sun_drm_get_subsystem(char *path)
244 {
245 char *p;
246 int err;
247
248 p = path;
249 if (strncmp(p, "/devices/", 9) == 0)
250 p += 8;
251 if (strncmp(p, "/pci", 4) == 0)
252 err = DRM_BUS_PCI;
253 else
254 err = -EINVAL;
255
256 return (err);
257 }
258
259 /*
260 * Helper function for drmParsePciBusInfo()
261 *
262 * Get PCI bus info for the give device path.
263 */
264 int
_sun_drm_get_pci_bus_info(char * path,drmPciBusInfo * info)265 _sun_drm_get_pci_bus_info(char *path, drmPciBusInfo *info)
266 {
267 int n, bus, slot, unit;
268
269 /* Skip the /devices prefix, if present. */
270 if (strncmp(path, "/devices/", 9) == 0)
271 path += 8; /* the next slash */
272
273 n = sscanf(path, "/pci@%d,%d/display@%d:",
274 &bus, &slot, &unit);
275 if (n != 3)
276 return (-EINVAL);
277
278 info->domain = 0;
279 info->bus = bus;
280 info->dev = slot;
281 info->func = unit;
282
283 return (0);
284 }
285
286 /*
287 * Helper function for drmParsePciDeviceInfo()
288 *
289 * Get PCI data for the give device path.
290 *
291 * Note path given is a full minor under /devices i.e.
292 * /devices/pci@0,0/display@2:drm
293 * and libdevinfo wants just:
294 * /pci@0,0/display@2
295 */
296 int
_sun_drm_get_pci_dev_info(char * path,drmPciDeviceInfo * pcii)297 _sun_drm_get_pci_dev_info(char *path, drmPciDeviceInfo *pcii)
298 {
299 char pathbuf[MAXPATHLEN];
300 di_node_t node;
301 char *s;
302 int *propval = NULL;
303 int ret = -EINVAL;
304
305 /* Skip the /devices prefix, if present. */
306 if (strncmp(path, "/devices/", 9) == 0)
307 path += 8; /* the next slash */
308 strlcpy(pathbuf, path, sizeof (pathbuf));
309
310 /* Strip :drm0 or whatever */
311 if ((s = strrchr(pathbuf, ':')) != NULL)
312 *s = '\0';
313
314 /*
315 * Ask libdevinfo about this device
316 */
317 node = di_init(pathbuf, DINFOCPYALL);
318 if (node == DI_NODE_NIL)
319 return (-EINVAL);
320
321 /*
322 * Get the various PCI properties.
323 * Only the first two are required.
324 */
325 memset(pcii, 0, sizeof (*pcii));
326 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
327 "vendor-id", &propval) > 0)
328 pcii->vendor_id = (uint16_t)*propval;
329 else
330 goto out;
331
332 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
333 "device-id", &propval) > 0)
334 pcii->device_id = (uint16_t)*propval;
335 else
336 goto out;
337
338 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
339 "subsystem-vendor-id", &propval) > 0)
340 pcii->subvendor_id = (uint16_t)*propval;
341
342 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
343 "subsystem-id", &propval) > 0)
344 pcii->subdevice_id = (uint16_t)*propval;
345
346 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
347 "revision-id", &propval) > 0)
348 pcii->revision_id = (uint16_t)*propval;
349
350 ret = 0;
351 out:
352 di_fini(node);
353 return (ret);
354 }
355