1 /*
2 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
3 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
4 * Copyright(c) 2019-2023, Intel Corporation
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files(the "Software"),
8 *to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 *and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions :
12 *
13 * The above copyright notice and this permission notice(including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL
20 * PRECISION INSIGHT AND / OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 *ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 */
25
26 #ifndef DRM_DEVICE_H_
27 #define DRM_DEVICE_H_
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdbool.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <ctype.h>
35 #include <dirent.h>
36 #include <stddef.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <limits.h>
40 #include <signal.h>
41 #include <time.h>
42 #include <sys/sysmacros.h> //<sys/types.h>
43 #include <sys/stat.h>
44 #define stat_t struct stat
45 #include <sys/ioctl.h>
46 #include <sys/time.h>
47 #include <stdarg.h>
48 #ifdef MAJOR_IN_MKDEV
49 #include <sys/mkdev.h>
50 #endif
51 #ifdef MAJOR_IN_SYSMACROS
52 #include <sys/sysmacros.h>
53 #endif
54 #include <math.h>
55 #include <string>
56 #include <cstring>
57
58 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
59
60 /* Not all systems have MAP_FAILED defined */
61 #ifndef MAP_FAILED
62 #define MAP_FAILED ((void *)-1)
63 #endif
64
65 #define DRM_DEV_UID 0
66 #define DRM_DEV_GID 0
67 /* Default /dev/dri directory permissions 0755 */
68 #define DRM_DEV_DIRMODE (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
69 #define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)
70 #define DRM_DEVICE_GET_PCI_REVISION (1 << 0)
71
72 #ifdef __OpenBSD__
73 #define DRM_DIR_NAME "/dev"
74 #define DRM_DEV_NAME "%s/drm%d"
75 #define DRM_CONTROL_DEV_NAME "%s/drmC%d"
76 #define DRM_RENDER_DEV_NAME "%s/drmR%d"
77 #else
78 #define DRM_DIR_NAME "/dev/dri"
79 #define DRM_DEV_NAME "%s/card%d"
80 #define DRM_CONTROL_DEV_NAME "%s/controlD%d"
81 #define DRM_RENDER_DEV_NAME "%s/renderD%d"
82 #define DRM_PROC_NAME "/proc/dri/" /* For backward Linux compatibility */
83 #endif
84
85 #ifdef __OpenBSD__
86 #define DRM_PRIMARY_MINOR_NAME "drm"
87 #define DRM_CONTROL_MINOR_NAME "drmC"
88 #define DRM_RENDER_MINOR_NAME "drmR"
89 #else
90 #define DRM_PRIMARY_MINOR_NAME "card"
91 #define DRM_CONTROL_MINOR_NAME "controlD"
92 #define DRM_RENDER_MINOR_NAME "renderD"
93 #endif
94
95 #define DRM_ERR_NO_DEVICE (-1001)
96 #define DRM_ERR_NO_ACCESS (-1002)
97 #define DRM_ERR_NOT_ROOT (-1003)
98 #define DRM_ERR_INVALID (-1004)
99 #define DRM_ERR_NO_FD (-1005)
100
101 #define DRM_AGP_NO_HANDLE 0
102
103 #define DRM_BUS_PCI 0
104 #define DRM_BUS_USB 1
105 #define DRM_BUS_PLATFORM 2
106 #define DRM_BUS_HOST1X 3
107 #define DRM_BUS_VIRTIO 0x10
108
109 #define DRM_NODE_PRIMARY 0
110 #define DRM_NODE_CONTROL 1
111 #define DRM_NODE_RENDER 2
112 #define DRM_NODE_MAX 3
113
114 typedef unsigned int drmSize, *drmSizePtr; /**< For mapped regions */
115 typedef void *drmAddress, **drmAddressPtr; /**< For mapped regions */
116
117 #if (__GNUC__ >= 3)
118 #define DRM_PRINTFLIKE(f, a) __attribute__ ((format(__printf__, f, a)))
119 #else
120 #define DRM_PRINTFLIKE(f, a)
121 #endif
122
123 #define MIN2( A, B ) ( (A)<(B) ? (A) : (B) )
124 #define MAX2( A, B ) ( (A)>(B) ? (A) : (B) )
125 #define MAX3( A, B, C ) ((A) > (B) ? MAX2(A, C) : MAX2(B, C))
126
127 #define __align_mask(value, mask) (((value) + (mask)) & ~(mask))
128 #define ALIGN_CEIL(value, alignment) __align_mask(value, (__typeof__(value))((alignment) - 1))
129 #define DRM_PLATFORM_DEVICE_NAME_LEN 512
130
131 typedef struct _drmPciBusInfo {
132 uint16_t domain;
133 uint8_t bus;
134 uint8_t dev;
135 uint8_t func;
136 } drmPciBusInfo, *drmPciBusInfoPtr;
137
138 typedef struct _drmPciDeviceInfo {
139 uint32_t vendor_id;
140 uint32_t device_id;
141 uint32_t subvendor_id;
142 uint32_t subdevice_id;
143 uint32_t revision_id;
144 char driverInfo[1024];
145 uint64_t videoMem[4] = {};
146 uint64_t systemMem[4] = {};
147 uint64_t sharedMem[4] = {};
148 uint64_t ioportMem[4] = {};
149 uint64_t ioMem[4] = {};
150 } drmPciDeviceInfo, *drmPciDeviceInfoPtr;
151
152
153 typedef struct _drmUsbBusInfo {
154 uint8_t bus;
155 uint8_t dev;
156 } drmUsbBusInfo, *drmUsbBusInfoPtr;
157
158 typedef struct _drmUsbDeviceInfo {
159 uint16_t vendor;
160 uint16_t product;
161 } drmUsbDeviceInfo, *drmUsbDeviceInfoPtr;
162
163
164
165 typedef struct _drmPlatformBusInfo {
166 char fullname[DRM_PLATFORM_DEVICE_NAME_LEN + 1];
167 } drmPlatformBusInfo, *drmPlatformBusInfoPtr;
168
169 typedef struct _drmPlatformDeviceInfo {
170 char **compatible; /* NULL terminated list of compatible strings */
171 } drmPlatformDeviceInfo, *drmPlatformDeviceInfoPtr;
172
173 #define DRM_HOST1X_DEVICE_NAME_LEN 512
174
175 typedef struct _drmHost1xBusInfo {
176 char fullname[DRM_HOST1X_DEVICE_NAME_LEN + 1];
177 } drmHost1xBusInfo, *drmHost1xBusInfoPtr;
178
179 typedef struct _drmHost1xDeviceInfo {
180 char **compatible; /* NULL terminated list of compatible strings */
181 } drmHost1xDeviceInfo, *drmHost1xDeviceInfoPtr;
182
183 typedef struct _drmDevice {
184 char **nodes; /* DRM_NODE_MAX sized array */
185 int available_nodes; /* DRM_NODE_* bitmask */
186 int bustype;
187 union {
188 drmPciBusInfoPtr pci;
189 drmUsbBusInfoPtr usb;
190 drmPlatformBusInfoPtr platform;
191 drmHost1xBusInfoPtr host1x;
192 } businfo;
193 union {
194 drmPciDeviceInfoPtr pci;
195 drmUsbDeviceInfoPtr usb;
196 drmPlatformDeviceInfoPtr platform;
197 drmHost1xDeviceInfoPtr host1x;
198 } deviceinfo;
199
200 unsigned int MaxThread = 0;
201 unsigned int EuNumber = 0;
202 unsigned int TileNumber = 0;
203 unsigned int reserved[16];
204 } drmDevice, *drmDevicePtr;
205
206 /*
207 * The kernel drm core has a number of places that assume maximum of
208 * 3x64 devices nodes. That's 64 for each of primary, control and
209 * render nodes. Rounded it up to 256 for simplicity.
210 */
211 #define MAX_DRM_NODES 256
212
memcpy_s(void * dst,size_t numberOfElements,const void * src,size_t count)213 inline int memcpy_s(void *dst, size_t numberOfElements, const void *src, size_t count)
214 {
215 if ((dst == nullptr) || (src == nullptr))
216 {
217 return EINVAL;
218 }
219 if (numberOfElements < count)
220 {
221 return ERANGE;
222 }
223 std::memcpy(dst, src, count);
224 return 0;
225 }
226
227 /* Check that the given flags are valid returning 0 on success */
228 static int
drm_device_validate_flags(uint32_t flags)229 drm_device_validate_flags(uint32_t flags)
230 {
231 return (flags & ~DRM_DEVICE_GET_PCI_REVISION);
232 }
233
234 static bool
drm_device_has_rdev(drmDevicePtr device,dev_t find_rdev)235 drm_device_has_rdev(drmDevicePtr device, dev_t find_rdev)
236 {
237 struct stat sbuf;
238
239 for (int i = 0; i < DRM_NODE_MAX; i++) {
240 if (device->available_nodes & 1 << i) {
241 if (stat(device->nodes[i], &sbuf) == 0 &&
242 sbuf.st_rdev == find_rdev)
243 return true;
244 }
245 }
246 return false;
247 }
248
drmGetMaxNodeName(void)249 static int drmGetMaxNodeName(void)
250 {
251 return sizeof(DRM_DIR_NAME) +
252 MAX3(sizeof(DRM_PRIMARY_MINOR_NAME),
253 sizeof(DRM_CONTROL_MINOR_NAME),
254 sizeof(DRM_RENDER_MINOR_NAME)) +
255 3 /* length of the node number */;
256 }
257
drmNodeIsDRM(int maj,int min)258 static bool drmNodeIsDRM(int maj, int min)
259 {
260 #ifdef __linux__
261 char path[64];
262 struct stat sbuf;
263
264 snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device/drm",
265 maj, min);
266 return stat(path, &sbuf) == 0;
267 #else
268 return maj == DRM_MAJOR;
269 #endif
270 }
271
drmDevicesEqual(drmDevicePtr a,drmDevicePtr b)272 static int drmDevicesEqual(drmDevicePtr a, drmDevicePtr b)
273 {
274 if (a == NULL || b == NULL)
275 return 0;
276
277 if (a->bustype != b->bustype)
278 return 0;
279
280 switch (a->bustype) {
281 case DRM_BUS_PCI:
282 return memcmp(a->businfo.pci, b->businfo.pci, sizeof(drmPciBusInfo)) == 0;
283
284 case DRM_BUS_USB:
285 return memcmp(a->businfo.usb, b->businfo.usb, sizeof(drmUsbBusInfo)) == 0;
286
287 case DRM_BUS_PLATFORM:
288 return memcmp(a->businfo.platform, b->businfo.platform, sizeof(drmPlatformBusInfo)) == 0;
289
290 case DRM_BUS_HOST1X:
291 return memcmp(a->businfo.host1x, b->businfo.host1x, sizeof(drmHost1xBusInfo)) == 0;
292
293 default:
294 break;
295 }
296 return 0;
297 }
298
drmDeviceAlloc(unsigned int type,const char * node,size_t bus_size,size_t device_size,char ** ptrp)299 static drmDevicePtr drmDeviceAlloc(unsigned int type, const char *node,
300 size_t bus_size, size_t device_size,
301 char **ptrp)
302 {
303 size_t max_node_length, extra, size;
304 drmDevicePtr device;
305 unsigned int i;
306 char *ptr;
307
308 max_node_length = ALIGN_CEIL(drmGetMaxNodeName(), sizeof(void *));
309
310 extra = DRM_NODE_MAX * (sizeof(void *) + max_node_length);
311
312 size = sizeof(*device) + extra + bus_size + device_size;
313
314 device = (drmDevicePtr)calloc(1, size);
315 if (!device)
316 return NULL;
317
318 device->available_nodes = 1 << type;
319
320 ptr = (char *)device + sizeof(*device);
321 device->nodes = (char **)ptr;
322
323 ptr += DRM_NODE_MAX * sizeof(void *);
324
325 for (i = 0; i < DRM_NODE_MAX; i++) {
326 device->nodes[i] = ptr;
327 ptr += max_node_length;
328 }
329
330 memcpy(device->nodes[type], node, max_node_length);
331 *ptrp = ptr;
332
333 return device;
334 }
335
336 static char * DRM_PRINTFLIKE(2, 3)
sysfs_uevent_get(const char * path,const char * fmt,...)337 sysfs_uevent_get(const char *path, const char *fmt, ...)
338 {
339 char filename[PATH_MAX + 1], *key, *line = NULL, *value = NULL;
340 size_t size = 0, len;
341 ssize_t num;
342 va_list ap;
343 FILE *fp;
344
345 va_start(ap, fmt);
346 num = vasprintf(&key, fmt, ap);
347 va_end(ap);
348 len = num;
349
350 snprintf(filename, sizeof(filename), "%s/uevent", path);
351
352 fp = fopen(filename, "r");
353 if (!fp) {
354 free(key);
355 return NULL;
356 }
357
358 while ((num = getline(&line, &size, fp)) >= 0) {
359 if ((strncmp(line, key, len) == 0) && (line[len] == '=')) {
360 char *start = line + len + 1, *end = line + num - 1;
361
362 if (*end != '\n')
363 end++;
364
365 value = strndup(start, end - start);
366 break;
367 }
368 }
369
370 free(line);
371 fclose(fp);
372
373 free(key);
374
375 return value;
376 }
377
drmParseHost1xDeviceInfo(int maj,int min,drmHost1xDeviceInfoPtr info)378 static int drmParseHost1xDeviceInfo(int maj,
379 int min,
380 drmHost1xDeviceInfoPtr info)
381 {
382 char path[PATH_MAX + 1];
383 int err = 0;
384
385 snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
386
387 char *value = sysfs_uevent_get(path, "OF_COMPATIBLE_N");
388 if (!value)
389 {
390 return -ENOENT;
391 }
392 unsigned int count = 0;
393 int scanned_value_count = sscanf(value, "%u", &count);
394 free(value);
395 if (scanned_value_count <= 0 || 0 == count)
396 {
397 return -ENOENT;
398 }
399
400 info->compatible = (char**)calloc(count + 1, sizeof(*info->compatible));
401 if (!info->compatible)
402 {
403 return -ENOMEM;
404 }
405
406 unsigned int i = 0;
407 for (; i < count; i++)
408 {
409 value = sysfs_uevent_get(path, "OF_COMPATIBLE_%u", i);
410 if (!value)
411 {
412 err = -ENOENT;
413 goto free;
414 }
415 info->compatible[i] = value;
416 }
417 return 0;
418
419 free:
420 while (i--)
421 {
422 free(info->compatible[i]);
423 }
424 free(info->compatible);
425 return err;
426 }
427
drmParseHost1xBusInfo(int maj,int min,drmHost1xBusInfoPtr info)428 static int drmParseHost1xBusInfo(int maj, int min, drmHost1xBusInfoPtr info)
429 {
430 char path[PATH_MAX + 1], *name;
431 snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
432 name = sysfs_uevent_get(path, "OF_FULLNAME");
433 if (!name)
434 return -ENOENT;
435 strncpy(info->fullname, name, DRM_HOST1X_DEVICE_NAME_LEN);
436 info->fullname[DRM_HOST1X_DEVICE_NAME_LEN - 1] = '\0';
437 free(name);
438 return 0;
439 }
440
drmProcessHost1xDevice(drmDevicePtr * device,const char * node,int node_type,int maj,int min,bool fetch_deviceinfo,uint32_t flags)441 static int drmProcessHost1xDevice(drmDevicePtr *device,
442 const char *node, int node_type,
443 int maj, int min, bool fetch_deviceinfo,
444 uint32_t flags)
445 {
446 drmDevicePtr dev;
447 char *ptr;
448 int ret;
449
450 dev = drmDeviceAlloc(node_type, node, sizeof(drmHost1xBusInfo),
451 sizeof(drmHost1xDeviceInfo), &ptr);
452 if (!dev)
453 return -ENOMEM;
454
455 dev->bustype = DRM_BUS_HOST1X;
456
457 dev->businfo.host1x = (drmHost1xBusInfoPtr)ptr;
458
459 ret = drmParseHost1xBusInfo(maj, min, dev->businfo.host1x);
460 if (ret < 0)
461 goto free_device;
462
463 if (fetch_deviceinfo) {
464 ptr += sizeof(drmHost1xBusInfo);
465 dev->deviceinfo.host1x = (drmHost1xDeviceInfoPtr)ptr;
466
467 ret = drmParseHost1xDeviceInfo(maj, min, dev->deviceinfo.host1x);
468 if (ret < 0)
469 goto free_device;
470 }
471
472 *device = dev;
473
474 return 0;
475
476 free_device:
477 free(dev);
478 return ret;
479 }
480
drmParsePlatformBusInfo(int maj,int min,drmPlatformBusInfoPtr info)481 static int drmParsePlatformBusInfo(int maj, int min, drmPlatformBusInfoPtr info)
482 {
483 char path[PATH_MAX + 1], *name;
484 snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
485 name = sysfs_uevent_get(path, "OF_FULLNAME");
486 if (!name)
487 return -ENOENT;
488 strncpy(info->fullname, name, DRM_PLATFORM_DEVICE_NAME_LEN);
489 info->fullname[DRM_PLATFORM_DEVICE_NAME_LEN - 1] = '\0';
490 free(name);
491 return 0;
492 }
493
drmParsePlatformDeviceInfo(int maj,int min,drmPlatformDeviceInfoPtr info)494 static int drmParsePlatformDeviceInfo(int maj, int min,
495 drmPlatformDeviceInfoPtr info)
496 {
497 char path[PATH_MAX + 1], *value;
498 unsigned int count, i;
499 int err;
500
501 snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
502 value = sysfs_uevent_get(path, "OF_COMPATIBLE_N");
503 if (!value)
504 return -ENOENT;
505
506 int scanned_value_count = sscanf(value, "%u", &count);
507 free(value);
508 if (scanned_value_count <= 0 || 0 == count)
509 {
510 return -ENOENT;
511 }
512
513 if (count <= MAX_DRM_NODES)
514 {
515 info->compatible = (char**)calloc(count + 1, sizeof(*info->compatible));
516 }else
517 return -ENOENT;
518
519 if (!info->compatible)
520 return -ENOMEM;
521 for (i = 0; i < count; i++) {
522 value = sysfs_uevent_get(path, "OF_COMPATIBLE_%u", i);
523 if (!value) {
524 err = -ENOENT;
525 goto free;
526 }
527 info->compatible[i] = value;
528 }
529 return 0;
530 free:
531 while (i--)
532 free(info->compatible[i]);
533 free(info->compatible);
534 return err;
535 }
536
537 static void
get_pci_path(int maj,int min,char * pci_path)538 get_pci_path(int maj, int min, char *pci_path)
539 {
540 char path[PATH_MAX + 1], *term;
541
542 snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
543 if (!realpath(path, pci_path)) {
544 strcpy(pci_path, path);
545 return;
546 }
547
548 term = strrchr(pci_path, '/');
549 if (term && strncmp(term, "/virtio", 7) == 0)
550 *term = 0;
551 }
552
parse_separate_sysfs_files(int maj,int min,drmPciDeviceInfoPtr device,bool ignore_revision)553 static int parse_separate_sysfs_files(int maj, int min,
554 drmPciDeviceInfoPtr device,
555 bool ignore_revision)
556 {
557 static const char *attrs[] = {
558 "revision", /* Older kernels are missing the file, so check for it first */
559 "vendor",
560 "device",
561 "subsystem_vendor",
562 "subsystem_device",
563 };
564 char path[PATH_MAX + 128], pci_path[PATH_MAX];
565 char resourcename[PATH_MAX + 64], driverpath[PATH_MAX + 64], drivername[PATH_MAX + 64], irqpath[PATH_MAX + 64];
566
567 unsigned int data[ARRAY_SIZE(attrs)];
568 FILE *fp;
569 int ret;
570 char *dev_path;
571 char *driver_name;
572
573 get_pci_path(maj, min, pci_path);
574 dev_path = strrchr(pci_path, '/');
575 dev_path++;
576
577 snprintf(driverpath, sizeof(driverpath), "%s/driver", pci_path);
578 snprintf(irqpath, sizeof(irqpath), "%s/irq", pci_path);
579 int fd = open(irqpath, O_RDONLY);
580
581 char buffer[512] ;
582 memset(buffer, 0, sizeof(buffer));
583 if (fd >= 0)
584 {
585 int count = 0;
586 count = read(fd, buffer, sizeof(buffer));
587 close(fd);
588 }
589
590 snprintf(resourcename, sizeof(resourcename), "%s/resource", pci_path);
591
592 memset(drivername, 0, sizeof(drivername));
593 if (readlink(driverpath, drivername, PATH_MAX) < 0)
594 {
595 /* Some devices might not be bound to a driver */
596 if (errno == ENOENT)
597 return -errno;
598 else
599 printf(" readlink -errno %d\n", errno);
600 }
601
602 driver_name = strrchr(drivername, '/');
603 driver_name++;
604 snprintf(device->driverInfo, sizeof(device->driverInfo), "Driver Module %s Bus %s IRQ %s", driver_name, dev_path, buffer);
605
606 FILE*resource = fopen(resourcename, "r");
607
608 if (resource)
609 {
610 while (!feof(resource))
611 {
612 unsigned long long start, end, flags;
613
614 start = end = flags = 0;
615
616 if (fscanf(resource, "%llx %llx %llx", &start, &end, &flags) != 3)
617 break;
618
619 if (flags & 0x101)
620 {
621 device->ioportMem[0] = end - start + 1;
622 }
623 else
624 if (flags & 0x100)
625 {
626 device->ioMem[0] = end - start + 1;
627 }
628 else
629 if (flags & 0x200)
630 {
631 int i;
632 for ( i = 0; i < 4; i++)
633 {
634 if (device->videoMem[i] == 0)
635 {
636 device->videoMem[i] = end - start + 1;
637 break;
638 }
639 }
640
641 }
642 }
643 fclose(resource);
644 }
645
646
647 for (unsigned i = ignore_revision ? 1 : 0; i < ARRAY_SIZE(attrs); i++) {
648 snprintf(path, PATH_MAX + 128, "%s/%s", pci_path, attrs[i]);
649
650 fp = fopen(path, "r");
651 if (!fp)
652 return -errno;
653
654 ret = fscanf(fp, "%x", &data[i]);
655 fclose(fp);
656 if (ret != 1)
657 return -errno;
658 }
659
660 device->revision_id = ignore_revision ? 0xff : data[0] & 0xff;
661 device->vendor_id = data[1] & 0xffff;
662 device->device_id = data[2] & 0xffff;
663 device->subvendor_id = data[3] & 0xffff;
664 device->subdevice_id = data[4] & 0xffff;
665 return 0;
666 }
667
parse_config_sysfs_file(int maj,int min,drmPciDeviceInfoPtr device)668 static int parse_config_sysfs_file(int maj, int min,
669 drmPciDeviceInfoPtr device)
670 {
671 char path[PATH_MAX + 128], pci_path[PATH_MAX + 1];
672 unsigned char config[64];
673 int fd, ret;
674
675 get_pci_path(maj, min, pci_path);
676
677 snprintf(path, PATH_MAX + 128, "%s/config", pci_path);
678
679 fd = open(path, O_RDONLY);
680 if (fd < 0)
681 return -errno;
682
683 ret = read(fd, config, sizeof(config));
684 close(fd);
685 if (ret < 0)
686 return -errno;
687
688 device->vendor_id = config[0] | (config[1] << 8);
689 device->device_id = config[2] | (config[3] << 8);
690 device->revision_id = config[8];
691 device->subvendor_id = config[44] | (config[45] << 8);
692 device->subdevice_id = config[46] | (config[47] << 8);
693
694 return 0;
695 }
696
drmParsePciDeviceInfo(int maj,int min,drmPciDeviceInfoPtr device,uint32_t flags)697 static int drmParsePciDeviceInfo(int maj, int min,
698 drmPciDeviceInfoPtr device,
699 uint32_t flags)
700 {
701 //#ifdef __linux__
702 if (!(flags & DRM_DEVICE_GET_PCI_REVISION))
703 return parse_separate_sysfs_files(maj, min, device, true);
704
705 if (parse_separate_sysfs_files(maj, min, device, false))
706 return parse_config_sysfs_file(maj, min, device);
707 return 0;
708 /*
709 #elif defined(__OpenBSD__) || defined(__DragonFly__)
710 struct drm_pciinfo pinfo;
711 int fd, type;
712
713 type = drmGetMinorType(min);
714 if (type == -1)
715 return -ENODEV;
716
717 fd = drmOpenMinor(min, 0, type);
718 if (fd < 0)
719 return -errno;
720
721 if (drmIoctl(fd, DRM_IOCTL_GET_PCIINFO, &pinfo)) {
722 close(fd);
723 return -errno;
724 }
725 close(fd);
726
727 device->vendor_id = pinfo.vendor_id;
728 device->device_id = pinfo.device_id;
729 device->revision_id = pinfo.revision_id;
730 device->subvendor_id = pinfo.subvendor_id;
731 device->subdevice_id = pinfo.subdevice_id;
732
733 return 0;
734 #else
735 #warning "Missing implementation of drmParsePciDeviceInfo"
736 return -EINVAL;
737 #endif
738 */
739 }
740
drmParsePciBusInfo(int maj,int min,drmPciBusInfoPtr info)741 static int drmParsePciBusInfo(int maj, int min, drmPciBusInfoPtr info)
742 {
743 unsigned int domain, bus, dev, func;
744 char pci_path[PATH_MAX + 1], *value;
745 int num;
746 get_pci_path(maj, min, pci_path);
747 value = sysfs_uevent_get(pci_path, "PCI_SLOT_NAME");
748 if (!value)
749 return -ENOENT;
750
751 num = sscanf(value, "%04x:%02x:%02x.%1u", &domain, &bus, &dev, &func);
752 free(value);
753 if (num != 4)
754 return -EINVAL;
755 info->domain = domain;
756 info->bus = bus;
757 info->dev = dev;
758 info->func = func;
759 return 0;
760 }
761
drmProcessPlatformDevice(drmDevicePtr * device,const char * node,int node_type,int maj,int min,bool fetch_deviceinfo,uint32_t flags)762 static int drmProcessPlatformDevice(drmDevicePtr *device,
763 const char *node, int node_type,
764 int maj, int min, bool fetch_deviceinfo,
765 uint32_t flags)
766 {
767 drmDevicePtr dev;
768 char *ptr;
769 int ret;
770
771 dev = drmDeviceAlloc(node_type, node, sizeof(drmPlatformBusInfo),
772 sizeof(drmPlatformDeviceInfo), &ptr);
773 if (!dev)
774 return -ENOMEM;
775
776 dev->bustype = DRM_BUS_PLATFORM;
777
778 dev->businfo.platform = (drmPlatformBusInfoPtr)ptr;
779
780 ret = drmParsePlatformBusInfo(maj, min, dev->businfo.platform);
781 if (ret < 0)
782 goto free_device;
783
784 if (fetch_deviceinfo) {
785 ptr += sizeof(drmPlatformBusInfo);
786 dev->deviceinfo.platform = (drmPlatformDeviceInfoPtr)ptr;
787
788 ret = drmParsePlatformDeviceInfo(maj, min, dev->deviceinfo.platform);
789 if (ret < 0)
790 goto free_device;
791 }
792
793 *device = dev;
794
795 return 0;
796
797 free_device:
798 free(dev);
799 return ret;
800 }
801
drmProcessPciDevice(drmDevicePtr * device,const char * node,int node_type,int maj,int min,bool fetch_deviceinfo,uint32_t flags)802 static int drmProcessPciDevice(drmDevicePtr *device,
803 const char *node, int node_type,
804 int maj, int min, bool fetch_deviceinfo,
805 uint32_t flags)
806 {
807 drmDevicePtr dev;
808 char *addr;
809 int ret;
810
811 dev = drmDeviceAlloc(node_type, node, sizeof(drmPciBusInfo),
812 sizeof(drmPciDeviceInfo), &addr);
813
814 if (!dev)
815 return -ENOMEM;
816
817 dev->bustype = DRM_BUS_PCI;
818
819 dev->businfo.pci = (drmPciBusInfoPtr)addr;
820
821 ret = drmParsePciBusInfo(maj, min, dev->businfo.pci);
822 if (ret)
823 goto free_device;
824
825 // Fetch the device info if the user has requested it
826 if (fetch_deviceinfo) {
827 addr += sizeof(drmPciBusInfo);
828 dev->deviceinfo.pci = (drmPciDeviceInfoPtr)addr;
829 ret = drmParsePciDeviceInfo(maj, min, dev->deviceinfo.pci, flags);
830
831 if (ret)
832 goto free_device;
833 }
834
835 *device = dev;
836
837 return 0;
838
839 free_device:
840 free(dev);
841 return ret;
842 }
843
drmParseUsbBusInfo(int maj,int min,drmUsbBusInfoPtr info)844 static int drmParseUsbBusInfo(int maj, int min, drmUsbBusInfoPtr info)
845 {
846 char path[PATH_MAX + 1], *value;
847 unsigned int bus, dev;
848 int ret;
849
850 snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
851 value = sysfs_uevent_get(path, "BUSNUM");
852 if (!value)
853 return -ENOENT;
854
855 ret = sscanf(value, "%03u", &bus);
856 free(value);
857
858 if (ret <= 0)
859 return -errno;
860
861 value = sysfs_uevent_get(path, "DEVNUM");
862 if (!value)
863 return -ENOENT;
864
865 ret = sscanf(value, "%03u", &dev);
866 free(value);
867
868 if (ret <= 0)
869 return -errno;
870
871 info->bus = bus;
872 info->dev = dev;
873 return 0;
874 }
875
drmParseUsbDeviceInfo(int maj,int min,drmUsbDeviceInfoPtr info)876 static int drmParseUsbDeviceInfo(int maj, int min, drmUsbDeviceInfoPtr info)
877 {
878 char path[PATH_MAX + 1], *value;
879 unsigned int vendor, product;
880 int ret;
881
882 snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
883 value = sysfs_uevent_get(path, "PRODUCT");
884 if (!value)
885 return -ENOENT;
886
887 ret = sscanf(value, "%x/%x", &vendor, &product);
888 free(value);
889
890 if (ret <= 0)
891 return -errno;
892
893 info->vendor = vendor;
894 info->product = product;
895 return 0;
896 }
897
drmProcessUsbDevice(drmDevicePtr * device,const char * node,int node_type,int maj,int min,bool fetch_deviceinfo,uint32_t flags)898 static int drmProcessUsbDevice(drmDevicePtr *device, const char *node,
899 int node_type, int maj, int min,
900 bool fetch_deviceinfo, uint32_t flags)
901 {
902 drmDevicePtr dev;
903 char *ptr;
904 int ret;
905
906 dev = drmDeviceAlloc(node_type, node, sizeof(drmUsbBusInfo),
907 sizeof(drmUsbDeviceInfo), &ptr);
908 if (!dev)
909 return -ENOMEM;
910
911 dev->bustype = DRM_BUS_USB;
912
913 dev->businfo.usb = (drmUsbBusInfoPtr)ptr;
914
915 ret = drmParseUsbBusInfo(maj, min, dev->businfo.usb);
916 if (ret < 0)
917 goto free_device;
918
919 if (fetch_deviceinfo) {
920 ptr += sizeof(drmUsbBusInfo);
921 dev->deviceinfo.usb = (drmUsbDeviceInfoPtr)ptr;
922
923 ret = drmParseUsbDeviceInfo(maj, min, dev->deviceinfo.usb);
924 if (ret < 0)
925 goto free_device;
926 }
927
928 *device = dev;
929
930 return 0;
931
932 free_device:
933 free(dev);
934 return ret;
935 }
936
937
drmParseSubsystemType(int maj,int min)938 static int drmParseSubsystemType(int maj, int min)
939 {
940 #ifdef __linux__
941 char path[PATH_MAX + 1];
942 char link[PATH_MAX + 1] = "";
943 char *name;
944 struct {
945 const char *name;
946 int bus_type;
947 } bus_types[] = {
948 { "/pci", DRM_BUS_PCI },
949 { "/usb", DRM_BUS_USB },
950 { "/platform", DRM_BUS_PLATFORM },
951 { "/spi", DRM_BUS_PLATFORM },
952 { "/host1x", DRM_BUS_HOST1X },
953 { "/virtio", DRM_BUS_VIRTIO },
954 };
955
956 snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/subsystem",
957 maj, min);
958
959 memset(link, 0, sizeof(link));
960 if (readlink(path, link, PATH_MAX) < 0)
961 return -errno;
962
963 name = strrchr(link, '/');
964 if (!name)
965 return -EINVAL;
966
967 for (unsigned i = 0; i < ARRAY_SIZE(bus_types); i++) {
968 if (strncmp(name, bus_types[i].name, strlen(bus_types[i].name)) == 0)
969 return bus_types[i].bus_type;
970 }
971
972 return -EINVAL;
973 #elif defined(__OpenBSD__) || defined(__DragonFly__)
974 return DRM_BUS_PCI;
975 #else
976 #warning "Missing implementation of drmParseSubsystemType"
977 return -EINVAL;
978 #endif
979 }
980
drmFreePlatformDevice(drmDevicePtr device)981 static void drmFreePlatformDevice(drmDevicePtr device)
982 {
983 if (device->deviceinfo.platform) {
984 if (device->deviceinfo.platform->compatible) {
985 char **compatible = device->deviceinfo.platform->compatible;
986
987 while (*compatible) {
988 free(*compatible);
989 compatible++;
990 }
991
992 free(device->deviceinfo.platform->compatible);
993 }
994 }
995 }
996
drmFreeHost1xDevice(drmDevicePtr device)997 static void drmFreeHost1xDevice(drmDevicePtr device)
998 {
999 if (device->deviceinfo.host1x) {
1000 if (device->deviceinfo.host1x->compatible) {
1001 char **compatible = device->deviceinfo.host1x->compatible;
1002
1003 while (*compatible) {
1004 free(*compatible);
1005 compatible++;
1006 }
1007
1008 free(device->deviceinfo.host1x->compatible);
1009 }
1010 }
1011 }
1012
drmGetNodeType(const char * name)1013 static int drmGetNodeType(const char *name)
1014 {
1015 if (strncmp(name, DRM_PRIMARY_MINOR_NAME,
1016 sizeof(DRM_PRIMARY_MINOR_NAME) - 1) == 0)
1017 return DRM_NODE_PRIMARY;
1018
1019 if (strncmp(name, DRM_CONTROL_MINOR_NAME,
1020 sizeof(DRM_CONTROL_MINOR_NAME) - 1) == 0)
1021 return DRM_NODE_CONTROL;
1022
1023 if (strncmp(name, DRM_RENDER_MINOR_NAME,
1024 sizeof(DRM_RENDER_MINOR_NAME) - 1) == 0)
1025 return DRM_NODE_RENDER;
1026
1027 return -EINVAL;
1028 }
1029
drmFreeDevice(drmDevicePtr * device)1030 void drmFreeDevice(drmDevicePtr *device)
1031 {
1032 if (device == NULL)
1033 return;
1034
1035 if (*device) {
1036 switch ((*device)->bustype) {
1037 case DRM_BUS_PLATFORM:
1038 drmFreePlatformDevice(*device);
1039 break;
1040
1041 case DRM_BUS_HOST1X:
1042 drmFreeHost1xDevice(*device);
1043 break;
1044 }
1045 }
1046
1047 free(*device);
1048 *device = NULL;
1049 }
1050
1051 static int
process_device(drmDevicePtr * device,const char * d_name,int req_subsystem_type,bool fetch_deviceinfo,uint32_t flags)1052 process_device(drmDevicePtr *device, const char *d_name,
1053 int req_subsystem_type,
1054 bool fetch_deviceinfo, uint32_t flags)
1055 {
1056 struct stat sbuf;
1057 char node[PATH_MAX + 1];
1058 int node_type, subsystem_type;
1059 unsigned int maj, min;
1060
1061 node_type = drmGetNodeType(d_name);
1062 if (node_type < 0)
1063 return -1;
1064
1065 snprintf(node, PATH_MAX, "%s/%s", DRM_DIR_NAME, d_name);
1066
1067 if (stat(node, &sbuf))
1068 return -1;
1069
1070 maj = major(sbuf.st_rdev);
1071 min = minor(sbuf.st_rdev);
1072
1073 if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
1074 {
1075 return -1;
1076 }
1077
1078 subsystem_type = drmParseSubsystemType(maj, min);
1079
1080 if (req_subsystem_type != -1 && req_subsystem_type != subsystem_type)
1081 return -1;
1082
1083 switch (subsystem_type) {
1084 case DRM_BUS_PCI:
1085 case DRM_BUS_VIRTIO:
1086 return drmProcessPciDevice(device, node, node_type, maj, min,
1087 fetch_deviceinfo, flags);
1088 case DRM_BUS_USB:
1089 return drmProcessUsbDevice(device, node, node_type, maj, min,
1090 fetch_deviceinfo, flags);
1091 case DRM_BUS_PLATFORM:
1092 return drmProcessPlatformDevice(device, node, node_type, maj, min,
1093 fetch_deviceinfo, flags);
1094 case DRM_BUS_HOST1X:
1095 return drmProcessHost1xDevice(device, node, node_type, maj, min,
1096 fetch_deviceinfo, flags);
1097 default:
1098 return -1;
1099 }
1100 }
1101
1102 /* Consider devices located on the same bus as duplicate and fold the respective
1103 * entries into a single one.
1104 *
1105 * Note: this leaves "gaps" in the array, while preserving the length.
1106 */
drmFoldDuplicatedDevices(drmDevicePtr local_devices[],int count)1107 static void drmFoldDuplicatedDevices(drmDevicePtr local_devices[], int count)
1108 {
1109 int node_type, i, j;
1110
1111 for (i = 0; i < count; i++) {
1112 for (j = i + 1; j < count; j++) {
1113 if (drmDevicesEqual(local_devices[i], local_devices[j])) {
1114 local_devices[i]->available_nodes |= local_devices[j]->available_nodes;
1115 node_type = log2(local_devices[j]->available_nodes);
1116 memcpy(local_devices[i]->nodes[node_type],
1117 local_devices[j]->nodes[node_type], drmGetMaxNodeName());
1118 drmFreeDevice(&local_devices[j]);
1119 }
1120 }
1121 }
1122 }
1123
1124 /**
1125 * Get drm devices on the system
1126 *
1127 * \param flags feature/behaviour bitmask
1128 * \param devices the array of devices with drmDevicePtr elements
1129 * can be NULL to get the device number first
1130 * \param max_devices the maximum number of devices for the array
1131 *
1132 * \return on error - negative error code,
1133 * if devices is NULL - total number of devices available on the system,
1134 * alternatively the number of devices stored in devices[], which is
1135 * capped by the max_devices.
1136 *
1137 * \note Unlike drmGetDevices it does not retrieve the pci device revision field
1138 * unless the DRM_DEVICE_GET_PCI_REVISION \p flag is set.
1139 */
drmGetDevices2(uint32_t flags,drmDevicePtr devices[],int max_devices)1140 int drmGetDevices2(uint32_t flags, drmDevicePtr devices[],
1141 int max_devices)
1142 {
1143 drmDevicePtr local_devices[MAX_DRM_NODES];
1144 drmDevicePtr device;
1145 DIR *sysdir;
1146 struct dirent *dent;
1147 int ret, i, node_count, device_count;
1148
1149 if (drm_device_validate_flags(flags))
1150 return -EINVAL;
1151
1152 sysdir = opendir(DRM_DIR_NAME);
1153 if (!sysdir)
1154 return -errno;
1155
1156 i = 0;
1157 while ((dent = readdir(sysdir))) {
1158 ret = process_device(&device, dent->d_name, -1, devices != NULL, flags);
1159 if (ret)
1160 continue;
1161
1162 if (i >= MAX_DRM_NODES) {
1163 fprintf(stderr, "More than %d drm nodes detected. "
1164 "Please report - that should not happen.\n"
1165 "Skipping extra nodes\n", MAX_DRM_NODES);
1166 break;
1167 }
1168 local_devices[i] = device;
1169 i++;
1170 }
1171 node_count = i;
1172 drmFoldDuplicatedDevices(local_devices, node_count);
1173
1174 device_count = 0;
1175 for (i = 0; i < node_count; i++) {
1176 if (!local_devices[i])
1177 continue;
1178
1179 if ((devices != NULL) && (device_count < max_devices))
1180 devices[device_count] = local_devices[i];
1181 else
1182 drmFreeDevice(&local_devices[i]);
1183
1184 device_count++;
1185 }
1186
1187 closedir(sysdir);
1188 return device_count;
1189 }
1190
1191 /**
1192 * Get drm devices on the system
1193 *
1194 * \param devices the array of devices with drmDevicePtr elements
1195 * can be NULL to get the device number first
1196 * \param max_devices the maximum number of devices for the array
1197 *
1198 * \return on error - negative error code,
1199 * if devices is NULL - total number of devices available on the system,
1200 * alternatively the number of devices stored in devices[], which is
1201 * capped by the max_devices.
1202 */
drmGetDevices(drmDevicePtr devices[],int max_devices)1203 int drmGetDevices(drmDevicePtr devices[], int max_devices)
1204 {
1205 return drmGetDevices2(DRM_DEVICE_GET_PCI_REVISION, devices, max_devices);
1206 }
1207
1208 /**
1209 * Get information about the opened drm device
1210 *
1211 * \param fd file descriptor of the drm device
1212 * \param flags feature/behaviour bitmask
1213 * \param device the address of a drmDevicePtr where the information
1214 * will be allocated in stored
1215 *
1216 * \return zero on success, negative error code otherwise.
1217 *
1218 * \note Unlike drmGetDevice it does not retrieve the pci device revision field
1219 * unless the DRM_DEVICE_GET_PCI_REVISION \p flag is set.
1220 */
drmGetDevice2(int fd,uint32_t flags,drmDevicePtr * device)1221 int drmGetDevice2(int fd, uint32_t flags, drmDevicePtr *device)
1222 {
1223 drmDevicePtr local_devices[MAX_DRM_NODES];
1224 drmDevicePtr d;
1225 DIR *sysdir;
1226 struct dirent *dent;
1227 struct stat sbuf;
1228 int subsystem_type;
1229 int maj, min;
1230 int ret, i, node_count;
1231 dev_t find_rdev;
1232
1233 if (drm_device_validate_flags(flags))
1234 return -EINVAL;
1235
1236 if (fd == -1 || device == NULL)
1237 return -EINVAL;
1238
1239 if (fstat(fd, &sbuf))
1240 return -errno;
1241
1242 find_rdev = sbuf.st_rdev;
1243 maj = major(sbuf.st_rdev);
1244 min = minor(sbuf.st_rdev);
1245
1246 if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
1247 return -EINVAL;
1248
1249 subsystem_type = drmParseSubsystemType(maj, min);
1250 if (subsystem_type < 0)
1251 return subsystem_type;
1252
1253 sysdir = opendir(DRM_DIR_NAME);
1254 if (!sysdir)
1255 return -errno;
1256
1257 i = 0;
1258 while ((dent = readdir(sysdir))) {
1259 ret = process_device(&d, dent->d_name, subsystem_type, true, flags);
1260 if (ret)
1261 continue;
1262
1263 if (i >= MAX_DRM_NODES) {
1264 fprintf(stderr, "More than %d drm nodes detected. "
1265 "Please report a bug - that should not happen.\n"
1266 "Skipping extra nodes\n", MAX_DRM_NODES);
1267 break;
1268 }
1269 local_devices[i] = d;
1270 i++;
1271 }
1272 node_count = i;
1273
1274 drmFoldDuplicatedDevices(local_devices, node_count);
1275
1276 *device = NULL;
1277
1278 for (i = 0; i < node_count; i++) {
1279 if (!local_devices[i])
1280 continue;
1281
1282 if (drm_device_has_rdev(local_devices[i], find_rdev))
1283 *device = local_devices[i];
1284 else
1285 drmFreeDevice(&local_devices[i]);
1286 }
1287
1288 closedir(sysdir);
1289 if (*device == NULL)
1290 return -ENODEV;
1291 return 0;
1292 }
1293
1294 /**
1295 * Get information about the opened drm device
1296 *
1297 * \param fd file descriptor of the drm device
1298 * \param device the address of a drmDevicePtr where the information
1299 * will be allocated in stored
1300 *
1301 * \return zero on success, negative error code otherwise.
1302 */
drmGetDevice(int fd,drmDevicePtr * device)1303 int drmGetDevice(int fd, drmDevicePtr *device)
1304 {
1305 return drmGetDevice2(fd, DRM_DEVICE_GET_PCI_REVISION, device);
1306 }
1307
GetRendererFileDescriptor(char * drm_node)1308 static int32_t GetRendererFileDescriptor(char * drm_node)
1309 {
1310 int32_t driFileDescriptor = -1;
1311
1312 driFileDescriptor = open(drm_node, O_RDWR);
1313 return driFileDescriptor;
1314 }
1315
1316 #endif // #ifndef DRM_DEVICE_H_
1317