1 /**
2  * \file xf86drm.c
3  * User-level interface to DRM device
4  *
5  * \author Rickard E. (Rik) Faith <faith@valinux.com>
6  * \author Kevin E. Martin <martin@valinux.com>
7  */
8 
9 /*
10  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
11  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
12  * All Rights Reserved.
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the "Software"),
16  * to deal in the Software without restriction, including without limitation
17  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18  * and/or sell copies of the Software, and to permit persons to whom the
19  * Software is furnished to do so, subject to the following conditions:
20  *
21  * The above copyright notice and this permission notice (including the next
22  * paragraph) shall be included in all copies or substantial portions of the
23  * Software.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
28  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
29  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
30  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31  * DEALINGS IN THE SOFTWARE.
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 # include <config.h>
36 #endif
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <strings.h>
42 #include <ctype.h>
43 #include <dirent.h>
44 #include <stddef.h>
45 #include <fcntl.h>
46 #include <errno.h>
47 #include <signal.h>
48 #include <time.h>
49 #include <sys/types.h>
50 #include <sys/sysmacros.h>
51 #include <sys/stat.h>
52 #define stat_t struct stat
53 #include <sys/ioctl.h>
54 #include <sys/time.h>
55 #include <stdarg.h>
56 #ifdef HAVE_SYS_MKDEV_H
57 # include <sys/mkdev.h> /* defines major(), minor(), and makedev() on Solaris */
58 #endif
59 
60 /* Not all systems have MAP_FAILED defined */
61 #ifndef MAP_FAILED
62 #define MAP_FAILED ((void *)-1)
63 #endif
64 
65 #include "xf86drm.h"
66 //#include "xf86drmCSC.h"
67 #include "libdrm_macros.h"
68 #include "i915_drm.h"
69 
70 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
71 #define DRM_MAJOR 145
72 #endif
73 
74 #ifdef __NetBSD__
75 #define DRM_MAJOR 34
76 #endif
77 
78 # ifdef __OpenBSD__
79 #  define DRM_MAJOR 81
80 # endif
81 
82 #ifndef DRM_MAJOR
83 #define DRM_MAJOR 226        /* Linux */
84 #endif
85 
86 /*
87  * This definition needs to be changed on some systems if dev_t is a structure.
88  * If there is a header file we can get it from, there would be best.
89  */
90 #ifndef makedev
91 #define makedev(x,y)    ((dev_t)(((x) << 8) | (y)))
92 #endif
93 
94 #define DRM_MSG_VERBOSITY 3
95 
96 #define memclear(s) memset(&s, 0, sizeof(s))
97 
98 static drmServerInfoPtr drm_server_info;
99 
drmSetServerInfo(drmServerInfoPtr info)100 void drmSetServerInfo(drmServerInfoPtr info)
101 {
102     drm_server_info = info;
103 }
104 
105 /**
106  * Output a message to stderr.
107  *
108  * \param format printf() like format string.
109  *
110  * \internal
111  * This function is a wrapper around vfprintf().
112  */
113 
114 static int DRM_PRINTFLIKE(1, 0)
drmDebugPrint(const char * format,va_list ap)115 drmDebugPrint(const char *format, va_list ap)
116 {
117     return vfprintf(stderr, format, ap);
118 }
119 
120 void
drmMsg(const char * format,...)121 drmMsg(const char *format, ...)
122 {
123     va_list    ap;
124     const char *env;
125     if (((env = getenv("LIBGL_DEBUG")) && strstr(env, "verbose")) || drm_server_info)
126     {
127     va_start(ap, format);
128     if (drm_server_info) {
129       drm_server_info->debug_print(format,ap);
130     } else {
131       drmDebugPrint(format, ap);
132     }
133     va_end(ap);
134     }
135 }
136 
137 static void *drmHashTable = nullptr; /* Context switch callbacks */
138 
drmGetHashTable(void)139 void *drmGetHashTable(void)
140 {
141     return drmHashTable;
142 }
143 
drmMalloc(int size)144 void *drmMalloc(int size)
145 {
146     void *pt;
147     if ((pt = malloc(size)))
148     memset(pt, 0, size);
149     return pt;
150 }
151 
drmFree(void * pt)152 void drmFree(void *pt)
153 {
154     if (pt)
155     free(pt);
156 }
157 
158 /**
159  * Call ioctl, restarting if it is interupted
160  */
161 
162 #if defined(__cplusplus)
163 extern "C" {
164 #endif
165 drm_export int
mosdrmIoctl(int fd,unsigned long request,void * arg)166 mosdrmIoctl(int fd, unsigned long request, void *arg)
167 {
168     int    ret;
169 
170     do {
171     ret = ioctl(fd, request, arg);
172     } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
173     return ret;
174 }
175 drm_export int
drmIoctl(int fd,unsigned long request,void * arg)176 drmIoctl(int fd, unsigned long request, void *arg)
177 {
178     return mosdrmIoctl(fd,request,arg);
179 }
180 #if defined(__cplusplus)
181 }
182 #endif
183 
drmGetKeyFromFd(int fd)184 static unsigned long drmGetKeyFromFd(int fd)
185 {
186     stat_t     st;
187 
188     st.st_rdev = 0;
189     fstat(fd, &st);
190     return st.st_rdev;
191 }
192 
drmGetEntry(int fd)193 drmHashEntry *drmGetEntry(int fd)
194 {
195     unsigned long key = drmGetKeyFromFd(fd);
196     void          *value = nullptr;
197     drmHashEntry  *entry;
198 
199     if (!drmHashTable)
200     drmHashTable = drmHashCreate();
201 
202     if (drmHashTable && drmHashLookup(drmHashTable, key, &value)) {
203     entry           = (drmHashEntry *)drmMalloc(sizeof(*entry));
204     if (!entry)
205         return nullptr;
206     entry->fd       = fd;
207     entry->f        = nullptr;
208     entry->tagTable = drmHashCreate();
209     if (entry->tagTable) {
210         drmHashInsert(drmHashTable, key, entry);
211     } else {
212         drmFree(entry);
213         entry = nullptr;
214     }
215     } else {
216     entry = (drmHashEntry *)value;
217     }
218     return entry;
219 }
220 /**
221  * Compare two busid strings
222  *
223  * \param first
224  * \param second
225  *
226  * \return 1 if matched.
227  *
228  * \internal
229  * This function compares two bus ID strings.  It understands the older
230  * PCI:b:d:f format and the newer pci:oooo:bb:dd.f format.  In the format, o is
231  * domain, b is bus, d is device, f is function.
232  */
drmMatchBusID(const char * id1,const char * id2,int pci_domain_ok)233 static int drmMatchBusID(const char *id1, const char *id2, int pci_domain_ok)
234 {
235     /* First, check if the IDs are exactly the same */
236     if (strcasecmp(id1, id2) == 0)
237     return 1;
238 
239     /* Try to match old/new-style PCI bus IDs. */
240     if (strncasecmp(id1, "pci", 3) == 0) {
241     unsigned int o1, b1, d1, f1;
242     unsigned int o2, b2, d2, f2;
243     int ret;
244 
245     ret = sscanf(id1, "pci:%04x:%02x:%02x.%u", &o1, &b1, &d1, &f1);
246     if (ret != 4) {
247         o1 = 0;
248         ret = sscanf(id1, "PCI:%u:%u:%u", &b1, &d1, &f1);
249         if (ret != 3)
250         return 0;
251     }
252 
253     ret = sscanf(id2, "pci:%04x:%02x:%02x.%u", &o2, &b2, &d2, &f2);
254     if (ret != 4) {
255         o2 = 0;
256         ret = sscanf(id2, "PCI:%u:%u:%u", &b2, &d2, &f2);
257         if (ret != 3)
258         return 0;
259     }
260 
261     /* If domains aren't properly supported by the kernel interface,
262      * just ignore them, which sucks less than picking a totally random
263      * card with "open by name"
264      */
265     if (!pci_domain_ok)
266         o1 = o2 = 0;
267 
268     if ((o1 != o2) || (b1 != b2) || (d1 != d2) || (f1 != f2))
269         return 0;
270     else
271         return 1;
272     }
273     return 0;
274 }
275 
276 /**
277  * Handles error checking for chown call.
278  *
279  * \param path to file.
280  * \param id of the new owner.
281  * \param id of the new group.
282  *
283  * \return zero if success or -1 if failure.
284  *
285  * \internal
286  * Checks for failure. If failure was caused by signal call chown again.
287  * If any other failure happened then it will output error mesage using
288  * drmMsg() call.
289  */
290 #if !defined(UDEV)
chown_check_return(const char * path,uid_t owner,gid_t group)291 static int chown_check_return(const char *path, uid_t owner, gid_t group)
292 {
293     int rv;
294 
295     do {
296         rv = chown(path, owner, group);
297     } while (rv != 0 && errno == EINTR);
298 
299     if (rv == 0)
300         return 0;
301 
302     drmMsg("Failed to change owner or group for file %s! %d: %s\n",
303             path, errno, strerror(errno));
304     return -1;
305 }
306 #endif
307 
308 /**
309  * Open the DRM device, creating it if necessary.
310  *
311  * \param dev major and minor numbers of the device.
312  * \param minor minor number of the device.
313  *
314  * \return a file descriptor on success, or a negative value on error.
315  *
316  * \internal
317  * Assembles the device name from \p minor and opens it, creating the device
318  * special file node with the major and minor numbers specified by \p dev and
319  * parent directory if necessary and was called by root.
320  */
drmOpenDevice(dev_t dev,int minor,int type)321 static int drmOpenDevice(dev_t dev, int minor, int type)
322 {
323     stat_t          st;
324     const char      *dev_name;
325     char            buf[64];
326     int             fd;
327     mode_t          devmode = DRM_DEV_MODE, serv_mode;
328     gid_t           serv_group;
329 #if !defined(UDEV)
330     int             isroot  = !geteuid();
331     uid_t           user    = DRM_DEV_UID;
332     gid_t           group   = DRM_DEV_GID;
333 #endif
334 
335     switch (type) {
336     case DRM_NODE_PRIMARY:
337         dev_name = DRM_DEV_NAME;
338         break;
339     case DRM_NODE_CONTROL:
340         dev_name = DRM_CONTROL_DEV_NAME;
341         break;
342     case DRM_NODE_RENDER:
343         dev_name = DRM_RENDER_DEV_NAME;
344         break;
345     default:
346         return -EINVAL;
347     };
348 
349     snprintf(buf, sizeof(buf), dev_name, DRM_DIR_NAME, minor);
350     drmMsg("drmOpenDevice: node name is %s\n", buf);
351 
352     if (drm_server_info) {
353     drm_server_info->get_perms(&serv_group, &serv_mode);
354     devmode  = serv_mode ? serv_mode : DRM_DEV_MODE;
355     devmode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
356     }
357 
358 #if !defined(UDEV)
359     if (stat(DRM_DIR_NAME, &st)) {
360     if (!isroot)
361         return DRM_ERR_NOT_ROOT;
362     mkdir(DRM_DIR_NAME, DRM_DEV_DIRMODE);
363     chown_check_return(DRM_DIR_NAME, 0, 0); /* root:root */
364     chmod(DRM_DIR_NAME, DRM_DEV_DIRMODE);
365     }
366 
367     /* Check if the device node exists and create it if necessary. */
368     if (stat(buf, &st)) {
369     if (!isroot)
370         return DRM_ERR_NOT_ROOT;
371     remove(buf);
372     mknod(buf, S_IFCHR | devmode, dev);
373     }
374 
375     if (drm_server_info) {
376     group = serv_group; /*(serv_group >= 0) ? serv_group : DRM_DEV_GID;*/
377     chown_check_return(buf, user, group);
378     chmod(buf, devmode);
379     }
380 #else
381     /* if we modprobed then wait for udev */
382     {
383     int udev_count = 0;
384 wait_for_udev:
385         if (stat(DRM_DIR_NAME, &st)) {
386         usleep(20);
387         udev_count++;
388 
389         if (udev_count == 50)
390             return -1;
391         goto wait_for_udev;
392     }
393 
394         if (stat(buf, &st)) {
395         usleep(20);
396         udev_count++;
397 
398         if (udev_count == 50)
399             return -1;
400         goto wait_for_udev;
401         }
402     }
403 #endif
404 
405     fd = open(buf, O_RDWR, 0);
406     drmMsg("drmOpenDevice: open result is %d, (%s)\n",
407         fd, fd < 0 ? strerror(errno) : "OK");
408     if (fd >= 0)
409     return fd;
410 
411 #if !defined(UDEV)
412     /* Check if the device node is not what we expect it to be, and recreate it
413      * and try again if so.
414      */
415     if (st.st_rdev != dev) {
416     if (!isroot)
417         return DRM_ERR_NOT_ROOT;
418     remove(buf);
419     mknod(buf, S_IFCHR | devmode, dev);
420     if (drm_server_info) {
421         chown_check_return(buf, user, group);
422         chmod(buf, devmode);
423     }
424     }
425     fd = open(buf, O_RDWR, 0);
426     drmMsg("drmOpenDevice: open result is %d, (%s)\n",
427         fd, fd < 0 ? strerror(errno) : "OK");
428     if (fd >= 0)
429     return fd;
430 
431     drmMsg("drmOpenDevice: Open failed\n");
432     remove(buf);
433 #endif
434     return -errno;
435 }
436 
437 /**
438  * Open the DRM device
439  *
440  * \param minor device minor number.
441  * \param create allow to create the device if set.
442  *
443  * \return a file descriptor on success, or a negative value on error.
444  *
445  * \internal
446  * Calls drmOpenDevice() if \p create is set, otherwise assembles the device
447  * name from \p minor and opens it.
448  */
drmOpenMinor(int minor,int create,int type)449 static int drmOpenMinor(int minor, int create, int type)
450 {
451     int  fd;
452     char buf[64];
453     const char *dev_name;
454 
455     if (create)
456     return drmOpenDevice(makedev(DRM_MAJOR, minor), minor, type);
457 
458     switch (type) {
459     case DRM_NODE_PRIMARY:
460         dev_name = DRM_DEV_NAME;
461         break;
462     case DRM_NODE_CONTROL:
463         dev_name = DRM_CONTROL_DEV_NAME;
464         break;
465     case DRM_NODE_RENDER:
466         dev_name = DRM_RENDER_DEV_NAME;
467         break;
468     default:
469         return -EINVAL;
470     };
471 
472     sprintf(buf, dev_name, DRM_DIR_NAME, minor);
473     if ((fd = open(buf, O_RDWR, 0)) >= 0)
474     return fd;
475     return -errno;
476 }
477 
478 /**
479  * Determine whether the DRM kernel driver has been loaded.
480  *
481  * \return 1 if the DRM driver is loaded, 0 otherwise.
482  *
483  * \internal
484  * Determine the presence of the kernel driver by attempting to open the 0
485  * minor and get version information.  For backward compatibility with older
486  * Linux implementations, /proc/dri is also checked.
487  */
drmAvailable(void)488 int drmAvailable(void)
489 {
490     drmVersionPtr version;
491     int           retval = 0;
492     int           fd;
493 
494     if ((fd = drmOpenMinor(0, 1, DRM_NODE_PRIMARY)) < 0) {
495 #ifdef __linux__
496     /* Try proc for backward Linux compatibility */
497     if (!access("/proc/dri/0", R_OK))
498         return 1;
499 #endif
500     return 0;
501     }
502 
503     if ((version = drmGetVersion(fd))) {
504     retval = 1;
505     drmFreeVersion(version);
506     }
507     close(fd);
508 
509     return retval;
510 }
511 
drmGetMinorBase(int type)512 static int drmGetMinorBase(int type)
513 {
514     switch (type) {
515     case DRM_NODE_PRIMARY:
516         return 0;
517     case DRM_NODE_CONTROL:
518         return 64;
519     case DRM_NODE_RENDER:
520         return 128;
521     default:
522         return -1;
523     };
524 }
525 
drmGetMinorType(int minor)526 static int drmGetMinorType(int minor)
527 {
528     int type = minor >> 6;
529 
530     if (minor < 0)
531         return -1;
532 
533     switch (type) {
534     case DRM_NODE_PRIMARY:
535     case DRM_NODE_CONTROL:
536     case DRM_NODE_RENDER:
537         return type;
538     default:
539         return -1;
540     }
541 }
542 
drmGetMinorName(int type)543 static const char *drmGetMinorName(int type)
544 {
545     switch (type) {
546     case DRM_NODE_PRIMARY:
547         return "card";
548     case DRM_NODE_CONTROL:
549         return "controlD";
550     case DRM_NODE_RENDER:
551         return "renderD";
552     default:
553         return nullptr;
554     }
555 }
556 
557 /**
558  * Open the device by bus ID.
559  *
560  * \param busid bus ID.
561  * \param type device node type.
562  *
563  * \return a file descriptor on success, or a negative value on error.
564  *
565  * \internal
566  * This function attempts to open every possible minor (up to DRM_MAX_MINOR),
567  * comparing the device bus ID with the one supplied.
568  *
569  * \sa drmOpenMinor() and drmGetBusid().
570  */
drmOpenByBusid(const char * busid,int type)571 static int drmOpenByBusid(const char *busid, int type)
572 {
573     int        i, pci_domain_ok = 1;
574     int        fd;
575     const char *buf;
576     drmSetVersion sv;
577     int        base = drmGetMinorBase(type);
578 
579     if (base < 0)
580         return -1;
581 
582     drmMsg("drmOpenByBusid: Searching for BusID %s\n", busid);
583     for (i = base; i < base + DRM_MAX_MINOR; i++) {
584         fd = drmOpenMinor(i, 1, type);
585         drmMsg("drmOpenByBusid: drmOpenMinor returns %d\n", fd);
586         if (fd >= 0) {
587             /* We need to try for 1.4 first for proper PCI domain support
588              * and if that fails, we know the kernel is busted
589              */
590             sv.drm_di_major = 1;
591             sv.drm_di_minor = 4;
592             sv.drm_dd_major = -1;    /* Don't care */
593             sv.drm_dd_minor = -1;    /* Don't care */
594             if (drmSetInterfaceVersion(fd, &sv)) {
595 #ifndef __alpha__
596                 pci_domain_ok = 0;
597 #endif
598                 sv.drm_di_major = 1;
599                 sv.drm_di_minor = 1;
600                 sv.drm_dd_major = -1;       /* Don't care */
601                 sv.drm_dd_minor = -1;       /* Don't care */
602                 drmMsg("drmOpenByBusid: Interface 1.4 failed, trying 1.1\n");
603                 drmSetInterfaceVersion(fd, &sv);
604             }
605             buf = drmGetBusid(fd);
606             if (buf) {
607                 drmMsg("drmOpenByBusid: drmGetBusid reports %s\n", buf);
608                 if (drmMatchBusID(buf, busid, pci_domain_ok)) {
609                     drmFreeBusid(buf);
610                     return fd;
611                 }
612                 drmFreeBusid(buf);
613             }
614             close(fd);
615         }
616     }
617     return -1;
618 }
619 
620 /**
621  * Open the device by name.
622  *
623  * \param name driver name.
624  * \param type the device node type.
625  *
626  * \return a file descriptor on success, or a negative value on error.
627  *
628  * \internal
629  * This function opens the first minor number that matches the driver name and
630  * isn't already in use.  If it's in use it then it will already have a bus ID
631  * assigned.
632  *
633  * \sa drmOpenMinor(), drmGetVersion() and drmGetBusid().
634  */
drmOpenByName(const char * name,int type)635 static int drmOpenByName(const char *name, int type)
636 {
637     int           i;
638     int           fd;
639     drmVersionPtr version;
640     char *        id;
641     int           base = drmGetMinorBase(type);
642 
643     if (base < 0)
644         return -1;
645 
646     /*
647      * Open the first minor number that matches the driver name and isn't
648      * already in use.  If it's in use it will have a busid assigned already.
649      */
650     for (i = base; i < base + DRM_MAX_MINOR; i++) {
651     if ((fd = drmOpenMinor(i, 1, type)) >= 0) {
652         if ((version = drmGetVersion(fd))) {
653         if (!strcmp(version->name, name)) {
654             drmFreeVersion(version);
655             id = drmGetBusid(fd);
656             drmMsg("drmGetBusid returned '%s'\n", id ? id : "NULL");
657             if (!id || !*id) {
658             if (id)
659                 drmFreeBusid(id);
660             return fd;
661             } else {
662             drmFreeBusid(id);
663             }
664         } else {
665             drmFreeVersion(version);
666         }
667         }
668         close(fd);
669     }
670     }
671 
672 #ifdef __linux__
673     /* Backward-compatibility /proc support */
674     for (i = 0; i < 8; i++) {
675     char proc_name[64], buf[512];
676     char *driver, *pt, *devstring;
677     int  retcode;
678 
679     sprintf(proc_name, "/proc/dri/%d/name", i);
680     if ((fd = open(proc_name, 0, 0)) >= 0) {
681         retcode = read(fd, buf, sizeof(buf)-1);
682         close(fd);
683         if (retcode > 0) {
684         buf[retcode-1] = '\0';
685         for (driver = pt = buf; *pt && *pt != ' '; ++pt)
686             ;
687         if (*pt) { /* Device is next */
688             *pt = '\0';
689             if (!strcmp(driver, name)) { /* Match */
690             for (devstring = ++pt; *pt && *pt != ' '; ++pt)
691                 ;
692             if (*pt) { /* Found busid */
693                 return drmOpenByBusid(++pt, type);
694             } else { /* No busid */
695                 return drmOpenDevice(strtol(devstring, nullptr, 0),i, type);
696             }
697             }
698         }
699         }
700     }
701     }
702 #endif
703 
704     return -1;
705 }
706 
707 /**
708  * Open the DRM device.
709  *
710  * Looks up the specified name and bus ID, and opens the device found.  The
711  * entry in /dev/dri is created if necessary and if called by root.
712  *
713  * \param name driver name. Not referenced if bus ID is supplied.
714  * \param busid bus ID. Zero if not known.
715  *
716  * \return a file descriptor on success, or a negative value on error.
717  *
718  * \internal
719  * It calls drmOpenByBusid() if \p busid is specified or drmOpenByName()
720  * otherwise.
721  */
drmOpen(const char * name,const char * busid)722 int drmOpen(const char *name, const char *busid)
723 {
724     return drmOpenWithType(name, busid, DRM_NODE_PRIMARY);
725 }
726 
727 /**
728  * Open the DRM device with specified type.
729  *
730  * Looks up the specified name and bus ID, and opens the device found.  The
731  * entry in /dev/dri is created if necessary and if called by root.
732  *
733  * \param name driver name. Not referenced if bus ID is supplied.
734  * \param busid bus ID. Zero if not known.
735  * \param type the device node type to open, PRIMARY, CONTROL or RENDER
736  *
737  * \return a file descriptor on success, or a negative value on error.
738  *
739  * \internal
740  * It calls drmOpenByBusid() if \p busid is specified or drmOpenByName()
741  * otherwise.
742  */
drmOpenWithType(const char * name,const char * busid,int type)743 int drmOpenWithType(const char *name, const char *busid, int type)
744 {
745     if (!drmAvailable() && name != nullptr && drm_server_info) {
746     /* try to load the kernel module */
747     if (!drm_server_info->load_module(name)) {
748         drmMsg("[drm] failed to load kernel module \"%s\"\n", name);
749         return -1;
750     }
751     }
752 
753     if (busid) {
754     int fd = drmOpenByBusid(busid, type);
755     if (fd >= 0)
756         return fd;
757     }
758 
759     if (name)
760     return drmOpenByName(name, type);
761 
762     return -1;
763 }
764 
drmOpenControl(int minor)765 int drmOpenControl(int minor)
766 {
767     return drmOpenMinor(minor, 0, DRM_NODE_CONTROL);
768 }
769 
drmOpenRender(int minor)770 int drmOpenRender(int minor)
771 {
772     return drmOpenMinor(minor, 0, DRM_NODE_RENDER);
773 }
774 
775 /**
776  * Free the version information returned by drmGetVersion().
777  *
778  * \param v pointer to the version information.
779  *
780  * \internal
781  * It frees the memory pointed by \p %v as well as all the non-null strings
782  * pointers in it.
783  */
drmFreeVersion(drmVersionPtr v)784 void drmFreeVersion(drmVersionPtr v)
785 {
786     if (!v)
787     return;
788     drmFree(v->name);
789     drmFree(v->date);
790     drmFree(v->desc);
791     drmFree(v);
792 }
793 
794 /**
795  * Free the non-public version information returned by the kernel.
796  *
797  * \param v pointer to the version information.
798  *
799  * \internal
800  * Used by drmGetVersion() to free the memory pointed by \p %v as well as all
801  * the non-null strings pointers in it.
802  */
drmFreeKernelVersion(drm_version_t * v)803 static void drmFreeKernelVersion(drm_version_t *v)
804 {
805     if (!v)
806     return;
807     drmFree(v->name);
808     drmFree(v->date);
809     drmFree(v->desc);
810     drmFree(v);
811 }
812 
813 /**
814  * Copy version information.
815  *
816  * \param d destination pointer.
817  * \param s source pointer.
818  *
819  * \internal
820  * Used by drmGetVersion() to translate the information returned by the ioctl
821  * interface in a private structure into the public structure counterpart.
822  */
drmCopyVersion(drmVersionPtr d,const drm_version_t * s)823 static void drmCopyVersion(drmVersionPtr d, const drm_version_t *s)
824 {
825     d->version_major      = s->version_major;
826     d->version_minor      = s->version_minor;
827     d->version_patchlevel = s->version_patchlevel;
828     d->name_len           = s->name_len;
829     d->name               = strdup(s->name);
830     d->date_len           = s->date_len;
831     d->date               = strdup(s->date);
832     d->desc_len           = s->desc_len;
833     d->desc               = strdup(s->desc);
834 }
835 
836 /**
837  * Query the driver version information.
838  *
839  * \param fd file descriptor.
840  *
841  * \return pointer to a drmVersion structure which should be freed with
842  * drmFreeVersion().
843  *
844  * \note Similar information is available via /proc/dri.
845  *
846  * \internal
847  * It gets the version information via successive DRM_IOCTL_VERSION ioctls,
848  * first with zeros to get the string lengths, and then the actually strings.
849  * It also null-terminates them since they might not be already.
850  */
drmGetVersion(int fd)851 drmVersionPtr drmGetVersion(int fd)
852 {
853     drmVersionPtr retval;
854     drm_version_t *version = (drm_version_t *)drmMalloc(sizeof(*version));
855     if (!version)
856     return nullptr;
857 
858     memclear(*version);
859 
860     if (drmIoctl(fd, DRM_IOCTL_VERSION, version)) {
861     drmFreeKernelVersion(version);
862     return nullptr;
863     }
864 
865     if (version->name_len)
866     version->name    = (char *)drmMalloc(version->name_len + 1);
867     if (version->date_len)
868     version->date    = (char *)drmMalloc(version->date_len + 1);
869     if (version->desc_len)
870     version->desc    = (char *)drmMalloc(version->desc_len + 1);
871 
872     if (drmIoctl(fd, DRM_IOCTL_VERSION, version)) {
873     drmMsg("DRM_IOCTL_VERSION: %s\n", strerror(errno));
874     drmFreeKernelVersion(version);
875     return nullptr;
876     }
877 
878     /* The results might not be null-terminated strings, so terminate them. */
879     if (version->name_len) version->name[version->name_len] = '\0';
880     if (version->date_len) version->date[version->date_len] = '\0';
881     if (version->desc_len) version->desc[version->desc_len] = '\0';
882 
883     retval = (drmVersionPtr)drmMalloc(sizeof(*retval));
884     if (retval)
885         drmCopyVersion(retval, version);
886 
887     drmFreeKernelVersion(version);
888     return retval;
889 }
890 
891 /**
892  * Free the bus ID information.
893  *
894  * \param busid bus ID information string as given by drmGetBusid().
895  *
896  * \internal
897  * This function is just frees the memory pointed by \p busid.
898  */
drmFreeBusid(const char * busid)899 void drmFreeBusid(const char *busid)
900 {
901     drmFree((void *)busid);
902 }
903 
904 /**
905  * Get the bus ID of the device.
906  *
907  * \param fd file descriptor.
908  *
909  * \return bus ID string.
910  *
911  * \internal
912  * This function gets the bus ID via successive DRM_IOCTL_GET_UNIQUE ioctls to
913  * get the string length and data, passing the arguments in a drm_unique
914  * structure.
915  */
drmGetBusid(int fd)916 char *drmGetBusid(int fd)
917 {
918     drm_unique_t u;
919 
920     memclear(u);
921 
922     if (drmIoctl(fd, DRM_IOCTL_GET_UNIQUE, &u))
923     return nullptr;
924     u.unique = (char *)drmMalloc(u.unique_len + 1);
925     if (drmIoctl(fd, DRM_IOCTL_GET_UNIQUE, &u))
926     return nullptr;
927     u.unique[u.unique_len] = '\0';
928 
929     return u.unique;
930 }
931 
932 /**
933  * Close the device.
934  *
935  * \param fd file descriptor.
936  *
937  * \internal
938  * This function closes the file descriptor.
939  */
drmClose(int fd)940 int drmClose(int fd)
941 {
942     unsigned long key    = drmGetKeyFromFd(fd);
943     drmHashEntry  *entry = drmGetEntry(fd);
944     if(!entry)
945     return -ENOMEM;
946 
947     drmHashDestroy(entry->tagTable);
948     entry->fd       = 0;
949     entry->f        = nullptr;
950     entry->tagTable = nullptr;
951 
952     drmHashDelete(drmHashTable, key);
953     drmFree(entry);
954 
955     return close(fd);
956 }
957 
958 /**
959  * Issue a set-version ioctl.
960  *
961  * \param fd file descriptor.
962  * \param drmCommandIndex command index
963  * \param data source pointer of the data to be read and written.
964  * \param size size of the data to be read and written.
965  *
966  * \return zero on success, or a negative value on failure.
967  *
968  * \internal
969  * It issues a read-write ioctl given by
970  * \code DRM_COMMAND_BASE + drmCommandIndex \endcode.
971  */
drmSetInterfaceVersion(int fd,drmSetVersion * version)972 int drmSetInterfaceVersion(int fd, drmSetVersion *version)
973 {
974     int retcode = 0;
975     drm_set_version_t sv;
976 
977     memclear(sv);
978     sv.drm_di_major = version->drm_di_major;
979     sv.drm_di_minor = version->drm_di_minor;
980     sv.drm_dd_major = version->drm_dd_major;
981     sv.drm_dd_minor = version->drm_dd_minor;
982 
983     if (drmIoctl(fd, DRM_IOCTL_SET_VERSION, &sv)) {
984     retcode = -errno;
985     }
986 
987     version->drm_di_major = sv.drm_di_major;
988     version->drm_di_minor = sv.drm_di_minor;
989     version->drm_dd_major = sv.drm_dd_major;
990     version->drm_dd_minor = sv.drm_dd_minor;
991 
992     return retcode;
993 }
994 
995 #define DRM_MAX_FDS 16
996 static struct {
997     char *BusID;
998     int fd;
999     int refcount;
1000     int type;
1001 } connection[DRM_MAX_FDS];
1002 
1003 static int nr_fds = 0;
1004 
drmPrimeHandleToFD(int fd,uint32_t handle,uint32_t flags,int * prime_fd)1005 int drmPrimeHandleToFD(int fd, uint32_t handle, uint32_t flags, int *prime_fd)
1006 {
1007     struct drm_prime_handle args;
1008     int ret;
1009 
1010     args.handle = handle;
1011     args.flags = flags;
1012     ret = drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args);
1013     if (ret)
1014         return ret;
1015 
1016     *prime_fd = args.fd;
1017     return 0;
1018 }
1019 
drmPrimeFDToHandle(int fd,int prime_fd,uint32_t * handle)1020 int drmPrimeFDToHandle(int fd, int prime_fd, uint32_t *handle)
1021 {
1022     struct drm_prime_handle args;
1023     int ret;
1024 
1025     args.fd = prime_fd;
1026     args.flags = 0;
1027     ret = drmIoctl(fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &args);
1028     if (ret)
1029         return ret;
1030 
1031     *handle = args.handle;
1032     return 0;
1033 }
1034