xf86drm.c revision 87bf8e7c
122944501Smrg/**
2fe517fc9Smrg * \file xf86drm.c
322944501Smrg * User-level interface to DRM device
422944501Smrg *
522944501Smrg * \author Rickard E. (Rik) Faith <faith@valinux.com>
622944501Smrg * \author Kevin E. Martin <martin@valinux.com>
722944501Smrg */
822944501Smrg
922944501Smrg/*
1022944501Smrg * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
1122944501Smrg * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
1222944501Smrg * All Rights Reserved.
1322944501Smrg *
1422944501Smrg * Permission is hereby granted, free of charge, to any person obtaining a
1522944501Smrg * copy of this software and associated documentation files (the "Software"),
1622944501Smrg * to deal in the Software without restriction, including without limitation
1722944501Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1822944501Smrg * and/or sell copies of the Software, and to permit persons to whom the
1922944501Smrg * Software is furnished to do so, subject to the following conditions:
2022944501Smrg *
2122944501Smrg * The above copyright notice and this permission notice (including the next
2222944501Smrg * paragraph) shall be included in all copies or substantial portions of the
2322944501Smrg * Software.
2422944501Smrg *
2522944501Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2622944501Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2722944501Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2822944501Smrg * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
2922944501Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
3022944501Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
3122944501Smrg * DEALINGS IN THE SOFTWARE.
3222944501Smrg */
3322944501Smrg
3422944501Smrg#include <stdio.h>
3522944501Smrg#include <stdlib.h>
36fe517fc9Smrg#include <stdbool.h>
3722944501Smrg#include <unistd.h>
3822944501Smrg#include <string.h>
3922944501Smrg#include <strings.h>
4022944501Smrg#include <ctype.h>
41424e9256Smrg#include <dirent.h>
42424e9256Smrg#include <stddef.h>
4322944501Smrg#include <fcntl.h>
4422944501Smrg#include <errno.h>
45fe517fc9Smrg#include <limits.h>
4622944501Smrg#include <signal.h>
4722944501Smrg#include <time.h>
4822944501Smrg#include <sys/types.h>
4922944501Smrg#include <sys/stat.h>
5022944501Smrg#define stat_t struct stat
5122944501Smrg#include <sys/ioctl.h>
5222944501Smrg#include <sys/time.h>
5322944501Smrg#include <stdarg.h>
54fe517fc9Smrg#ifdef MAJOR_IN_MKDEV
55fe517fc9Smrg#include <sys/mkdev.h>
56424e9256Smrg#endif
57fe517fc9Smrg#ifdef MAJOR_IN_SYSMACROS
58fe517fc9Smrg#include <sys/sysmacros.h>
59fe517fc9Smrg#endif
6087bf8e7cSmrg#if HAVE_SYS_SYSCTL_H
6187bf8e7cSmrg#include <sys/sysctl.h>
6287bf8e7cSmrg#endif
63fe517fc9Smrg#include <math.h>
6422944501Smrg
6587bf8e7cSmrg#if defined(__FreeBSD__)
6687bf8e7cSmrg#include <sys/param.h>
6787bf8e7cSmrg#include <sys/pciio.h>
6887bf8e7cSmrg#endif
6987bf8e7cSmrg
704545e80cSmrg#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
714545e80cSmrg
722ee35494Smrg/* Not all systems have MAP_FAILED defined */
732ee35494Smrg#ifndef MAP_FAILED
742ee35494Smrg#define MAP_FAILED ((void *)-1)
752ee35494Smrg#endif
7622944501Smrg
7722944501Smrg#include "xf86drm.h"
78424e9256Smrg#include "libdrm_macros.h"
7922944501Smrg
80fe517fc9Smrg#include "util_math.h"
81fe517fc9Smrg
8287bf8e7cSmrg#ifdef __DragonFly__
8322944501Smrg#define DRM_MAJOR 145
8422944501Smrg#endif
8522944501Smrg
8622944501Smrg#ifdef __NetBSD__
872e6867f6Smrg#undef DRM_MAJOR
882e6867f6Smrg#define DRM_MAJOR 180
8906815bcbSmaya#include <sys/param.h>
90a970b457Sriastradh#include <dev/pci/pcireg.h>
91a970b457Sriastradh#include <pci.h>
9222944501Smrg#endif
9322944501Smrg
94fe517fc9Smrg#ifdef __OpenBSD__
95fe517fc9Smrg#ifdef __i386__
96fe517fc9Smrg#define DRM_MAJOR 88
97fe517fc9Smrg#else
98fe517fc9Smrg#define DRM_MAJOR 87
9922944501Smrg#endif
100fe517fc9Smrg#endif /* __OpenBSD__ */
10122944501Smrg
102fe517fc9Smrg#ifndef DRM_MAJOR
103fe517fc9Smrg#define DRM_MAJOR 226 /* Linux */
10422944501Smrg#endif
10522944501Smrg
1064545e80cSmrg#if defined(__OpenBSD__) || defined(__DragonFly__)
1072ee35494Smrgstruct drm_pciinfo {
1082ee35494Smrg	uint16_t	domain;
1092ee35494Smrg	uint8_t		bus;
1102ee35494Smrg	uint8_t		dev;
1112ee35494Smrg	uint8_t		func;
1122ee35494Smrg	uint16_t	vendor_id;
1132ee35494Smrg	uint16_t	device_id;
1142ee35494Smrg	uint16_t	subvendor_id;
1152ee35494Smrg	uint16_t	subdevice_id;
1162ee35494Smrg	uint8_t		revision_id;
1172ee35494Smrg};
1182ee35494Smrg
1192ee35494Smrg#define DRM_IOCTL_GET_PCIINFO	DRM_IOR(0x15, struct drm_pciinfo)
12011c53d23Schristos#endif
12111c53d23Schristos
12222944501Smrg#define DRM_MSG_VERBOSITY 3
12322944501Smrg
124424e9256Smrg#define memclear(s) memset(&s, 0, sizeof(s))
12522944501Smrg
12622944501Smrgstatic drmServerInfoPtr drm_server_info;
12722944501Smrg
12887bf8e7cSmrgstatic bool drmNodeIsDRM(int maj, int min);
12987bf8e7cSmrgstatic char *drmGetMinorNameForFD(int fd, int type);
13087bf8e7cSmrg
1316260e5d5Smrgdrm_public void drmSetServerInfo(drmServerInfoPtr info)
13222944501Smrg{
13322944501Smrg    drm_server_info = info;
13422944501Smrg}
13522944501Smrg
13622944501Smrg/**
13722944501Smrg * Output a message to stderr.
13822944501Smrg *
13922944501Smrg * \param format printf() like format string.
14022944501Smrg *
14122944501Smrg * \internal
14222944501Smrg * This function is a wrapper around vfprintf().
14322944501Smrg */
14422944501Smrg
145a7d7de1eSmrgstatic int DRM_PRINTFLIKE(1, 0)
146a7d7de1eSmrgdrmDebugPrint(const char *format, va_list ap)
14722944501Smrg{
14822944501Smrg    return vfprintf(stderr, format, ap);
14922944501Smrg}
15022944501Smrg
1516260e5d5Smrgdrm_public void
15222944501SmrgdrmMsg(const char *format, ...)
15322944501Smrg{
154fe517fc9Smrg    va_list ap;
15522944501Smrg    const char *env;
156fe517fc9Smrg    if (((env = getenv("LIBGL_DEBUG")) && strstr(env, "verbose")) ||
157fe517fc9Smrg        (drm_server_info && drm_server_info->debug_print))
15822944501Smrg    {
159fe517fc9Smrg        va_start(ap, format);
160fe517fc9Smrg        if (drm_server_info) {
161fe517fc9Smrg            drm_server_info->debug_print(format,ap);
162fe517fc9Smrg        } else {
163fe517fc9Smrg            drmDebugPrint(format, ap);
164fe517fc9Smrg        }
165fe517fc9Smrg        va_end(ap);
16622944501Smrg    }
16722944501Smrg}
16822944501Smrg
16922944501Smrgstatic void *drmHashTable = NULL; /* Context switch callbacks */
17022944501Smrg
1716260e5d5Smrgdrm_public void *drmGetHashTable(void)
17222944501Smrg{
17322944501Smrg    return drmHashTable;
17422944501Smrg}
17522944501Smrg
1766260e5d5Smrgdrm_public void *drmMalloc(int size)
17722944501Smrg{
178424e9256Smrg    return calloc(1, size);
17922944501Smrg}
18022944501Smrg
1816260e5d5Smrgdrm_public void drmFree(void *pt)
18222944501Smrg{
183424e9256Smrg    free(pt);
18422944501Smrg}
18522944501Smrg
18622944501Smrg/**
187bf6cc7dcSmrg * Call ioctl, restarting if it is interrupted
18822944501Smrg */
1896260e5d5Smrgdrm_public int
19022944501SmrgdrmIoctl(int fd, unsigned long request, void *arg)
19122944501Smrg{
192fe517fc9Smrg    int ret;
19322944501Smrg
19422944501Smrg    do {
195fe517fc9Smrg        ret = ioctl(fd, request, arg);
19622944501Smrg    } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
19722944501Smrg    return ret;
19822944501Smrg}
19922944501Smrg
20022944501Smrgstatic unsigned long drmGetKeyFromFd(int fd)
20122944501Smrg{
20222944501Smrg    stat_t     st;
20322944501Smrg
20422944501Smrg    st.st_rdev = 0;
20522944501Smrg    fstat(fd, &st);
20622944501Smrg    return st.st_rdev;
20722944501Smrg}
20822944501Smrg
2096260e5d5Smrgdrm_public drmHashEntry *drmGetEntry(int fd)
21022944501Smrg{
21122944501Smrg    unsigned long key = drmGetKeyFromFd(fd);
21222944501Smrg    void          *value;
21322944501Smrg    drmHashEntry  *entry;
21422944501Smrg
21522944501Smrg    if (!drmHashTable)
216fe517fc9Smrg        drmHashTable = drmHashCreate();
21722944501Smrg
21822944501Smrg    if (drmHashLookup(drmHashTable, key, &value)) {
219fe517fc9Smrg        entry           = drmMalloc(sizeof(*entry));
220fe517fc9Smrg        entry->fd       = fd;
221fe517fc9Smrg        entry->f        = NULL;
222fe517fc9Smrg        entry->tagTable = drmHashCreate();
223fe517fc9Smrg        drmHashInsert(drmHashTable, key, entry);
22422944501Smrg    } else {
225fe517fc9Smrg        entry = value;
22622944501Smrg    }
22722944501Smrg    return entry;
22822944501Smrg}
22922944501Smrg
23022944501Smrg/**
23122944501Smrg * Compare two busid strings
23222944501Smrg *
23322944501Smrg * \param first
23422944501Smrg * \param second
23522944501Smrg *
23622944501Smrg * \return 1 if matched.
23722944501Smrg *
23822944501Smrg * \internal
23922944501Smrg * This function compares two bus ID strings.  It understands the older
24022944501Smrg * PCI:b:d:f format and the newer pci:oooo:bb:dd.f format.  In the format, o is
24122944501Smrg * domain, b is bus, d is device, f is function.
24222944501Smrg */
2436d98c517Smrgstatic int drmMatchBusID(const char *id1, const char *id2, int pci_domain_ok)
24422944501Smrg{
24522944501Smrg    /* First, check if the IDs are exactly the same */
24622944501Smrg    if (strcasecmp(id1, id2) == 0)
247fe517fc9Smrg        return 1;
24822944501Smrg
24922944501Smrg    /* Try to match old/new-style PCI bus IDs. */
25022944501Smrg    if (strncasecmp(id1, "pci", 3) == 0) {
251fe517fc9Smrg        unsigned int o1, b1, d1, f1;
252fe517fc9Smrg        unsigned int o2, b2, d2, f2;
253fe517fc9Smrg        int ret;
254fe517fc9Smrg
255fe517fc9Smrg        ret = sscanf(id1, "pci:%04x:%02x:%02x.%u", &o1, &b1, &d1, &f1);
256fe517fc9Smrg        if (ret != 4) {
257fe517fc9Smrg            o1 = 0;
258fe517fc9Smrg            ret = sscanf(id1, "PCI:%u:%u:%u", &b1, &d1, &f1);
259fe517fc9Smrg            if (ret != 3)
260fe517fc9Smrg                return 0;
261fe517fc9Smrg        }
262fe517fc9Smrg
263fe517fc9Smrg        ret = sscanf(id2, "pci:%04x:%02x:%02x.%u", &o2, &b2, &d2, &f2);
264fe517fc9Smrg        if (ret != 4) {
265fe517fc9Smrg            o2 = 0;
266fe517fc9Smrg            ret = sscanf(id2, "PCI:%u:%u:%u", &b2, &d2, &f2);
267fe517fc9Smrg            if (ret != 3)
268fe517fc9Smrg                return 0;
269fe517fc9Smrg        }
270fe517fc9Smrg
271fe517fc9Smrg        /* If domains aren't properly supported by the kernel interface,
272fe517fc9Smrg         * just ignore them, which sucks less than picking a totally random
273fe517fc9Smrg         * card with "open by name"
274fe517fc9Smrg         */
275fe517fc9Smrg        if (!pci_domain_ok)
276fe517fc9Smrg            o1 = o2 = 0;
277fe517fc9Smrg
278fe517fc9Smrg        if ((o1 != o2) || (b1 != b2) || (d1 != d2) || (f1 != f2))
279fe517fc9Smrg            return 0;
280fe517fc9Smrg        else
281fe517fc9Smrg            return 1;
28222944501Smrg    }
28322944501Smrg    return 0;
28422944501Smrg}
28522944501Smrg
28622944501Smrg/**
28722944501Smrg * Handles error checking for chown call.
28822944501Smrg *
28922944501Smrg * \param path to file.
29022944501Smrg * \param id of the new owner.
29122944501Smrg * \param id of the new group.
29222944501Smrg *
29322944501Smrg * \return zero if success or -1 if failure.
29422944501Smrg *
29522944501Smrg * \internal
29622944501Smrg * Checks for failure. If failure was caused by signal call chown again.
297bf6cc7dcSmrg * If any other failure happened then it will output error message using
29822944501Smrg * drmMsg() call.
29922944501Smrg */
3006260e5d5Smrg#if !UDEV
30122944501Smrgstatic int chown_check_return(const char *path, uid_t owner, gid_t group)
30222944501Smrg{
303fe517fc9Smrg        int rv;
30422944501Smrg
305fe517fc9Smrg        do {
306fe517fc9Smrg            rv = chown(path, owner, group);
307fe517fc9Smrg        } while (rv != 0 && errno == EINTR);
30822944501Smrg
309fe517fc9Smrg        if (rv == 0)
310fe517fc9Smrg            return 0;
31122944501Smrg
312fe517fc9Smrg        drmMsg("Failed to change owner or group for file %s! %d: %s\n",
313fe517fc9Smrg               path, errno, strerror(errno));
314fe517fc9Smrg        return -1;
31522944501Smrg}
316424e9256Smrg#endif
31722944501Smrg
31882025ec7Smrgstatic const char *drmGetDeviceName(int type)
31982025ec7Smrg{
32082025ec7Smrg    switch (type) {
32182025ec7Smrg    case DRM_NODE_PRIMARY:
32282025ec7Smrg        return DRM_DEV_NAME;
32382025ec7Smrg    case DRM_NODE_CONTROL:
32482025ec7Smrg        return DRM_CONTROL_DEV_NAME;
32582025ec7Smrg    case DRM_NODE_RENDER:
32682025ec7Smrg        return DRM_RENDER_DEV_NAME;
32782025ec7Smrg    }
32882025ec7Smrg    return NULL;
32982025ec7Smrg}
33082025ec7Smrg
33122944501Smrg/**
33222944501Smrg * Open the DRM device, creating it if necessary.
33322944501Smrg *
33422944501Smrg * \param dev major and minor numbers of the device.
33522944501Smrg * \param minor minor number of the device.
336fe517fc9Smrg *
33722944501Smrg * \return a file descriptor on success, or a negative value on error.
33822944501Smrg *
33922944501Smrg * \internal
34022944501Smrg * Assembles the device name from \p minor and opens it, creating the device
34122944501Smrg * special file node with the major and minor numbers specified by \p dev and
34222944501Smrg * parent directory if necessary and was called by root.
34322944501Smrg */
344424e9256Smrgstatic int drmOpenDevice(dev_t dev, int minor, int type)
34522944501Smrg{
34622944501Smrg    stat_t          st;
34782025ec7Smrg    const char      *dev_name = drmGetDeviceName(type);
34882025ec7Smrg    char            buf[DRM_NODE_NAME_MAX];
34922944501Smrg    int             fd;
35022944501Smrg    mode_t          devmode = DRM_DEV_MODE, serv_mode;
351424e9256Smrg    gid_t           serv_group;
3526260e5d5Smrg#if !UDEV
35322944501Smrg    int             isroot  = !geteuid();
35422944501Smrg    uid_t           user    = DRM_DEV_UID;
355424e9256Smrg    gid_t           group   = DRM_DEV_GID;
356424e9256Smrg#endif
357424e9256Smrg
35882025ec7Smrg    if (!dev_name)
359fe517fc9Smrg        return -EINVAL;
360424e9256Smrg
361424e9256Smrg    sprintf(buf, dev_name, DRM_DIR_NAME, minor);
36222944501Smrg    drmMsg("drmOpenDevice: node name is %s\n", buf);
36322944501Smrg
364fe517fc9Smrg    if (drm_server_info && drm_server_info->get_perms) {
365fe517fc9Smrg        drm_server_info->get_perms(&serv_group, &serv_mode);
366fe517fc9Smrg        devmode  = serv_mode ? serv_mode : DRM_DEV_MODE;
367fe517fc9Smrg        devmode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
36822944501Smrg    }
36922944501Smrg
3706260e5d5Smrg#if !UDEV
37122944501Smrg    if (stat(DRM_DIR_NAME, &st)) {
372fe517fc9Smrg        if (!isroot)
373fe517fc9Smrg            return DRM_ERR_NOT_ROOT;
374fe517fc9Smrg        mkdir(DRM_DIR_NAME, DRM_DEV_DIRMODE);
375fe517fc9Smrg        chown_check_return(DRM_DIR_NAME, 0, 0); /* root:root */
376fe517fc9Smrg        chmod(DRM_DIR_NAME, DRM_DEV_DIRMODE);
37722944501Smrg    }
37822944501Smrg
37922944501Smrg    /* Check if the device node exists and create it if necessary. */
38022944501Smrg    if (stat(buf, &st)) {
381fe517fc9Smrg        if (!isroot)
382fe517fc9Smrg            return DRM_ERR_NOT_ROOT;
383fe517fc9Smrg        remove(buf);
384fe517fc9Smrg        mknod(buf, S_IFCHR | devmode, dev);
38522944501Smrg    }
38622944501Smrg
387fe517fc9Smrg    if (drm_server_info && drm_server_info->get_perms) {
388fe517fc9Smrg        group = ((int)serv_group >= 0) ? serv_group : DRM_DEV_GID;
389fe517fc9Smrg        chown_check_return(buf, user, group);
390fe517fc9Smrg        chmod(buf, devmode);
39122944501Smrg    }
39222944501Smrg#else
39322944501Smrg    /* if we modprobed then wait for udev */
39422944501Smrg    {
395fe517fc9Smrg        int udev_count = 0;
39622944501Smrgwait_for_udev:
39722944501Smrg        if (stat(DRM_DIR_NAME, &st)) {
398fe517fc9Smrg            usleep(20);
399fe517fc9Smrg            udev_count++;
400fe517fc9Smrg
401fe517fc9Smrg            if (udev_count == 50)
402fe517fc9Smrg                return -1;
403fe517fc9Smrg            goto wait_for_udev;
404fe517fc9Smrg        }
405fe517fc9Smrg
406fe517fc9Smrg        if (stat(buf, &st)) {
407fe517fc9Smrg            usleep(20);
408fe517fc9Smrg            udev_count++;
409fe517fc9Smrg
410fe517fc9Smrg            if (udev_count == 50)
411fe517fc9Smrg                return -1;
412fe517fc9Smrg            goto wait_for_udev;
413fe517fc9Smrg        }
41422944501Smrg    }
41522944501Smrg#endif
41622944501Smrg
4176260e5d5Smrg    fd = open(buf, O_RDWR | O_CLOEXEC, 0);
41822944501Smrg    drmMsg("drmOpenDevice: open result is %d, (%s)\n",
419fe517fc9Smrg           fd, fd < 0 ? strerror(errno) : "OK");
42022944501Smrg    if (fd >= 0)
421fe517fc9Smrg        return fd;
42222944501Smrg
4236260e5d5Smrg#if !UDEV
42422944501Smrg    /* Check if the device node is not what we expect it to be, and recreate it
42522944501Smrg     * and try again if so.
42622944501Smrg     */
42722944501Smrg    if (st.st_rdev != dev) {
428fe517fc9Smrg        if (!isroot)
429fe517fc9Smrg            return DRM_ERR_NOT_ROOT;
430fe517fc9Smrg        remove(buf);
431fe517fc9Smrg        mknod(buf, S_IFCHR | devmode, dev);
432fe517fc9Smrg        if (drm_server_info && drm_server_info->get_perms) {
433fe517fc9Smrg            chown_check_return(buf, user, group);
434fe517fc9Smrg            chmod(buf, devmode);
435fe517fc9Smrg        }
43622944501Smrg    }
4376260e5d5Smrg    fd = open(buf, O_RDWR | O_CLOEXEC, 0);
43822944501Smrg    drmMsg("drmOpenDevice: open result is %d, (%s)\n",
439fe517fc9Smrg           fd, fd < 0 ? strerror(errno) : "OK");
44022944501Smrg    if (fd >= 0)
441fe517fc9Smrg        return fd;
44222944501Smrg
44322944501Smrg    drmMsg("drmOpenDevice: Open failed\n");
44422944501Smrg    remove(buf);
4459ce4edccSmrg#endif
44622944501Smrg    return -errno;
44722944501Smrg}
44822944501Smrg
44922944501Smrg
45022944501Smrg/**
45122944501Smrg * Open the DRM device
45222944501Smrg *
45322944501Smrg * \param minor device minor number.
45422944501Smrg * \param create allow to create the device if set.
45522944501Smrg *
45622944501Smrg * \return a file descriptor on success, or a negative value on error.
457fe517fc9Smrg *
45822944501Smrg * \internal
45922944501Smrg * Calls drmOpenDevice() if \p create is set, otherwise assembles the device
46022944501Smrg * name from \p minor and opens it.
46122944501Smrg */
46222944501Smrgstatic int drmOpenMinor(int minor, int create, int type)
46322944501Smrg{
46422944501Smrg    int  fd;
46582025ec7Smrg    char buf[DRM_NODE_NAME_MAX];
46682025ec7Smrg    const char *dev_name = drmGetDeviceName(type);
467fe517fc9Smrg
46822944501Smrg    if (create)
469fe517fc9Smrg        return drmOpenDevice(makedev(DRM_MAJOR, minor), minor, type);
470fe517fc9Smrg
47182025ec7Smrg    if (!dev_name)
472fe517fc9Smrg        return -EINVAL;
473424e9256Smrg
474424e9256Smrg    sprintf(buf, dev_name, DRM_DIR_NAME, minor);
4756260e5d5Smrg    if ((fd = open(buf, O_RDWR | O_CLOEXEC, 0)) >= 0)
476fe517fc9Smrg        return fd;
47722944501Smrg    return -errno;
47822944501Smrg}
47922944501Smrg
48022944501Smrg
48122944501Smrg/**
48222944501Smrg * Determine whether the DRM kernel driver has been loaded.
483fe517fc9Smrg *
48422944501Smrg * \return 1 if the DRM driver is loaded, 0 otherwise.
48522944501Smrg *
486fe517fc9Smrg * \internal
48722944501Smrg * Determine the presence of the kernel driver by attempting to open the 0
48822944501Smrg * minor and get version information.  For backward compatibility with older
48922944501Smrg * Linux implementations, /proc/dri is also checked.
49022944501Smrg */
4916260e5d5Smrgdrm_public int drmAvailable(void)
49222944501Smrg{
49322944501Smrg    drmVersionPtr version;
49422944501Smrg    int           retval = 0;
49522944501Smrg    int           fd;
49622944501Smrg
497424e9256Smrg    if ((fd = drmOpenMinor(0, 1, DRM_NODE_PRIMARY)) < 0) {
49822944501Smrg#ifdef __linux__
499fe517fc9Smrg        /* Try proc for backward Linux compatibility */
500fe517fc9Smrg        if (!access("/proc/dri/0", R_OK))
501fe517fc9Smrg            return 1;
50222944501Smrg#endif
503fe517fc9Smrg        return 0;
50422944501Smrg    }
505fe517fc9Smrg
50622944501Smrg    if ((version = drmGetVersion(fd))) {
507fe517fc9Smrg        retval = 1;
508fe517fc9Smrg        drmFreeVersion(version);
50922944501Smrg    }
51022944501Smrg    close(fd);
51122944501Smrg
51222944501Smrg    return retval;
51322944501Smrg}
51422944501Smrg
515424e9256Smrgstatic int drmGetMinorBase(int type)
516424e9256Smrg{
517424e9256Smrg    switch (type) {
518424e9256Smrg    case DRM_NODE_PRIMARY:
519424e9256Smrg        return 0;
520424e9256Smrg    case DRM_NODE_CONTROL:
521424e9256Smrg        return 64;
522424e9256Smrg    case DRM_NODE_RENDER:
523424e9256Smrg        return 128;
524424e9256Smrg    default:
525424e9256Smrg        return -1;
526424e9256Smrg    };
527424e9256Smrg}
528424e9256Smrg
52987bf8e7cSmrgstatic int drmGetMinorType(int major, int minor)
530424e9256Smrg{
53187bf8e7cSmrg#ifdef __FreeBSD__
53287bf8e7cSmrg    char name[SPECNAMELEN];
53387bf8e7cSmrg    int id;
53487bf8e7cSmrg
53587bf8e7cSmrg    if (!devname_r(makedev(major, minor), S_IFCHR, name, sizeof(name)))
53687bf8e7cSmrg        return -1;
53787bf8e7cSmrg
53887bf8e7cSmrg    if (sscanf(name, "drm/%d", &id) != 1) {
53987bf8e7cSmrg        // If not in /dev/drm/ we have the type in the name
54087bf8e7cSmrg        if (sscanf(name, "dri/card%d\n", &id) >= 1)
54187bf8e7cSmrg           return DRM_NODE_PRIMARY;
54287bf8e7cSmrg        else if (sscanf(name, "dri/control%d\n", &id) >= 1)
54387bf8e7cSmrg           return DRM_NODE_CONTROL;
54487bf8e7cSmrg        else if (sscanf(name, "dri/renderD%d\n", &id) >= 1)
54587bf8e7cSmrg           return DRM_NODE_RENDER;
54687bf8e7cSmrg        return -1;
54787bf8e7cSmrg    }
54887bf8e7cSmrg
54987bf8e7cSmrg    minor = id;
55087bf8e7cSmrg#endif
551424e9256Smrg    int type = minor >> 6;
552424e9256Smrg
553424e9256Smrg    if (minor < 0)
554424e9256Smrg        return -1;
555424e9256Smrg
556424e9256Smrg    switch (type) {
557424e9256Smrg    case DRM_NODE_PRIMARY:
558424e9256Smrg    case DRM_NODE_CONTROL:
559424e9256Smrg    case DRM_NODE_RENDER:
560424e9256Smrg        return type;
561424e9256Smrg    default:
562424e9256Smrg        return -1;
563424e9256Smrg    }
564424e9256Smrg}
565424e9256Smrg
566424e9256Smrgstatic const char *drmGetMinorName(int type)
567424e9256Smrg{
568424e9256Smrg    switch (type) {
569424e9256Smrg    case DRM_NODE_PRIMARY:
570fe517fc9Smrg        return DRM_PRIMARY_MINOR_NAME;
571424e9256Smrg    case DRM_NODE_CONTROL:
572fe517fc9Smrg        return DRM_CONTROL_MINOR_NAME;
573424e9256Smrg    case DRM_NODE_RENDER:
574fe517fc9Smrg        return DRM_RENDER_MINOR_NAME;
575424e9256Smrg    default:
576424e9256Smrg        return NULL;
577424e9256Smrg    }
578424e9256Smrg}
57922944501Smrg
58022944501Smrg/**
58122944501Smrg * Open the device by bus ID.
58222944501Smrg *
58322944501Smrg * \param busid bus ID.
584424e9256Smrg * \param type device node type.
58522944501Smrg *
58622944501Smrg * \return a file descriptor on success, or a negative value on error.
58722944501Smrg *
58822944501Smrg * \internal
58922944501Smrg * This function attempts to open every possible minor (up to DRM_MAX_MINOR),
59022944501Smrg * comparing the device bus ID with the one supplied.
59122944501Smrg *
59222944501Smrg * \sa drmOpenMinor() and drmGetBusid().
59322944501Smrg */
594424e9256Smrgstatic int drmOpenByBusid(const char *busid, int type)
59522944501Smrg{
5966d98c517Smrg    int        i, pci_domain_ok = 1;
59722944501Smrg    int        fd;
59822944501Smrg    const char *buf;
59922944501Smrg    drmSetVersion sv;
600424e9256Smrg    int        base = drmGetMinorBase(type);
601424e9256Smrg
602424e9256Smrg    if (base < 0)
603424e9256Smrg        return -1;
60422944501Smrg
60522944501Smrg    drmMsg("drmOpenByBusid: Searching for BusID %s\n", busid);
606424e9256Smrg    for (i = base; i < base + DRM_MAX_MINOR; i++) {
607fe517fc9Smrg        fd = drmOpenMinor(i, 1, type);
608fe517fc9Smrg        drmMsg("drmOpenByBusid: drmOpenMinor returns %d\n", fd);
609fe517fc9Smrg        if (fd >= 0) {
610fe517fc9Smrg            /* We need to try for 1.4 first for proper PCI domain support
611fe517fc9Smrg             * and if that fails, we know the kernel is busted
612fe517fc9Smrg             */
613fe517fc9Smrg            sv.drm_di_major = 1;
614fe517fc9Smrg            sv.drm_di_minor = 4;
615fe517fc9Smrg            sv.drm_dd_major = -1;        /* Don't care */
616fe517fc9Smrg            sv.drm_dd_minor = -1;        /* Don't care */
617fe517fc9Smrg            if (drmSetInterfaceVersion(fd, &sv)) {
6186d98c517Smrg#ifndef __alpha__
619fe517fc9Smrg                pci_domain_ok = 0;
6206d98c517Smrg#endif
621fe517fc9Smrg                sv.drm_di_major = 1;
622fe517fc9Smrg                sv.drm_di_minor = 1;
623fe517fc9Smrg                sv.drm_dd_major = -1;       /* Don't care */
624fe517fc9Smrg                sv.drm_dd_minor = -1;       /* Don't care */
625fe517fc9Smrg                drmMsg("drmOpenByBusid: Interface 1.4 failed, trying 1.1\n");
626fe517fc9Smrg                drmSetInterfaceVersion(fd, &sv);
627fe517fc9Smrg            }
628fe517fc9Smrg            buf = drmGetBusid(fd);
629fe517fc9Smrg            drmMsg("drmOpenByBusid: drmGetBusid reports %s\n", buf);
630fe517fc9Smrg            if (buf && drmMatchBusID(buf, busid, pci_domain_ok)) {
631fe517fc9Smrg                drmFreeBusid(buf);
632fe517fc9Smrg                return fd;
633fe517fc9Smrg            }
634fe517fc9Smrg            if (buf)
635fe517fc9Smrg                drmFreeBusid(buf);
636fe517fc9Smrg            close(fd);
637fe517fc9Smrg        }
63822944501Smrg    }
63922944501Smrg    return -1;
64022944501Smrg}
64122944501Smrg
64222944501Smrg
64322944501Smrg/**
64422944501Smrg * Open the device by name.
64522944501Smrg *
64622944501Smrg * \param name driver name.
647424e9256Smrg * \param type the device node type.
648fe517fc9Smrg *
64922944501Smrg * \return a file descriptor on success, or a negative value on error.
650fe517fc9Smrg *
65122944501Smrg * \internal
65222944501Smrg * This function opens the first minor number that matches the driver name and
65322944501Smrg * isn't already in use.  If it's in use it then it will already have a bus ID
65422944501Smrg * assigned.
655fe517fc9Smrg *
65622944501Smrg * \sa drmOpenMinor(), drmGetVersion() and drmGetBusid().
65722944501Smrg */
658424e9256Smrgstatic int drmOpenByName(const char *name, int type)
65922944501Smrg{
66022944501Smrg    int           i;
66122944501Smrg    int           fd;
66222944501Smrg    drmVersionPtr version;
66322944501Smrg    char *        id;
664424e9256Smrg    int           base = drmGetMinorBase(type);
665424e9256Smrg
666424e9256Smrg    if (base < 0)
667424e9256Smrg        return -1;
66822944501Smrg
66922944501Smrg    /*
67022944501Smrg     * Open the first minor number that matches the driver name and isn't
67122944501Smrg     * already in use.  If it's in use it will have a busid assigned already.
67222944501Smrg     */
673424e9256Smrg    for (i = base; i < base + DRM_MAX_MINOR; i++) {
674fe517fc9Smrg        if ((fd = drmOpenMinor(i, 1, type)) >= 0) {
675fe517fc9Smrg            if ((version = drmGetVersion(fd))) {
676fe517fc9Smrg                if (!strcmp(version->name, name)) {
677fe517fc9Smrg                    drmFreeVersion(version);
678fe517fc9Smrg                    id = drmGetBusid(fd);
679fe517fc9Smrg                    drmMsg("drmGetBusid returned '%s'\n", id ? id : "NULL");
680fe517fc9Smrg                    if (!id || !*id) {
681fe517fc9Smrg                        if (id)
682fe517fc9Smrg                            drmFreeBusid(id);
683fe517fc9Smrg                        return fd;
684fe517fc9Smrg                    } else {
685fe517fc9Smrg                        drmFreeBusid(id);
686fe517fc9Smrg                    }
687fe517fc9Smrg                } else {
688fe517fc9Smrg                    drmFreeVersion(version);
689fe517fc9Smrg                }
690fe517fc9Smrg            }
691fe517fc9Smrg            close(fd);
692fe517fc9Smrg        }
69322944501Smrg    }
69422944501Smrg
69522944501Smrg#ifdef __linux__
69622944501Smrg    /* Backward-compatibility /proc support */
69722944501Smrg    for (i = 0; i < 8; i++) {
698fe517fc9Smrg        char proc_name[64], buf[512];
699fe517fc9Smrg        char *driver, *pt, *devstring;
700fe517fc9Smrg        int  retcode;
701fe517fc9Smrg
702fe517fc9Smrg        sprintf(proc_name, "/proc/dri/%d/name", i);
703fe517fc9Smrg        if ((fd = open(proc_name, 0, 0)) >= 0) {
704fe517fc9Smrg            retcode = read(fd, buf, sizeof(buf)-1);
705fe517fc9Smrg            close(fd);
706fe517fc9Smrg            if (retcode) {
707fe517fc9Smrg                buf[retcode-1] = '\0';
708fe517fc9Smrg                for (driver = pt = buf; *pt && *pt != ' '; ++pt)
709fe517fc9Smrg                    ;
710fe517fc9Smrg                if (*pt) { /* Device is next */
711fe517fc9Smrg                    *pt = '\0';
712fe517fc9Smrg                    if (!strcmp(driver, name)) { /* Match */
713fe517fc9Smrg                        for (devstring = ++pt; *pt && *pt != ' '; ++pt)
714fe517fc9Smrg                            ;
715fe517fc9Smrg                        if (*pt) { /* Found busid */
716fe517fc9Smrg                            return drmOpenByBusid(++pt, type);
717fe517fc9Smrg                        } else { /* No busid */
718fe517fc9Smrg                            return drmOpenDevice(strtol(devstring, NULL, 0),i, type);
719fe517fc9Smrg                        }
720fe517fc9Smrg                    }
721fe517fc9Smrg                }
722fe517fc9Smrg            }
723fe517fc9Smrg        }
72422944501Smrg    }
72522944501Smrg#endif
72622944501Smrg
72722944501Smrg    return -1;
72822944501Smrg}
72922944501Smrg
73022944501Smrg
73122944501Smrg/**
73222944501Smrg * Open the DRM device.
73322944501Smrg *
73422944501Smrg * Looks up the specified name and bus ID, and opens the device found.  The
73522944501Smrg * entry in /dev/dri is created if necessary and if called by root.
73622944501Smrg *
73722944501Smrg * \param name driver name. Not referenced if bus ID is supplied.
73822944501Smrg * \param busid bus ID. Zero if not known.
739fe517fc9Smrg *
74022944501Smrg * \return a file descriptor on success, or a negative value on error.
741fe517fc9Smrg *
74222944501Smrg * \internal
74322944501Smrg * It calls drmOpenByBusid() if \p busid is specified or drmOpenByName()
74422944501Smrg * otherwise.
74522944501Smrg */
7466260e5d5Smrgdrm_public int drmOpen(const char *name, const char *busid)
747424e9256Smrg{
748424e9256Smrg    return drmOpenWithType(name, busid, DRM_NODE_PRIMARY);
749424e9256Smrg}
750424e9256Smrg
751424e9256Smrg/**
752424e9256Smrg * Open the DRM device with specified type.
753424e9256Smrg *
754424e9256Smrg * Looks up the specified name and bus ID, and opens the device found.  The
755424e9256Smrg * entry in /dev/dri is created if necessary and if called by root.
756424e9256Smrg *
757424e9256Smrg * \param name driver name. Not referenced if bus ID is supplied.
758424e9256Smrg * \param busid bus ID. Zero if not known.
759424e9256Smrg * \param type the device node type to open, PRIMARY, CONTROL or RENDER
760424e9256Smrg *
761424e9256Smrg * \return a file descriptor on success, or a negative value on error.
762424e9256Smrg *
763424e9256Smrg * \internal
764424e9256Smrg * It calls drmOpenByBusid() if \p busid is specified or drmOpenByName()
765424e9256Smrg * otherwise.
766424e9256Smrg */
7676260e5d5Smrgdrm_public int drmOpenWithType(const char *name, const char *busid, int type)
76822944501Smrg{
769bf6cc7dcSmrg    if (name != NULL && drm_server_info &&
770bf6cc7dcSmrg        drm_server_info->load_module && !drmAvailable()) {
771fe517fc9Smrg        /* try to load the kernel module */
772fe517fc9Smrg        if (!drm_server_info->load_module(name)) {
773fe517fc9Smrg            drmMsg("[drm] failed to load kernel module \"%s\"\n", name);
774fe517fc9Smrg            return -1;
775fe517fc9Smrg        }
77622944501Smrg    }
77722944501Smrg
77822944501Smrg    if (busid) {
779fe517fc9Smrg        int fd = drmOpenByBusid(busid, type);
780fe517fc9Smrg        if (fd >= 0)
781fe517fc9Smrg            return fd;
78222944501Smrg    }
783fe517fc9Smrg
78422944501Smrg    if (name)
785fe517fc9Smrg        return drmOpenByName(name, type);
78622944501Smrg
78722944501Smrg    return -1;
78822944501Smrg}
78922944501Smrg
7906260e5d5Smrgdrm_public int drmOpenControl(int minor)
79122944501Smrg{
79222944501Smrg    return drmOpenMinor(minor, 0, DRM_NODE_CONTROL);
79322944501Smrg}
79422944501Smrg
7956260e5d5Smrgdrm_public int drmOpenRender(int minor)
796424e9256Smrg{
797424e9256Smrg    return drmOpenMinor(minor, 0, DRM_NODE_RENDER);
798424e9256Smrg}
799424e9256Smrg
80022944501Smrg/**
80122944501Smrg * Free the version information returned by drmGetVersion().
80222944501Smrg *
80322944501Smrg * \param v pointer to the version information.
80422944501Smrg *
80522944501Smrg * \internal
80622944501Smrg * It frees the memory pointed by \p %v as well as all the non-null strings
80722944501Smrg * pointers in it.
80822944501Smrg */
8096260e5d5Smrgdrm_public void drmFreeVersion(drmVersionPtr v)
81022944501Smrg{
81122944501Smrg    if (!v)
812fe517fc9Smrg        return;
81322944501Smrg    drmFree(v->name);
81422944501Smrg    drmFree(v->date);
81522944501Smrg    drmFree(v->desc);
81622944501Smrg    drmFree(v);
81722944501Smrg}
81822944501Smrg
81922944501Smrg
82022944501Smrg/**
82122944501Smrg * Free the non-public version information returned by the kernel.
82222944501Smrg *
82322944501Smrg * \param v pointer to the version information.
82422944501Smrg *
82522944501Smrg * \internal
82622944501Smrg * Used by drmGetVersion() to free the memory pointed by \p %v as well as all
82722944501Smrg * the non-null strings pointers in it.
82822944501Smrg */
82922944501Smrgstatic void drmFreeKernelVersion(drm_version_t *v)
83022944501Smrg{
83122944501Smrg    if (!v)
832fe517fc9Smrg        return;
83322944501Smrg    drmFree(v->name);
83422944501Smrg    drmFree(v->date);
83522944501Smrg    drmFree(v->desc);
83622944501Smrg    drmFree(v);
83722944501Smrg}
83822944501Smrg
83922944501Smrg
84022944501Smrg/**
84122944501Smrg * Copy version information.
842fe517fc9Smrg *
84322944501Smrg * \param d destination pointer.
84422944501Smrg * \param s source pointer.
845fe517fc9Smrg *
84622944501Smrg * \internal
84722944501Smrg * Used by drmGetVersion() to translate the information returned by the ioctl
84822944501Smrg * interface in a private structure into the public structure counterpart.
84922944501Smrg */
85022944501Smrgstatic void drmCopyVersion(drmVersionPtr d, const drm_version_t *s)
85122944501Smrg{
85222944501Smrg    d->version_major      = s->version_major;
85322944501Smrg    d->version_minor      = s->version_minor;
85422944501Smrg    d->version_patchlevel = s->version_patchlevel;
85522944501Smrg    d->name_len           = s->name_len;
8569ce4edccSmrg    d->name               = strdup(s->name);
85722944501Smrg    d->date_len           = s->date_len;
8589ce4edccSmrg    d->date               = strdup(s->date);
85922944501Smrg    d->desc_len           = s->desc_len;
8609ce4edccSmrg    d->desc               = strdup(s->desc);
86122944501Smrg}
86222944501Smrg
86322944501Smrg
86422944501Smrg/**
86522944501Smrg * Query the driver version information.
86622944501Smrg *
86722944501Smrg * \param fd file descriptor.
868fe517fc9Smrg *
86922944501Smrg * \return pointer to a drmVersion structure which should be freed with
87022944501Smrg * drmFreeVersion().
871fe517fc9Smrg *
87222944501Smrg * \note Similar information is available via /proc/dri.
873fe517fc9Smrg *
87422944501Smrg * \internal
87522944501Smrg * It gets the version information via successive DRM_IOCTL_VERSION ioctls,
87622944501Smrg * first with zeros to get the string lengths, and then the actually strings.
87722944501Smrg * It also null-terminates them since they might not be already.
87822944501Smrg */
8796260e5d5Smrgdrm_public drmVersionPtr drmGetVersion(int fd)
88022944501Smrg{
88122944501Smrg    drmVersionPtr retval;
88222944501Smrg    drm_version_t *version = drmMalloc(sizeof(*version));
88322944501Smrg
88422944501Smrg    if (drmIoctl(fd, DRM_IOCTL_VERSION, version)) {
885fe517fc9Smrg        drmFreeKernelVersion(version);
886fe517fc9Smrg        return NULL;
88722944501Smrg    }
88822944501Smrg
88922944501Smrg    if (version->name_len)
890fe517fc9Smrg        version->name    = drmMalloc(version->name_len + 1);
89122944501Smrg    if (version->date_len)
892fe517fc9Smrg        version->date    = drmMalloc(version->date_len + 1);
89322944501Smrg    if (version->desc_len)
894fe517fc9Smrg        version->desc    = drmMalloc(version->desc_len + 1);
89522944501Smrg
89622944501Smrg    if (drmIoctl(fd, DRM_IOCTL_VERSION, version)) {
897fe517fc9Smrg        drmMsg("DRM_IOCTL_VERSION: %s\n", strerror(errno));
898fe517fc9Smrg        drmFreeKernelVersion(version);
899fe517fc9Smrg        return NULL;
90022944501Smrg    }
90122944501Smrg
90222944501Smrg    /* The results might not be null-terminated strings, so terminate them. */
90322944501Smrg    if (version->name_len) version->name[version->name_len] = '\0';
90422944501Smrg    if (version->date_len) version->date[version->date_len] = '\0';
90522944501Smrg    if (version->desc_len) version->desc[version->desc_len] = '\0';
90622944501Smrg
90722944501Smrg    retval = drmMalloc(sizeof(*retval));
90822944501Smrg    drmCopyVersion(retval, version);
90922944501Smrg    drmFreeKernelVersion(version);
91022944501Smrg    return retval;
91122944501Smrg}
91222944501Smrg
91322944501Smrg
91422944501Smrg/**
91522944501Smrg * Get version information for the DRM user space library.
916fe517fc9Smrg *
91722944501Smrg * This version number is driver independent.
918fe517fc9Smrg *
91922944501Smrg * \param fd file descriptor.
92022944501Smrg *
92122944501Smrg * \return version information.
922fe517fc9Smrg *
92322944501Smrg * \internal
92422944501Smrg * This function allocates and fills a drm_version structure with a hard coded
92522944501Smrg * version number.
92622944501Smrg */
9276260e5d5Smrgdrm_public drmVersionPtr drmGetLibVersion(int fd)
92822944501Smrg{
92922944501Smrg    drm_version_t *version = drmMalloc(sizeof(*version));
93022944501Smrg
93122944501Smrg    /* Version history:
93222944501Smrg     *   NOTE THIS MUST NOT GO ABOVE VERSION 1.X due to drivers needing it
93322944501Smrg     *   revision 1.0.x = original DRM interface with no drmGetLibVersion
93422944501Smrg     *                    entry point and many drm<Device> extensions
93522944501Smrg     *   revision 1.1.x = added drmCommand entry points for device extensions
93622944501Smrg     *                    added drmGetLibVersion to identify libdrm.a version
93722944501Smrg     *   revision 1.2.x = added drmSetInterfaceVersion
93822944501Smrg     *                    modified drmOpen to handle both busid and name
93922944501Smrg     *   revision 1.3.x = added server + memory manager
94022944501Smrg     */
94122944501Smrg    version->version_major      = 1;
94222944501Smrg    version->version_minor      = 3;
94322944501Smrg    version->version_patchlevel = 0;
94422944501Smrg
94522944501Smrg    return (drmVersionPtr)version;
94622944501Smrg}
94722944501Smrg
9486260e5d5Smrgdrm_public int drmGetCap(int fd, uint64_t capability, uint64_t *value)
94920131375Smrg{
950fe517fc9Smrg    struct drm_get_cap cap;
951fe517fc9Smrg    int ret;
95220131375Smrg
953fe517fc9Smrg    memclear(cap);
954fe517fc9Smrg    cap.capability = capability;
955424e9256Smrg
956fe517fc9Smrg    ret = drmIoctl(fd, DRM_IOCTL_GET_CAP, &cap);
957fe517fc9Smrg    if (ret)
958fe517fc9Smrg        return ret;
95920131375Smrg
960fe517fc9Smrg    *value = cap.value;
961fe517fc9Smrg    return 0;
96220131375Smrg}
96320131375Smrg
9646260e5d5Smrgdrm_public int drmSetClientCap(int fd, uint64_t capability, uint64_t value)
96520131375Smrg{
966fe517fc9Smrg    struct drm_set_client_cap cap;
967424e9256Smrg
968fe517fc9Smrg    memclear(cap);
969fe517fc9Smrg    cap.capability = capability;
970fe517fc9Smrg    cap.value = value;
97120131375Smrg
972fe517fc9Smrg    return drmIoctl(fd, DRM_IOCTL_SET_CLIENT_CAP, &cap);
97320131375Smrg}
97422944501Smrg
97522944501Smrg/**
97622944501Smrg * Free the bus ID information.
97722944501Smrg *
97822944501Smrg * \param busid bus ID information string as given by drmGetBusid().
97922944501Smrg *
98022944501Smrg * \internal
98122944501Smrg * This function is just frees the memory pointed by \p busid.
98222944501Smrg */
9836260e5d5Smrgdrm_public void drmFreeBusid(const char *busid)
98422944501Smrg{
98522944501Smrg    drmFree((void *)busid);
98622944501Smrg}
98722944501Smrg
98822944501Smrg
98922944501Smrg/**
99022944501Smrg * Get the bus ID of the device.
99122944501Smrg *
99222944501Smrg * \param fd file descriptor.
99322944501Smrg *
99422944501Smrg * \return bus ID string.
99522944501Smrg *
99622944501Smrg * \internal
99722944501Smrg * This function gets the bus ID via successive DRM_IOCTL_GET_UNIQUE ioctls to
99822944501Smrg * get the string length and data, passing the arguments in a drm_unique
99922944501Smrg * structure.
100022944501Smrg */
10016260e5d5Smrgdrm_public char *drmGetBusid(int fd)
100222944501Smrg{
100322944501Smrg    drm_unique_t u;
100422944501Smrg
1005424e9256Smrg    memclear(u);
100622944501Smrg
100722944501Smrg    if (drmIoctl(fd, DRM_IOCTL_GET_UNIQUE, &u))
1008fe517fc9Smrg        return NULL;
100922944501Smrg    u.unique = drmMalloc(u.unique_len + 1);
10100655efefSmrg    if (drmIoctl(fd, DRM_IOCTL_GET_UNIQUE, &u)) {
10110655efefSmrg        drmFree(u.unique);
1012fe517fc9Smrg        return NULL;
10130655efefSmrg    }
101422944501Smrg    u.unique[u.unique_len] = '\0';
101522944501Smrg
101622944501Smrg    return u.unique;
101722944501Smrg}
101822944501Smrg
101922944501Smrg
102022944501Smrg/**
102122944501Smrg * Set the bus ID of the device.
102222944501Smrg *
102322944501Smrg * \param fd file descriptor.
102422944501Smrg * \param busid bus ID string.
102522944501Smrg *
102622944501Smrg * \return zero on success, negative on failure.
102722944501Smrg *
102822944501Smrg * \internal
102922944501Smrg * This function is a wrapper around the DRM_IOCTL_SET_UNIQUE ioctl, passing
103022944501Smrg * the arguments in a drm_unique structure.
103122944501Smrg */
10326260e5d5Smrgdrm_public int drmSetBusid(int fd, const char *busid)
103322944501Smrg{
103422944501Smrg    drm_unique_t u;
103522944501Smrg
1036424e9256Smrg    memclear(u);
103722944501Smrg    u.unique     = (char *)busid;
103822944501Smrg    u.unique_len = strlen(busid);
103922944501Smrg
104022944501Smrg    if (drmIoctl(fd, DRM_IOCTL_SET_UNIQUE, &u)) {
1041fe517fc9Smrg        return -errno;
104222944501Smrg    }
104322944501Smrg    return 0;
104422944501Smrg}
104522944501Smrg
10466260e5d5Smrgdrm_public int drmGetMagic(int fd, drm_magic_t * magic)
104722944501Smrg{
104822944501Smrg    drm_auth_t auth;
104922944501Smrg
1050424e9256Smrg    memclear(auth);
1051424e9256Smrg
105222944501Smrg    *magic = 0;
105322944501Smrg    if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth))
1054fe517fc9Smrg        return -errno;
105522944501Smrg    *magic = auth.magic;
105622944501Smrg    return 0;
105722944501Smrg}
105822944501Smrg
10596260e5d5Smrgdrm_public int drmAuthMagic(int fd, drm_magic_t magic)
106022944501Smrg{
106122944501Smrg    drm_auth_t auth;
106222944501Smrg
1063424e9256Smrg    memclear(auth);
106422944501Smrg    auth.magic = magic;
106522944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AUTH_MAGIC, &auth))
1066fe517fc9Smrg        return -errno;
106722944501Smrg    return 0;
106822944501Smrg}
106922944501Smrg
107022944501Smrg/**
107122944501Smrg * Specifies a range of memory that is available for mapping by a
107222944501Smrg * non-root process.
107322944501Smrg *
107422944501Smrg * \param fd file descriptor.
107522944501Smrg * \param offset usually the physical address. The actual meaning depends of
107622944501Smrg * the \p type parameter. See below.
107722944501Smrg * \param size of the memory in bytes.
107822944501Smrg * \param type type of the memory to be mapped.
107922944501Smrg * \param flags combination of several flags to modify the function actions.
108022944501Smrg * \param handle will be set to a value that may be used as the offset
108122944501Smrg * parameter for mmap().
1082fe517fc9Smrg *
108322944501Smrg * \return zero on success or a negative value on error.
108422944501Smrg *
108522944501Smrg * \par Mapping the frame buffer
108622944501Smrg * For the frame buffer
108722944501Smrg * - \p offset will be the physical address of the start of the frame buffer,
108822944501Smrg * - \p size will be the size of the frame buffer in bytes, and
108922944501Smrg * - \p type will be DRM_FRAME_BUFFER.
109022944501Smrg *
109122944501Smrg * \par
109222944501Smrg * The area mapped will be uncached. If MTRR support is available in the
1093fe517fc9Smrg * kernel, the frame buffer area will be set to write combining.
109422944501Smrg *
109522944501Smrg * \par Mapping the MMIO register area
109622944501Smrg * For the MMIO register area,
109722944501Smrg * - \p offset will be the physical address of the start of the register area,
109822944501Smrg * - \p size will be the size of the register area bytes, and
109922944501Smrg * - \p type will be DRM_REGISTERS.
110022944501Smrg * \par
1101fe517fc9Smrg * The area mapped will be uncached.
1102fe517fc9Smrg *
110322944501Smrg * \par Mapping the SAREA
110422944501Smrg * For the SAREA,
110522944501Smrg * - \p offset will be ignored and should be set to zero,
110622944501Smrg * - \p size will be the desired size of the SAREA in bytes,
110722944501Smrg * - \p type will be DRM_SHM.
1108fe517fc9Smrg *
110922944501Smrg * \par
111022944501Smrg * A shared memory area of the requested size will be created and locked in
111122944501Smrg * kernel memory. This area may be mapped into client-space by using the handle
1112fe517fc9Smrg * returned.
1113fe517fc9Smrg *
111422944501Smrg * \note May only be called by root.
111522944501Smrg *
111622944501Smrg * \internal
111722944501Smrg * This function is a wrapper around the DRM_IOCTL_ADD_MAP ioctl, passing
111822944501Smrg * the arguments in a drm_map structure.
111922944501Smrg */
11206260e5d5Smrgdrm_public int drmAddMap(int fd, drm_handle_t offset, drmSize size, drmMapType type,
11216260e5d5Smrg                         drmMapFlags flags, drm_handle_t *handle)
112222944501Smrg{
112322944501Smrg    drm_map_t map;
112422944501Smrg
1125424e9256Smrg    memclear(map);
112622944501Smrg    map.offset  = offset;
112722944501Smrg    map.size    = size;
112822944501Smrg    map.type    = type;
112922944501Smrg    map.flags   = flags;
113022944501Smrg    if (drmIoctl(fd, DRM_IOCTL_ADD_MAP, &map))
1131fe517fc9Smrg        return -errno;
113222944501Smrg    if (handle)
1133fe517fc9Smrg        *handle = (drm_handle_t)(uintptr_t)map.handle;
113422944501Smrg    return 0;
113522944501Smrg}
113622944501Smrg
11376260e5d5Smrgdrm_public int drmRmMap(int fd, drm_handle_t handle)
113822944501Smrg{
113922944501Smrg    drm_map_t map;
114022944501Smrg
1141424e9256Smrg    memclear(map);
114220131375Smrg    map.handle = (void *)(uintptr_t)handle;
114322944501Smrg
114422944501Smrg    if(drmIoctl(fd, DRM_IOCTL_RM_MAP, &map))
1145fe517fc9Smrg        return -errno;
114622944501Smrg    return 0;
114722944501Smrg}
114822944501Smrg
114922944501Smrg/**
115022944501Smrg * Make buffers available for DMA transfers.
1151fe517fc9Smrg *
115222944501Smrg * \param fd file descriptor.
115322944501Smrg * \param count number of buffers.
115422944501Smrg * \param size size of each buffer.
115522944501Smrg * \param flags buffer allocation flags.
1156fe517fc9Smrg * \param agp_offset offset in the AGP aperture
115722944501Smrg *
115822944501Smrg * \return number of buffers allocated, negative on error.
115922944501Smrg *
116022944501Smrg * \internal
116122944501Smrg * This function is a wrapper around DRM_IOCTL_ADD_BUFS ioctl.
116222944501Smrg *
116322944501Smrg * \sa drm_buf_desc.
116422944501Smrg */
11656260e5d5Smrgdrm_public int drmAddBufs(int fd, int count, int size, drmBufDescFlags flags,
11666260e5d5Smrg                          int agp_offset)
116722944501Smrg{
116822944501Smrg    drm_buf_desc_t request;
116922944501Smrg
1170424e9256Smrg    memclear(request);
117122944501Smrg    request.count     = count;
117222944501Smrg    request.size      = size;
117322944501Smrg    request.flags     = flags;
117422944501Smrg    request.agp_start = agp_offset;
117522944501Smrg
117622944501Smrg    if (drmIoctl(fd, DRM_IOCTL_ADD_BUFS, &request))
1177fe517fc9Smrg        return -errno;
117822944501Smrg    return request.count;
117922944501Smrg}
118022944501Smrg
11816260e5d5Smrgdrm_public int drmMarkBufs(int fd, double low, double high)
118222944501Smrg{
118322944501Smrg    drm_buf_info_t info;
118422944501Smrg    int            i;
118522944501Smrg
1186424e9256Smrg    memclear(info);
118722944501Smrg
118822944501Smrg    if (drmIoctl(fd, DRM_IOCTL_INFO_BUFS, &info))
1189fe517fc9Smrg        return -EINVAL;
119022944501Smrg
119122944501Smrg    if (!info.count)
1192fe517fc9Smrg        return -EINVAL;
119322944501Smrg
119422944501Smrg    if (!(info.list = drmMalloc(info.count * sizeof(*info.list))))
1195fe517fc9Smrg        return -ENOMEM;
119622944501Smrg
119722944501Smrg    if (drmIoctl(fd, DRM_IOCTL_INFO_BUFS, &info)) {
1198fe517fc9Smrg        int retval = -errno;
1199fe517fc9Smrg        drmFree(info.list);
1200fe517fc9Smrg        return retval;
120122944501Smrg    }
120222944501Smrg
120322944501Smrg    for (i = 0; i < info.count; i++) {
1204fe517fc9Smrg        info.list[i].low_mark  = low  * info.list[i].count;
1205fe517fc9Smrg        info.list[i].high_mark = high * info.list[i].count;
1206fe517fc9Smrg        if (drmIoctl(fd, DRM_IOCTL_MARK_BUFS, &info.list[i])) {
1207fe517fc9Smrg            int retval = -errno;
1208fe517fc9Smrg            drmFree(info.list);
1209fe517fc9Smrg            return retval;
1210fe517fc9Smrg        }
121122944501Smrg    }
121222944501Smrg    drmFree(info.list);
121322944501Smrg
121422944501Smrg    return 0;
121522944501Smrg}
121622944501Smrg
121722944501Smrg/**
121822944501Smrg * Free buffers.
121922944501Smrg *
122022944501Smrg * \param fd file descriptor.
122122944501Smrg * \param count number of buffers to free.
122222944501Smrg * \param list list of buffers to be freed.
122322944501Smrg *
122422944501Smrg * \return zero on success, or a negative value on failure.
1225fe517fc9Smrg *
122622944501Smrg * \note This function is primarily used for debugging.
1227fe517fc9Smrg *
122822944501Smrg * \internal
122922944501Smrg * This function is a wrapper around the DRM_IOCTL_FREE_BUFS ioctl, passing
123022944501Smrg * the arguments in a drm_buf_free structure.
123122944501Smrg */
12326260e5d5Smrgdrm_public int drmFreeBufs(int fd, int count, int *list)
123322944501Smrg{
123422944501Smrg    drm_buf_free_t request;
123522944501Smrg
1236424e9256Smrg    memclear(request);
123722944501Smrg    request.count = count;
123822944501Smrg    request.list  = list;
123922944501Smrg    if (drmIoctl(fd, DRM_IOCTL_FREE_BUFS, &request))
1240fe517fc9Smrg        return -errno;
124122944501Smrg    return 0;
124222944501Smrg}
124322944501Smrg
124422944501Smrg
124522944501Smrg/**
124622944501Smrg * Close the device.
124722944501Smrg *
124822944501Smrg * \param fd file descriptor.
124922944501Smrg *
125022944501Smrg * \internal
125122944501Smrg * This function closes the file descriptor.
125222944501Smrg */
12536260e5d5Smrgdrm_public int drmClose(int fd)
125422944501Smrg{
125522944501Smrg    unsigned long key    = drmGetKeyFromFd(fd);
125622944501Smrg    drmHashEntry  *entry = drmGetEntry(fd);
125722944501Smrg
125822944501Smrg    drmHashDestroy(entry->tagTable);
125922944501Smrg    entry->fd       = 0;
126022944501Smrg    entry->f        = NULL;
126122944501Smrg    entry->tagTable = NULL;
126222944501Smrg
126322944501Smrg    drmHashDelete(drmHashTable, key);
126422944501Smrg    drmFree(entry);
126522944501Smrg
126622944501Smrg    return close(fd);
126722944501Smrg}
126822944501Smrg
126922944501Smrg
127022944501Smrg/**
127122944501Smrg * Map a region of memory.
127222944501Smrg *
127322944501Smrg * \param fd file descriptor.
127422944501Smrg * \param handle handle returned by drmAddMap().
127522944501Smrg * \param size size in bytes. Must match the size used by drmAddMap().
127622944501Smrg * \param address will contain the user-space virtual address where the mapping
127722944501Smrg * begins.
127822944501Smrg *
127922944501Smrg * \return zero on success, or a negative value on failure.
1280fe517fc9Smrg *
128122944501Smrg * \internal
128222944501Smrg * This function is a wrapper for mmap().
128322944501Smrg */
12846260e5d5Smrgdrm_public int drmMap(int fd, drm_handle_t handle, drmSize size,
12856260e5d5Smrg                      drmAddressPtr address)
128622944501Smrg{
128722944501Smrg    static unsigned long pagesize_mask = 0;
128822944501Smrg
128922944501Smrg    if (fd < 0)
1290fe517fc9Smrg        return -EINVAL;
129122944501Smrg
129222944501Smrg    if (!pagesize_mask)
1293fe517fc9Smrg        pagesize_mask = getpagesize() - 1;
129422944501Smrg
129522944501Smrg    size = (size + pagesize_mask) & ~pagesize_mask;
129622944501Smrg
1297a884aba1Smrg    *address = drm_mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, handle);
129822944501Smrg    if (*address == MAP_FAILED)
1299fe517fc9Smrg        return -errno;
130022944501Smrg    return 0;
130122944501Smrg}
130222944501Smrg
130322944501Smrg
130422944501Smrg/**
130522944501Smrg * Unmap mappings obtained with drmMap().
130622944501Smrg *
130722944501Smrg * \param address address as given by drmMap().
130822944501Smrg * \param size size in bytes. Must match the size used by drmMap().
1309fe517fc9Smrg *
131022944501Smrg * \return zero on success, or a negative value on failure.
131122944501Smrg *
131222944501Smrg * \internal
131322944501Smrg * This function is a wrapper for munmap().
131422944501Smrg */
13156260e5d5Smrgdrm_public int drmUnmap(drmAddress address, drmSize size)
131622944501Smrg{
1317a884aba1Smrg    return drm_munmap(address, size);
131822944501Smrg}
131922944501Smrg
13206260e5d5Smrgdrm_public drmBufInfoPtr drmGetBufInfo(int fd)
132122944501Smrg{
132222944501Smrg    drm_buf_info_t info;
132322944501Smrg    drmBufInfoPtr  retval;
132422944501Smrg    int            i;
132522944501Smrg
1326424e9256Smrg    memclear(info);
132722944501Smrg
132822944501Smrg    if (drmIoctl(fd, DRM_IOCTL_INFO_BUFS, &info))
1329fe517fc9Smrg        return NULL;
133022944501Smrg
133122944501Smrg    if (info.count) {
1332fe517fc9Smrg        if (!(info.list = drmMalloc(info.count * sizeof(*info.list))))
1333fe517fc9Smrg            return NULL;
1334fe517fc9Smrg
1335fe517fc9Smrg        if (drmIoctl(fd, DRM_IOCTL_INFO_BUFS, &info)) {
1336fe517fc9Smrg            drmFree(info.list);
1337fe517fc9Smrg            return NULL;
1338fe517fc9Smrg        }
1339fe517fc9Smrg
1340fe517fc9Smrg        retval = drmMalloc(sizeof(*retval));
1341fe517fc9Smrg        retval->count = info.count;
1342fe517fc9Smrg        retval->list  = drmMalloc(info.count * sizeof(*retval->list));
1343fe517fc9Smrg        for (i = 0; i < info.count; i++) {
1344fe517fc9Smrg            retval->list[i].count     = info.list[i].count;
1345fe517fc9Smrg            retval->list[i].size      = info.list[i].size;
1346fe517fc9Smrg            retval->list[i].low_mark  = info.list[i].low_mark;
1347fe517fc9Smrg            retval->list[i].high_mark = info.list[i].high_mark;
1348fe517fc9Smrg        }
1349fe517fc9Smrg        drmFree(info.list);
1350fe517fc9Smrg        return retval;
135122944501Smrg    }
135222944501Smrg    return NULL;
135322944501Smrg}
135422944501Smrg
135522944501Smrg/**
135622944501Smrg * Map all DMA buffers into client-virtual space.
135722944501Smrg *
135822944501Smrg * \param fd file descriptor.
135922944501Smrg *
136022944501Smrg * \return a pointer to a ::drmBufMap structure.
136122944501Smrg *
136222944501Smrg * \note The client may not use these buffers until obtaining buffer indices
136322944501Smrg * with drmDMA().
1364fe517fc9Smrg *
136522944501Smrg * \internal
136622944501Smrg * This function calls the DRM_IOCTL_MAP_BUFS ioctl and copies the returned
136722944501Smrg * information about the buffers in a drm_buf_map structure into the
136822944501Smrg * client-visible data structures.
1369fe517fc9Smrg */
13706260e5d5Smrgdrm_public drmBufMapPtr drmMapBufs(int fd)
137122944501Smrg{
137222944501Smrg    drm_buf_map_t bufs;
137322944501Smrg    drmBufMapPtr  retval;
137422944501Smrg    int           i;
137522944501Smrg
1376424e9256Smrg    memclear(bufs);
137722944501Smrg    if (drmIoctl(fd, DRM_IOCTL_MAP_BUFS, &bufs))
1378fe517fc9Smrg        return NULL;
137922944501Smrg
138022944501Smrg    if (!bufs.count)
1381fe517fc9Smrg        return NULL;
138222944501Smrg
1383fe517fc9Smrg    if (!(bufs.list = drmMalloc(bufs.count * sizeof(*bufs.list))))
1384fe517fc9Smrg        return NULL;
138522944501Smrg
1386fe517fc9Smrg    if (drmIoctl(fd, DRM_IOCTL_MAP_BUFS, &bufs)) {
1387fe517fc9Smrg        drmFree(bufs.list);
1388fe517fc9Smrg        return NULL;
1389fe517fc9Smrg    }
139022944501Smrg
1391fe517fc9Smrg    retval = drmMalloc(sizeof(*retval));
1392fe517fc9Smrg    retval->count = bufs.count;
1393fe517fc9Smrg    retval->list  = drmMalloc(bufs.count * sizeof(*retval->list));
1394fe517fc9Smrg    for (i = 0; i < bufs.count; i++) {
1395fe517fc9Smrg        retval->list[i].idx     = bufs.list[i].idx;
1396fe517fc9Smrg        retval->list[i].total   = bufs.list[i].total;
1397fe517fc9Smrg        retval->list[i].used    = 0;
1398fe517fc9Smrg        retval->list[i].address = bufs.list[i].address;
1399fe517fc9Smrg    }
140022944501Smrg
1401fe517fc9Smrg    drmFree(bufs.list);
1402fe517fc9Smrg    return retval;
140322944501Smrg}
140422944501Smrg
140522944501Smrg
140622944501Smrg/**
140722944501Smrg * Unmap buffers allocated with drmMapBufs().
140822944501Smrg *
140922944501Smrg * \return zero on success, or negative value on failure.
141022944501Smrg *
141122944501Smrg * \internal
141222944501Smrg * Calls munmap() for every buffer stored in \p bufs and frees the
141322944501Smrg * memory allocated by drmMapBufs().
141422944501Smrg */
14156260e5d5Smrgdrm_public int drmUnmapBufs(drmBufMapPtr bufs)
141622944501Smrg{
141722944501Smrg    int i;
141822944501Smrg
141922944501Smrg    for (i = 0; i < bufs->count; i++) {
1420fe517fc9Smrg        drm_munmap(bufs->list[i].address, bufs->list[i].total);
142122944501Smrg    }
142222944501Smrg
142322944501Smrg    drmFree(bufs->list);
142422944501Smrg    drmFree(bufs);
142522944501Smrg    return 0;
142622944501Smrg}
142722944501Smrg
142822944501Smrg
1429fe517fc9Smrg#define DRM_DMA_RETRY  16
143022944501Smrg
143122944501Smrg/**
143222944501Smrg * Reserve DMA buffers.
143322944501Smrg *
143422944501Smrg * \param fd file descriptor.
1435fe517fc9Smrg * \param request
1436fe517fc9Smrg *
143722944501Smrg * \return zero on success, or a negative value on failure.
143822944501Smrg *
143922944501Smrg * \internal
144022944501Smrg * Assemble the arguments into a drm_dma structure and keeps issuing the
144122944501Smrg * DRM_IOCTL_DMA ioctl until success or until maximum number of retries.
144222944501Smrg */
14436260e5d5Smrgdrm_public int drmDMA(int fd, drmDMAReqPtr request)
144422944501Smrg{
144522944501Smrg    drm_dma_t dma;
144622944501Smrg    int ret, i = 0;
144722944501Smrg
144822944501Smrg    dma.context         = request->context;
144922944501Smrg    dma.send_count      = request->send_count;
145022944501Smrg    dma.send_indices    = request->send_list;
145122944501Smrg    dma.send_sizes      = request->send_sizes;
145222944501Smrg    dma.flags           = request->flags;
145322944501Smrg    dma.request_count   = request->request_count;
145422944501Smrg    dma.request_size    = request->request_size;
145522944501Smrg    dma.request_indices = request->request_list;
145622944501Smrg    dma.request_sizes   = request->request_sizes;
145722944501Smrg    dma.granted_count   = 0;
145822944501Smrg
145922944501Smrg    do {
1460fe517fc9Smrg        ret = ioctl( fd, DRM_IOCTL_DMA, &dma );
146122944501Smrg    } while ( ret && errno == EAGAIN && i++ < DRM_DMA_RETRY );
146222944501Smrg
146322944501Smrg    if ( ret == 0 ) {
1464fe517fc9Smrg        request->granted_count = dma.granted_count;
1465fe517fc9Smrg        return 0;
146622944501Smrg    } else {
1467fe517fc9Smrg        return -errno;
146822944501Smrg    }
146922944501Smrg}
147022944501Smrg
147122944501Smrg
147222944501Smrg/**
147322944501Smrg * Obtain heavyweight hardware lock.
147422944501Smrg *
147522944501Smrg * \param fd file descriptor.
147622944501Smrg * \param context context.
1477bf6cc7dcSmrg * \param flags flags that determine the state of the hardware when the function
147822944501Smrg * returns.
1479fe517fc9Smrg *
148022944501Smrg * \return always zero.
1481fe517fc9Smrg *
148222944501Smrg * \internal
148322944501Smrg * This function translates the arguments into a drm_lock structure and issue
148422944501Smrg * the DRM_IOCTL_LOCK ioctl until the lock is successfully acquired.
148522944501Smrg */
14866260e5d5Smrgdrm_public int drmGetLock(int fd, drm_context_t context, drmLockFlags flags)
148722944501Smrg{
148822944501Smrg    drm_lock_t lock;
148922944501Smrg
1490424e9256Smrg    memclear(lock);
149122944501Smrg    lock.context = context;
149222944501Smrg    lock.flags   = 0;
149322944501Smrg    if (flags & DRM_LOCK_READY)      lock.flags |= _DRM_LOCK_READY;
149422944501Smrg    if (flags & DRM_LOCK_QUIESCENT)  lock.flags |= _DRM_LOCK_QUIESCENT;
149522944501Smrg    if (flags & DRM_LOCK_FLUSH)      lock.flags |= _DRM_LOCK_FLUSH;
149622944501Smrg    if (flags & DRM_LOCK_FLUSH_ALL)  lock.flags |= _DRM_LOCK_FLUSH_ALL;
149722944501Smrg    if (flags & DRM_HALT_ALL_QUEUES) lock.flags |= _DRM_HALT_ALL_QUEUES;
149822944501Smrg    if (flags & DRM_HALT_CUR_QUEUES) lock.flags |= _DRM_HALT_CUR_QUEUES;
149922944501Smrg
150022944501Smrg    while (drmIoctl(fd, DRM_IOCTL_LOCK, &lock))
1501fe517fc9Smrg        ;
150222944501Smrg    return 0;
150322944501Smrg}
150422944501Smrg
150522944501Smrg/**
150622944501Smrg * Release the hardware lock.
150722944501Smrg *
150822944501Smrg * \param fd file descriptor.
150922944501Smrg * \param context context.
1510fe517fc9Smrg *
151122944501Smrg * \return zero on success, or a negative value on failure.
1512fe517fc9Smrg *
151322944501Smrg * \internal
151422944501Smrg * This function is a wrapper around the DRM_IOCTL_UNLOCK ioctl, passing the
151522944501Smrg * argument in a drm_lock structure.
151622944501Smrg */
15176260e5d5Smrgdrm_public int drmUnlock(int fd, drm_context_t context)
151822944501Smrg{
151922944501Smrg    drm_lock_t lock;
152022944501Smrg
1521424e9256Smrg    memclear(lock);
152222944501Smrg    lock.context = context;
152322944501Smrg    return drmIoctl(fd, DRM_IOCTL_UNLOCK, &lock);
152422944501Smrg}
152522944501Smrg
15266260e5d5Smrgdrm_public drm_context_t *drmGetReservedContextList(int fd, int *count)
152722944501Smrg{
152822944501Smrg    drm_ctx_res_t res;
152922944501Smrg    drm_ctx_t     *list;
153022944501Smrg    drm_context_t * retval;
153122944501Smrg    int           i;
153222944501Smrg
1533424e9256Smrg    memclear(res);
153422944501Smrg    if (drmIoctl(fd, DRM_IOCTL_RES_CTX, &res))
1535fe517fc9Smrg        return NULL;
153622944501Smrg
153722944501Smrg    if (!res.count)
1538fe517fc9Smrg        return NULL;
153922944501Smrg
154022944501Smrg    if (!(list   = drmMalloc(res.count * sizeof(*list))))
1541fe517fc9Smrg        return NULL;
15420655efefSmrg    if (!(retval = drmMalloc(res.count * sizeof(*retval))))
15430655efefSmrg        goto err_free_list;
154422944501Smrg
154522944501Smrg    res.contexts = list;
154622944501Smrg    if (drmIoctl(fd, DRM_IOCTL_RES_CTX, &res))
15470655efefSmrg        goto err_free_context;
154822944501Smrg
154922944501Smrg    for (i = 0; i < res.count; i++)
1550fe517fc9Smrg        retval[i] = list[i].handle;
155122944501Smrg    drmFree(list);
155222944501Smrg
155322944501Smrg    *count = res.count;
155422944501Smrg    return retval;
15550655efefSmrg
15560655efefSmrgerr_free_list:
15570655efefSmrg    drmFree(list);
15580655efefSmrgerr_free_context:
15590655efefSmrg    drmFree(retval);
15600655efefSmrg    return NULL;
156122944501Smrg}
156222944501Smrg
15636260e5d5Smrgdrm_public void drmFreeReservedContextList(drm_context_t *pt)
156422944501Smrg{
156522944501Smrg    drmFree(pt);
156622944501Smrg}
156722944501Smrg
156822944501Smrg/**
156922944501Smrg * Create context.
157022944501Smrg *
157122944501Smrg * Used by the X server during GLXContext initialization. This causes
157222944501Smrg * per-context kernel-level resources to be allocated.
157322944501Smrg *
157422944501Smrg * \param fd file descriptor.
157522944501Smrg * \param handle is set on success. To be used by the client when requesting DMA
157622944501Smrg * dispatch with drmDMA().
1577fe517fc9Smrg *
157822944501Smrg * \return zero on success, or a negative value on failure.
1579fe517fc9Smrg *
158022944501Smrg * \note May only be called by root.
1581fe517fc9Smrg *
158222944501Smrg * \internal
158322944501Smrg * This function is a wrapper around the DRM_IOCTL_ADD_CTX ioctl, passing the
158422944501Smrg * argument in a drm_ctx structure.
158522944501Smrg */
15866260e5d5Smrgdrm_public int drmCreateContext(int fd, drm_context_t *handle)
158722944501Smrg{
158822944501Smrg    drm_ctx_t ctx;
158922944501Smrg
1590424e9256Smrg    memclear(ctx);
159122944501Smrg    if (drmIoctl(fd, DRM_IOCTL_ADD_CTX, &ctx))
1592fe517fc9Smrg        return -errno;
159322944501Smrg    *handle = ctx.handle;
159422944501Smrg    return 0;
159522944501Smrg}
159622944501Smrg
15976260e5d5Smrgdrm_public int drmSwitchToContext(int fd, drm_context_t context)
159822944501Smrg{
159922944501Smrg    drm_ctx_t ctx;
160022944501Smrg
1601424e9256Smrg    memclear(ctx);
160222944501Smrg    ctx.handle = context;
160322944501Smrg    if (drmIoctl(fd, DRM_IOCTL_SWITCH_CTX, &ctx))
1604fe517fc9Smrg        return -errno;
160522944501Smrg    return 0;
160622944501Smrg}
160722944501Smrg
16086260e5d5Smrgdrm_public int drmSetContextFlags(int fd, drm_context_t context,
16096260e5d5Smrg                                  drm_context_tFlags flags)
161022944501Smrg{
161122944501Smrg    drm_ctx_t ctx;
161222944501Smrg
161322944501Smrg    /*
161422944501Smrg     * Context preserving means that no context switches are done between DMA
161522944501Smrg     * buffers from one context and the next.  This is suitable for use in the
161622944501Smrg     * X server (which promises to maintain hardware context), or in the
161722944501Smrg     * client-side library when buffers are swapped on behalf of two threads.
161822944501Smrg     */
1619424e9256Smrg    memclear(ctx);
162022944501Smrg    ctx.handle = context;
162122944501Smrg    if (flags & DRM_CONTEXT_PRESERVED)
1622fe517fc9Smrg        ctx.flags |= _DRM_CONTEXT_PRESERVED;
162322944501Smrg    if (flags & DRM_CONTEXT_2DONLY)
1624fe517fc9Smrg        ctx.flags |= _DRM_CONTEXT_2DONLY;
162522944501Smrg    if (drmIoctl(fd, DRM_IOCTL_MOD_CTX, &ctx))
1626fe517fc9Smrg        return -errno;
162722944501Smrg    return 0;
162822944501Smrg}
162922944501Smrg
16306260e5d5Smrgdrm_public int drmGetContextFlags(int fd, drm_context_t context,
16316260e5d5Smrg                                  drm_context_tFlagsPtr flags)
163222944501Smrg{
163322944501Smrg    drm_ctx_t ctx;
163422944501Smrg
1635424e9256Smrg    memclear(ctx);
163622944501Smrg    ctx.handle = context;
163722944501Smrg    if (drmIoctl(fd, DRM_IOCTL_GET_CTX, &ctx))
1638fe517fc9Smrg        return -errno;
163922944501Smrg    *flags = 0;
164022944501Smrg    if (ctx.flags & _DRM_CONTEXT_PRESERVED)
1641fe517fc9Smrg        *flags |= DRM_CONTEXT_PRESERVED;
164222944501Smrg    if (ctx.flags & _DRM_CONTEXT_2DONLY)
1643fe517fc9Smrg        *flags |= DRM_CONTEXT_2DONLY;
164422944501Smrg    return 0;
164522944501Smrg}
164622944501Smrg
164722944501Smrg/**
164822944501Smrg * Destroy context.
164922944501Smrg *
165022944501Smrg * Free any kernel-level resources allocated with drmCreateContext() associated
165122944501Smrg * with the context.
1652fe517fc9Smrg *
165322944501Smrg * \param fd file descriptor.
165422944501Smrg * \param handle handle given by drmCreateContext().
1655fe517fc9Smrg *
165622944501Smrg * \return zero on success, or a negative value on failure.
1657fe517fc9Smrg *
165822944501Smrg * \note May only be called by root.
1659fe517fc9Smrg *
166022944501Smrg * \internal
166122944501Smrg * This function is a wrapper around the DRM_IOCTL_RM_CTX ioctl, passing the
166222944501Smrg * argument in a drm_ctx structure.
166322944501Smrg */
16646260e5d5Smrgdrm_public int drmDestroyContext(int fd, drm_context_t handle)
166522944501Smrg{
166622944501Smrg    drm_ctx_t ctx;
1667424e9256Smrg
1668424e9256Smrg    memclear(ctx);
166922944501Smrg    ctx.handle = handle;
167022944501Smrg    if (drmIoctl(fd, DRM_IOCTL_RM_CTX, &ctx))
1671fe517fc9Smrg        return -errno;
167222944501Smrg    return 0;
167322944501Smrg}
167422944501Smrg
16756260e5d5Smrgdrm_public int drmCreateDrawable(int fd, drm_drawable_t *handle)
167622944501Smrg{
167722944501Smrg    drm_draw_t draw;
1678424e9256Smrg
1679424e9256Smrg    memclear(draw);
168022944501Smrg    if (drmIoctl(fd, DRM_IOCTL_ADD_DRAW, &draw))
1681fe517fc9Smrg        return -errno;
168222944501Smrg    *handle = draw.handle;
168322944501Smrg    return 0;
168422944501Smrg}
168522944501Smrg
16866260e5d5Smrgdrm_public int drmDestroyDrawable(int fd, drm_drawable_t handle)
168722944501Smrg{
168822944501Smrg    drm_draw_t draw;
1689424e9256Smrg
1690424e9256Smrg    memclear(draw);
169122944501Smrg    draw.handle = handle;
169222944501Smrg    if (drmIoctl(fd, DRM_IOCTL_RM_DRAW, &draw))
1693fe517fc9Smrg        return -errno;
169422944501Smrg    return 0;
169522944501Smrg}
169622944501Smrg
16976260e5d5Smrgdrm_public int drmUpdateDrawableInfo(int fd, drm_drawable_t handle,
16986260e5d5Smrg                                     drm_drawable_info_type_t type,
16996260e5d5Smrg                                     unsigned int num, void *data)
170022944501Smrg{
170122944501Smrg    drm_update_draw_t update;
170222944501Smrg
1703424e9256Smrg    memclear(update);
170422944501Smrg    update.handle = handle;
170522944501Smrg    update.type = type;
170622944501Smrg    update.num = num;
170722944501Smrg    update.data = (unsigned long long)(unsigned long)data;
170822944501Smrg
170922944501Smrg    if (drmIoctl(fd, DRM_IOCTL_UPDATE_DRAW, &update))
1710fe517fc9Smrg        return -errno;
171122944501Smrg
171222944501Smrg    return 0;
171322944501Smrg}
171422944501Smrg
17156260e5d5Smrgdrm_public int drmCrtcGetSequence(int fd, uint32_t crtcId, uint64_t *sequence,
17166260e5d5Smrg                                  uint64_t *ns)
17172b90624aSmrg{
17182b90624aSmrg    struct drm_crtc_get_sequence get_seq;
17192b90624aSmrg    int ret;
17202b90624aSmrg
17212b90624aSmrg    memclear(get_seq);
17222b90624aSmrg    get_seq.crtc_id = crtcId;
17232b90624aSmrg    ret = drmIoctl(fd, DRM_IOCTL_CRTC_GET_SEQUENCE, &get_seq);
17242b90624aSmrg    if (ret)
17252b90624aSmrg        return ret;
17262b90624aSmrg
17272b90624aSmrg    if (sequence)
17282b90624aSmrg        *sequence = get_seq.sequence;
17292b90624aSmrg    if (ns)
17302b90624aSmrg        *ns = get_seq.sequence_ns;
17312b90624aSmrg    return 0;
17322b90624aSmrg}
17332b90624aSmrg
17346260e5d5Smrgdrm_public int drmCrtcQueueSequence(int fd, uint32_t crtcId, uint32_t flags,
17356260e5d5Smrg                                    uint64_t sequence,
17366260e5d5Smrg                                    uint64_t *sequence_queued,
17376260e5d5Smrg                                    uint64_t user_data)
17382b90624aSmrg{
17392b90624aSmrg    struct drm_crtc_queue_sequence queue_seq;
17402b90624aSmrg    int ret;
17412b90624aSmrg
17422b90624aSmrg    memclear(queue_seq);
17432b90624aSmrg    queue_seq.crtc_id = crtcId;
17442b90624aSmrg    queue_seq.flags = flags;
17452b90624aSmrg    queue_seq.sequence = sequence;
17462b90624aSmrg    queue_seq.user_data = user_data;
17472b90624aSmrg
17482b90624aSmrg    ret = drmIoctl(fd, DRM_IOCTL_CRTC_QUEUE_SEQUENCE, &queue_seq);
17492b90624aSmrg    if (ret == 0 && sequence_queued)
17502b90624aSmrg        *sequence_queued = queue_seq.sequence;
17512b90624aSmrg
17522b90624aSmrg    return ret;
17532b90624aSmrg}
17542b90624aSmrg
175522944501Smrg/**
175622944501Smrg * Acquire the AGP device.
175722944501Smrg *
175822944501Smrg * Must be called before any of the other AGP related calls.
175922944501Smrg *
176022944501Smrg * \param fd file descriptor.
1761fe517fc9Smrg *
176222944501Smrg * \return zero on success, or a negative value on failure.
1763fe517fc9Smrg *
176422944501Smrg * \internal
176522944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_ACQUIRE ioctl.
176622944501Smrg */
17676260e5d5Smrgdrm_public int drmAgpAcquire(int fd)
176822944501Smrg{
176922944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_ACQUIRE, NULL))
1770fe517fc9Smrg        return -errno;
177122944501Smrg    return 0;
177222944501Smrg}
177322944501Smrg
177422944501Smrg
177522944501Smrg/**
177622944501Smrg * Release the AGP device.
177722944501Smrg *
177822944501Smrg * \param fd file descriptor.
1779fe517fc9Smrg *
178022944501Smrg * \return zero on success, or a negative value on failure.
1781fe517fc9Smrg *
178222944501Smrg * \internal
178322944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_RELEASE ioctl.
178422944501Smrg */
17856260e5d5Smrgdrm_public int drmAgpRelease(int fd)
178622944501Smrg{
178722944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_RELEASE, NULL))
1788fe517fc9Smrg        return -errno;
178922944501Smrg    return 0;
179022944501Smrg}
179122944501Smrg
179222944501Smrg
179322944501Smrg/**
179422944501Smrg * Set the AGP mode.
179522944501Smrg *
179622944501Smrg * \param fd file descriptor.
179722944501Smrg * \param mode AGP mode.
1798fe517fc9Smrg *
179922944501Smrg * \return zero on success, or a negative value on failure.
1800fe517fc9Smrg *
180122944501Smrg * \internal
180222944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_ENABLE ioctl, passing the
180322944501Smrg * argument in a drm_agp_mode structure.
180422944501Smrg */
18056260e5d5Smrgdrm_public int drmAgpEnable(int fd, unsigned long mode)
180622944501Smrg{
180722944501Smrg    drm_agp_mode_t m;
180822944501Smrg
1809424e9256Smrg    memclear(m);
181022944501Smrg    m.mode = mode;
181122944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_ENABLE, &m))
1812fe517fc9Smrg        return -errno;
181322944501Smrg    return 0;
181422944501Smrg}
181522944501Smrg
181622944501Smrg
181722944501Smrg/**
181822944501Smrg * Allocate a chunk of AGP memory.
181922944501Smrg *
182022944501Smrg * \param fd file descriptor.
182122944501Smrg * \param size requested memory size in bytes. Will be rounded to page boundary.
182222944501Smrg * \param type type of memory to allocate.
182322944501Smrg * \param address if not zero, will be set to the physical address of the
182422944501Smrg * allocated memory.
182522944501Smrg * \param handle on success will be set to a handle of the allocated memory.
1826fe517fc9Smrg *
182722944501Smrg * \return zero on success, or a negative value on failure.
1828fe517fc9Smrg *
182922944501Smrg * \internal
183022944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_ALLOC ioctl, passing the
183122944501Smrg * arguments in a drm_agp_buffer structure.
183222944501Smrg */
18336260e5d5Smrgdrm_public int drmAgpAlloc(int fd, unsigned long size, unsigned long type,
18346260e5d5Smrg                           unsigned long *address, drm_handle_t *handle)
183522944501Smrg{
183622944501Smrg    drm_agp_buffer_t b;
183722944501Smrg
1838424e9256Smrg    memclear(b);
183922944501Smrg    *handle = DRM_AGP_NO_HANDLE;
184022944501Smrg    b.size   = size;
184122944501Smrg    b.type   = type;
184222944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_ALLOC, &b))
1843fe517fc9Smrg        return -errno;
184422944501Smrg    if (address != 0UL)
1845fe517fc9Smrg        *address = b.physical;
184622944501Smrg    *handle = b.handle;
184722944501Smrg    return 0;
184822944501Smrg}
184922944501Smrg
185022944501Smrg
185122944501Smrg/**
185222944501Smrg * Free a chunk of AGP memory.
185322944501Smrg *
185422944501Smrg * \param fd file descriptor.
185522944501Smrg * \param handle handle to the allocated memory, as given by drmAgpAllocate().
1856fe517fc9Smrg *
185722944501Smrg * \return zero on success, or a negative value on failure.
1858fe517fc9Smrg *
185922944501Smrg * \internal
186022944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_FREE ioctl, passing the
186122944501Smrg * argument in a drm_agp_buffer structure.
186222944501Smrg */
18636260e5d5Smrgdrm_public int drmAgpFree(int fd, drm_handle_t handle)
186422944501Smrg{
186522944501Smrg    drm_agp_buffer_t b;
186622944501Smrg
1867424e9256Smrg    memclear(b);
186822944501Smrg    b.handle = handle;
186922944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_FREE, &b))
1870fe517fc9Smrg        return -errno;
187122944501Smrg    return 0;
187222944501Smrg}
187322944501Smrg
187422944501Smrg
187522944501Smrg/**
187622944501Smrg * Bind a chunk of AGP memory.
187722944501Smrg *
187822944501Smrg * \param fd file descriptor.
187922944501Smrg * \param handle handle to the allocated memory, as given by drmAgpAllocate().
188022944501Smrg * \param offset offset in bytes. It will round to page boundary.
1881fe517fc9Smrg *
188222944501Smrg * \return zero on success, or a negative value on failure.
1883fe517fc9Smrg *
188422944501Smrg * \internal
188522944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_BIND ioctl, passing the
188622944501Smrg * argument in a drm_agp_binding structure.
188722944501Smrg */
18886260e5d5Smrgdrm_public int drmAgpBind(int fd, drm_handle_t handle, unsigned long offset)
188922944501Smrg{
189022944501Smrg    drm_agp_binding_t b;
189122944501Smrg
1892424e9256Smrg    memclear(b);
189322944501Smrg    b.handle = handle;
189422944501Smrg    b.offset = offset;
189522944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_BIND, &b))
1896fe517fc9Smrg        return -errno;
189722944501Smrg    return 0;
189822944501Smrg}
189922944501Smrg
190022944501Smrg
190122944501Smrg/**
190222944501Smrg * Unbind a chunk of AGP memory.
190322944501Smrg *
190422944501Smrg * \param fd file descriptor.
190522944501Smrg * \param handle handle to the allocated memory, as given by drmAgpAllocate().
1906fe517fc9Smrg *
190722944501Smrg * \return zero on success, or a negative value on failure.
1908fe517fc9Smrg *
190922944501Smrg * \internal
191022944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_UNBIND ioctl, passing
191122944501Smrg * the argument in a drm_agp_binding structure.
191222944501Smrg */
19136260e5d5Smrgdrm_public int drmAgpUnbind(int fd, drm_handle_t handle)
191422944501Smrg{
191522944501Smrg    drm_agp_binding_t b;
191622944501Smrg
1917424e9256Smrg    memclear(b);
191822944501Smrg    b.handle = handle;
191922944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_UNBIND, &b))
1920fe517fc9Smrg        return -errno;
192122944501Smrg    return 0;
192222944501Smrg}
192322944501Smrg
192422944501Smrg
192522944501Smrg/**
192622944501Smrg * Get AGP driver major version number.
192722944501Smrg *
192822944501Smrg * \param fd file descriptor.
1929fe517fc9Smrg *
193022944501Smrg * \return major version number on success, or a negative value on failure..
1931fe517fc9Smrg *
193222944501Smrg * \internal
193322944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
193422944501Smrg * necessary information in a drm_agp_info structure.
193522944501Smrg */
19366260e5d5Smrgdrm_public int drmAgpVersionMajor(int fd)
193722944501Smrg{
193822944501Smrg    drm_agp_info_t i;
193922944501Smrg
1940424e9256Smrg    memclear(i);
1941424e9256Smrg
194222944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
1943fe517fc9Smrg        return -errno;
194422944501Smrg    return i.agp_version_major;
194522944501Smrg}
194622944501Smrg
194722944501Smrg
194822944501Smrg/**
194922944501Smrg * Get AGP driver minor version number.
195022944501Smrg *
195122944501Smrg * \param fd file descriptor.
1952fe517fc9Smrg *
195322944501Smrg * \return minor version number on success, or a negative value on failure.
1954fe517fc9Smrg *
195522944501Smrg * \internal
195622944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
195722944501Smrg * necessary information in a drm_agp_info structure.
195822944501Smrg */
19596260e5d5Smrgdrm_public int drmAgpVersionMinor(int fd)
196022944501Smrg{
196122944501Smrg    drm_agp_info_t i;
196222944501Smrg
1963424e9256Smrg    memclear(i);
1964424e9256Smrg
196522944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
1966fe517fc9Smrg        return -errno;
196722944501Smrg    return i.agp_version_minor;
196822944501Smrg}
196922944501Smrg
197022944501Smrg
197122944501Smrg/**
197222944501Smrg * Get AGP mode.
197322944501Smrg *
197422944501Smrg * \param fd file descriptor.
1975fe517fc9Smrg *
197622944501Smrg * \return mode on success, or zero on failure.
1977fe517fc9Smrg *
197822944501Smrg * \internal
197922944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
198022944501Smrg * necessary information in a drm_agp_info structure.
198122944501Smrg */
19826260e5d5Smrgdrm_public unsigned long drmAgpGetMode(int fd)
198322944501Smrg{
198422944501Smrg    drm_agp_info_t i;
198522944501Smrg
1986424e9256Smrg    memclear(i);
1987424e9256Smrg
198822944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
1989fe517fc9Smrg        return 0;
199022944501Smrg    return i.mode;
199122944501Smrg}
199222944501Smrg
199322944501Smrg
199422944501Smrg/**
199522944501Smrg * Get AGP aperture base.
199622944501Smrg *
199722944501Smrg * \param fd file descriptor.
1998fe517fc9Smrg *
199922944501Smrg * \return aperture base on success, zero on failure.
2000fe517fc9Smrg *
200122944501Smrg * \internal
200222944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
200322944501Smrg * necessary information in a drm_agp_info structure.
200422944501Smrg */
20056260e5d5Smrgdrm_public unsigned long drmAgpBase(int fd)
200622944501Smrg{
200722944501Smrg    drm_agp_info_t i;
200822944501Smrg
2009424e9256Smrg    memclear(i);
2010424e9256Smrg
201122944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
2012fe517fc9Smrg        return 0;
201322944501Smrg    return i.aperture_base;
201422944501Smrg}
201522944501Smrg
201622944501Smrg
201722944501Smrg/**
201822944501Smrg * Get AGP aperture size.
201922944501Smrg *
202022944501Smrg * \param fd file descriptor.
2021fe517fc9Smrg *
202222944501Smrg * \return aperture size on success, zero on failure.
2023fe517fc9Smrg *
202422944501Smrg * \internal
202522944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
202622944501Smrg * necessary information in a drm_agp_info structure.
202722944501Smrg */
20286260e5d5Smrgdrm_public unsigned long drmAgpSize(int fd)
202922944501Smrg{
203022944501Smrg    drm_agp_info_t i;
203122944501Smrg
2032424e9256Smrg    memclear(i);
2033424e9256Smrg
203422944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
2035fe517fc9Smrg        return 0;
203622944501Smrg    return i.aperture_size;
203722944501Smrg}
203822944501Smrg
203922944501Smrg
204022944501Smrg/**
204122944501Smrg * Get used AGP memory.
204222944501Smrg *
204322944501Smrg * \param fd file descriptor.
2044fe517fc9Smrg *
204522944501Smrg * \return memory used on success, or zero on failure.
2046fe517fc9Smrg *
204722944501Smrg * \internal
204822944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
204922944501Smrg * necessary information in a drm_agp_info structure.
205022944501Smrg */
20516260e5d5Smrgdrm_public unsigned long drmAgpMemoryUsed(int fd)
205222944501Smrg{
205322944501Smrg    drm_agp_info_t i;
205422944501Smrg
2055424e9256Smrg    memclear(i);
2056424e9256Smrg
205722944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
2058fe517fc9Smrg        return 0;
205922944501Smrg    return i.memory_used;
206022944501Smrg}
206122944501Smrg
206222944501Smrg
206322944501Smrg/**
206422944501Smrg * Get available AGP memory.
206522944501Smrg *
206622944501Smrg * \param fd file descriptor.
2067fe517fc9Smrg *
206822944501Smrg * \return memory available on success, or zero on failure.
2069fe517fc9Smrg *
207022944501Smrg * \internal
207122944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
207222944501Smrg * necessary information in a drm_agp_info structure.
207322944501Smrg */
20746260e5d5Smrgdrm_public unsigned long drmAgpMemoryAvail(int fd)
207522944501Smrg{
207622944501Smrg    drm_agp_info_t i;
207722944501Smrg
2078424e9256Smrg    memclear(i);
2079424e9256Smrg
208022944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
2081fe517fc9Smrg        return 0;
208222944501Smrg    return i.memory_allowed;
208322944501Smrg}
208422944501Smrg
208522944501Smrg
208622944501Smrg/**
208722944501Smrg * Get hardware vendor ID.
208822944501Smrg *
208922944501Smrg * \param fd file descriptor.
2090fe517fc9Smrg *
209122944501Smrg * \return vendor ID on success, or zero on failure.
2092fe517fc9Smrg *
209322944501Smrg * \internal
209422944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
209522944501Smrg * necessary information in a drm_agp_info structure.
209622944501Smrg */
20976260e5d5Smrgdrm_public unsigned int drmAgpVendorId(int fd)
209822944501Smrg{
209922944501Smrg    drm_agp_info_t i;
210022944501Smrg
2101424e9256Smrg    memclear(i);
2102424e9256Smrg
210322944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
2104fe517fc9Smrg        return 0;
210522944501Smrg    return i.id_vendor;
210622944501Smrg}
210722944501Smrg
210822944501Smrg
210922944501Smrg/**
211022944501Smrg * Get hardware device ID.
211122944501Smrg *
211222944501Smrg * \param fd file descriptor.
2113fe517fc9Smrg *
211422944501Smrg * \return zero on success, or zero on failure.
2115fe517fc9Smrg *
211622944501Smrg * \internal
211722944501Smrg * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the
211822944501Smrg * necessary information in a drm_agp_info structure.
211922944501Smrg */
21206260e5d5Smrgdrm_public unsigned int drmAgpDeviceId(int fd)
212122944501Smrg{
212222944501Smrg    drm_agp_info_t i;
212322944501Smrg
2124424e9256Smrg    memclear(i);
2125424e9256Smrg
212622944501Smrg    if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
2127fe517fc9Smrg        return 0;
212822944501Smrg    return i.id_device;
212922944501Smrg}
213022944501Smrg
21316260e5d5Smrgdrm_public int drmScatterGatherAlloc(int fd, unsigned long size,
21326260e5d5Smrg                                     drm_handle_t *handle)
213322944501Smrg{
213422944501Smrg    drm_scatter_gather_t sg;
213522944501Smrg
2136424e9256Smrg    memclear(sg);
2137424e9256Smrg
213822944501Smrg    *handle = 0;
213922944501Smrg    sg.size   = size;
214022944501Smrg    if (drmIoctl(fd, DRM_IOCTL_SG_ALLOC, &sg))
2141fe517fc9Smrg        return -errno;
214222944501Smrg    *handle = sg.handle;
214322944501Smrg    return 0;
214422944501Smrg}
214522944501Smrg
21466260e5d5Smrgdrm_public int drmScatterGatherFree(int fd, drm_handle_t handle)
214722944501Smrg{
214822944501Smrg    drm_scatter_gather_t sg;
214922944501Smrg
2150424e9256Smrg    memclear(sg);
215122944501Smrg    sg.handle = handle;
215222944501Smrg    if (drmIoctl(fd, DRM_IOCTL_SG_FREE, &sg))
2153fe517fc9Smrg        return -errno;
215422944501Smrg    return 0;
215522944501Smrg}
215622944501Smrg
215722944501Smrg/**
215822944501Smrg * Wait for VBLANK.
215922944501Smrg *
216022944501Smrg * \param fd file descriptor.
216122944501Smrg * \param vbl pointer to a drmVBlank structure.
2162fe517fc9Smrg *
216322944501Smrg * \return zero on success, or a negative value on failure.
2164fe517fc9Smrg *
216522944501Smrg * \internal
216622944501Smrg * This function is a wrapper around the DRM_IOCTL_WAIT_VBLANK ioctl.
216722944501Smrg */
21686260e5d5Smrgdrm_public int drmWaitVBlank(int fd, drmVBlankPtr vbl)
216922944501Smrg{
217022944501Smrg    struct timespec timeout, cur;
217122944501Smrg    int ret;
217222944501Smrg
217322944501Smrg    ret = clock_gettime(CLOCK_MONOTONIC, &timeout);
217422944501Smrg    if (ret < 0) {
2175fe517fc9Smrg        fprintf(stderr, "clock_gettime failed: %s\n", strerror(errno));
2176fe517fc9Smrg        goto out;
217722944501Smrg    }
217822944501Smrg    timeout.tv_sec++;
217922944501Smrg
218022944501Smrg    do {
218122944501Smrg       ret = ioctl(fd, DRM_IOCTL_WAIT_VBLANK, vbl);
218222944501Smrg       vbl->request.type &= ~DRM_VBLANK_RELATIVE;
218322944501Smrg       if (ret && errno == EINTR) {
2184fe517fc9Smrg           clock_gettime(CLOCK_MONOTONIC, &cur);
2185fe517fc9Smrg           /* Timeout after 1s */
2186fe517fc9Smrg           if (cur.tv_sec > timeout.tv_sec + 1 ||
2187fe517fc9Smrg               (cur.tv_sec == timeout.tv_sec && cur.tv_nsec >=
2188fe517fc9Smrg                timeout.tv_nsec)) {
2189fe517fc9Smrg                   errno = EBUSY;
2190fe517fc9Smrg                   ret = -1;
2191fe517fc9Smrg                   break;
2192fe517fc9Smrg           }
219322944501Smrg       }
219422944501Smrg    } while (ret && errno == EINTR);
219522944501Smrg
219622944501Smrgout:
219722944501Smrg    return ret;
219822944501Smrg}
219922944501Smrg
22006260e5d5Smrgdrm_public int drmError(int err, const char *label)
220122944501Smrg{
220222944501Smrg    switch (err) {
220322944501Smrg    case DRM_ERR_NO_DEVICE:
2204fe517fc9Smrg        fprintf(stderr, "%s: no device\n", label);
2205fe517fc9Smrg        break;
220622944501Smrg    case DRM_ERR_NO_ACCESS:
2207fe517fc9Smrg        fprintf(stderr, "%s: no access\n", label);
2208fe517fc9Smrg        break;
220922944501Smrg    case DRM_ERR_NOT_ROOT:
2210fe517fc9Smrg        fprintf(stderr, "%s: not root\n", label);
2211fe517fc9Smrg        break;
221222944501Smrg    case DRM_ERR_INVALID:
2213fe517fc9Smrg        fprintf(stderr, "%s: invalid args\n", label);
2214fe517fc9Smrg        break;
221522944501Smrg    default:
2216fe517fc9Smrg        if (err < 0)
2217fe517fc9Smrg            err = -err;
2218fe517fc9Smrg        fprintf( stderr, "%s: error %d (%s)\n", label, err, strerror(err) );
2219fe517fc9Smrg        break;
222022944501Smrg    }
222122944501Smrg
222222944501Smrg    return 1;
222322944501Smrg}
222422944501Smrg
222522944501Smrg/**
222622944501Smrg * Install IRQ handler.
222722944501Smrg *
222822944501Smrg * \param fd file descriptor.
222922944501Smrg * \param irq IRQ number.
2230fe517fc9Smrg *
223122944501Smrg * \return zero on success, or a negative value on failure.
2232fe517fc9Smrg *
223322944501Smrg * \internal
223422944501Smrg * This function is a wrapper around the DRM_IOCTL_CONTROL ioctl, passing the
223522944501Smrg * argument in a drm_control structure.
223622944501Smrg */
22376260e5d5Smrgdrm_public int drmCtlInstHandler(int fd, int irq)
223822944501Smrg{
223922944501Smrg    drm_control_t ctl;
224022944501Smrg
2241424e9256Smrg    memclear(ctl);
224222944501Smrg    ctl.func  = DRM_INST_HANDLER;
224322944501Smrg    ctl.irq   = irq;
224422944501Smrg    if (drmIoctl(fd, DRM_IOCTL_CONTROL, &ctl))
2245fe517fc9Smrg        return -errno;
224622944501Smrg    return 0;
224722944501Smrg}
224822944501Smrg
224922944501Smrg
225022944501Smrg/**
225122944501Smrg * Uninstall IRQ handler.
225222944501Smrg *
225322944501Smrg * \param fd file descriptor.
2254fe517fc9Smrg *
225522944501Smrg * \return zero on success, or a negative value on failure.
2256fe517fc9Smrg *
225722944501Smrg * \internal
225822944501Smrg * This function is a wrapper around the DRM_IOCTL_CONTROL ioctl, passing the
225922944501Smrg * argument in a drm_control structure.
226022944501Smrg */
22616260e5d5Smrgdrm_public int drmCtlUninstHandler(int fd)
226222944501Smrg{
226322944501Smrg    drm_control_t ctl;
226422944501Smrg
2265424e9256Smrg    memclear(ctl);
226622944501Smrg    ctl.func  = DRM_UNINST_HANDLER;
226722944501Smrg    ctl.irq   = 0;
226822944501Smrg    if (drmIoctl(fd, DRM_IOCTL_CONTROL, &ctl))
2269fe517fc9Smrg        return -errno;
227022944501Smrg    return 0;
227122944501Smrg}
227222944501Smrg
22736260e5d5Smrgdrm_public int drmFinish(int fd, int context, drmLockFlags flags)
227422944501Smrg{
227522944501Smrg    drm_lock_t lock;
227622944501Smrg
2277424e9256Smrg    memclear(lock);
227822944501Smrg    lock.context = context;
227922944501Smrg    if (flags & DRM_LOCK_READY)      lock.flags |= _DRM_LOCK_READY;
228022944501Smrg    if (flags & DRM_LOCK_QUIESCENT)  lock.flags |= _DRM_LOCK_QUIESCENT;
228122944501Smrg    if (flags & DRM_LOCK_FLUSH)      lock.flags |= _DRM_LOCK_FLUSH;
228222944501Smrg    if (flags & DRM_LOCK_FLUSH_ALL)  lock.flags |= _DRM_LOCK_FLUSH_ALL;
228322944501Smrg    if (flags & DRM_HALT_ALL_QUEUES) lock.flags |= _DRM_HALT_ALL_QUEUES;
228422944501Smrg    if (flags & DRM_HALT_CUR_QUEUES) lock.flags |= _DRM_HALT_CUR_QUEUES;
228522944501Smrg    if (drmIoctl(fd, DRM_IOCTL_FINISH, &lock))
2286fe517fc9Smrg        return -errno;
228722944501Smrg    return 0;
228822944501Smrg}
228922944501Smrg
229022944501Smrg/**
229122944501Smrg * Get IRQ from bus ID.
229222944501Smrg *
229322944501Smrg * \param fd file descriptor.
229422944501Smrg * \param busnum bus number.
229522944501Smrg * \param devnum device number.
229622944501Smrg * \param funcnum function number.
2297fe517fc9Smrg *
229822944501Smrg * \return IRQ number on success, or a negative value on failure.
2299fe517fc9Smrg *
230022944501Smrg * \internal
230122944501Smrg * This function is a wrapper around the DRM_IOCTL_IRQ_BUSID ioctl, passing the
230222944501Smrg * arguments in a drm_irq_busid structure.
230322944501Smrg */
23046260e5d5Smrgdrm_public int drmGetInterruptFromBusID(int fd, int busnum, int devnum,
23056260e5d5Smrg                                        int funcnum)
230622944501Smrg{
230722944501Smrg    drm_irq_busid_t p;
230822944501Smrg
2309424e9256Smrg    memclear(p);
231022944501Smrg    p.busnum  = busnum;
231122944501Smrg    p.devnum  = devnum;
231222944501Smrg    p.funcnum = funcnum;
231322944501Smrg    if (drmIoctl(fd, DRM_IOCTL_IRQ_BUSID, &p))
2314fe517fc9Smrg        return -errno;
231522944501Smrg    return p.irq;
231622944501Smrg}
231722944501Smrg
23186260e5d5Smrgdrm_public int drmAddContextTag(int fd, drm_context_t context, void *tag)
231922944501Smrg{
232022944501Smrg    drmHashEntry  *entry = drmGetEntry(fd);
232122944501Smrg
232222944501Smrg    if (drmHashInsert(entry->tagTable, context, tag)) {
2323fe517fc9Smrg        drmHashDelete(entry->tagTable, context);
2324fe517fc9Smrg        drmHashInsert(entry->tagTable, context, tag);
232522944501Smrg    }
232622944501Smrg    return 0;
232722944501Smrg}
232822944501Smrg
23296260e5d5Smrgdrm_public int drmDelContextTag(int fd, drm_context_t context)
233022944501Smrg{
233122944501Smrg    drmHashEntry  *entry = drmGetEntry(fd);
233222944501Smrg
233322944501Smrg    return drmHashDelete(entry->tagTable, context);
233422944501Smrg}
233522944501Smrg
23366260e5d5Smrgdrm_public void *drmGetContextTag(int fd, drm_context_t context)
233722944501Smrg{
233822944501Smrg    drmHashEntry  *entry = drmGetEntry(fd);
233922944501Smrg    void          *value;
234022944501Smrg
234122944501Smrg    if (drmHashLookup(entry->tagTable, context, &value))
2342fe517fc9Smrg        return NULL;
234322944501Smrg
234422944501Smrg    return value;
234522944501Smrg}
234622944501Smrg
23476260e5d5Smrgdrm_public int drmAddContextPrivateMapping(int fd, drm_context_t ctx_id,
23486260e5d5Smrg                                           drm_handle_t handle)
234922944501Smrg{
235022944501Smrg    drm_ctx_priv_map_t map;
235122944501Smrg
2352424e9256Smrg    memclear(map);
235322944501Smrg    map.ctx_id = ctx_id;
235420131375Smrg    map.handle = (void *)(uintptr_t)handle;
235522944501Smrg
235622944501Smrg    if (drmIoctl(fd, DRM_IOCTL_SET_SAREA_CTX, &map))
2357fe517fc9Smrg        return -errno;
235822944501Smrg    return 0;
235922944501Smrg}
236022944501Smrg
23616260e5d5Smrgdrm_public int drmGetContextPrivateMapping(int fd, drm_context_t ctx_id,
23626260e5d5Smrg                                           drm_handle_t *handle)
236322944501Smrg{
236422944501Smrg    drm_ctx_priv_map_t map;
236522944501Smrg
2366424e9256Smrg    memclear(map);
236722944501Smrg    map.ctx_id = ctx_id;
236822944501Smrg
236922944501Smrg    if (drmIoctl(fd, DRM_IOCTL_GET_SAREA_CTX, &map))
2370fe517fc9Smrg        return -errno;
237122944501Smrg    if (handle)
2372fe517fc9Smrg        *handle = (drm_handle_t)(uintptr_t)map.handle;
237322944501Smrg
237422944501Smrg    return 0;
237522944501Smrg}
237622944501Smrg
23776260e5d5Smrgdrm_public int drmGetMap(int fd, int idx, drm_handle_t *offset, drmSize *size,
23786260e5d5Smrg                         drmMapType *type, drmMapFlags *flags,
23796260e5d5Smrg                         drm_handle_t *handle, int *mtrr)
238022944501Smrg{
238122944501Smrg    drm_map_t map;
238222944501Smrg
2383424e9256Smrg    memclear(map);
238422944501Smrg    map.offset = idx;
238522944501Smrg    if (drmIoctl(fd, DRM_IOCTL_GET_MAP, &map))
2386fe517fc9Smrg        return -errno;
238722944501Smrg    *offset = map.offset;
238822944501Smrg    *size   = map.size;
238922944501Smrg    *type   = map.type;
239022944501Smrg    *flags  = map.flags;
239122944501Smrg    *handle = (unsigned long)map.handle;
239222944501Smrg    *mtrr   = map.mtrr;
239322944501Smrg    return 0;
239422944501Smrg}
239522944501Smrg
23966260e5d5Smrgdrm_public int drmGetClient(int fd, int idx, int *auth, int *pid, int *uid,
23976260e5d5Smrg                            unsigned long *magic, unsigned long *iocs)
239822944501Smrg{
239922944501Smrg    drm_client_t client;
240022944501Smrg
2401424e9256Smrg    memclear(client);
240222944501Smrg    client.idx = idx;
240322944501Smrg    if (drmIoctl(fd, DRM_IOCTL_GET_CLIENT, &client))
2404fe517fc9Smrg        return -errno;
240522944501Smrg    *auth      = client.auth;
240622944501Smrg    *pid       = client.pid;
240722944501Smrg    *uid       = client.uid;
240822944501Smrg    *magic     = client.magic;
240922944501Smrg    *iocs      = client.iocs;
241022944501Smrg    return 0;
241122944501Smrg}
241222944501Smrg
24136260e5d5Smrgdrm_public int drmGetStats(int fd, drmStatsT *stats)
241422944501Smrg{
241522944501Smrg    drm_stats_t s;
2416424e9256Smrg    unsigned    i;
241722944501Smrg
2418424e9256Smrg    memclear(s);
241922944501Smrg    if (drmIoctl(fd, DRM_IOCTL_GET_STATS, &s))
2420fe517fc9Smrg        return -errno;
242122944501Smrg
242222944501Smrg    stats->count = 0;
242322944501Smrg    memset(stats, 0, sizeof(*stats));
242422944501Smrg    if (s.count > sizeof(stats->data)/sizeof(stats->data[0]))
2425fe517fc9Smrg        return -1;
242622944501Smrg
242722944501Smrg#define SET_VALUE                              \
242822944501Smrg    stats->data[i].long_format = "%-20.20s";   \
242922944501Smrg    stats->data[i].rate_format = "%8.8s";      \
243022944501Smrg    stats->data[i].isvalue     = 1;            \
243122944501Smrg    stats->data[i].verbose     = 0
243222944501Smrg
243322944501Smrg#define SET_COUNT                              \
243422944501Smrg    stats->data[i].long_format = "%-20.20s";   \
243522944501Smrg    stats->data[i].rate_format = "%5.5s";      \
243622944501Smrg    stats->data[i].isvalue     = 0;            \
243722944501Smrg    stats->data[i].mult_names  = "kgm";        \
243822944501Smrg    stats->data[i].mult        = 1000;         \
243922944501Smrg    stats->data[i].verbose     = 0
244022944501Smrg
244122944501Smrg#define SET_BYTE                               \
244222944501Smrg    stats->data[i].long_format = "%-20.20s";   \
244322944501Smrg    stats->data[i].rate_format = "%5.5s";      \
244422944501Smrg    stats->data[i].isvalue     = 0;            \
244522944501Smrg    stats->data[i].mult_names  = "KGM";        \
244622944501Smrg    stats->data[i].mult        = 1024;         \
244722944501Smrg    stats->data[i].verbose     = 0
244822944501Smrg
244922944501Smrg
245022944501Smrg    stats->count = s.count;
245122944501Smrg    for (i = 0; i < s.count; i++) {
2452fe517fc9Smrg        stats->data[i].value = s.data[i].value;
2453fe517fc9Smrg        switch (s.data[i].type) {
2454fe517fc9Smrg        case _DRM_STAT_LOCK:
2455fe517fc9Smrg            stats->data[i].long_name = "Lock";
2456fe517fc9Smrg            stats->data[i].rate_name = "Lock";
2457fe517fc9Smrg            SET_VALUE;
2458fe517fc9Smrg            break;
2459fe517fc9Smrg        case _DRM_STAT_OPENS:
2460fe517fc9Smrg            stats->data[i].long_name = "Opens";
2461fe517fc9Smrg            stats->data[i].rate_name = "O";
2462fe517fc9Smrg            SET_COUNT;
2463fe517fc9Smrg            stats->data[i].verbose   = 1;
2464fe517fc9Smrg            break;
2465fe517fc9Smrg        case _DRM_STAT_CLOSES:
2466fe517fc9Smrg            stats->data[i].long_name = "Closes";
2467fe517fc9Smrg            stats->data[i].rate_name = "Lock";
2468fe517fc9Smrg            SET_COUNT;
2469fe517fc9Smrg            stats->data[i].verbose   = 1;
2470fe517fc9Smrg            break;
2471fe517fc9Smrg        case _DRM_STAT_IOCTLS:
2472fe517fc9Smrg            stats->data[i].long_name = "Ioctls";
2473fe517fc9Smrg            stats->data[i].rate_name = "Ioc/s";
2474fe517fc9Smrg            SET_COUNT;
2475fe517fc9Smrg            break;
2476fe517fc9Smrg        case _DRM_STAT_LOCKS:
2477fe517fc9Smrg            stats->data[i].long_name = "Locks";
2478fe517fc9Smrg            stats->data[i].rate_name = "Lck/s";
2479fe517fc9Smrg            SET_COUNT;
2480fe517fc9Smrg            break;
2481fe517fc9Smrg        case _DRM_STAT_UNLOCKS:
2482fe517fc9Smrg            stats->data[i].long_name = "Unlocks";
2483fe517fc9Smrg            stats->data[i].rate_name = "Unl/s";
2484fe517fc9Smrg            SET_COUNT;
2485fe517fc9Smrg            break;
2486fe517fc9Smrg        case _DRM_STAT_IRQ:
2487fe517fc9Smrg            stats->data[i].long_name = "IRQs";
2488fe517fc9Smrg            stats->data[i].rate_name = "IRQ/s";
2489fe517fc9Smrg            SET_COUNT;
2490fe517fc9Smrg            break;
2491fe517fc9Smrg        case _DRM_STAT_PRIMARY:
2492fe517fc9Smrg            stats->data[i].long_name = "Primary Bytes";
2493fe517fc9Smrg            stats->data[i].rate_name = "PB/s";
2494fe517fc9Smrg            SET_BYTE;
2495fe517fc9Smrg            break;
2496fe517fc9Smrg        case _DRM_STAT_SECONDARY:
2497fe517fc9Smrg            stats->data[i].long_name = "Secondary Bytes";
2498fe517fc9Smrg            stats->data[i].rate_name = "SB/s";
2499fe517fc9Smrg            SET_BYTE;
2500fe517fc9Smrg            break;
2501fe517fc9Smrg        case _DRM_STAT_DMA:
2502fe517fc9Smrg            stats->data[i].long_name = "DMA";
2503fe517fc9Smrg            stats->data[i].rate_name = "DMA/s";
2504fe517fc9Smrg            SET_COUNT;
2505fe517fc9Smrg            break;
2506fe517fc9Smrg        case _DRM_STAT_SPECIAL:
2507fe517fc9Smrg            stats->data[i].long_name = "Special DMA";
2508fe517fc9Smrg            stats->data[i].rate_name = "dma/s";
2509fe517fc9Smrg            SET_COUNT;
2510fe517fc9Smrg            break;
2511fe517fc9Smrg        case _DRM_STAT_MISSED:
2512fe517fc9Smrg            stats->data[i].long_name = "Miss";
2513fe517fc9Smrg            stats->data[i].rate_name = "Ms/s";
2514fe517fc9Smrg            SET_COUNT;
2515fe517fc9Smrg            break;
2516fe517fc9Smrg        case _DRM_STAT_VALUE:
2517fe517fc9Smrg            stats->data[i].long_name = "Value";
2518fe517fc9Smrg            stats->data[i].rate_name = "Value";
2519fe517fc9Smrg            SET_VALUE;
2520fe517fc9Smrg            break;
2521fe517fc9Smrg        case _DRM_STAT_BYTE:
2522fe517fc9Smrg            stats->data[i].long_name = "Bytes";
2523fe517fc9Smrg            stats->data[i].rate_name = "B/s";
2524fe517fc9Smrg            SET_BYTE;
2525fe517fc9Smrg            break;
2526fe517fc9Smrg        case _DRM_STAT_COUNT:
2527fe517fc9Smrg        default:
2528fe517fc9Smrg            stats->data[i].long_name = "Count";
2529fe517fc9Smrg            stats->data[i].rate_name = "Cnt/s";
2530fe517fc9Smrg            SET_COUNT;
2531fe517fc9Smrg            break;
2532fe517fc9Smrg        }
253322944501Smrg    }
253422944501Smrg    return 0;
253522944501Smrg}
253622944501Smrg
253722944501Smrg/**
253822944501Smrg * Issue a set-version ioctl.
253922944501Smrg *
254022944501Smrg * \param fd file descriptor.
2541fe517fc9Smrg * \param drmCommandIndex command index
254222944501Smrg * \param data source pointer of the data to be read and written.
254322944501Smrg * \param size size of the data to be read and written.
2544fe517fc9Smrg *
254522944501Smrg * \return zero on success, or a negative value on failure.
2546fe517fc9Smrg *
254722944501Smrg * \internal
2548fe517fc9Smrg * It issues a read-write ioctl given by
254922944501Smrg * \code DRM_COMMAND_BASE + drmCommandIndex \endcode.
255022944501Smrg */
25516260e5d5Smrgdrm_public int drmSetInterfaceVersion(int fd, drmSetVersion *version)
255222944501Smrg{
255322944501Smrg    int retcode = 0;
255422944501Smrg    drm_set_version_t sv;
255522944501Smrg
2556424e9256Smrg    memclear(sv);
255722944501Smrg    sv.drm_di_major = version->drm_di_major;
255822944501Smrg    sv.drm_di_minor = version->drm_di_minor;
255922944501Smrg    sv.drm_dd_major = version->drm_dd_major;
256022944501Smrg    sv.drm_dd_minor = version->drm_dd_minor;
256122944501Smrg
256222944501Smrg    if (drmIoctl(fd, DRM_IOCTL_SET_VERSION, &sv)) {
2563fe517fc9Smrg        retcode = -errno;
256422944501Smrg    }
256522944501Smrg
256622944501Smrg    version->drm_di_major = sv.drm_di_major;
256722944501Smrg    version->drm_di_minor = sv.drm_di_minor;
256822944501Smrg    version->drm_dd_major = sv.drm_dd_major;
256922944501Smrg    version->drm_dd_minor = sv.drm_dd_minor;
257022944501Smrg
257122944501Smrg    return retcode;
257222944501Smrg}
257322944501Smrg
257422944501Smrg/**
257522944501Smrg * Send a device-specific command.
257622944501Smrg *
257722944501Smrg * \param fd file descriptor.
2578fe517fc9Smrg * \param drmCommandIndex command index
2579fe517fc9Smrg *
258022944501Smrg * \return zero on success, or a negative value on failure.
2581fe517fc9Smrg *
258222944501Smrg * \internal
2583fe517fc9Smrg * It issues a ioctl given by
258422944501Smrg * \code DRM_COMMAND_BASE + drmCommandIndex \endcode.
258522944501Smrg */
25866260e5d5Smrgdrm_public int drmCommandNone(int fd, unsigned long drmCommandIndex)
258722944501Smrg{
258822944501Smrg    unsigned long request;
258922944501Smrg
259022944501Smrg    request = DRM_IO( DRM_COMMAND_BASE + drmCommandIndex);
259122944501Smrg
2592424e9256Smrg    if (drmIoctl(fd, request, NULL)) {
2593fe517fc9Smrg        return -errno;
259422944501Smrg    }
259522944501Smrg    return 0;
259622944501Smrg}
259722944501Smrg
259822944501Smrg
259922944501Smrg/**
260022944501Smrg * Send a device-specific read command.
260122944501Smrg *
260222944501Smrg * \param fd file descriptor.
2603fe517fc9Smrg * \param drmCommandIndex command index
260422944501Smrg * \param data destination pointer of the data to be read.
260522944501Smrg * \param size size of the data to be read.
2606fe517fc9Smrg *
260722944501Smrg * \return zero on success, or a negative value on failure.
260822944501Smrg *
260922944501Smrg * \internal
2610fe517fc9Smrg * It issues a read ioctl given by
261122944501Smrg * \code DRM_COMMAND_BASE + drmCommandIndex \endcode.
261222944501Smrg */
26136260e5d5Smrgdrm_public int drmCommandRead(int fd, unsigned long drmCommandIndex,
26146260e5d5Smrg                              void *data, unsigned long size)
261522944501Smrg{
261622944501Smrg    unsigned long request;
261722944501Smrg
2618fe517fc9Smrg    request = DRM_IOC( DRM_IOC_READ, DRM_IOCTL_BASE,
2619fe517fc9Smrg        DRM_COMMAND_BASE + drmCommandIndex, size);
262022944501Smrg
262122944501Smrg    if (drmIoctl(fd, request, data)) {
2622fe517fc9Smrg        return -errno;
262322944501Smrg    }
262422944501Smrg    return 0;
262522944501Smrg}
262622944501Smrg
262722944501Smrg
262822944501Smrg/**
262922944501Smrg * Send a device-specific write command.
263022944501Smrg *
263122944501Smrg * \param fd file descriptor.
2632fe517fc9Smrg * \param drmCommandIndex command index
263322944501Smrg * \param data source pointer of the data to be written.
263422944501Smrg * \param size size of the data to be written.
2635fe517fc9Smrg *
263622944501Smrg * \return zero on success, or a negative value on failure.
2637fe517fc9Smrg *
263822944501Smrg * \internal
2639fe517fc9Smrg * It issues a write ioctl given by
264022944501Smrg * \code DRM_COMMAND_BASE + drmCommandIndex \endcode.
264122944501Smrg */
26426260e5d5Smrgdrm_public int drmCommandWrite(int fd, unsigned long drmCommandIndex,
26436260e5d5Smrg                               void *data, unsigned long size)
264422944501Smrg{
264522944501Smrg    unsigned long request;
264622944501Smrg
2647fe517fc9Smrg    request = DRM_IOC( DRM_IOC_WRITE, DRM_IOCTL_BASE,
2648fe517fc9Smrg        DRM_COMMAND_BASE + drmCommandIndex, size);
264922944501Smrg
265022944501Smrg    if (drmIoctl(fd, request, data)) {
2651fe517fc9Smrg        return -errno;
265222944501Smrg    }
265322944501Smrg    return 0;
265422944501Smrg}
265522944501Smrg
265622944501Smrg
265722944501Smrg/**
265822944501Smrg * Send a device-specific read-write command.
265922944501Smrg *
266022944501Smrg * \param fd file descriptor.
2661fe517fc9Smrg * \param drmCommandIndex command index
266222944501Smrg * \param data source pointer of the data to be read and written.
266322944501Smrg * \param size size of the data to be read and written.
2664fe517fc9Smrg *
266522944501Smrg * \return zero on success, or a negative value on failure.
2666fe517fc9Smrg *
266722944501Smrg * \internal
2668fe517fc9Smrg * It issues a read-write ioctl given by
266922944501Smrg * \code DRM_COMMAND_BASE + drmCommandIndex \endcode.
267022944501Smrg */
26716260e5d5Smrgdrm_public int drmCommandWriteRead(int fd, unsigned long drmCommandIndex,
26726260e5d5Smrg                                   void *data, unsigned long size)
267322944501Smrg{
267422944501Smrg    unsigned long request;
267522944501Smrg
2676fe517fc9Smrg    request = DRM_IOC( DRM_IOC_READ|DRM_IOC_WRITE, DRM_IOCTL_BASE,
2677fe517fc9Smrg        DRM_COMMAND_BASE + drmCommandIndex, size);
267822944501Smrg
267922944501Smrg    if (drmIoctl(fd, request, data))
2680fe517fc9Smrg        return -errno;
268122944501Smrg    return 0;
268222944501Smrg}
268322944501Smrg
268422944501Smrg#define DRM_MAX_FDS 16
268522944501Smrgstatic struct {
268622944501Smrg    char *BusID;
268722944501Smrg    int fd;
268822944501Smrg    int refcount;
2689424e9256Smrg    int type;
269022944501Smrg} connection[DRM_MAX_FDS];
269122944501Smrg
269222944501Smrgstatic int nr_fds = 0;
269322944501Smrg
26946260e5d5Smrgdrm_public int drmOpenOnce(void *unused, const char *BusID, int *newlyopened)
2695424e9256Smrg{
2696424e9256Smrg    return drmOpenOnceWithType(BusID, newlyopened, DRM_NODE_PRIMARY);
2697424e9256Smrg}
2698424e9256Smrg
26996260e5d5Smrgdrm_public int drmOpenOnceWithType(const char *BusID, int *newlyopened,
27006260e5d5Smrg                                   int type)
270122944501Smrg{
270222944501Smrg    int i;
270322944501Smrg    int fd;
2704fe517fc9Smrg
270522944501Smrg    for (i = 0; i < nr_fds; i++)
2706fe517fc9Smrg        if ((strcmp(BusID, connection[i].BusID) == 0) &&
2707fe517fc9Smrg            (connection[i].type == type)) {
2708fe517fc9Smrg            connection[i].refcount++;
2709fe517fc9Smrg            *newlyopened = 0;
2710fe517fc9Smrg            return connection[i].fd;
2711fe517fc9Smrg        }
271222944501Smrg
2713424e9256Smrg    fd = drmOpenWithType(NULL, BusID, type);
2714fe517fc9Smrg    if (fd < 0 || nr_fds == DRM_MAX_FDS)
2715fe517fc9Smrg        return fd;
2716fe517fc9Smrg
271722944501Smrg    connection[nr_fds].BusID = strdup(BusID);
271822944501Smrg    connection[nr_fds].fd = fd;
271922944501Smrg    connection[nr_fds].refcount = 1;
2720424e9256Smrg    connection[nr_fds].type = type;
272122944501Smrg    *newlyopened = 1;
272222944501Smrg
272322944501Smrg    if (0)
2724fe517fc9Smrg        fprintf(stderr, "saved connection %d for %s %d\n",
2725fe517fc9Smrg                nr_fds, connection[nr_fds].BusID,
2726fe517fc9Smrg                strcmp(BusID, connection[nr_fds].BusID));
272722944501Smrg
272822944501Smrg    nr_fds++;
272922944501Smrg
273022944501Smrg    return fd;
273122944501Smrg}
273222944501Smrg
27336260e5d5Smrgdrm_public void drmCloseOnce(int fd)
273422944501Smrg{
273522944501Smrg    int i;
273622944501Smrg
273722944501Smrg    for (i = 0; i < nr_fds; i++) {
2738fe517fc9Smrg        if (fd == connection[i].fd) {
2739fe517fc9Smrg            if (--connection[i].refcount == 0) {
2740fe517fc9Smrg                drmClose(connection[i].fd);
2741fe517fc9Smrg                free(connection[i].BusID);
2742fe517fc9Smrg
2743fe517fc9Smrg                if (i < --nr_fds)
2744fe517fc9Smrg                    connection[i] = connection[nr_fds];
2745fe517fc9Smrg
2746fe517fc9Smrg                return;
2747fe517fc9Smrg            }
2748fe517fc9Smrg        }
274922944501Smrg    }
275022944501Smrg}
275122944501Smrg
27526260e5d5Smrgdrm_public int drmSetMaster(int fd)
275322944501Smrg{
2754fe517fc9Smrg        return drmIoctl(fd, DRM_IOCTL_SET_MASTER, NULL);
275522944501Smrg}
275622944501Smrg
27576260e5d5Smrgdrm_public int drmDropMaster(int fd)
275822944501Smrg{
2759fe517fc9Smrg        return drmIoctl(fd, DRM_IOCTL_DROP_MASTER, NULL);
276022944501Smrg}
276122944501Smrg
2762bf6cc7dcSmrgdrm_public int drmIsMaster(int fd)
2763bf6cc7dcSmrg{
2764bf6cc7dcSmrg        /* Detect master by attempting something that requires master.
2765bf6cc7dcSmrg         *
2766bf6cc7dcSmrg         * Authenticating magic tokens requires master and 0 is an
2767bf6cc7dcSmrg         * internal kernel detail which we could use. Attempting this on
2768bf6cc7dcSmrg         * a master fd would fail therefore fail with EINVAL because 0
2769bf6cc7dcSmrg         * is invalid.
2770bf6cc7dcSmrg         *
2771bf6cc7dcSmrg         * A non-master fd will fail with EACCES, as the kernel checks
2772bf6cc7dcSmrg         * for master before attempting to do anything else.
2773bf6cc7dcSmrg         *
2774bf6cc7dcSmrg         * Since we don't want to leak implementation details, use
2775bf6cc7dcSmrg         * EACCES.
2776bf6cc7dcSmrg         */
2777bf6cc7dcSmrg        return drmAuthMagic(fd, 0) != -EACCES;
2778bf6cc7dcSmrg}
2779bf6cc7dcSmrg
27806260e5d5Smrgdrm_public char *drmGetDeviceNameFromFd(int fd)
278122944501Smrg{
278287bf8e7cSmrg#ifdef __FreeBSD__
278387bf8e7cSmrg    struct stat sbuf;
278487bf8e7cSmrg    int maj, min;
278587bf8e7cSmrg    int nodetype;
278687bf8e7cSmrg
278787bf8e7cSmrg    if (fstat(fd, &sbuf))
278887bf8e7cSmrg        return NULL;
278987bf8e7cSmrg
279087bf8e7cSmrg    maj = major(sbuf.st_rdev);
279187bf8e7cSmrg    min = minor(sbuf.st_rdev);
279287bf8e7cSmrg    nodetype = drmGetMinorType(maj, min);
279387bf8e7cSmrg    return drmGetMinorNameForFD(fd, nodetype);
279487bf8e7cSmrg#else
2795fe517fc9Smrg    char name[128];
2796fe517fc9Smrg    struct stat sbuf;
2797fe517fc9Smrg    dev_t d;
2798fe517fc9Smrg    int i;
279922944501Smrg
2800fe517fc9Smrg    /* The whole drmOpen thing is a fiasco and we need to find a way
2801fe517fc9Smrg     * back to just using open(2).  For now, however, lets just make
2802fe517fc9Smrg     * things worse with even more ad hoc directory walking code to
2803fe517fc9Smrg     * discover the device file name. */
280422944501Smrg
2805fe517fc9Smrg    fstat(fd, &sbuf);
2806fe517fc9Smrg    d = sbuf.st_rdev;
280722944501Smrg
2808fe517fc9Smrg    for (i = 0; i < DRM_MAX_MINOR; i++) {
2809fe517fc9Smrg        snprintf(name, sizeof name, DRM_DEV_NAME, DRM_DIR_NAME, i);
2810fe517fc9Smrg        if (stat(name, &sbuf) == 0 && sbuf.st_rdev == d)
2811fe517fc9Smrg            break;
2812fe517fc9Smrg    }
2813fe517fc9Smrg    if (i == DRM_MAX_MINOR)
2814fe517fc9Smrg        return NULL;
281522944501Smrg
2816fe517fc9Smrg    return strdup(name);
281787bf8e7cSmrg#endif
281822944501Smrg}
281920131375Smrg
28206260e5d5Smrgstatic bool drmNodeIsDRM(int maj, int min)
28216260e5d5Smrg{
28226260e5d5Smrg#ifdef __linux__
28236260e5d5Smrg    char path[64];
28246260e5d5Smrg    struct stat sbuf;
28256260e5d5Smrg
28266260e5d5Smrg    snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device/drm",
28276260e5d5Smrg             maj, min);
28286260e5d5Smrg    return stat(path, &sbuf) == 0;
282987bf8e7cSmrg#elif __FreeBSD__
283087bf8e7cSmrg    char name[SPECNAMELEN];
283187bf8e7cSmrg
283287bf8e7cSmrg    if (!devname_r(makedev(maj, min), S_IFCHR, name, sizeof(name)))
283387bf8e7cSmrg      return 0;
283487bf8e7cSmrg    /* Handle drm/ and dri/ as both are present in different FreeBSD version
283587bf8e7cSmrg     * FreeBSD on amd64/i386/powerpc external kernel modules create node in
283687bf8e7cSmrg     * in /dev/drm/ and links in /dev/dri while a WIP in kernel driver creates
283787bf8e7cSmrg     * only device nodes in /dev/dri/ */
283887bf8e7cSmrg    return (!strncmp(name, "drm/", 4) || !strncmp(name, "dri/", 4));
28396260e5d5Smrg#else
28406260e5d5Smrg    return maj == DRM_MAJOR;
28416260e5d5Smrg#endif
28426260e5d5Smrg}
28436260e5d5Smrg
28446260e5d5Smrgdrm_public int drmGetNodeTypeFromFd(int fd)
2845424e9256Smrg{
2846fe517fc9Smrg    struct stat sbuf;
2847fe517fc9Smrg    int maj, min, type;
2848424e9256Smrg
2849fe517fc9Smrg    if (fstat(fd, &sbuf))
2850fe517fc9Smrg        return -1;
2851424e9256Smrg
2852fe517fc9Smrg    maj = major(sbuf.st_rdev);
2853fe517fc9Smrg    min = minor(sbuf.st_rdev);
2854424e9256Smrg
28556260e5d5Smrg    if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode)) {
2856fe517fc9Smrg        errno = EINVAL;
2857fe517fc9Smrg        return -1;
2858fe517fc9Smrg    }
2859424e9256Smrg
286087bf8e7cSmrg    type = drmGetMinorType(maj, min);
2861fe517fc9Smrg    if (type == -1)
2862fe517fc9Smrg        errno = ENODEV;
2863fe517fc9Smrg    return type;
2864424e9256Smrg}
2865424e9256Smrg
28666260e5d5Smrgdrm_public int drmPrimeHandleToFD(int fd, uint32_t handle, uint32_t flags,
28676260e5d5Smrg                                  int *prime_fd)
286820131375Smrg{
2869fe517fc9Smrg    struct drm_prime_handle args;
2870fe517fc9Smrg    int ret;
287120131375Smrg
2872fe517fc9Smrg    memclear(args);
2873fe517fc9Smrg    args.fd = -1;
2874fe517fc9Smrg    args.handle = handle;
2875fe517fc9Smrg    args.flags = flags;
2876fe517fc9Smrg    ret = drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args);
2877fe517fc9Smrg    if (ret)
2878fe517fc9Smrg        return ret;
287920131375Smrg
2880fe517fc9Smrg    *prime_fd = args.fd;
2881fe517fc9Smrg    return 0;
288220131375Smrg}
288320131375Smrg
28846260e5d5Smrgdrm_public int drmPrimeFDToHandle(int fd, int prime_fd, uint32_t *handle)
288520131375Smrg{
2886fe517fc9Smrg    struct drm_prime_handle args;
2887fe517fc9Smrg    int ret;
288820131375Smrg
2889fe517fc9Smrg    memclear(args);
2890fe517fc9Smrg    args.fd = prime_fd;
2891fe517fc9Smrg    ret = drmIoctl(fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &args);
2892fe517fc9Smrg    if (ret)
2893fe517fc9Smrg        return ret;
289420131375Smrg
2895fe517fc9Smrg    *handle = args.handle;
2896fe517fc9Smrg    return 0;
289720131375Smrg}
2898424e9256Smrg
2899424e9256Smrgstatic char *drmGetMinorNameForFD(int fd, int type)
2900424e9256Smrg{
2901424e9256Smrg#ifdef __linux__
2902fe517fc9Smrg    DIR *sysdir;
29036260e5d5Smrg    struct dirent *ent;
2904fe517fc9Smrg    struct stat sbuf;
2905fe517fc9Smrg    const char *name = drmGetMinorName(type);
2906fe517fc9Smrg    int len;
2907fe517fc9Smrg    char dev_name[64], buf[64];
2908fe517fc9Smrg    int maj, min;
2909fe517fc9Smrg
2910fe517fc9Smrg    if (!name)
2911fe517fc9Smrg        return NULL;
2912424e9256Smrg
2913fe517fc9Smrg    len = strlen(name);
2914424e9256Smrg
2915fe517fc9Smrg    if (fstat(fd, &sbuf))
2916fe517fc9Smrg        return NULL;
2917424e9256Smrg
2918fe517fc9Smrg    maj = major(sbuf.st_rdev);
2919fe517fc9Smrg    min = minor(sbuf.st_rdev);
2920424e9256Smrg
29216260e5d5Smrg    if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
2922fe517fc9Smrg        return NULL;
2923424e9256Smrg
2924fe517fc9Smrg    snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/device/drm", maj, min);
2925424e9256Smrg
2926fe517fc9Smrg    sysdir = opendir(buf);
2927fe517fc9Smrg    if (!sysdir)
2928fe517fc9Smrg        return NULL;
2929424e9256Smrg
29306260e5d5Smrg    while ((ent = readdir(sysdir))) {
2931fe517fc9Smrg        if (strncmp(ent->d_name, name, len) == 0) {
2932fe517fc9Smrg            snprintf(dev_name, sizeof(dev_name), DRM_DIR_NAME "/%s",
2933fe517fc9Smrg                 ent->d_name);
2934424e9256Smrg
2935fe517fc9Smrg            closedir(sysdir);
2936fe517fc9Smrg            return strdup(dev_name);
2937fe517fc9Smrg        }
2938fe517fc9Smrg    }
2939424e9256Smrg
2940fe517fc9Smrg    closedir(sysdir);
29416260e5d5Smrg    return NULL;
294287bf8e7cSmrg#elif __FreeBSD__
294387bf8e7cSmrg    struct stat sbuf;
294487bf8e7cSmrg    char dname[SPECNAMELEN];
294587bf8e7cSmrg    const char *mname;
294687bf8e7cSmrg    char name[SPECNAMELEN];
294787bf8e7cSmrg    int id, maj, min, nodetype, i;
294887bf8e7cSmrg
294987bf8e7cSmrg    if (fstat(fd, &sbuf))
295087bf8e7cSmrg        return NULL;
295187bf8e7cSmrg
295287bf8e7cSmrg    maj = major(sbuf.st_rdev);
295387bf8e7cSmrg    min = minor(sbuf.st_rdev);
295487bf8e7cSmrg
295587bf8e7cSmrg    if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
295687bf8e7cSmrg        return NULL;
295787bf8e7cSmrg
295887bf8e7cSmrg    if (!devname_r(sbuf.st_rdev, S_IFCHR, dname, sizeof(dname)))
295987bf8e7cSmrg        return NULL;
296087bf8e7cSmrg
296187bf8e7cSmrg    /* Handle both /dev/drm and /dev/dri
296287bf8e7cSmrg     * FreeBSD on amd64/i386/powerpc external kernel modules create node in
296387bf8e7cSmrg     * in /dev/drm/ and links in /dev/dri while a WIP in kernel driver creates
296487bf8e7cSmrg     * only device nodes in /dev/dri/ */
296587bf8e7cSmrg
296687bf8e7cSmrg    /* Get the node type represented by fd so we can deduce the target name */
296787bf8e7cSmrg    nodetype = drmGetMinorType(maj, min);
296887bf8e7cSmrg    if (nodetype == -1)
296987bf8e7cSmrg        return (NULL);
297087bf8e7cSmrg    mname = drmGetMinorName(type);
297187bf8e7cSmrg
297287bf8e7cSmrg    for (i = 0; i < SPECNAMELEN; i++) {
297387bf8e7cSmrg        if (isalpha(dname[i]) == 0 && dname[i] != '/')
297487bf8e7cSmrg           break;
297587bf8e7cSmrg    }
297687bf8e7cSmrg    if (dname[i] == '\0')
297787bf8e7cSmrg        return (NULL);
297887bf8e7cSmrg
297987bf8e7cSmrg    id = (int)strtol(&dname[i], NULL, 10);
298087bf8e7cSmrg    id -= drmGetMinorBase(nodetype);
298187bf8e7cSmrg    snprintf(name, sizeof(name), DRM_DIR_NAME "/%s%d", mname,
298287bf8e7cSmrg         id + drmGetMinorBase(type));
298387bf8e7cSmrg
298487bf8e7cSmrg    return strdup(name);
2985fe517fc9Smrg#else
29862ee35494Smrg    struct stat sbuf;
29872ee35494Smrg    char buf[PATH_MAX + 1];
298882025ec7Smrg    const char *dev_name = drmGetDeviceName(type);
29892ee35494Smrg    unsigned int maj, min;
299082025ec7Smrg    int n;
29912ee35494Smrg
29922ee35494Smrg    if (fstat(fd, &sbuf))
29932ee35494Smrg        return NULL;
29942ee35494Smrg
29952ee35494Smrg    maj = major(sbuf.st_rdev);
29962ee35494Smrg    min = minor(sbuf.st_rdev);
29972ee35494Smrg
29986260e5d5Smrg    if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
29992ee35494Smrg        return NULL;
30002ee35494Smrg
300182025ec7Smrg    if (!dev_name)
30022ee35494Smrg        return NULL;
30032ee35494Smrg
300482025ec7Smrg    n = snprintf(buf, sizeof(buf), dev_name, DRM_DIR_NAME, min);
30052ee35494Smrg    if (n == -1 || n >= sizeof(buf))
30062ee35494Smrg        return NULL;
30072ee35494Smrg
30082ee35494Smrg    return strdup(buf);
3009424e9256Smrg#endif
3010424e9256Smrg}
3011424e9256Smrg
30126260e5d5Smrgdrm_public char *drmGetPrimaryDeviceNameFromFd(int fd)
3013424e9256Smrg{
3014fe517fc9Smrg    return drmGetMinorNameForFD(fd, DRM_NODE_PRIMARY);
3015424e9256Smrg}
3016424e9256Smrg
30176260e5d5Smrgdrm_public char *drmGetRenderDeviceNameFromFd(int fd)
3018424e9256Smrg{
3019fe517fc9Smrg    return drmGetMinorNameForFD(fd, DRM_NODE_RENDER);
3020fe517fc9Smrg}
3021fe517fc9Smrg
30222ee35494Smrg#ifdef __linux__
30232ee35494Smrgstatic char * DRM_PRINTFLIKE(2, 3)
30242ee35494Smrgsysfs_uevent_get(const char *path, const char *fmt, ...)
30252ee35494Smrg{
30262ee35494Smrg    char filename[PATH_MAX + 1], *key, *line = NULL, *value = NULL;
30272ee35494Smrg    size_t size = 0, len;
30282ee35494Smrg    ssize_t num;
30292ee35494Smrg    va_list ap;
30302ee35494Smrg    FILE *fp;
30312ee35494Smrg
30322ee35494Smrg    va_start(ap, fmt);
30332ee35494Smrg    num = vasprintf(&key, fmt, ap);
30342ee35494Smrg    va_end(ap);
30352ee35494Smrg    len = num;
30362ee35494Smrg
30372ee35494Smrg    snprintf(filename, sizeof(filename), "%s/uevent", path);
30382ee35494Smrg
30392ee35494Smrg    fp = fopen(filename, "r");
30402ee35494Smrg    if (!fp) {
30412ee35494Smrg        free(key);
30422ee35494Smrg        return NULL;
30432ee35494Smrg    }
30442ee35494Smrg
30452ee35494Smrg    while ((num = getline(&line, &size, fp)) >= 0) {
30462ee35494Smrg        if ((strncmp(line, key, len) == 0) && (line[len] == '=')) {
30472ee35494Smrg            char *start = line + len + 1, *end = line + num - 1;
30482ee35494Smrg
30492ee35494Smrg            if (*end != '\n')
30502ee35494Smrg                end++;
30512ee35494Smrg
30522ee35494Smrg            value = strndup(start, end - start);
30532ee35494Smrg            break;
30542ee35494Smrg        }
30552ee35494Smrg    }
30562ee35494Smrg
30572ee35494Smrg    free(line);
30582ee35494Smrg    fclose(fp);
30592ee35494Smrg
30602ee35494Smrg    free(key);
30612ee35494Smrg
30622ee35494Smrg    return value;
30632ee35494Smrg}
30642ee35494Smrg#endif
30652ee35494Smrg
30666260e5d5Smrg/* Little white lie to avoid major rework of the existing code */
30676260e5d5Smrg#define DRM_BUS_VIRTIO 0x10
30686260e5d5Smrg
3069fe517fc9Smrg#ifdef __linux__
307087bf8e7cSmrgstatic int get_subsystem_type(const char *device_path)
307187bf8e7cSmrg{
307287bf8e7cSmrg    char path[PATH_MAX + 1] = "";
3073fe517fc9Smrg    char link[PATH_MAX + 1] = "";
3074fe517fc9Smrg    char *name;
30754545e80cSmrg    struct {
30764545e80cSmrg        const char *name;
30774545e80cSmrg        int bus_type;
30784545e80cSmrg    } bus_types[] = {
30794545e80cSmrg        { "/pci", DRM_BUS_PCI },
30804545e80cSmrg        { "/usb", DRM_BUS_USB },
30814545e80cSmrg        { "/platform", DRM_BUS_PLATFORM },
30824545e80cSmrg        { "/spi", DRM_BUS_PLATFORM },
30834545e80cSmrg        { "/host1x", DRM_BUS_HOST1X },
30844545e80cSmrg        { "/virtio", DRM_BUS_VIRTIO },
30854545e80cSmrg    };
3086fe517fc9Smrg
308787bf8e7cSmrg    strncpy(path, device_path, PATH_MAX);
308887bf8e7cSmrg    strncat(path, "/subsystem", PATH_MAX);
3089fe517fc9Smrg
3090fe517fc9Smrg    if (readlink(path, link, PATH_MAX) < 0)
3091fe517fc9Smrg        return -errno;
3092fe517fc9Smrg
3093fe517fc9Smrg    name = strrchr(link, '/');
3094fe517fc9Smrg    if (!name)
3095fe517fc9Smrg        return -EINVAL;
3096fe517fc9Smrg
30974545e80cSmrg    for (unsigned i = 0; i < ARRAY_SIZE(bus_types); i++) {
30984545e80cSmrg        if (strncmp(name, bus_types[i].name, strlen(bus_types[i].name)) == 0)
30994545e80cSmrg            return bus_types[i].bus_type;
31004545e80cSmrg    }
31016260e5d5Smrg
3102fe517fc9Smrg    return -EINVAL;
310387bf8e7cSmrg}
310487bf8e7cSmrg#endif
310587bf8e7cSmrg
310687bf8e7cSmrgstatic int drmParseSubsystemType(int maj, int min)
310787bf8e7cSmrg{
310887bf8e7cSmrg#ifdef __linux__
310987bf8e7cSmrg    char path[PATH_MAX + 1] = "";
311087bf8e7cSmrg    char real_path[PATH_MAX + 1] = "";
311187bf8e7cSmrg    int subsystem_type;
311287bf8e7cSmrg
311387bf8e7cSmrg    snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
311487bf8e7cSmrg
311587bf8e7cSmrg    subsystem_type = get_subsystem_type(path);
311687bf8e7cSmrg    /* Try to get the parent (underlying) device type */
311787bf8e7cSmrg    if (subsystem_type == DRM_BUS_VIRTIO) {
311887bf8e7cSmrg        /* Assume virtio-pci on error */
311987bf8e7cSmrg        if (!realpath(path, real_path))
312087bf8e7cSmrg            return DRM_BUS_VIRTIO;
312187bf8e7cSmrg        strncat(path, "/..", PATH_MAX);
312287bf8e7cSmrg        subsystem_type = get_subsystem_type(path);
312387bf8e7cSmrg        if (subsystem_type < 0)
312487bf8e7cSmrg            return DRM_BUS_VIRTIO;
312587bf8e7cSmrg     }
3126a970b457Sriastradh#elif defined(__NetBSD__)
3127a970b457Sriastradh    int type, fd;
3128a970b457Sriastradh    drmSetVersion sv;
3129a970b457Sriastradh    char *buf;
3130a970b457Sriastradh    unsigned domain, bus, dev;
3131a970b457Sriastradh    int func;
3132a970b457Sriastradh    int ret;
3133a970b457Sriastradh
3134a970b457Sriastradh    /* Get the type of device we're looking for to pick the right pathname.  */
3135a970b457Sriastradh    type = drmGetMinorType(min);
3136a970b457Sriastradh    if (type == -1)
3137a970b457Sriastradh	return -ENODEV;
3138a970b457Sriastradh
3139a970b457Sriastradh    /* Open the device.  Don't try to create it if it's not there.  */
3140a970b457Sriastradh    fd = drmOpenMinor(min, 0, type);
3141a970b457Sriastradh    if (fd < 0)
3142a970b457Sriastradh	return -errno;
3143a970b457Sriastradh
3144a970b457Sriastradh    /*
3145a970b457Sriastradh     * Set the interface version to 1.4 or 1.1, which has the effect of
3146a970b457Sriastradh     * populating the bus id for us.
3147a970b457Sriastradh     */
3148a970b457Sriastradh    sv.drm_di_major = 1;
3149a970b457Sriastradh    sv.drm_di_minor = 4;
3150a970b457Sriastradh    sv.drm_dd_major = -1;
3151a970b457Sriastradh    sv.drm_dd_minor = -1;
3152a970b457Sriastradh    if (drmSetInterfaceVersion(fd, &sv)) {
3153a970b457Sriastradh	sv.drm_di_major = 1;
3154a970b457Sriastradh	sv.drm_di_minor = 1;
3155a970b457Sriastradh	sv.drm_dd_major = -1;
3156a970b457Sriastradh	sv.drm_dd_minor = -1;
3157a970b457Sriastradh	if (drmSetInterfaceVersion(fd, &sv)) {
31585046d36bSriastradh	    /*
31595046d36bSriastradh	     * We're probably not the master.  Hope the master already
31605046d36bSriastradh	     * set the version to >=1.1 so that we can get the busid.
31615046d36bSriastradh	     */
3162a970b457Sriastradh	}
3163a970b457Sriastradh    }
3164a970b457Sriastradh
3165a970b457Sriastradh    /* Get the bus id.  */
3166a970b457Sriastradh    buf = drmGetBusid(fd);
3167a970b457Sriastradh
3168a970b457Sriastradh    /* We're done with the device now.  */
3169a970b457Sriastradh    (void)close(fd);
3170a970b457Sriastradh
3171a970b457Sriastradh    /* If there is no bus id, fail.  */
3172a970b457Sriastradh    if (buf == NULL)
3173a970b457Sriastradh	return -ENODEV;
3174a970b457Sriastradh
3175a970b457Sriastradh    /* Find a string we know about; otherwise -EINVAL.  */
3176a970b457Sriastradh    ret = -EINVAL;
317748994cb0Sriastradh    if (strncmp(buf, "pci:", 4) == 0)
3178a970b457Sriastradh	ret = DRM_BUS_PCI;
3179a970b457Sriastradh
3180a970b457Sriastradh    /* We're done with the bus id.  */
3181a970b457Sriastradh    free(buf);
3182a970b457Sriastradh
3183a970b457Sriastradh    /* Success or not, we're done.  */
3184a970b457Sriastradh    return ret;
31854545e80cSmrg#elif defined(__OpenBSD__) || defined(__DragonFly__)
31862ee35494Smrg    return DRM_BUS_PCI;
3187fe517fc9Smrg#else
3188fe517fc9Smrg#warning "Missing implementation of drmParseSubsystemType"
3189fe517fc9Smrg    return -EINVAL;
3190fe517fc9Smrg#endif
3191fe517fc9Smrg}
3192fe517fc9Smrg
319387bf8e7cSmrg#ifdef __linux__
31946260e5d5Smrgstatic void
31956260e5d5Smrgget_pci_path(int maj, int min, char *pci_path)
31966260e5d5Smrg{
31976260e5d5Smrg    char path[PATH_MAX + 1], *term;
31986260e5d5Smrg
31996260e5d5Smrg    snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
32006260e5d5Smrg    if (!realpath(path, pci_path)) {
32016260e5d5Smrg        strcpy(pci_path, path);
32026260e5d5Smrg        return;
32036260e5d5Smrg    }
32046260e5d5Smrg
32056260e5d5Smrg    term = strrchr(pci_path, '/');
32066260e5d5Smrg    if (term && strncmp(term, "/virtio", 7) == 0)
32076260e5d5Smrg        *term = 0;
32086260e5d5Smrg}
320987bf8e7cSmrg#endif
321087bf8e7cSmrg
321187bf8e7cSmrg#ifdef __FreeBSD__
321287bf8e7cSmrgstatic int get_sysctl_pci_bus_info(int maj, int min, drmPciBusInfoPtr info)
321387bf8e7cSmrg{
321487bf8e7cSmrg    char dname[SPECNAMELEN];
321587bf8e7cSmrg    char sysctl_name[16];
321687bf8e7cSmrg    char sysctl_val[256];
321787bf8e7cSmrg    size_t sysctl_len;
321887bf8e7cSmrg    int id, type, nelem;
321987bf8e7cSmrg    unsigned int rdev, majmin, domain, bus, dev, func;
322087bf8e7cSmrg
322187bf8e7cSmrg    rdev = makedev(maj, min);
322287bf8e7cSmrg    if (!devname_r(rdev, S_IFCHR, dname, sizeof(dname)))
322387bf8e7cSmrg      return -EINVAL;
322487bf8e7cSmrg
322587bf8e7cSmrg    if (sscanf(dname, "drm/%d\n", &id) != 1)
322687bf8e7cSmrg        return -EINVAL;
322787bf8e7cSmrg    type = drmGetMinorType(maj, min);
322887bf8e7cSmrg    if (type == -1)
322987bf8e7cSmrg        return -EINVAL;
323087bf8e7cSmrg
323187bf8e7cSmrg    /* BUG: This above section is iffy, since it mandates that a driver will
323287bf8e7cSmrg     * create both card and render node.
323387bf8e7cSmrg     * If it does not, the next DRM device will create card#X and
323487bf8e7cSmrg     * renderD#(128+X)-1.
323587bf8e7cSmrg     * This is a possibility in FreeBSD but for now there is no good way for
323687bf8e7cSmrg     * obtaining the info.
323787bf8e7cSmrg     */
323887bf8e7cSmrg    switch (type) {
323987bf8e7cSmrg    case DRM_NODE_PRIMARY:
324087bf8e7cSmrg         break;
324187bf8e7cSmrg    case DRM_NODE_CONTROL:
324287bf8e7cSmrg         id -= 64;
324387bf8e7cSmrg         break;
324487bf8e7cSmrg    case DRM_NODE_RENDER:
324587bf8e7cSmrg         id -= 128;
324687bf8e7cSmrg          break;
324787bf8e7cSmrg    }
324887bf8e7cSmrg    if (id < 0)
324987bf8e7cSmrg        return -EINVAL;
325087bf8e7cSmrg
325187bf8e7cSmrg    if (snprintf(sysctl_name, sizeof(sysctl_name), "hw.dri.%d.busid", id) <= 0)
325287bf8e7cSmrg      return -EINVAL;
325387bf8e7cSmrg    sysctl_len = sizeof(sysctl_val);
325487bf8e7cSmrg    if (sysctlbyname(sysctl_name, sysctl_val, &sysctl_len, NULL, 0))
325587bf8e7cSmrg      return -EINVAL;
325687bf8e7cSmrg
325787bf8e7cSmrg    #define bus_fmt "pci:%04x:%02x:%02x.%u"
325887bf8e7cSmrg
325987bf8e7cSmrg    nelem = sscanf(sysctl_val, bus_fmt, &domain, &bus, &dev, &func);
326087bf8e7cSmrg    if (nelem != 4)
326187bf8e7cSmrg      return -EINVAL;
326287bf8e7cSmrg    info->domain = domain;
326387bf8e7cSmrg    info->bus = bus;
326487bf8e7cSmrg    info->dev = dev;
326587bf8e7cSmrg    info->func = func;
326687bf8e7cSmrg
326787bf8e7cSmrg    return 0;
326887bf8e7cSmrg}
326987bf8e7cSmrg#endif
32706260e5d5Smrg
3271fe517fc9Smrgstatic int drmParsePciBusInfo(int maj, int min, drmPciBusInfoPtr info)
3272fe517fc9Smrg{
3273fe517fc9Smrg#ifdef __linux__
32742ee35494Smrg    unsigned int domain, bus, dev, func;
32756260e5d5Smrg    char pci_path[PATH_MAX + 1], *value;
32762ee35494Smrg    int num;
3277fe517fc9Smrg
32786260e5d5Smrg    get_pci_path(maj, min, pci_path);
3279fe517fc9Smrg
32806260e5d5Smrg    value = sysfs_uevent_get(pci_path, "PCI_SLOT_NAME");
32812ee35494Smrg    if (!value)
32822ee35494Smrg        return -ENOENT;
3283fe517fc9Smrg
32842ee35494Smrg    num = sscanf(value, "%04x:%02x:%02x.%1u", &domain, &bus, &dev, &func);
32852ee35494Smrg    free(value);
3286fe517fc9Smrg
32872ee35494Smrg    if (num != 4)
3288fe517fc9Smrg        return -EINVAL;
3289fe517fc9Smrg
3290fe517fc9Smrg    info->domain = domain;
3291fe517fc9Smrg    info->bus = bus;
3292fe517fc9Smrg    info->dev = dev;
3293fe517fc9Smrg    info->func = func;
3294fe517fc9Smrg
3295a970b457Sriastradh    return 0;
3296a970b457Sriastradh#elif defined(__NetBSD__)
3297a970b457Sriastradh    int type, fd;
3298a970b457Sriastradh    drmSetVersion sv;
3299a970b457Sriastradh    char *buf;
3300a970b457Sriastradh    unsigned domain, bus, dev;
3301a970b457Sriastradh    int func;
3302a970b457Sriastradh    int ret;
3303a970b457Sriastradh
3304a970b457Sriastradh    /* Get the type of device we're looking for to pick the right pathname.  */
3305a970b457Sriastradh    type = drmGetMinorType(min);
3306a970b457Sriastradh    if (type == -1)
3307a970b457Sriastradh	return -ENODEV;
3308a970b457Sriastradh
3309a970b457Sriastradh    /* Open the device.  Don't try to create it if it's not there.  */
3310a970b457Sriastradh    fd = drmOpenMinor(min, 0, type);
3311a970b457Sriastradh    if (fd < 0)
3312a970b457Sriastradh	return -errno;
3313a970b457Sriastradh
3314a970b457Sriastradh    /*
3315a970b457Sriastradh     * Set the interface version to 1.4 or 1.1, which has the effect of
3316a970b457Sriastradh     * populating the bus id for us.
3317a970b457Sriastradh     */
3318a970b457Sriastradh    sv.drm_di_major = 1;
3319a970b457Sriastradh    sv.drm_di_minor = 4;
3320a970b457Sriastradh    sv.drm_dd_major = -1;
3321a970b457Sriastradh    sv.drm_dd_minor = -1;
3322a970b457Sriastradh    if (drmSetInterfaceVersion(fd, &sv)) {
3323a970b457Sriastradh	sv.drm_di_major = 1;
3324a970b457Sriastradh	sv.drm_di_minor = 1;
3325a970b457Sriastradh	sv.drm_dd_major = -1;
3326a970b457Sriastradh	sv.drm_dd_minor = -1;
3327a970b457Sriastradh	if (drmSetInterfaceVersion(fd, &sv)) {
332806815bcbSmaya            /*
332906815bcbSmaya	     * We're probably not the master.  Hope the master already
333006815bcbSmaya	     * set the version to >=1.1 so that we can get the busid.
333106815bcbSmaya	     */
3332a970b457Sriastradh	}
3333a970b457Sriastradh    }
3334a970b457Sriastradh
3335a970b457Sriastradh    /* Get the bus id.  */
3336a970b457Sriastradh    buf = drmGetBusid(fd);
3337a970b457Sriastradh
3338a970b457Sriastradh    /* We're done with the device now.  */
3339a970b457Sriastradh    (void)close(fd);
3340a970b457Sriastradh
3341a970b457Sriastradh    /* If there is no bus id, fail.  */
3342a970b457Sriastradh    if (buf == NULL)
3343a970b457Sriastradh	return -ENODEV;
3344a970b457Sriastradh
3345a970b457Sriastradh    /* Parse the bus id.  */
3346a970b457Sriastradh    ret = sscanf(buf, "pci:%04x:%02x:%02x.%d", &domain, &bus, &dev, &func);
3347a970b457Sriastradh
3348a970b457Sriastradh    /* We're done with the bus id.  */
3349a970b457Sriastradh    free(buf);
3350a970b457Sriastradh
3351a970b457Sriastradh    /* If scanf didn't return 4 -- domain, bus, dev, func -- then fail.  */
3352a970b457Sriastradh    if (ret != 4)
3353a970b457Sriastradh	return -ENODEV;
3354a970b457Sriastradh
3355a970b457Sriastradh    /* Populate the results.  */
3356a970b457Sriastradh    info->domain = domain;
3357a970b457Sriastradh    info->bus = bus;
3358a970b457Sriastradh    info->dev = dev;
3359a970b457Sriastradh    info->func = func;
3360a970b457Sriastradh
3361a970b457Sriastradh    /* Success!  */
33622ee35494Smrg    return 0;
33634545e80cSmrg#elif defined(__OpenBSD__) || defined(__DragonFly__)
33642ee35494Smrg    struct drm_pciinfo pinfo;
33652ee35494Smrg    int fd, type;
33662ee35494Smrg
336787bf8e7cSmrg    type = drmGetMinorType(maj, min);
33682ee35494Smrg    if (type == -1)
33692ee35494Smrg        return -ENODEV;
33702ee35494Smrg
33712ee35494Smrg    fd = drmOpenMinor(min, 0, type);
33722ee35494Smrg    if (fd < 0)
33732ee35494Smrg        return -errno;
33742ee35494Smrg
33752ee35494Smrg    if (drmIoctl(fd, DRM_IOCTL_GET_PCIINFO, &pinfo)) {
33762ee35494Smrg        close(fd);
33772ee35494Smrg        return -errno;
33782ee35494Smrg    }
33792ee35494Smrg    close(fd);
33802ee35494Smrg
33812ee35494Smrg    info->domain = pinfo.domain;
33822ee35494Smrg    info->bus = pinfo.bus;
33832ee35494Smrg    info->dev = pinfo.dev;
33842ee35494Smrg    info->func = pinfo.func;
33852ee35494Smrg
3386fe517fc9Smrg    return 0;
338787bf8e7cSmrg#elif __FreeBSD__
338887bf8e7cSmrg    return get_sysctl_pci_bus_info(maj, min, info);
3389fe517fc9Smrg#else
3390fe517fc9Smrg#warning "Missing implementation of drmParsePciBusInfo"
3391fe517fc9Smrg    return -EINVAL;
3392fe517fc9Smrg#endif
3393fe517fc9Smrg}
3394fe517fc9Smrg
33956260e5d5Smrgdrm_public int drmDevicesEqual(drmDevicePtr a, drmDevicePtr b)
3396fe517fc9Smrg{
3397fe517fc9Smrg    if (a == NULL || b == NULL)
33980655efefSmrg        return 0;
3399fe517fc9Smrg
3400fe517fc9Smrg    if (a->bustype != b->bustype)
34010655efefSmrg        return 0;
3402fe517fc9Smrg
3403fe517fc9Smrg    switch (a->bustype) {
3404fe517fc9Smrg    case DRM_BUS_PCI:
34050655efefSmrg        return memcmp(a->businfo.pci, b->businfo.pci, sizeof(drmPciBusInfo)) == 0;
34062ee35494Smrg
34072ee35494Smrg    case DRM_BUS_USB:
34080655efefSmrg        return memcmp(a->businfo.usb, b->businfo.usb, sizeof(drmUsbBusInfo)) == 0;
34092ee35494Smrg
34102ee35494Smrg    case DRM_BUS_PLATFORM:
34110655efefSmrg        return memcmp(a->businfo.platform, b->businfo.platform, sizeof(drmPlatformBusInfo)) == 0;
34122ee35494Smrg
34132ee35494Smrg    case DRM_BUS_HOST1X:
34140655efefSmrg        return memcmp(a->businfo.host1x, b->businfo.host1x, sizeof(drmHost1xBusInfo)) == 0;
34152ee35494Smrg
3416fe517fc9Smrg    default:
3417fe517fc9Smrg        break;
3418fe517fc9Smrg    }
3419fe517fc9Smrg
34200655efefSmrg    return 0;
3421fe517fc9Smrg}
3422fe517fc9Smrg
3423fe517fc9Smrgstatic int drmGetNodeType(const char *name)
3424fe517fc9Smrg{
3425fe517fc9Smrg    if (strncmp(name, DRM_CONTROL_MINOR_NAME,
3426fe517fc9Smrg        sizeof(DRM_CONTROL_MINOR_NAME ) - 1) == 0)
3427fe517fc9Smrg        return DRM_NODE_CONTROL;
3428fe517fc9Smrg
3429fe517fc9Smrg    if (strncmp(name, DRM_RENDER_MINOR_NAME,
3430fe517fc9Smrg        sizeof(DRM_RENDER_MINOR_NAME) - 1) == 0)
3431fe517fc9Smrg        return DRM_NODE_RENDER;
3432fe517fc9Smrg
343382025ec7Smrg    if (strncmp(name, DRM_PRIMARY_MINOR_NAME,
343482025ec7Smrg        sizeof(DRM_PRIMARY_MINOR_NAME) - 1) == 0)
343582025ec7Smrg        return DRM_NODE_PRIMARY;
343682025ec7Smrg
3437fe517fc9Smrg    return -EINVAL;
3438fe517fc9Smrg}
3439fe517fc9Smrg
3440fe517fc9Smrgstatic int drmGetMaxNodeName(void)
3441fe517fc9Smrg{
3442fe517fc9Smrg    return sizeof(DRM_DIR_NAME) +
3443fe517fc9Smrg           MAX3(sizeof(DRM_PRIMARY_MINOR_NAME),
3444fe517fc9Smrg                sizeof(DRM_CONTROL_MINOR_NAME),
3445fe517fc9Smrg                sizeof(DRM_RENDER_MINOR_NAME)) +
3446fe517fc9Smrg           3 /* length of the node number */;
3447fe517fc9Smrg}
3448fe517fc9Smrg
3449fe517fc9Smrg#ifdef __linux__
34502ee35494Smrgstatic int parse_separate_sysfs_files(int maj, int min,
34512ee35494Smrg                                      drmPciDeviceInfoPtr device,
34522ee35494Smrg                                      bool ignore_revision)
34532ee35494Smrg{
34542ee35494Smrg    static const char *attrs[] = {
34552ee35494Smrg      "revision", /* Older kernels are missing the file, so check for it first */
34562ee35494Smrg      "vendor",
34572ee35494Smrg      "device",
34582ee35494Smrg      "subsystem_vendor",
34592ee35494Smrg      "subsystem_device",
34602ee35494Smrg    };
34616260e5d5Smrg    char path[PATH_MAX + 1], pci_path[PATH_MAX + 1];
34622ee35494Smrg    unsigned int data[ARRAY_SIZE(attrs)];
34632ee35494Smrg    FILE *fp;
34642ee35494Smrg    int ret;
34652ee35494Smrg
34666260e5d5Smrg    get_pci_path(maj, min, pci_path);
34676260e5d5Smrg
34682ee35494Smrg    for (unsigned i = ignore_revision ? 1 : 0; i < ARRAY_SIZE(attrs); i++) {
34696260e5d5Smrg        snprintf(path, PATH_MAX, "%s/%s", pci_path, attrs[i]);
34702ee35494Smrg        fp = fopen(path, "r");
34712ee35494Smrg        if (!fp)
34722ee35494Smrg            return -errno;
34732ee35494Smrg
34742ee35494Smrg        ret = fscanf(fp, "%x", &data[i]);
34752ee35494Smrg        fclose(fp);
34762ee35494Smrg        if (ret != 1)
34772ee35494Smrg            return -errno;
34782ee35494Smrg
34792ee35494Smrg    }
34802ee35494Smrg
34812ee35494Smrg    device->revision_id = ignore_revision ? 0xff : data[0] & 0xff;
34822ee35494Smrg    device->vendor_id = data[1] & 0xffff;
34832ee35494Smrg    device->device_id = data[2] & 0xffff;
34842ee35494Smrg    device->subvendor_id = data[3] & 0xffff;
34852ee35494Smrg    device->subdevice_id = data[4] & 0xffff;
34862ee35494Smrg
34872ee35494Smrg    return 0;
34882ee35494Smrg}
34892ee35494Smrg
34902ee35494Smrgstatic int parse_config_sysfs_file(int maj, int min,
34912ee35494Smrg                                   drmPciDeviceInfoPtr device)
34922ee35494Smrg{
34936260e5d5Smrg    char path[PATH_MAX + 1], pci_path[PATH_MAX + 1];
3494fe517fc9Smrg    unsigned char config[64];
3495fe517fc9Smrg    int fd, ret;
3496fe517fc9Smrg
34976260e5d5Smrg    get_pci_path(maj, min, pci_path);
34986260e5d5Smrg
34996260e5d5Smrg    snprintf(path, PATH_MAX, "%s/config", pci_path);
3500fe517fc9Smrg    fd = open(path, O_RDONLY);
3501fe517fc9Smrg    if (fd < 0)
3502fe517fc9Smrg        return -errno;
3503fe517fc9Smrg
3504fe517fc9Smrg    ret = read(fd, config, sizeof(config));
3505fe517fc9Smrg    close(fd);
3506fe517fc9Smrg    if (ret < 0)
3507fe517fc9Smrg        return -errno;
3508fe517fc9Smrg
3509fe517fc9Smrg    device->vendor_id = config[0] | (config[1] << 8);
3510fe517fc9Smrg    device->device_id = config[2] | (config[3] << 8);
3511fe517fc9Smrg    device->revision_id = config[8];
3512fe517fc9Smrg    device->subvendor_id = config[44] | (config[45] << 8);
3513fe517fc9Smrg    device->subdevice_id = config[46] | (config[47] << 8);
3514fe517fc9Smrg
35152ee35494Smrg    return 0;
35162ee35494Smrg}
35172ee35494Smrg#endif
35182ee35494Smrg
35192ee35494Smrgstatic int drmParsePciDeviceInfo(int maj, int min,
35202ee35494Smrg                                 drmPciDeviceInfoPtr device,
35212ee35494Smrg                                 uint32_t flags)
35222ee35494Smrg{
35232ee35494Smrg#ifdef __linux__
35242ee35494Smrg    if (!(flags & DRM_DEVICE_GET_PCI_REVISION))
35252ee35494Smrg        return parse_separate_sysfs_files(maj, min, device, true);
35262ee35494Smrg
35272ee35494Smrg    if (parse_separate_sysfs_files(maj, min, device, false))
35282ee35494Smrg        return parse_config_sysfs_file(maj, min, device);
35292ee35494Smrg
35302ee35494Smrg    return 0;
3531a970b457Sriastradh#elif defined(__NetBSD__)
3532a970b457Sriastradh    drmPciBusInfo businfo;
3533a970b457Sriastradh    char fname[PATH_MAX];
3534a970b457Sriastradh    int pcifd;
3535a970b457Sriastradh    pcireg_t id, class, subsys;
3536a970b457Sriastradh    int ret;
3537a970b457Sriastradh
3538a970b457Sriastradh    /* Find where on the bus the device lives.  */
3539a970b457Sriastradh    ret = drmParsePciBusInfo(maj, min, &businfo);
3540a970b457Sriastradh    if (ret)
3541a970b457Sriastradh	return ret;
3542a970b457Sriastradh
3543a970b457Sriastradh    /* Open the pciN device node to get at its config registers.  */
3544a970b457Sriastradh    if (snprintf(fname, sizeof fname, "/dev/pci%u", businfo.domain)
3545a970b457Sriastradh	>= sizeof fname)
3546a970b457Sriastradh	return -ENODEV;
3547a970b457Sriastradh    if ((pcifd = open(fname, O_RDONLY)) == -1)
3548a970b457Sriastradh	return -errno;
3549a970b457Sriastradh
3550f8b67707Schristos    ret = -1;
3551a970b457Sriastradh    /* Read the id and class pci config registers.  */
3552a970b457Sriastradh    if (pcibus_conf_read(pcifd, businfo.bus, businfo.dev, businfo.func,
3553a970b457Sriastradh	    PCI_ID_REG, &id) == -1)
3554f8b67707Schristos	goto out;
3555a970b457Sriastradh    if (pcibus_conf_read(pcifd, businfo.bus, businfo.dev, businfo.func,
3556a970b457Sriastradh	    PCI_CLASS_REG, &class) == -1)
3557f8b67707Schristos	goto out;
3558a970b457Sriastradh    if (pcibus_conf_read(pcifd, businfo.bus, businfo.dev, businfo.func,
3559a970b457Sriastradh	    PCI_SUBSYS_ID_REG, &subsys) == -1)
3560f8b67707Schristos	goto out;
3561a970b457Sriastradh
3562f8b67707Schristos    ret = 0;
3563a970b457Sriastradh    device->vendor_id = PCI_VENDOR(id);
3564a970b457Sriastradh    device->device_id = PCI_PRODUCT(id);
3565a970b457Sriastradh    device->subvendor_id = PCI_SUBSYS_VENDOR(subsys);
3566a970b457Sriastradh    device->subdevice_id = PCI_SUBSYS_ID(subsys);
3567a970b457Sriastradh    device->revision_id = PCI_REVISION(class);
3568f8b67707Schristosout:
3569f8b67707Schristos    if (ret == -1)
3570f8b67707Schristos	ret = -errno;
3571f8b67707Schristos    close(pcifd);
3572f8b67707Schristos    return ret;
35734545e80cSmrg#elif defined(__OpenBSD__) || defined(__DragonFly__)
35742ee35494Smrg    struct drm_pciinfo pinfo;
35752ee35494Smrg    int fd, type;
35762ee35494Smrg
357787bf8e7cSmrg    type = drmGetMinorType(maj, min);
35782ee35494Smrg    if (type == -1)
35792ee35494Smrg        return -ENODEV;
35802ee35494Smrg
35812ee35494Smrg    fd = drmOpenMinor(min, 0, type);
35822ee35494Smrg    if (fd < 0)
35832ee35494Smrg        return -errno;
35842ee35494Smrg
35852ee35494Smrg    if (drmIoctl(fd, DRM_IOCTL_GET_PCIINFO, &pinfo)) {
35862ee35494Smrg        close(fd);
35872ee35494Smrg        return -errno;
35882ee35494Smrg    }
35892ee35494Smrg    close(fd);
35902ee35494Smrg
35912ee35494Smrg    device->vendor_id = pinfo.vendor_id;
35922ee35494Smrg    device->device_id = pinfo.device_id;
35932ee35494Smrg    device->revision_id = pinfo.revision_id;
35942ee35494Smrg    device->subvendor_id = pinfo.subvendor_id;
35952ee35494Smrg    device->subdevice_id = pinfo.subdevice_id;
35962ee35494Smrg
359787bf8e7cSmrg    return 0;
359887bf8e7cSmrg#elif __FreeBSD__
359987bf8e7cSmrg    drmPciBusInfo info;
360087bf8e7cSmrg    struct pci_conf_io pc;
360187bf8e7cSmrg    struct pci_match_conf patterns[1];
360287bf8e7cSmrg    struct pci_conf results[1];
360387bf8e7cSmrg    int fd, error;
360487bf8e7cSmrg
360587bf8e7cSmrg    if (get_sysctl_pci_bus_info(maj, min, &info) != 0)
360687bf8e7cSmrg        return -EINVAL;
360787bf8e7cSmrg
360887bf8e7cSmrg    fd = open("/dev/pci", O_RDONLY, 0);
360987bf8e7cSmrg    if (fd < 0)
361087bf8e7cSmrg        return -errno;
361187bf8e7cSmrg
361287bf8e7cSmrg    bzero(&patterns, sizeof(patterns));
361387bf8e7cSmrg    patterns[0].pc_sel.pc_domain = info.domain;
361487bf8e7cSmrg    patterns[0].pc_sel.pc_bus = info.bus;
361587bf8e7cSmrg    patterns[0].pc_sel.pc_dev = info.dev;
361687bf8e7cSmrg    patterns[0].pc_sel.pc_func = info.func;
361787bf8e7cSmrg    patterns[0].flags = PCI_GETCONF_MATCH_DOMAIN | PCI_GETCONF_MATCH_BUS
361887bf8e7cSmrg                      | PCI_GETCONF_MATCH_DEV | PCI_GETCONF_MATCH_FUNC;
361987bf8e7cSmrg    bzero(&pc, sizeof(struct pci_conf_io));
362087bf8e7cSmrg    pc.num_patterns = 1;
362187bf8e7cSmrg    pc.pat_buf_len = sizeof(patterns);
362287bf8e7cSmrg    pc.patterns = patterns;
362387bf8e7cSmrg    pc.match_buf_len = sizeof(results);
362487bf8e7cSmrg    pc.matches = results;
362587bf8e7cSmrg
362687bf8e7cSmrg    if (ioctl(fd, PCIOCGETCONF, &pc) || pc.status == PCI_GETCONF_ERROR) {
362787bf8e7cSmrg        error = errno;
362887bf8e7cSmrg        close(fd);
362987bf8e7cSmrg        return -error;
363087bf8e7cSmrg    }
363187bf8e7cSmrg    close(fd);
363287bf8e7cSmrg
363387bf8e7cSmrg    device->vendor_id = results[0].pc_vendor;
363487bf8e7cSmrg    device->device_id = results[0].pc_device;
363587bf8e7cSmrg    device->subvendor_id = results[0].pc_subvendor;
363687bf8e7cSmrg    device->subdevice_id = results[0].pc_subdevice;
363787bf8e7cSmrg    device->revision_id = results[0].pc_revid;
363887bf8e7cSmrg
3639fe517fc9Smrg    return 0;
3640fe517fc9Smrg#else
3641fe517fc9Smrg#warning "Missing implementation of drmParsePciDeviceInfo"
3642fe517fc9Smrg    return -EINVAL;
3643fe517fc9Smrg#endif
3644fe517fc9Smrg}
3645fe517fc9Smrg
36462ee35494Smrgstatic void drmFreePlatformDevice(drmDevicePtr device)
36472ee35494Smrg{
36482ee35494Smrg    if (device->deviceinfo.platform) {
36492ee35494Smrg        if (device->deviceinfo.platform->compatible) {
36502ee35494Smrg            char **compatible = device->deviceinfo.platform->compatible;
36512ee35494Smrg
36522ee35494Smrg            while (*compatible) {
36532ee35494Smrg                free(*compatible);
36542ee35494Smrg                compatible++;
36552ee35494Smrg            }
36562ee35494Smrg
36572ee35494Smrg            free(device->deviceinfo.platform->compatible);
36582ee35494Smrg        }
36592ee35494Smrg    }
36602ee35494Smrg}
36612ee35494Smrg
36622ee35494Smrgstatic void drmFreeHost1xDevice(drmDevicePtr device)
36632ee35494Smrg{
36642ee35494Smrg    if (device->deviceinfo.host1x) {
36652ee35494Smrg        if (device->deviceinfo.host1x->compatible) {
36662ee35494Smrg            char **compatible = device->deviceinfo.host1x->compatible;
36672ee35494Smrg
36682ee35494Smrg            while (*compatible) {
36692ee35494Smrg                free(*compatible);
36702ee35494Smrg                compatible++;
36712ee35494Smrg            }
36722ee35494Smrg
36732ee35494Smrg            free(device->deviceinfo.host1x->compatible);
36742ee35494Smrg        }
36752ee35494Smrg    }
36762ee35494Smrg}
36772ee35494Smrg
36786260e5d5Smrgdrm_public void drmFreeDevice(drmDevicePtr *device)
3679fe517fc9Smrg{
3680fe517fc9Smrg    if (device == NULL)
3681fe517fc9Smrg        return;
3682fe517fc9Smrg
36832ee35494Smrg    if (*device) {
36842ee35494Smrg        switch ((*device)->bustype) {
36852ee35494Smrg        case DRM_BUS_PLATFORM:
36862ee35494Smrg            drmFreePlatformDevice(*device);
36872ee35494Smrg            break;
36882ee35494Smrg
36892ee35494Smrg        case DRM_BUS_HOST1X:
36902ee35494Smrg            drmFreeHost1xDevice(*device);
36912ee35494Smrg            break;
36922ee35494Smrg        }
36932ee35494Smrg    }
36942ee35494Smrg
3695fe517fc9Smrg    free(*device);
3696fe517fc9Smrg    *device = NULL;
3697fe517fc9Smrg}
3698fe517fc9Smrg
36996260e5d5Smrgdrm_public void drmFreeDevices(drmDevicePtr devices[], int count)
3700fe517fc9Smrg{
3701fe517fc9Smrg    int i;
3702fe517fc9Smrg
3703fe517fc9Smrg    if (devices == NULL)
3704fe517fc9Smrg        return;
3705fe517fc9Smrg
3706fe517fc9Smrg    for (i = 0; i < count; i++)
3707fe517fc9Smrg        if (devices[i])
3708fe517fc9Smrg            drmFreeDevice(&devices[i]);
3709fe517fc9Smrg}
3710fe517fc9Smrg
37112ee35494Smrgstatic drmDevicePtr drmDeviceAlloc(unsigned int type, const char *node,
37122ee35494Smrg                                   size_t bus_size, size_t device_size,
37132ee35494Smrg                                   char **ptrp)
3714fe517fc9Smrg{
37152ee35494Smrg    size_t max_node_length, extra, size;
37162ee35494Smrg    drmDevicePtr device;
37172ee35494Smrg    unsigned int i;
37182ee35494Smrg    char *ptr;
3719fe517fc9Smrg
37202ee35494Smrg    max_node_length = ALIGN(drmGetMaxNodeName(), sizeof(void *));
37212ee35494Smrg    extra = DRM_NODE_MAX * (sizeof(void *) + max_node_length);
3722fe517fc9Smrg
37232ee35494Smrg    size = sizeof(*device) + extra + bus_size + device_size;
3724fe517fc9Smrg
37252ee35494Smrg    device = calloc(1, size);
37262ee35494Smrg    if (!device)
37272ee35494Smrg        return NULL;
37282ee35494Smrg
37292ee35494Smrg    device->available_nodes = 1 << type;
3730fe517fc9Smrg
37312ee35494Smrg    ptr = (char *)device + sizeof(*device);
37322ee35494Smrg    device->nodes = (char **)ptr;
37332ee35494Smrg
37342ee35494Smrg    ptr += DRM_NODE_MAX * sizeof(void *);
3735fe517fc9Smrg
3736fe517fc9Smrg    for (i = 0; i < DRM_NODE_MAX; i++) {
37372ee35494Smrg        device->nodes[i] = ptr;
37382ee35494Smrg        ptr += max_node_length;
3739fe517fc9Smrg    }
3740fe517fc9Smrg
37412ee35494Smrg    memcpy(device->nodes[type], node, max_node_length);
37422ee35494Smrg
37432ee35494Smrg    *ptrp = ptr;
37442ee35494Smrg
37452ee35494Smrg    return device;
37462ee35494Smrg}
37472ee35494Smrg
37482ee35494Smrgstatic int drmProcessPciDevice(drmDevicePtr *device,
37492ee35494Smrg                               const char *node, int node_type,
37502ee35494Smrg                               int maj, int min, bool fetch_deviceinfo,
37512ee35494Smrg                               uint32_t flags)
37522ee35494Smrg{
37532ee35494Smrg    drmDevicePtr dev;
37542ee35494Smrg    char *addr;
37552ee35494Smrg    int ret;
37562ee35494Smrg
37572ee35494Smrg    dev = drmDeviceAlloc(node_type, node, sizeof(drmPciBusInfo),
37582ee35494Smrg                         sizeof(drmPciDeviceInfo), &addr);
37592ee35494Smrg    if (!dev)
37602ee35494Smrg        return -ENOMEM;
37612ee35494Smrg
37622ee35494Smrg    dev->bustype = DRM_BUS_PCI;
3763fe517fc9Smrg
37642ee35494Smrg    dev->businfo.pci = (drmPciBusInfoPtr)addr;
37652ee35494Smrg
37662ee35494Smrg    ret = drmParsePciBusInfo(maj, min, dev->businfo.pci);
3767fe517fc9Smrg    if (ret)
3768fe517fc9Smrg        goto free_device;
3769fe517fc9Smrg
3770fe517fc9Smrg    // Fetch the device info if the user has requested it
3771fe517fc9Smrg    if (fetch_deviceinfo) {
3772fe517fc9Smrg        addr += sizeof(drmPciBusInfo);
37732ee35494Smrg        dev->deviceinfo.pci = (drmPciDeviceInfoPtr)addr;
3774fe517fc9Smrg
37752ee35494Smrg        ret = drmParsePciDeviceInfo(maj, min, dev->deviceinfo.pci, flags);
3776fe517fc9Smrg        if (ret)
3777fe517fc9Smrg            goto free_device;
3778fe517fc9Smrg    }
37792ee35494Smrg
37802ee35494Smrg    *device = dev;
37812ee35494Smrg
3782fe517fc9Smrg    return 0;
3783fe517fc9Smrg
3784fe517fc9Smrgfree_device:
37852ee35494Smrg    free(dev);
37862ee35494Smrg    return ret;
37872ee35494Smrg}
37882ee35494Smrg
378987bf8e7cSmrg#ifdef __linux__
379087bf8e7cSmrgstatic int drm_usb_dev_path(int maj, int min, char *path, size_t len)
379187bf8e7cSmrg{
379287bf8e7cSmrg    char *value, *tmp_path, *slash;
379387bf8e7cSmrg
379487bf8e7cSmrg    snprintf(path, len, "/sys/dev/char/%d:%d/device", maj, min);
379587bf8e7cSmrg
379687bf8e7cSmrg    value = sysfs_uevent_get(path, "DEVTYPE");
379787bf8e7cSmrg    if (!value)
379887bf8e7cSmrg        return -ENOENT;
379987bf8e7cSmrg
380087bf8e7cSmrg    if (strcmp(value, "usb_device") == 0)
380187bf8e7cSmrg        return 0;
380287bf8e7cSmrg    if (strcmp(value, "usb_interface") != 0)
380387bf8e7cSmrg        return -ENOTSUP;
380487bf8e7cSmrg
380587bf8e7cSmrg    /* The parent of a usb_interface is a usb_device */
380687bf8e7cSmrg
380787bf8e7cSmrg    tmp_path = realpath(path, NULL);
380887bf8e7cSmrg    if (!tmp_path)
380987bf8e7cSmrg        return -errno;
381087bf8e7cSmrg
381187bf8e7cSmrg    slash = strrchr(tmp_path, '/');
381287bf8e7cSmrg    if (!slash) {
381387bf8e7cSmrg        free(tmp_path);
381487bf8e7cSmrg        return -EINVAL;
381587bf8e7cSmrg    }
381687bf8e7cSmrg
381787bf8e7cSmrg    *slash = '\0';
381887bf8e7cSmrg
381987bf8e7cSmrg    if (snprintf(path, len, "%s", tmp_path) >= (int)len) {
382087bf8e7cSmrg        free(tmp_path);
382187bf8e7cSmrg        return -EINVAL;
382287bf8e7cSmrg    }
382387bf8e7cSmrg
382487bf8e7cSmrg    free(tmp_path);
382587bf8e7cSmrg    return 0;
382687bf8e7cSmrg}
382787bf8e7cSmrg#endif
382887bf8e7cSmrg
38292ee35494Smrgstatic int drmParseUsbBusInfo(int maj, int min, drmUsbBusInfoPtr info)
38302ee35494Smrg{
38312ee35494Smrg#ifdef __linux__
38322ee35494Smrg    char path[PATH_MAX + 1], *value;
38332ee35494Smrg    unsigned int bus, dev;
38342ee35494Smrg    int ret;
38352ee35494Smrg
383687bf8e7cSmrg    ret = drm_usb_dev_path(maj, min, path, sizeof(path));
383787bf8e7cSmrg    if (ret < 0)
383887bf8e7cSmrg        return ret;
38392ee35494Smrg
38402ee35494Smrg    value = sysfs_uevent_get(path, "BUSNUM");
38412ee35494Smrg    if (!value)
38422ee35494Smrg        return -ENOENT;
38432ee35494Smrg
38442ee35494Smrg    ret = sscanf(value, "%03u", &bus);
38452ee35494Smrg    free(value);
38462ee35494Smrg
38472ee35494Smrg    if (ret <= 0)
38482ee35494Smrg        return -errno;
38492ee35494Smrg
38502ee35494Smrg    value = sysfs_uevent_get(path, "DEVNUM");
38512ee35494Smrg    if (!value)
38522ee35494Smrg        return -ENOENT;
38532ee35494Smrg
38542ee35494Smrg    ret = sscanf(value, "%03u", &dev);
38552ee35494Smrg    free(value);
38562ee35494Smrg
38572ee35494Smrg    if (ret <= 0)
38582ee35494Smrg        return -errno;
38592ee35494Smrg
38602ee35494Smrg    info->bus = bus;
38612ee35494Smrg    info->dev = dev;
38622ee35494Smrg
38632ee35494Smrg    return 0;
38642ee35494Smrg#else
38652ee35494Smrg#warning "Missing implementation of drmParseUsbBusInfo"
38662ee35494Smrg    return -EINVAL;
38672ee35494Smrg#endif
38682ee35494Smrg}
38692ee35494Smrg
38702ee35494Smrgstatic int drmParseUsbDeviceInfo(int maj, int min, drmUsbDeviceInfoPtr info)
38712ee35494Smrg{
38722ee35494Smrg#ifdef __linux__
38732ee35494Smrg    char path[PATH_MAX + 1], *value;
38742ee35494Smrg    unsigned int vendor, product;
38752ee35494Smrg    int ret;
38762ee35494Smrg
387787bf8e7cSmrg    ret = drm_usb_dev_path(maj, min, path, sizeof(path));
387887bf8e7cSmrg    if (ret < 0)
387987bf8e7cSmrg        return ret;
38802ee35494Smrg
38812ee35494Smrg    value = sysfs_uevent_get(path, "PRODUCT");
38822ee35494Smrg    if (!value)
38832ee35494Smrg        return -ENOENT;
38842ee35494Smrg
38852ee35494Smrg    ret = sscanf(value, "%x/%x", &vendor, &product);
38862ee35494Smrg    free(value);
38872ee35494Smrg
38882ee35494Smrg    if (ret <= 0)
38892ee35494Smrg        return -errno;
38902ee35494Smrg
38912ee35494Smrg    info->vendor = vendor;
38922ee35494Smrg    info->product = product;
38932ee35494Smrg
38942ee35494Smrg    return 0;
38952ee35494Smrg#else
38962ee35494Smrg#warning "Missing implementation of drmParseUsbDeviceInfo"
38972ee35494Smrg    return -EINVAL;
38982ee35494Smrg#endif
38992ee35494Smrg}
39002ee35494Smrg
39012ee35494Smrgstatic int drmProcessUsbDevice(drmDevicePtr *device, const char *node,
39022ee35494Smrg                               int node_type, int maj, int min,
39032ee35494Smrg                               bool fetch_deviceinfo, uint32_t flags)
39042ee35494Smrg{
39052ee35494Smrg    drmDevicePtr dev;
39062ee35494Smrg    char *ptr;
39072ee35494Smrg    int ret;
39082ee35494Smrg
39092ee35494Smrg    dev = drmDeviceAlloc(node_type, node, sizeof(drmUsbBusInfo),
39102ee35494Smrg                         sizeof(drmUsbDeviceInfo), &ptr);
39112ee35494Smrg    if (!dev)
39122ee35494Smrg        return -ENOMEM;
39132ee35494Smrg
39142ee35494Smrg    dev->bustype = DRM_BUS_USB;
39152ee35494Smrg
39162ee35494Smrg    dev->businfo.usb = (drmUsbBusInfoPtr)ptr;
39172ee35494Smrg
39182ee35494Smrg    ret = drmParseUsbBusInfo(maj, min, dev->businfo.usb);
39192ee35494Smrg    if (ret < 0)
39202ee35494Smrg        goto free_device;
39212ee35494Smrg
39222ee35494Smrg    if (fetch_deviceinfo) {
39232ee35494Smrg        ptr += sizeof(drmUsbBusInfo);
39242ee35494Smrg        dev->deviceinfo.usb = (drmUsbDeviceInfoPtr)ptr;
39252ee35494Smrg
39262ee35494Smrg        ret = drmParseUsbDeviceInfo(maj, min, dev->deviceinfo.usb);
39272ee35494Smrg        if (ret < 0)
39282ee35494Smrg            goto free_device;
39292ee35494Smrg    }
39302ee35494Smrg
39312ee35494Smrg    *device = dev;
39322ee35494Smrg
39332ee35494Smrg    return 0;
39342ee35494Smrg
39352ee35494Smrgfree_device:
39362ee35494Smrg    free(dev);
39372ee35494Smrg    return ret;
39382ee35494Smrg}
39392ee35494Smrg
3940bf6cc7dcSmrgstatic int drmParseOFBusInfo(int maj, int min, char *fullname)
39412ee35494Smrg{
39422ee35494Smrg#ifdef __linux__
3943bf6cc7dcSmrg    char path[PATH_MAX + 1], *name, *tmp_name;
39442ee35494Smrg
39452ee35494Smrg    snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
39462ee35494Smrg
39472ee35494Smrg    name = sysfs_uevent_get(path, "OF_FULLNAME");
3948bf6cc7dcSmrg    tmp_name = name;
3949bf6cc7dcSmrg    if (!name) {
3950bf6cc7dcSmrg        /* If the device lacks OF data, pick the MODALIAS info */
3951bf6cc7dcSmrg        name = sysfs_uevent_get(path, "MODALIAS");
3952bf6cc7dcSmrg        if (!name)
3953bf6cc7dcSmrg            return -ENOENT;
3954bf6cc7dcSmrg
3955bf6cc7dcSmrg        /* .. and strip the MODALIAS=[platform,usb...]: part. */
3956bf6cc7dcSmrg        tmp_name = strrchr(name, ':');
3957bf6cc7dcSmrg        if (!tmp_name) {
3958bf6cc7dcSmrg            free(name);
3959bf6cc7dcSmrg            return -ENOENT;
3960bf6cc7dcSmrg        }
3961bf6cc7dcSmrg        tmp_name++;
3962bf6cc7dcSmrg    }
39632ee35494Smrg
3964bf6cc7dcSmrg    strncpy(fullname, tmp_name, DRM_PLATFORM_DEVICE_NAME_LEN);
3965bf6cc7dcSmrg    fullname[DRM_PLATFORM_DEVICE_NAME_LEN - 1] = '\0';
39662ee35494Smrg    free(name);
39672ee35494Smrg
39682ee35494Smrg    return 0;
39692ee35494Smrg#else
3970bf6cc7dcSmrg#warning "Missing implementation of drmParseOFBusInfo"
39712ee35494Smrg    return -EINVAL;
39722ee35494Smrg#endif
39732ee35494Smrg}
39742ee35494Smrg
3975bf6cc7dcSmrgstatic int drmParseOFDeviceInfo(int maj, int min, char ***compatible)
39762ee35494Smrg{
39772ee35494Smrg#ifdef __linux__
3978bf6cc7dcSmrg    char path[PATH_MAX + 1], *value, *tmp_name;
39792ee35494Smrg    unsigned int count, i;
39802ee35494Smrg    int err;
39812ee35494Smrg
39822ee35494Smrg    snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
39832ee35494Smrg
39842ee35494Smrg    value = sysfs_uevent_get(path, "OF_COMPATIBLE_N");
3985bf6cc7dcSmrg    if (value) {
3986bf6cc7dcSmrg        sscanf(value, "%u", &count);
3987bf6cc7dcSmrg        free(value);
3988bf6cc7dcSmrg    } else {
3989bf6cc7dcSmrg        /* Assume one entry if the device lack OF data */
3990bf6cc7dcSmrg        count = 1;
3991bf6cc7dcSmrg    }
39922ee35494Smrg
3993bf6cc7dcSmrg    *compatible = calloc(count + 1, sizeof(char *));
3994bf6cc7dcSmrg    if (!*compatible)
39952ee35494Smrg        return -ENOMEM;
39962ee35494Smrg
39972ee35494Smrg    for (i = 0; i < count; i++) {
39982ee35494Smrg        value = sysfs_uevent_get(path, "OF_COMPATIBLE_%u", i);
3999bf6cc7dcSmrg        tmp_name = value;
40002ee35494Smrg        if (!value) {
4001bf6cc7dcSmrg            /* If the device lacks OF data, pick the MODALIAS info */
4002bf6cc7dcSmrg            value = sysfs_uevent_get(path, "MODALIAS");
4003bf6cc7dcSmrg            if (!value) {
4004bf6cc7dcSmrg                err = -ENOENT;
4005bf6cc7dcSmrg                goto free;
4006bf6cc7dcSmrg            }
4007bf6cc7dcSmrg
4008bf6cc7dcSmrg            /* .. and strip the MODALIAS=[platform,usb...]: part. */
4009bf6cc7dcSmrg            tmp_name = strrchr(value, ':');
4010bf6cc7dcSmrg            if (!tmp_name) {
4011bf6cc7dcSmrg                free(value);
4012bf6cc7dcSmrg                return -ENOENT;
4013bf6cc7dcSmrg            }
4014bf6cc7dcSmrg            tmp_name = strdup(tmp_name + 1);
4015bf6cc7dcSmrg            free(value);
40162ee35494Smrg        }
40172ee35494Smrg
4018bf6cc7dcSmrg        (*compatible)[i] = tmp_name;
40192ee35494Smrg    }
40202ee35494Smrg
40212ee35494Smrg    return 0;
40222ee35494Smrg
40232ee35494Smrgfree:
40242ee35494Smrg    while (i--)
4025bf6cc7dcSmrg        free((*compatible)[i]);
40262ee35494Smrg
4027bf6cc7dcSmrg    free(*compatible);
40282ee35494Smrg    return err;
40292ee35494Smrg#else
4030bf6cc7dcSmrg#warning "Missing implementation of drmParseOFDeviceInfo"
40312ee35494Smrg    return -EINVAL;
40322ee35494Smrg#endif
40332ee35494Smrg}
40342ee35494Smrg
40352ee35494Smrgstatic int drmProcessPlatformDevice(drmDevicePtr *device,
40362ee35494Smrg                                    const char *node, int node_type,
40372ee35494Smrg                                    int maj, int min, bool fetch_deviceinfo,
40382ee35494Smrg                                    uint32_t flags)
40392ee35494Smrg{
40402ee35494Smrg    drmDevicePtr dev;
40412ee35494Smrg    char *ptr;
40422ee35494Smrg    int ret;
40432ee35494Smrg
40442ee35494Smrg    dev = drmDeviceAlloc(node_type, node, sizeof(drmPlatformBusInfo),
40452ee35494Smrg                         sizeof(drmPlatformDeviceInfo), &ptr);
40462ee35494Smrg    if (!dev)
40472ee35494Smrg        return -ENOMEM;
40482ee35494Smrg
40492ee35494Smrg    dev->bustype = DRM_BUS_PLATFORM;
40502ee35494Smrg
40512ee35494Smrg    dev->businfo.platform = (drmPlatformBusInfoPtr)ptr;
40522ee35494Smrg
4053bf6cc7dcSmrg    ret = drmParseOFBusInfo(maj, min, dev->businfo.platform->fullname);
40542ee35494Smrg    if (ret < 0)
40552ee35494Smrg        goto free_device;
40562ee35494Smrg
40572ee35494Smrg    if (fetch_deviceinfo) {
40582ee35494Smrg        ptr += sizeof(drmPlatformBusInfo);
40592ee35494Smrg        dev->deviceinfo.platform = (drmPlatformDeviceInfoPtr)ptr;
40602ee35494Smrg
4061bf6cc7dcSmrg        ret = drmParseOFDeviceInfo(maj, min, &dev->deviceinfo.platform->compatible);
40622ee35494Smrg        if (ret < 0)
40632ee35494Smrg            goto free_device;
40642ee35494Smrg    }
40652ee35494Smrg
40662ee35494Smrg    *device = dev;
40672ee35494Smrg
40682ee35494Smrg    return 0;
40692ee35494Smrg
40702ee35494Smrgfree_device:
40712ee35494Smrg    free(dev);
40722ee35494Smrg    return ret;
40732ee35494Smrg}
40742ee35494Smrg
40752ee35494Smrgstatic int drmProcessHost1xDevice(drmDevicePtr *device,
40762ee35494Smrg                                  const char *node, int node_type,
40772ee35494Smrg                                  int maj, int min, bool fetch_deviceinfo,
40782ee35494Smrg                                  uint32_t flags)
40792ee35494Smrg{
40802ee35494Smrg    drmDevicePtr dev;
40812ee35494Smrg    char *ptr;
40822ee35494Smrg    int ret;
40832ee35494Smrg
40842ee35494Smrg    dev = drmDeviceAlloc(node_type, node, sizeof(drmHost1xBusInfo),
40852ee35494Smrg                         sizeof(drmHost1xDeviceInfo), &ptr);
40862ee35494Smrg    if (!dev)
40872ee35494Smrg        return -ENOMEM;
40882ee35494Smrg
40892ee35494Smrg    dev->bustype = DRM_BUS_HOST1X;
40902ee35494Smrg
40912ee35494Smrg    dev->businfo.host1x = (drmHost1xBusInfoPtr)ptr;
40922ee35494Smrg
4093bf6cc7dcSmrg    ret = drmParseOFBusInfo(maj, min, dev->businfo.host1x->fullname);
40942ee35494Smrg    if (ret < 0)
40952ee35494Smrg        goto free_device;
40962ee35494Smrg
40972ee35494Smrg    if (fetch_deviceinfo) {
40982ee35494Smrg        ptr += sizeof(drmHost1xBusInfo);
40992ee35494Smrg        dev->deviceinfo.host1x = (drmHost1xDeviceInfoPtr)ptr;
41002ee35494Smrg
4101bf6cc7dcSmrg        ret = drmParseOFDeviceInfo(maj, min, &dev->deviceinfo.host1x->compatible);
41022ee35494Smrg        if (ret < 0)
41032ee35494Smrg            goto free_device;
41042ee35494Smrg    }
41052ee35494Smrg
41062ee35494Smrg    *device = dev;
41072ee35494Smrg
41082ee35494Smrg    return 0;
41092ee35494Smrg
41102ee35494Smrgfree_device:
41112ee35494Smrg    free(dev);
4112fe517fc9Smrg    return ret;
4113fe517fc9Smrg}
4114fe517fc9Smrg
41156260e5d5Smrgstatic int
41166260e5d5Smrgprocess_device(drmDevicePtr *device, const char *d_name,
41176260e5d5Smrg               int req_subsystem_type,
41186260e5d5Smrg               bool fetch_deviceinfo, uint32_t flags)
41196260e5d5Smrg{
41206260e5d5Smrg    struct stat sbuf;
41216260e5d5Smrg    char node[PATH_MAX + 1];
41226260e5d5Smrg    int node_type, subsystem_type;
41236260e5d5Smrg    unsigned int maj, min;
41246260e5d5Smrg
41256260e5d5Smrg    node_type = drmGetNodeType(d_name);
41266260e5d5Smrg    if (node_type < 0)
41276260e5d5Smrg        return -1;
41286260e5d5Smrg
41296260e5d5Smrg    snprintf(node, PATH_MAX, "%s/%s", DRM_DIR_NAME, d_name);
41306260e5d5Smrg    if (stat(node, &sbuf))
41316260e5d5Smrg        return -1;
41326260e5d5Smrg
41336260e5d5Smrg    maj = major(sbuf.st_rdev);
41346260e5d5Smrg    min = minor(sbuf.st_rdev);
41356260e5d5Smrg
41366260e5d5Smrg    if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
41376260e5d5Smrg        return -1;
41386260e5d5Smrg
41396260e5d5Smrg    subsystem_type = drmParseSubsystemType(maj, min);
41406260e5d5Smrg    if (req_subsystem_type != -1 && req_subsystem_type != subsystem_type)
41416260e5d5Smrg        return -1;
41426260e5d5Smrg
41436260e5d5Smrg    switch (subsystem_type) {
41446260e5d5Smrg    case DRM_BUS_PCI:
41456260e5d5Smrg    case DRM_BUS_VIRTIO:
41466260e5d5Smrg        return drmProcessPciDevice(device, node, node_type, maj, min,
41476260e5d5Smrg                                   fetch_deviceinfo, flags);
41486260e5d5Smrg    case DRM_BUS_USB:
41496260e5d5Smrg        return drmProcessUsbDevice(device, node, node_type, maj, min,
41506260e5d5Smrg                                   fetch_deviceinfo, flags);
41516260e5d5Smrg    case DRM_BUS_PLATFORM:
41526260e5d5Smrg        return drmProcessPlatformDevice(device, node, node_type, maj, min,
41536260e5d5Smrg                                        fetch_deviceinfo, flags);
41546260e5d5Smrg    case DRM_BUS_HOST1X:
41556260e5d5Smrg        return drmProcessHost1xDevice(device, node, node_type, maj, min,
41566260e5d5Smrg                                      fetch_deviceinfo, flags);
41576260e5d5Smrg    default:
41586260e5d5Smrg        return -1;
41596260e5d5Smrg   }
41606260e5d5Smrg}
41616260e5d5Smrg
4162fe517fc9Smrg/* Consider devices located on the same bus as duplicate and fold the respective
4163fe517fc9Smrg * entries into a single one.
4164fe517fc9Smrg *
4165fe517fc9Smrg * Note: this leaves "gaps" in the array, while preserving the length.
4166fe517fc9Smrg */
4167fe517fc9Smrgstatic void drmFoldDuplicatedDevices(drmDevicePtr local_devices[], int count)
4168fe517fc9Smrg{
4169fe517fc9Smrg    int node_type, i, j;
4170fe517fc9Smrg
4171fe517fc9Smrg    for (i = 0; i < count; i++) {
4172fe517fc9Smrg        for (j = i + 1; j < count; j++) {
41730655efefSmrg            if (drmDevicesEqual(local_devices[i], local_devices[j])) {
4174fe517fc9Smrg                local_devices[i]->available_nodes |= local_devices[j]->available_nodes;
4175fe517fc9Smrg                node_type = log2(local_devices[j]->available_nodes);
4176fe517fc9Smrg                memcpy(local_devices[i]->nodes[node_type],
4177fe517fc9Smrg                       local_devices[j]->nodes[node_type], drmGetMaxNodeName());
4178fe517fc9Smrg                drmFreeDevice(&local_devices[j]);
4179fe517fc9Smrg            }
4180fe517fc9Smrg        }
4181fe517fc9Smrg    }
4182fe517fc9Smrg}
4183fe517fc9Smrg
41842ee35494Smrg/* Check that the given flags are valid returning 0 on success */
41852ee35494Smrgstatic int
41862ee35494Smrgdrm_device_validate_flags(uint32_t flags)
41872ee35494Smrg{
41882ee35494Smrg        return (flags & ~DRM_DEVICE_GET_PCI_REVISION);
41892ee35494Smrg}
41902ee35494Smrg
41916260e5d5Smrgstatic bool
41926260e5d5Smrgdrm_device_has_rdev(drmDevicePtr device, dev_t find_rdev)
41936260e5d5Smrg{
41946260e5d5Smrg    struct stat sbuf;
41956260e5d5Smrg
41966260e5d5Smrg    for (int i = 0; i < DRM_NODE_MAX; i++) {
41976260e5d5Smrg        if (device->available_nodes & 1 << i) {
41986260e5d5Smrg            if (stat(device->nodes[i], &sbuf) == 0 &&
41996260e5d5Smrg                sbuf.st_rdev == find_rdev)
42006260e5d5Smrg                return true;
42016260e5d5Smrg        }
42026260e5d5Smrg    }
42036260e5d5Smrg    return false;
42046260e5d5Smrg}
42056260e5d5Smrg
42066260e5d5Smrg/*
42076260e5d5Smrg * The kernel drm core has a number of places that assume maximum of
42086260e5d5Smrg * 3x64 devices nodes. That's 64 for each of primary, control and
42096260e5d5Smrg * render nodes. Rounded it up to 256 for simplicity.
42106260e5d5Smrg */
42116260e5d5Smrg#define MAX_DRM_NODES 256
42126260e5d5Smrg
4213fe517fc9Smrg/**
4214fe517fc9Smrg * Get information about the opened drm device
4215fe517fc9Smrg *
4216fe517fc9Smrg * \param fd file descriptor of the drm device
42172ee35494Smrg * \param flags feature/behaviour bitmask
4218fe517fc9Smrg * \param device the address of a drmDevicePtr where the information
4219fe517fc9Smrg *               will be allocated in stored
4220fe517fc9Smrg *
4221fe517fc9Smrg * \return zero on success, negative error code otherwise.
42222ee35494Smrg *
42232ee35494Smrg * \note Unlike drmGetDevice it does not retrieve the pci device revision field
42242ee35494Smrg * unless the DRM_DEVICE_GET_PCI_REVISION \p flag is set.
4225fe517fc9Smrg */
42266260e5d5Smrgdrm_public int drmGetDevice2(int fd, uint32_t flags, drmDevicePtr *device)
4227fe517fc9Smrg{
42282ee35494Smrg#ifdef __OpenBSD__
42292ee35494Smrg    /*
42302ee35494Smrg     * DRI device nodes on OpenBSD are not in their own directory, they reside
42312ee35494Smrg     * in /dev along with a large number of statically generated /dev nodes.
42322ee35494Smrg     * Avoid stat'ing all of /dev needlessly by implementing this custom path.
42332ee35494Smrg     */
42342ee35494Smrg    drmDevicePtr     d;
42352ee35494Smrg    struct stat      sbuf;
42362ee35494Smrg    char             node[PATH_MAX + 1];
42372ee35494Smrg    const char      *dev_name;
42382ee35494Smrg    int              node_type, subsystem_type;
423982025ec7Smrg    int              maj, min, n, ret;
42402ee35494Smrg
42412ee35494Smrg    if (fd == -1 || device == NULL)
42422ee35494Smrg        return -EINVAL;
42432ee35494Smrg
42442ee35494Smrg    if (fstat(fd, &sbuf))
42452ee35494Smrg        return -errno;
42462ee35494Smrg
42472ee35494Smrg    maj = major(sbuf.st_rdev);
42482ee35494Smrg    min = minor(sbuf.st_rdev);
42492ee35494Smrg
42506260e5d5Smrg    if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
42512ee35494Smrg        return -EINVAL;
42522ee35494Smrg
425387bf8e7cSmrg    node_type = drmGetMinorType(maj, min);
42542ee35494Smrg    if (node_type == -1)
42552ee35494Smrg        return -ENODEV;
42562ee35494Smrg
425782025ec7Smrg    dev_name = drmGetDeviceName(node_type);
425882025ec7Smrg    if (!dev_name)
42592ee35494Smrg        return -EINVAL;
42602ee35494Smrg
426182025ec7Smrg    n = snprintf(node, PATH_MAX, dev_name, DRM_DIR_NAME, min);
42622ee35494Smrg    if (n == -1 || n >= PATH_MAX)
42632ee35494Smrg      return -errno;
42642ee35494Smrg    if (stat(node, &sbuf))
42652ee35494Smrg        return -EINVAL;
42662ee35494Smrg
42672ee35494Smrg    subsystem_type = drmParseSubsystemType(maj, min);
42682ee35494Smrg    if (subsystem_type != DRM_BUS_PCI)
42692ee35494Smrg        return -ENODEV;
42702ee35494Smrg
42712ee35494Smrg    ret = drmProcessPciDevice(&d, node, node_type, maj, min, true, flags);
42722ee35494Smrg    if (ret)
42732ee35494Smrg        return ret;
42742ee35494Smrg
42752ee35494Smrg    *device = d;
42762ee35494Smrg
42772ee35494Smrg    return 0;
42782ee35494Smrg#else
42796260e5d5Smrg    drmDevicePtr local_devices[MAX_DRM_NODES];
4280fe517fc9Smrg    drmDevicePtr d;
4281fe517fc9Smrg    DIR *sysdir;
4282fe517fc9Smrg    struct dirent *dent;
4283fe517fc9Smrg    struct stat sbuf;
42846260e5d5Smrg    int subsystem_type;
4285fe517fc9Smrg    int maj, min;
4286fe517fc9Smrg    int ret, i, node_count;
4287fe517fc9Smrg    dev_t find_rdev;
4288fe517fc9Smrg
42892ee35494Smrg    if (drm_device_validate_flags(flags))
42902ee35494Smrg        return -EINVAL;
42912ee35494Smrg
4292fe517fc9Smrg    if (fd == -1 || device == NULL)
4293fe517fc9Smrg        return -EINVAL;
4294fe517fc9Smrg
4295fe517fc9Smrg    if (fstat(fd, &sbuf))
4296fe517fc9Smrg        return -errno;
4297fe517fc9Smrg
4298fe517fc9Smrg    find_rdev = sbuf.st_rdev;
4299fe517fc9Smrg    maj = major(sbuf.st_rdev);
4300fe517fc9Smrg    min = minor(sbuf.st_rdev);
4301fe517fc9Smrg
43026260e5d5Smrg    if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
4303fe517fc9Smrg        return -EINVAL;
4304fe517fc9Smrg
4305fe517fc9Smrg    subsystem_type = drmParseSubsystemType(maj, min);
43066260e5d5Smrg    if (subsystem_type < 0)
43076260e5d5Smrg        return subsystem_type;
4308fe517fc9Smrg
4309fe517fc9Smrg    sysdir = opendir(DRM_DIR_NAME);
43106260e5d5Smrg    if (!sysdir)
43116260e5d5Smrg        return -errno;
4312fe517fc9Smrg
4313fe517fc9Smrg    i = 0;
4314fe517fc9Smrg    while ((dent = readdir(sysdir))) {
43156260e5d5Smrg        ret = process_device(&d, dent->d_name, subsystem_type, true, flags);
43166260e5d5Smrg        if (ret)
4317fe517fc9Smrg            continue;
4318fe517fc9Smrg
43196260e5d5Smrg        if (i >= MAX_DRM_NODES) {
43206260e5d5Smrg            fprintf(stderr, "More than %d drm nodes detected. "
43216260e5d5Smrg                    "Please report a bug - that should not happen.\n"
43226260e5d5Smrg                    "Skipping extra nodes\n", MAX_DRM_NODES);
4323fe517fc9Smrg            break;
4324fe517fc9Smrg        }
43256260e5d5Smrg        local_devices[i] = d;
4326fe517fc9Smrg        i++;
4327fe517fc9Smrg    }
4328fe517fc9Smrg    node_count = i;
4329fe517fc9Smrg
4330fe517fc9Smrg    drmFoldDuplicatedDevices(local_devices, node_count);
4331fe517fc9Smrg
43326260e5d5Smrg    *device = NULL;
43336260e5d5Smrg
43346260e5d5Smrg    for (i = 0; i < node_count; i++) {
43356260e5d5Smrg        if (!local_devices[i])
43366260e5d5Smrg            continue;
43376260e5d5Smrg
43386260e5d5Smrg        if (drm_device_has_rdev(local_devices[i], find_rdev))
43396260e5d5Smrg            *device = local_devices[i];
43406260e5d5Smrg        else
43416260e5d5Smrg            drmFreeDevice(&local_devices[i]);
43426260e5d5Smrg    }
4343fe517fc9Smrg
4344fe517fc9Smrg    closedir(sysdir);
43452ee35494Smrg    if (*device == NULL)
43462ee35494Smrg        return -ENODEV;
4347fe517fc9Smrg    return 0;
43482ee35494Smrg#endif
43492ee35494Smrg}
43502ee35494Smrg
43512ee35494Smrg/**
43522ee35494Smrg * Get information about the opened drm device
43532ee35494Smrg *
43542ee35494Smrg * \param fd file descriptor of the drm device
43552ee35494Smrg * \param device the address of a drmDevicePtr where the information
43562ee35494Smrg *               will be allocated in stored
43572ee35494Smrg *
43582ee35494Smrg * \return zero on success, negative error code otherwise.
43592ee35494Smrg */
43606260e5d5Smrgdrm_public int drmGetDevice(int fd, drmDevicePtr *device)
43612ee35494Smrg{
43622ee35494Smrg    return drmGetDevice2(fd, DRM_DEVICE_GET_PCI_REVISION, device);
4363fe517fc9Smrg}
4364fe517fc9Smrg
4365fe517fc9Smrg/**
4366fe517fc9Smrg * Get drm devices on the system
4367fe517fc9Smrg *
43682ee35494Smrg * \param flags feature/behaviour bitmask
4369fe517fc9Smrg * \param devices the array of devices with drmDevicePtr elements
4370fe517fc9Smrg *                can be NULL to get the device number first
4371fe517fc9Smrg * \param max_devices the maximum number of devices for the array
4372fe517fc9Smrg *
4373fe517fc9Smrg * \return on error - negative error code,
4374fe517fc9Smrg *         if devices is NULL - total number of devices available on the system,
4375fe517fc9Smrg *         alternatively the number of devices stored in devices[], which is
4376fe517fc9Smrg *         capped by the max_devices.
43772ee35494Smrg *
43782ee35494Smrg * \note Unlike drmGetDevices it does not retrieve the pci device revision field
43792ee35494Smrg * unless the DRM_DEVICE_GET_PCI_REVISION \p flag is set.
4380fe517fc9Smrg */
43816260e5d5Smrgdrm_public int drmGetDevices2(uint32_t flags, drmDevicePtr devices[],
43826260e5d5Smrg                              int max_devices)
4383fe517fc9Smrg{
43846260e5d5Smrg    drmDevicePtr local_devices[MAX_DRM_NODES];
4385fe517fc9Smrg    drmDevicePtr device;
4386fe517fc9Smrg    DIR *sysdir;
4387fe517fc9Smrg    struct dirent *dent;
4388fe517fc9Smrg    int ret, i, node_count, device_count;
4389fe517fc9Smrg
43902ee35494Smrg    if (drm_device_validate_flags(flags))
43912ee35494Smrg        return -EINVAL;
43922ee35494Smrg
4393fe517fc9Smrg    sysdir = opendir(DRM_DIR_NAME);
43946260e5d5Smrg    if (!sysdir)
43956260e5d5Smrg        return -errno;
4396fe517fc9Smrg
4397fe517fc9Smrg    i = 0;
4398fe517fc9Smrg    while ((dent = readdir(sysdir))) {
43996260e5d5Smrg        ret = process_device(&device, dent->d_name, -1, devices != NULL, flags);
44006260e5d5Smrg        if (ret)
4401fe517fc9Smrg            continue;
4402fe517fc9Smrg
44036260e5d5Smrg        if (i >= MAX_DRM_NODES) {
44046260e5d5Smrg            fprintf(stderr, "More than %d drm nodes detected. "
44056260e5d5Smrg                    "Please report a bug - that should not happen.\n"
44066260e5d5Smrg                    "Skipping extra nodes\n", MAX_DRM_NODES);
44072ee35494Smrg            break;
4408fe517fc9Smrg        }
4409fe517fc9Smrg        local_devices[i] = device;
4410fe517fc9Smrg        i++;
4411fe517fc9Smrg    }
4412fe517fc9Smrg    node_count = i;
4413fe517fc9Smrg
4414fe517fc9Smrg    drmFoldDuplicatedDevices(local_devices, node_count);
4415fe517fc9Smrg
4416fe517fc9Smrg    device_count = 0;
4417fe517fc9Smrg    for (i = 0; i < node_count; i++) {
4418fe517fc9Smrg        if (!local_devices[i])
4419fe517fc9Smrg            continue;
4420fe517fc9Smrg
4421fe517fc9Smrg        if ((devices != NULL) && (device_count < max_devices))
4422fe517fc9Smrg            devices[device_count] = local_devices[i];
4423fe517fc9Smrg        else
4424fe517fc9Smrg            drmFreeDevice(&local_devices[i]);
4425fe517fc9Smrg
4426fe517fc9Smrg        device_count++;
4427fe517fc9Smrg    }
4428fe517fc9Smrg
4429fe517fc9Smrg    closedir(sysdir);
4430fe517fc9Smrg    return device_count;
4431424e9256Smrg}
44322ee35494Smrg
44332ee35494Smrg/**
44342ee35494Smrg * Get drm devices on the system
44352ee35494Smrg *
44362ee35494Smrg * \param devices the array of devices with drmDevicePtr elements
44372ee35494Smrg *                can be NULL to get the device number first
44382ee35494Smrg * \param max_devices the maximum number of devices for the array
44392ee35494Smrg *
44402ee35494Smrg * \return on error - negative error code,
44412ee35494Smrg *         if devices is NULL - total number of devices available on the system,
44422ee35494Smrg *         alternatively the number of devices stored in devices[], which is
44432ee35494Smrg *         capped by the max_devices.
44442ee35494Smrg */
44456260e5d5Smrgdrm_public int drmGetDevices(drmDevicePtr devices[], int max_devices)
44462ee35494Smrg{
44472ee35494Smrg    return drmGetDevices2(DRM_DEVICE_GET_PCI_REVISION, devices, max_devices);
44482ee35494Smrg}
44492ee35494Smrg
44506260e5d5Smrgdrm_public char *drmGetDeviceNameFromFd2(int fd)
44512ee35494Smrg{
44522ee35494Smrg#ifdef __linux__
44532ee35494Smrg    struct stat sbuf;
44542ee35494Smrg    char path[PATH_MAX + 1], *value;
44552ee35494Smrg    unsigned int maj, min;
44562ee35494Smrg
44572ee35494Smrg    if (fstat(fd, &sbuf))
44582ee35494Smrg        return NULL;
44592ee35494Smrg
44602ee35494Smrg    maj = major(sbuf.st_rdev);
44612ee35494Smrg    min = minor(sbuf.st_rdev);
44622ee35494Smrg
44636260e5d5Smrg    if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
44642ee35494Smrg        return NULL;
44652ee35494Smrg
44662ee35494Smrg    snprintf(path, sizeof(path), "/sys/dev/char/%d:%d", maj, min);
44672ee35494Smrg
44682ee35494Smrg    value = sysfs_uevent_get(path, "DEVNAME");
44692ee35494Smrg    if (!value)
44702ee35494Smrg        return NULL;
44712ee35494Smrg
44722ee35494Smrg    snprintf(path, sizeof(path), "/dev/%s", value);
44732ee35494Smrg    free(value);
44742ee35494Smrg
44752ee35494Smrg    return strdup(path);
447687bf8e7cSmrg#elif __FreeBSD__
447787bf8e7cSmrg    return drmGetDeviceNameFromFd(fd);
44782ee35494Smrg#else
44792ee35494Smrg    struct stat      sbuf;
44802ee35494Smrg    char             node[PATH_MAX + 1];
44812ee35494Smrg    const char      *dev_name;
44822ee35494Smrg    int              node_type;
448382025ec7Smrg    int              maj, min, n;
44842ee35494Smrg
44852ee35494Smrg    if (fstat(fd, &sbuf))
44862ee35494Smrg        return NULL;
44872ee35494Smrg
44882ee35494Smrg    maj = major(sbuf.st_rdev);
44892ee35494Smrg    min = minor(sbuf.st_rdev);
44902ee35494Smrg
44916260e5d5Smrg    if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
44922ee35494Smrg        return NULL;
44932ee35494Smrg
449487bf8e7cSmrg    node_type = drmGetMinorType(maj, min);
44952ee35494Smrg    if (node_type == -1)
44962ee35494Smrg        return NULL;
44972ee35494Smrg
449882025ec7Smrg    dev_name = drmGetDeviceName(node_type);
449982025ec7Smrg    if (!dev_name)
45002ee35494Smrg        return NULL;
45012ee35494Smrg
450282025ec7Smrg    n = snprintf(node, PATH_MAX, dev_name, DRM_DIR_NAME, min);
45032ee35494Smrg    if (n == -1 || n >= PATH_MAX)
45042ee35494Smrg      return NULL;
45052ee35494Smrg
45062ee35494Smrg    return strdup(node);
45072ee35494Smrg#endif
45082ee35494Smrg}
45090655efefSmrg
45106260e5d5Smrgdrm_public int drmSyncobjCreate(int fd, uint32_t flags, uint32_t *handle)
45110655efefSmrg{
45120655efefSmrg    struct drm_syncobj_create args;
45130655efefSmrg    int ret;
45140655efefSmrg
45150655efefSmrg    memclear(args);
45160655efefSmrg    args.flags = flags;
45170655efefSmrg    args.handle = 0;
45180655efefSmrg    ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_CREATE, &args);
45190655efefSmrg    if (ret)
45202b90624aSmrg        return ret;
45210655efefSmrg    *handle = args.handle;
45220655efefSmrg    return 0;
45230655efefSmrg}
45240655efefSmrg
45256260e5d5Smrgdrm_public int drmSyncobjDestroy(int fd, uint32_t handle)
45260655efefSmrg{
45270655efefSmrg    struct drm_syncobj_destroy args;
45280655efefSmrg
45290655efefSmrg    memclear(args);
45300655efefSmrg    args.handle = handle;
45310655efefSmrg    return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_DESTROY, &args);
45320655efefSmrg}
45330655efefSmrg
45346260e5d5Smrgdrm_public int drmSyncobjHandleToFD(int fd, uint32_t handle, int *obj_fd)
45350655efefSmrg{
45360655efefSmrg    struct drm_syncobj_handle args;
45370655efefSmrg    int ret;
45380655efefSmrg
45390655efefSmrg    memclear(args);
45400655efefSmrg    args.fd = -1;
45410655efefSmrg    args.handle = handle;
45420655efefSmrg    ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &args);
45430655efefSmrg    if (ret)
45442b90624aSmrg        return ret;
45450655efefSmrg    *obj_fd = args.fd;
45460655efefSmrg    return 0;
45470655efefSmrg}
45480655efefSmrg
45496260e5d5Smrgdrm_public int drmSyncobjFDToHandle(int fd, int obj_fd, uint32_t *handle)
45500655efefSmrg{
45510655efefSmrg    struct drm_syncobj_handle args;
45520655efefSmrg    int ret;
45530655efefSmrg
45540655efefSmrg    memclear(args);
45550655efefSmrg    args.fd = obj_fd;
45560655efefSmrg    args.handle = 0;
45570655efefSmrg    ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &args);
45580655efefSmrg    if (ret)
45592b90624aSmrg        return ret;
45600655efefSmrg    *handle = args.handle;
45610655efefSmrg    return 0;
45620655efefSmrg}
45630655efefSmrg
45646260e5d5Smrgdrm_public int drmSyncobjImportSyncFile(int fd, uint32_t handle,
45656260e5d5Smrg                                        int sync_file_fd)
45660655efefSmrg{
45670655efefSmrg    struct drm_syncobj_handle args;
45680655efefSmrg
45690655efefSmrg    memclear(args);
45700655efefSmrg    args.fd = sync_file_fd;
45710655efefSmrg    args.handle = handle;
45720655efefSmrg    args.flags = DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE;
45730655efefSmrg    return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &args);
45740655efefSmrg}
45750655efefSmrg
45766260e5d5Smrgdrm_public int drmSyncobjExportSyncFile(int fd, uint32_t handle,
45776260e5d5Smrg                                        int *sync_file_fd)
45780655efefSmrg{
45790655efefSmrg    struct drm_syncobj_handle args;
45800655efefSmrg    int ret;
45810655efefSmrg
45820655efefSmrg    memclear(args);
45830655efefSmrg    args.fd = -1;
45840655efefSmrg    args.handle = handle;
45850655efefSmrg    args.flags = DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE;
45860655efefSmrg    ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &args);
45870655efefSmrg    if (ret)
45882b90624aSmrg        return ret;
45890655efefSmrg    *sync_file_fd = args.fd;
45900655efefSmrg    return 0;
45910655efefSmrg}
45922b90624aSmrg
45936260e5d5Smrgdrm_public int drmSyncobjWait(int fd, uint32_t *handles, unsigned num_handles,
45946260e5d5Smrg                              int64_t timeout_nsec, unsigned flags,
45956260e5d5Smrg                              uint32_t *first_signaled)
45962b90624aSmrg{
45972b90624aSmrg    struct drm_syncobj_wait args;
45982b90624aSmrg    int ret;
45992b90624aSmrg
46002b90624aSmrg    memclear(args);
46012b90624aSmrg    args.handles = (uintptr_t)handles;
46022b90624aSmrg    args.timeout_nsec = timeout_nsec;
46032b90624aSmrg    args.count_handles = num_handles;
46042b90624aSmrg    args.flags = flags;
46052b90624aSmrg
46062b90624aSmrg    ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &args);
46072b90624aSmrg    if (ret < 0)
46082b90624aSmrg        return -errno;
46092b90624aSmrg
46102b90624aSmrg    if (first_signaled)
46112b90624aSmrg        *first_signaled = args.first_signaled;
46122b90624aSmrg    return ret;
46132b90624aSmrg}
46142b90624aSmrg
46156260e5d5Smrgdrm_public int drmSyncobjReset(int fd, const uint32_t *handles,
46166260e5d5Smrg                               uint32_t handle_count)
46172b90624aSmrg{
46182b90624aSmrg    struct drm_syncobj_array args;
46192b90624aSmrg    int ret;
46202b90624aSmrg
46212b90624aSmrg    memclear(args);
46222b90624aSmrg    args.handles = (uintptr_t)handles;
46232b90624aSmrg    args.count_handles = handle_count;
46242b90624aSmrg
46252b90624aSmrg    ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_RESET, &args);
46262b90624aSmrg    return ret;
46272b90624aSmrg}
46282b90624aSmrg
46296260e5d5Smrgdrm_public int drmSyncobjSignal(int fd, const uint32_t *handles,
46306260e5d5Smrg                                uint32_t handle_count)
46312b90624aSmrg{
46322b90624aSmrg    struct drm_syncobj_array args;
46332b90624aSmrg    int ret;
46342b90624aSmrg
46352b90624aSmrg    memclear(args);
46362b90624aSmrg    args.handles = (uintptr_t)handles;
46372b90624aSmrg    args.count_handles = handle_count;
46382b90624aSmrg
46392b90624aSmrg    ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_SIGNAL, &args);
46402b90624aSmrg    return ret;
46412b90624aSmrg}
4642bf6cc7dcSmrg
4643bf6cc7dcSmrgdrm_public int drmSyncobjTimelineSignal(int fd, const uint32_t *handles,
4644bf6cc7dcSmrg					uint64_t *points, uint32_t handle_count)
4645bf6cc7dcSmrg{
4646bf6cc7dcSmrg    struct drm_syncobj_timeline_array args;
4647bf6cc7dcSmrg    int ret;
4648bf6cc7dcSmrg
4649bf6cc7dcSmrg    memclear(args);
4650bf6cc7dcSmrg    args.handles = (uintptr_t)handles;
4651bf6cc7dcSmrg    args.points = (uintptr_t)points;
4652bf6cc7dcSmrg    args.count_handles = handle_count;
4653bf6cc7dcSmrg
4654bf6cc7dcSmrg    ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
4655bf6cc7dcSmrg    return ret;
4656bf6cc7dcSmrg}
4657bf6cc7dcSmrg
4658bf6cc7dcSmrgdrm_public int drmSyncobjTimelineWait(int fd, uint32_t *handles, uint64_t *points,
4659bf6cc7dcSmrg				      unsigned num_handles,
4660bf6cc7dcSmrg				      int64_t timeout_nsec, unsigned flags,
4661bf6cc7dcSmrg				      uint32_t *first_signaled)
4662bf6cc7dcSmrg{
4663bf6cc7dcSmrg    struct drm_syncobj_timeline_wait args;
4664bf6cc7dcSmrg    int ret;
4665bf6cc7dcSmrg
4666bf6cc7dcSmrg    memclear(args);
4667bf6cc7dcSmrg    args.handles = (uintptr_t)handles;
4668bf6cc7dcSmrg    args.points = (uintptr_t)points;
4669bf6cc7dcSmrg    args.timeout_nsec = timeout_nsec;
4670bf6cc7dcSmrg    args.count_handles = num_handles;
4671bf6cc7dcSmrg    args.flags = flags;
4672bf6cc7dcSmrg
4673bf6cc7dcSmrg    ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &args);
4674bf6cc7dcSmrg    if (ret < 0)
4675bf6cc7dcSmrg        return -errno;
4676bf6cc7dcSmrg
4677bf6cc7dcSmrg    if (first_signaled)
4678bf6cc7dcSmrg        *first_signaled = args.first_signaled;
4679bf6cc7dcSmrg    return ret;
4680bf6cc7dcSmrg}
4681bf6cc7dcSmrg
4682bf6cc7dcSmrg
4683bf6cc7dcSmrgdrm_public int drmSyncobjQuery(int fd, uint32_t *handles, uint64_t *points,
4684bf6cc7dcSmrg			       uint32_t handle_count)
4685bf6cc7dcSmrg{
4686bf6cc7dcSmrg    struct drm_syncobj_timeline_array args;
4687bf6cc7dcSmrg    int ret;
4688bf6cc7dcSmrg
4689bf6cc7dcSmrg    memclear(args);
4690bf6cc7dcSmrg    args.handles = (uintptr_t)handles;
4691bf6cc7dcSmrg    args.points = (uintptr_t)points;
4692bf6cc7dcSmrg    args.count_handles = handle_count;
4693bf6cc7dcSmrg
4694bf6cc7dcSmrg    ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
4695bf6cc7dcSmrg    if (ret)
4696bf6cc7dcSmrg        return ret;
4697bf6cc7dcSmrg    return 0;
4698bf6cc7dcSmrg}
4699bf6cc7dcSmrg
470087bf8e7cSmrgdrm_public int drmSyncobjQuery2(int fd, uint32_t *handles, uint64_t *points,
470187bf8e7cSmrg				uint32_t handle_count, uint32_t flags)
470287bf8e7cSmrg{
470387bf8e7cSmrg    struct drm_syncobj_timeline_array args;
470487bf8e7cSmrg
470587bf8e7cSmrg    memclear(args);
470687bf8e7cSmrg    args.handles = (uintptr_t)handles;
470787bf8e7cSmrg    args.points = (uintptr_t)points;
470887bf8e7cSmrg    args.count_handles = handle_count;
470987bf8e7cSmrg    args.flags = flags;
471087bf8e7cSmrg
471187bf8e7cSmrg    return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
471287bf8e7cSmrg}
471387bf8e7cSmrg
471487bf8e7cSmrg
4715bf6cc7dcSmrgdrm_public int drmSyncobjTransfer(int fd,
4716bf6cc7dcSmrg				  uint32_t dst_handle, uint64_t dst_point,
4717bf6cc7dcSmrg				  uint32_t src_handle, uint64_t src_point,
4718bf6cc7dcSmrg				  uint32_t flags)
4719bf6cc7dcSmrg{
4720bf6cc7dcSmrg    struct drm_syncobj_transfer args;
4721bf6cc7dcSmrg    int ret;
4722bf6cc7dcSmrg
4723bf6cc7dcSmrg    memclear(args);
4724bf6cc7dcSmrg    args.src_handle = src_handle;
4725bf6cc7dcSmrg    args.dst_handle = dst_handle;
4726bf6cc7dcSmrg    args.src_point = src_point;
4727bf6cc7dcSmrg    args.dst_point = dst_point;
4728bf6cc7dcSmrg    args.flags = flags;
4729bf6cc7dcSmrg
4730bf6cc7dcSmrg    ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
4731bf6cc7dcSmrg
4732bf6cc7dcSmrg    return ret;
4733bf6cc7dcSmrg}
4734