xref: /aosp_15_r20/external/intel-media-driver/cmrtlib/linux/hardware/drm_device.h (revision ba62d9d3abf0e404f2022b4cd7a85e107f48596f)
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