#include <string.h>
#include <strings.h>
#include <ctype.h>
+#include <dirent.h>
+#include <stddef.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <stdarg.h>
+#ifdef HAVE_SYS_MKDEV_H
+# include <sys/mkdev.h> /* defines major(), minor(), and makedev() on Solaris */
+#endif
+#include <math.h>
/* Not all systems have MAP_FAILED defined */
#ifndef MAP_FAILED
#endif
#include "xf86drm.h"
-#include "libdrm.h"
+#include "libdrm_macros.h"
+
+#ifdef __OpenBSD__
+#define DRM_PRIMARY_MINOR_NAME "drm"
+#define DRM_CONTROL_MINOR_NAME "drmC"
+#define DRM_RENDER_MINOR_NAME "drmR"
+#else
+#define DRM_PRIMARY_MINOR_NAME "card"
+#define DRM_CONTROL_MINOR_NAME "controlD"
+#define DRM_RENDER_MINOR_NAME "renderD"
+#endif
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
#define DRM_MAJOR 145
#define DRM_MAJOR 34
#endif
-# ifdef __OpenBSD__
-# define DRM_MAJOR 81
-# endif
+#ifdef __OpenBSD__
+#ifdef __i386__
+#define DRM_MAJOR 88
+#else
+#define DRM_MAJOR 87
+#endif
+#endif /* __OpenBSD__ */
#ifndef DRM_MAJOR
#define DRM_MAJOR 226 /* Linux */
#define DRM_MSG_VERBOSITY 3
-#define DRM_NODE_CONTROL 0
-#define DRM_NODE_PRIMARY 1
-#define DRM_NODE_RENDER 2
+#define memclear(s) memset(&s, 0, sizeof(s))
static drmServerInfoPtr drm_server_info;
return vfprintf(stderr, format, ap);
}
-typedef int DRM_PRINTFLIKE(1, 0) (*debug_msg_func_t)(const char *format,
- va_list ap);
-
-static debug_msg_func_t drm_debug_print = drmDebugPrint;
-
void
drmMsg(const char *format, ...)
{
if (drm_server_info) {
drm_server_info->debug_print(format,ap);
} else {
- drm_debug_print(format, ap);
+ drmDebugPrint(format, ap);
}
va_end(ap);
}
}
-void
-drmSetDebugMsgFunction(debug_msg_func_t debug_msg_ptr)
-{
- drm_debug_print = debug_msg_ptr;
-}
-
static void *drmHashTable = NULL; /* Context switch callbacks */
void *drmGetHashTable(void)
void *drmMalloc(int size)
{
- void *pt;
- if ((pt = malloc(size)))
- memset(pt, 0, size);
- return pt;
+ return calloc(1, size);
}
void drmFree(void *pt)
{
- if (pt)
- free(pt);
+ free(pt);
}
/**
* If any other failure happened then it will output error mesage using
* drmMsg() call.
*/
+#if !defined(UDEV)
static int chown_check_return(const char *path, uid_t owner, gid_t group)
{
int rv;
path, errno, strerror(errno));
return -1;
}
+#endif
/**
* Open the DRM device, creating it if necessary.
* special file node with the major and minor numbers specified by \p dev and
* parent directory if necessary and was called by root.
*/
-static int drmOpenDevice(long dev, int minor, int type)
+static int drmOpenDevice(dev_t dev, int minor, int type)
{
stat_t st;
const char *dev_name;
char buf[64];
int fd;
mode_t devmode = DRM_DEV_MODE, serv_mode;
+ gid_t serv_group;
+#if !defined(UDEV)
int isroot = !geteuid();
uid_t user = DRM_DEV_UID;
- gid_t group = DRM_DEV_GID, serv_group;
-
+ gid_t group = DRM_DEV_GID;
+#endif
+
switch (type) {
case DRM_NODE_PRIMARY:
dev_name = DRM_DEV_NAME;
drm_server_info->get_perms(&serv_group, &serv_mode);
devmode = serv_mode ? serv_mode : DRM_DEV_MODE;
devmode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
- group = (serv_group >= 0) ? serv_group : DRM_DEV_GID;
}
#if !defined(UDEV)
}
if (drm_server_info) {
+ group = ((int)serv_group >= 0) ? serv_group : DRM_DEV_GID;
chown_check_return(buf, user, group);
chmod(buf, devmode);
}
return retval;
}
+static int drmGetMinorBase(int type)
+{
+ switch (type) {
+ case DRM_NODE_PRIMARY:
+ return 0;
+ case DRM_NODE_CONTROL:
+ return 64;
+ case DRM_NODE_RENDER:
+ return 128;
+ default:
+ return -1;
+ };
+}
+
+static int drmGetMinorType(int minor)
+{
+ int type = minor >> 6;
+
+ if (minor < 0)
+ return -1;
+
+ switch (type) {
+ case DRM_NODE_PRIMARY:
+ case DRM_NODE_CONTROL:
+ case DRM_NODE_RENDER:
+ return type;
+ default:
+ return -1;
+ }
+}
+
+static const char *drmGetMinorName(int type)
+{
+ switch (type) {
+ case DRM_NODE_PRIMARY:
+ return DRM_PRIMARY_MINOR_NAME;
+ case DRM_NODE_CONTROL:
+ return DRM_CONTROL_MINOR_NAME;
+ case DRM_NODE_RENDER:
+ return DRM_RENDER_MINOR_NAME;
+ default:
+ return NULL;
+ }
+}
/**
* Open the device by bus ID.
*
* \param busid bus ID.
+ * \param type device node type.
*
* \return a file descriptor on success, or a negative value on error.
*
*
* \sa drmOpenMinor() and drmGetBusid().
*/
-static int drmOpenByBusid(const char *busid)
+static int drmOpenByBusid(const char *busid, int type)
{
int i, pci_domain_ok = 1;
int fd;
const char *buf;
drmSetVersion sv;
+ int base = drmGetMinorBase(type);
+
+ if (base < 0)
+ return -1;
drmMsg("drmOpenByBusid: Searching for BusID %s\n", busid);
- for (i = 0; i < DRM_MAX_MINOR; i++) {
- fd = drmOpenMinor(i, 1, DRM_NODE_PRIMARY);
+ for (i = base; i < base + DRM_MAX_MINOR; i++) {
+ fd = drmOpenMinor(i, 1, type);
drmMsg("drmOpenByBusid: drmOpenMinor returns %d\n", fd);
if (fd >= 0) {
/* We need to try for 1.4 first for proper PCI domain support
* Open the device by name.
*
* \param name driver name.
+ * \param type the device node type.
*
* \return a file descriptor on success, or a negative value on error.
*
*
* \sa drmOpenMinor(), drmGetVersion() and drmGetBusid().
*/
-static int drmOpenByName(const char *name)
+static int drmOpenByName(const char *name, int type)
{
int i;
int fd;
drmVersionPtr version;
char * id;
+ int base = drmGetMinorBase(type);
+
+ if (base < 0)
+ return -1;
/*
* Open the first minor number that matches the driver name and isn't
* already in use. If it's in use it will have a busid assigned already.
*/
- for (i = 0; i < DRM_MAX_MINOR; i++) {
- if ((fd = drmOpenMinor(i, 1, DRM_NODE_PRIMARY)) >= 0) {
+ for (i = base; i < base + DRM_MAX_MINOR; i++) {
+ if ((fd = drmOpenMinor(i, 1, type)) >= 0) {
if ((version = drmGetVersion(fd))) {
if (!strcmp(version->name, name)) {
drmFreeVersion(version);
for (devstring = ++pt; *pt && *pt != ' '; ++pt)
;
if (*pt) { /* Found busid */
- return drmOpenByBusid(++pt);
+ return drmOpenByBusid(++pt, type);
} else { /* No busid */
- return drmOpenDevice(strtol(devstring, NULL, 0),i, DRM_NODE_PRIMARY);
+ return drmOpenDevice(strtol(devstring, NULL, 0),i, type);
}
}
}
*/
int drmOpen(const char *name, const char *busid)
{
+ return drmOpenWithType(name, busid, DRM_NODE_PRIMARY);
+}
+
+/**
+ * Open the DRM device with specified type.
+ *
+ * Looks up the specified name and bus ID, and opens the device found. The
+ * entry in /dev/dri is created if necessary and if called by root.
+ *
+ * \param name driver name. Not referenced if bus ID is supplied.
+ * \param busid bus ID. Zero if not known.
+ * \param type the device node type to open, PRIMARY, CONTROL or RENDER
+ *
+ * \return a file descriptor on success, or a negative value on error.
+ *
+ * \internal
+ * It calls drmOpenByBusid() if \p busid is specified or drmOpenByName()
+ * otherwise.
+ */
+int drmOpenWithType(const char *name, const char *busid, int type)
+{
if (!drmAvailable() && name != NULL && drm_server_info) {
- /* try to load the kernel */
+ /* try to load the kernel module */
if (!drm_server_info->load_module(name)) {
drmMsg("[drm] failed to load kernel module \"%s\"\n", name);
return -1;
}
if (busid) {
- int fd = drmOpenByBusid(busid);
+ int fd = drmOpenByBusid(busid, type);
if (fd >= 0)
return fd;
}
if (name)
- return drmOpenByName(name);
+ return drmOpenByName(name, type);
return -1;
}
drmVersionPtr retval;
drm_version_t *version = drmMalloc(sizeof(*version));
- version->name_len = 0;
- version->name = NULL;
- version->date_len = 0;
- version->date = NULL;
- version->desc_len = 0;
- version->desc = NULL;
+ memclear(*version);
if (drmIoctl(fd, DRM_IOCTL_VERSION, version)) {
drmFreeKernelVersion(version);
int drmGetCap(int fd, uint64_t capability, uint64_t *value)
{
- struct drm_get_cap cap = { capability, 0 };
+ struct drm_get_cap cap;
int ret;
+ memclear(cap);
+ cap.capability = capability;
+
ret = drmIoctl(fd, DRM_IOCTL_GET_CAP, &cap);
if (ret)
return ret;
int drmSetClientCap(int fd, uint64_t capability, uint64_t value)
{
- struct drm_set_client_cap cap = { capability, value };
+ struct drm_set_client_cap cap;
+
+ memclear(cap);
+ cap.capability = capability;
+ cap.value = value;
return drmIoctl(fd, DRM_IOCTL_SET_CLIENT_CAP, &cap);
}
{
drm_unique_t u;
- u.unique_len = 0;
- u.unique = NULL;
+ memclear(u);
if (drmIoctl(fd, DRM_IOCTL_GET_UNIQUE, &u))
return NULL;
{
drm_unique_t u;
+ memclear(u);
u.unique = (char *)busid;
u.unique_len = strlen(busid);
{
drm_auth_t auth;
+ memclear(auth);
+
*magic = 0;
if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth))
return -errno;
{
drm_auth_t auth;
+ memclear(auth);
auth.magic = magic;
if (drmIoctl(fd, DRM_IOCTL_AUTH_MAGIC, &auth))
return -errno;
{
drm_map_t map;
+ memclear(map);
map.offset = offset;
map.size = size;
- map.handle = 0;
map.type = type;
map.flags = flags;
if (drmIoctl(fd, DRM_IOCTL_ADD_MAP, &map))
{
drm_map_t map;
+ memclear(map);
map.handle = (void *)(uintptr_t)handle;
if(drmIoctl(fd, DRM_IOCTL_RM_MAP, &map))
{
drm_buf_desc_t request;
+ memclear(request);
request.count = count;
request.size = size;
- request.low_mark = 0;
- request.high_mark = 0;
request.flags = flags;
request.agp_start = agp_offset;
drm_buf_info_t info;
int i;
- info.count = 0;
- info.list = NULL;
+ memclear(info);
if (drmIoctl(fd, DRM_IOCTL_INFO_BUFS, &info))
return -EINVAL;
{
drm_buf_free_t request;
+ memclear(request);
request.count = count;
request.list = list;
if (drmIoctl(fd, DRM_IOCTL_FREE_BUFS, &request))
drmBufInfoPtr retval;
int i;
- info.count = 0;
- info.list = NULL;
+ memclear(info);
if (drmIoctl(fd, DRM_IOCTL_INFO_BUFS, &info))
return NULL;
drmBufMapPtr retval;
int i;
- bufs.count = 0;
- bufs.list = NULL;
- bufs.virtual = NULL;
+ memclear(bufs);
if (drmIoctl(fd, DRM_IOCTL_MAP_BUFS, &bufs))
return NULL;
{
drm_lock_t lock;
+ memclear(lock);
lock.context = context;
lock.flags = 0;
if (flags & DRM_LOCK_READY) lock.flags |= _DRM_LOCK_READY;
{
drm_lock_t lock;
+ memclear(lock);
lock.context = context;
- lock.flags = 0;
return drmIoctl(fd, DRM_IOCTL_UNLOCK, &lock);
}
drm_context_t * retval;
int i;
- res.count = 0;
- res.contexts = NULL;
+ memclear(res);
if (drmIoctl(fd, DRM_IOCTL_RES_CTX, &res))
return NULL;
{
drm_ctx_t ctx;
- ctx.flags = 0; /* Modified with functions below */
+ memclear(ctx);
if (drmIoctl(fd, DRM_IOCTL_ADD_CTX, &ctx))
return -errno;
*handle = ctx.handle;
{
drm_ctx_t ctx;
+ memclear(ctx);
ctx.handle = context;
if (drmIoctl(fd, DRM_IOCTL_SWITCH_CTX, &ctx))
return -errno;
* X server (which promises to maintain hardware context), or in the
* client-side library when buffers are swapped on behalf of two threads.
*/
+ memclear(ctx);
ctx.handle = context;
- ctx.flags = 0;
if (flags & DRM_CONTEXT_PRESERVED)
ctx.flags |= _DRM_CONTEXT_PRESERVED;
if (flags & DRM_CONTEXT_2DONLY)
{
drm_ctx_t ctx;
+ memclear(ctx);
ctx.handle = context;
if (drmIoctl(fd, DRM_IOCTL_GET_CTX, &ctx))
return -errno;
int drmDestroyContext(int fd, drm_context_t handle)
{
drm_ctx_t ctx;
+
+ memclear(ctx);
ctx.handle = handle;
if (drmIoctl(fd, DRM_IOCTL_RM_CTX, &ctx))
return -errno;
int drmCreateDrawable(int fd, drm_drawable_t *handle)
{
drm_draw_t draw;
+
+ memclear(draw);
if (drmIoctl(fd, DRM_IOCTL_ADD_DRAW, &draw))
return -errno;
*handle = draw.handle;
int drmDestroyDrawable(int fd, drm_drawable_t handle)
{
drm_draw_t draw;
+
+ memclear(draw);
draw.handle = handle;
if (drmIoctl(fd, DRM_IOCTL_RM_DRAW, &draw))
return -errno;
{
drm_update_draw_t update;
+ memclear(update);
update.handle = handle;
update.type = type;
update.num = num;
{
drm_agp_mode_t m;
+ memclear(m);
m.mode = mode;
if (drmIoctl(fd, DRM_IOCTL_AGP_ENABLE, &m))
return -errno;
{
drm_agp_buffer_t b;
+ memclear(b);
*handle = DRM_AGP_NO_HANDLE;
b.size = size;
- b.handle = 0;
b.type = type;
if (drmIoctl(fd, DRM_IOCTL_AGP_ALLOC, &b))
return -errno;
{
drm_agp_buffer_t b;
- b.size = 0;
+ memclear(b);
b.handle = handle;
if (drmIoctl(fd, DRM_IOCTL_AGP_FREE, &b))
return -errno;
{
drm_agp_binding_t b;
+ memclear(b);
b.handle = handle;
b.offset = offset;
if (drmIoctl(fd, DRM_IOCTL_AGP_BIND, &b))
{
drm_agp_binding_t b;
+ memclear(b);
b.handle = handle;
- b.offset = 0;
if (drmIoctl(fd, DRM_IOCTL_AGP_UNBIND, &b))
return -errno;
return 0;
{
drm_agp_info_t i;
+ memclear(i);
+
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return -errno;
return i.agp_version_major;
{
drm_agp_info_t i;
+ memclear(i);
+
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return -errno;
return i.agp_version_minor;
{
drm_agp_info_t i;
+ memclear(i);
+
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.mode;
{
drm_agp_info_t i;
+ memclear(i);
+
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.aperture_base;
{
drm_agp_info_t i;
+ memclear(i);
+
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.aperture_size;
{
drm_agp_info_t i;
+ memclear(i);
+
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.memory_used;
{
drm_agp_info_t i;
+ memclear(i);
+
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.memory_allowed;
{
drm_agp_info_t i;
+ memclear(i);
+
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.id_vendor;
{
drm_agp_info_t i;
+ memclear(i);
+
if (drmIoctl(fd, DRM_IOCTL_AGP_INFO, &i))
return 0;
return i.id_device;
{
drm_scatter_gather_t sg;
+ memclear(sg);
+
*handle = 0;
sg.size = size;
- sg.handle = 0;
if (drmIoctl(fd, DRM_IOCTL_SG_ALLOC, &sg))
return -errno;
*handle = sg.handle;
{
drm_scatter_gather_t sg;
- sg.size = 0;
+ memclear(sg);
sg.handle = handle;
if (drmIoctl(fd, DRM_IOCTL_SG_FREE, &sg))
return -errno;
{
drm_control_t ctl;
+ memclear(ctl);
ctl.func = DRM_INST_HANDLER;
ctl.irq = irq;
if (drmIoctl(fd, DRM_IOCTL_CONTROL, &ctl))
{
drm_control_t ctl;
+ memclear(ctl);
ctl.func = DRM_UNINST_HANDLER;
ctl.irq = 0;
if (drmIoctl(fd, DRM_IOCTL_CONTROL, &ctl))
{
drm_lock_t lock;
+ memclear(lock);
lock.context = context;
- lock.flags = 0;
if (flags & DRM_LOCK_READY) lock.flags |= _DRM_LOCK_READY;
if (flags & DRM_LOCK_QUIESCENT) lock.flags |= _DRM_LOCK_QUIESCENT;
if (flags & DRM_LOCK_FLUSH) lock.flags |= _DRM_LOCK_FLUSH;
{
drm_irq_busid_t p;
+ memclear(p);
p.busnum = busnum;
p.devnum = devnum;
p.funcnum = funcnum;
{
drm_ctx_priv_map_t map;
+ memclear(map);
map.ctx_id = ctx_id;
map.handle = (void *)(uintptr_t)handle;
{
drm_ctx_priv_map_t map;
+ memclear(map);
map.ctx_id = ctx_id;
if (drmIoctl(fd, DRM_IOCTL_GET_SAREA_CTX, &map))
{
drm_map_t map;
+ memclear(map);
map.offset = idx;
if (drmIoctl(fd, DRM_IOCTL_GET_MAP, &map))
return -errno;
{
drm_client_t client;
+ memclear(client);
client.idx = idx;
if (drmIoctl(fd, DRM_IOCTL_GET_CLIENT, &client))
return -errno;
int drmGetStats(int fd, drmStatsT *stats)
{
drm_stats_t s;
- int i;
+ unsigned i;
+ memclear(s);
if (drmIoctl(fd, DRM_IOCTL_GET_STATS, &s))
return -errno;
int retcode = 0;
drm_set_version_t sv;
+ memclear(sv);
sv.drm_di_major = version->drm_di_major;
sv.drm_di_minor = version->drm_di_minor;
sv.drm_dd_major = version->drm_dd_major;
*/
int drmCommandNone(int fd, unsigned long drmCommandIndex)
{
- void *data = NULL; /* dummy */
unsigned long request;
request = DRM_IO( DRM_COMMAND_BASE + drmCommandIndex);
- if (drmIoctl(fd, request, data)) {
+ if (drmIoctl(fd, request, NULL)) {
return -errno;
}
return 0;
char *BusID;
int fd;
int refcount;
+ int type;
} connection[DRM_MAX_FDS];
static int nr_fds = 0;
const char *BusID,
int *newlyopened)
{
+ return drmOpenOnceWithType(BusID, newlyopened, DRM_NODE_PRIMARY);
+}
+
+int drmOpenOnceWithType(const char *BusID, int *newlyopened, int type)
+{
int i;
int fd;
for (i = 0; i < nr_fds; i++)
- if (strcmp(BusID, connection[i].BusID) == 0) {
+ if ((strcmp(BusID, connection[i].BusID) == 0) &&
+ (connection[i].type == type)) {
connection[i].refcount++;
*newlyopened = 0;
return connection[i].fd;
}
- fd = drmOpen(unused, BusID);
- if (fd <= 0 || nr_fds == DRM_MAX_FDS)
+ fd = drmOpenWithType(NULL, BusID, type);
+ if (fd < 0 || nr_fds == DRM_MAX_FDS)
return fd;
connection[nr_fds].BusID = strdup(BusID);
connection[nr_fds].fd = fd;
connection[nr_fds].refcount = 1;
+ connection[nr_fds].type = type;
*newlyopened = 1;
if (0)
int drmSetMaster(int fd)
{
- return ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
+ return drmIoctl(fd, DRM_IOCTL_SET_MASTER, NULL);
}
int drmDropMaster(int fd)
{
- return ioctl(fd, DRM_IOCTL_DROP_MASTER, 0);
+ return drmIoctl(fd, DRM_IOCTL_DROP_MASTER, NULL);
}
char *drmGetDeviceNameFromFd(int fd)
return strdup(name);
}
+int drmGetNodeTypeFromFd(int fd)
+{
+ struct stat sbuf;
+ int maj, min, type;
+
+ if (fstat(fd, &sbuf))
+ return -1;
+
+ maj = major(sbuf.st_rdev);
+ min = minor(sbuf.st_rdev);
+
+ if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ type = drmGetMinorType(min);
+ if (type == -1)
+ errno = ENODEV;
+ return type;
+}
+
int drmPrimeHandleToFD(int fd, uint32_t handle, uint32_t flags, int *prime_fd)
{
struct drm_prime_handle args;
int ret;
+ memclear(args);
+ args.fd = -1;
args.handle = handle;
args.flags = flags;
ret = drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args);
struct drm_prime_handle args;
int ret;
+ memclear(args);
args.fd = prime_fd;
- args.flags = 0;
ret = drmIoctl(fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &args);
if (ret)
return ret;
return 0;
}
+static char *drmGetMinorNameForFD(int fd, int type)
+{
+#ifdef __linux__
+ DIR *sysdir;
+ struct dirent *pent, *ent;
+ struct stat sbuf;
+ const char *name = drmGetMinorName(type);
+ int len;
+ char dev_name[64], buf[64];
+ long name_max;
+ int maj, min;
+
+ if (!name)
+ return NULL;
+
+ len = strlen(name);
+
+ if (fstat(fd, &sbuf))
+ return NULL;
+
+ maj = major(sbuf.st_rdev);
+ min = minor(sbuf.st_rdev);
+
+ if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
+ return NULL;
+
+ snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/device/drm", maj, min);
+
+ sysdir = opendir(buf);
+ if (!sysdir)
+ return NULL;
+
+ name_max = fpathconf(dirfd(sysdir), _PC_NAME_MAX);
+ if (name_max == -1)
+ goto out_close_dir;
+
+ pent = malloc(offsetof(struct dirent, d_name) + name_max + 1);
+ if (pent == NULL)
+ goto out_close_dir;
+
+ while (readdir_r(sysdir, pent, &ent) == 0 && ent != NULL) {
+ if (strncmp(ent->d_name, name, len) == 0) {
+ snprintf(dev_name, sizeof(dev_name), DRM_DIR_NAME "/%s",
+ ent->d_name);
+
+ free(pent);
+ closedir(sysdir);
+
+ return strdup(dev_name);
+ }
+ }
+
+ free(pent);
+
+out_close_dir:
+ closedir(sysdir);
+#endif
+ return NULL;
+}
+
+char *drmGetPrimaryDeviceNameFromFd(int fd)
+{
+ return drmGetMinorNameForFD(fd, DRM_NODE_PRIMARY);
+}
+
+char *drmGetRenderDeviceNameFromFd(int fd)
+{
+ return drmGetMinorNameForFD(fd, DRM_NODE_RENDER);
+}
+
+#ifdef __linux__
+static int drmParseSubsystemType(const char *str)
+{
+ char link[PATH_MAX + 1] = "";
+ char *name;
+
+ if (readlink(str, link, PATH_MAX) < 0)
+ return -EINVAL;
+
+ name = strrchr(link, '/');
+ if (!name)
+ return -EINVAL;
+
+ name++;
+
+ if (strncmp(name, "pci", 3) == 0)
+ return DRM_BUS_PCI;
+
+ return -EINVAL;
+}
+
+static int drmParsePciBusInfo(const char *str, drmPciBusInfoPtr info)
+{
+ int domain, bus, dev, func;
+ char *value;
+
+ if (str == NULL)
+ return -EINVAL;
+
+ value = strstr(str, "PCI_SLOT_NAME=");
+ if (value == NULL)
+ return -EINVAL;
+
+ value += strlen("PCI_SLOT_NAME=");
+
+ if (sscanf(value, "%04x:%02x:%02x.%1u",
+ &domain, &bus, &dev, &func) != 4)
+ return -EINVAL;
+
+ info->domain = domain;
+ info->bus = bus;
+ info->dev = dev;
+ info->func = func;
+
+ return 0;
+}
+
+static int drmSameDevice(drmDevicePtr a, drmDevicePtr b)
+{
+ if (a->bustype != b->bustype)
+ return 0;
+
+ switch (a->bustype) {
+ case DRM_BUS_PCI:
+ if (memcmp(a->businfo.pci, b->businfo.pci, sizeof(drmPciBusInfo)) == 0)
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int drmGetNodeType(const char *name)
+{
+ if (strncmp(name, DRM_PRIMARY_MINOR_NAME,
+ sizeof(DRM_PRIMARY_MINOR_NAME) - 1) == 0)
+ return DRM_NODE_PRIMARY;
+
+ if (strncmp(name, DRM_CONTROL_MINOR_NAME,
+ sizeof(DRM_CONTROL_MINOR_NAME ) - 1) == 0)
+ return DRM_NODE_CONTROL;
+
+ if (strncmp(name, DRM_RENDER_MINOR_NAME,
+ sizeof(DRM_RENDER_MINOR_NAME) - 1) == 0)
+ return DRM_NODE_RENDER;
+
+ return -EINVAL;
+}
+
+static int drmParsePciDeviceInfo(const unsigned char *config,
+ drmPciDeviceInfoPtr device)
+{
+ if (config == NULL)
+ return -EINVAL;
+
+ device->vendor_id = config[0] | (config[1] << 8);
+ device->device_id = config[2] | (config[3] << 8);
+ device->revision_id = config[8];
+ device->subvendor_id = config[44] | (config[45] << 8);
+ device->subdevice_id = config[46] | (config[47] << 8);
+
+ return 0;
+}
+
+static void drmFreeDevice(drmDevicePtr device)
+{
+ int i;
+
+ if (device == NULL)
+ return;
+
+ if (device->nodes != NULL)
+ for (i = 0; i < DRM_NODE_MAX; i++)
+ free(device->nodes[i]);
+
+ free(device->nodes);
+ free(device->businfo.pci);
+ free(device->deviceinfo.pci);
+}
+
+void drmFreeDevices(drmDevicePtr devices[], int count)
+{
+ int i;
+
+ if (devices == NULL)
+ return;
+
+ for (i = 0; i < count; i++) {
+ drmFreeDevice(devices[i]);
+ free(devices[i]);
+ devices[i] = NULL;
+ }
+}
+
+/**
+ * Get drm devices on the system
+ *
+ * \param devices the array of devices with drmDevicePtr elements
+ * can be NULL to get the device number first
+ * \param max_devices the maximum number of devices for the array
+ *
+ * \return on error - negative error code,
+ * if devices is NULL - total number of devices available on the system,
+ * alternatively the number of devices stored in devices[], which is
+ * capped by the max_devices.
+ */
+int drmGetDevices(drmDevicePtr devices[], int max_devices)
+{
+ drmDevicePtr devs = NULL;
+ drmPciBusInfoPtr pcibus = NULL;
+ drmPciDeviceInfoPtr pcidevice = NULL;
+ DIR *sysdir = NULL;
+ struct dirent *dent = NULL;
+ struct stat sbuf = {0};
+ char node[PATH_MAX + 1] = "";
+ char path[PATH_MAX + 1] = "";
+ char data[128] = "";
+ unsigned char config[64] = "";
+ int node_type, subsystem_type;
+ int maj, min;
+ int fd;
+ int ret, i = 0, j, node_count, device_count = 0;
+ int max_count = 16;
+ int *duplicated = NULL;
+
+ devs = calloc(max_count, sizeof(*devs));
+ if (devs == NULL)
+ return -ENOMEM;
+
+ sysdir = opendir(DRM_DIR_NAME);
+ if (!sysdir) {
+ ret = -errno;
+ goto free_locals;
+ }
+
+ while ((dent = readdir(sysdir))) {
+ node_type = drmGetNodeType(dent->d_name);
+ if (node_type < 0)
+ continue;
+
+ snprintf(node, PATH_MAX, "%s/%s", DRM_DIR_NAME, dent->d_name);
+ if (stat(node, &sbuf))
+ continue;
+
+ maj = major(sbuf.st_rdev);
+ min = minor(sbuf.st_rdev);
+
+ if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
+ continue;
+
+ snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/subsystem",
+ maj, min);
+ subsystem_type = drmParseSubsystemType(path);
+
+ if (subsystem_type < 0)
+ continue;
+
+ switch (subsystem_type) {
+ case DRM_BUS_PCI:
+ pcibus = calloc(1, sizeof(*pcibus));
+ if (pcibus == NULL) {
+ ret = -ENOMEM;
+ goto free_locals;
+ }
+
+ snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/uevent",
+ maj, min);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ ret = -errno;
+ goto free_locals;
+ }
+ ret = read(fd, data, sizeof(data));
+ if (ret < 0) {
+ ret = -errno;
+ close(fd);
+ goto free_locals;
+ }
+
+ ret = drmParsePciBusInfo(data, pcibus);
+ close(fd);
+ if (ret)
+ goto free_locals;
+
+ if (i >= max_count) {
+ max_count += 16;
+ devs = realloc(devs, max_count * sizeof(*devs));
+ }
+
+ devs[i].businfo.pci = pcibus;
+ devs[i].bustype = subsystem_type;
+ devs[i].nodes = calloc(DRM_NODE_MAX, sizeof(char *));
+ if (devs[i].nodes == NULL) {
+ ret = -ENOMEM;
+ goto free_locals;
+ }
+ devs[i].nodes[node_type] = strdup(node);
+ if (devs[i].nodes[node_type] == NULL) {
+ ret = -ENOMEM;
+ goto free_locals;
+ }
+ devs[i].available_nodes = 1 << node_type;
+
+ if (devices != NULL) {
+ snprintf(path, PATH_MAX, "/sys/class/drm/%s/device/config",
+ dent->d_name);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ ret = -errno;
+ goto free_locals;
+ }
+ ret = read(fd, config, 64);
+ if (ret < 0) {
+ ret = -errno;
+ close(fd);
+ goto free_locals;
+ }
+
+ pcidevice = calloc(1, sizeof(*pcidevice));
+ if (pcidevice == NULL) {
+ ret = -ENOMEM;
+ goto free_locals;
+ }
+
+ ret = drmParsePciDeviceInfo(config, pcidevice);
+ if (ret)
+ goto free_locals;
+
+ devs[i].deviceinfo.pci = pcidevice;
+ close(fd);
+ }
+ break;
+ default:
+ fprintf(stderr, "The subsystem type is not supported yet\n");
+ break;
+ }
+ i++;
+ }
+
+ node_count = i;
+
+ /* merge duplicated devices with same domain/bus/device/func IDs */
+ duplicated = calloc(node_count, sizeof(*duplicated));
+ if (duplicated == NULL) {
+ ret = -ENOMEM;
+ goto free_locals;
+ }
+
+ for (i = 0; i < node_count; i++) {
+ for (j = i+1; j < node_count; j++) {
+ if (duplicated[i] || duplicated[j])
+ continue;
+ if (drmSameDevice(&devs[i], &devs[j])) {
+ duplicated[j] = 1;
+ devs[i].available_nodes |= devs[j].available_nodes;
+ node_type = log2(devs[j].available_nodes);
+ devs[i].nodes[node_type] = devs[j].nodes[node_type];
+ free(devs[j].nodes);
+ free(devs[j].businfo.pci);
+ free(devs[j].deviceinfo.pci);
+ }
+ }
+ }
+
+ for (i = 0; i < node_count; i++) {
+ if(duplicated[i] == 0) {
+ if ((devices != NULL) && (device_count < max_devices)) {
+ devices[device_count] = calloc(1, sizeof(drmDevice));
+ if (devices[device_count] == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
+ memcpy(devices[device_count], &devs[i], sizeof(drmDevice));
+ } else
+ drmFreeDevice(&devs[i]);
+ device_count++;
+ }
+ }
+
+ if (i < node_count) {
+ drmFreeDevices(devices, device_count);
+ for ( ; i < node_count; i++)
+ if(duplicated[i] == 0)
+ drmFreeDevice(&devs[i]);
+ } else
+ ret = device_count;
+
+ free(duplicated);
+ free(devs);
+ closedir(sysdir);
+ return ret;
+
+free_locals:
+ for (j = 0; j < i; j++)
+ drmFreeDevice(&devs[j]);
+ free(pcidevice);
+ free(pcibus);
+ free(devs);
+ closedir(sysdir);
+ return ret;
+}
+#else
+void drmFreeDevices(drmDevicePtr devices[], int count)
+{
+ (void)devices;
+ (void)count;
+}
+
+int drmGetDevices(drmDevicePtr devices[], int max_devices)
+{
+ (void)devices;
+ (void)max_devices;
+ return -EINVAL;
+}
+
+#warning "Missing implementation of drmGetDevices/drmFreeDevices"
+
+#endif