/** * \file xf86drm.c * User-level interface to DRM device * * \author Rickard E. (Rik) Faith * \author Kevin E. Martin */ /* * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #define stat_t struct stat #include #include #include #include /* Not all systems have MAP_FAILED defined */ #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif #include "xf86drm.h" #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #define DRM_MAJOR 145 #endif #ifdef __NetBSD__ #define DRM_MAJOR 34 #endif # ifdef __OpenBSD__ # define DRM_MAJOR 81 # endif #ifndef DRM_MAJOR #define DRM_MAJOR 226 /* Linux */ #endif #ifndef DRM_MAX_MINOR #define DRM_MAX_MINOR 16 #endif /* * This definition needs to be changed on some systems if dev_t is a structure. * If there is a header file we can get it from, there would be best. */ #ifndef makedev #define makedev(x,y) ((dev_t)(((x) << 8) | (y))) #endif #define DRM_MSG_VERBOSITY 3 static drmServerInfoPtr drm_server_info; void drmSetServerInfo(drmServerInfoPtr info) { drm_server_info = info; } /** * Output a message to stderr. * * \param format printf() like format string. * * \internal * This function is a wrapper around vfprintf(). */ static int drmDebugPrint(const char *format, va_list ap) { return vfprintf(stderr, format, ap); } static int (*drm_debug_print)(const char *format, va_list ap) = drmDebugPrint; static void drmMsg(const char *format, ...) { va_list ap; const char *env; if (((env = getenv("LIBGL_DEBUG")) && strstr(env, "verbose")) || drm_server_info) { va_start(ap, format); if (drm_server_info) { drm_server_info->debug_print(format,ap); } else { drm_debug_print(format, ap); } va_end(ap); } } void drmSetDebugMsgFunction(int (*debug_msg_ptr)(const char *format, va_list ap)) { drm_debug_print = debug_msg_ptr; } static void *drmHashTable = NULL; /* Context switch callbacks */ void *drmGetHashTable(void) { return drmHashTable; } void *drmMalloc(int size) { void *pt; if ((pt = malloc(size))) memset(pt, 0, size); return pt; } void drmFree(void *pt) { if (pt) free(pt); } /* drmStrdup can't use strdup(3), since it doesn't call _DRM_MALLOC... */ static char *drmStrdup(const char *s) { char *retval; if (!s) return NULL; retval = malloc(strlen(s)+1); if (!retval) return NULL; strcpy(retval, s); return retval; } static unsigned long drmGetKeyFromFd(int fd) { stat_t st; st.st_rdev = 0; fstat(fd, &st); return st.st_rdev; } drmHashEntry *drmGetEntry(int fd) { unsigned long key = drmGetKeyFromFd(fd); void *value; drmHashEntry *entry; if (!drmHashTable) drmHashTable = drmHashCreate(); if (drmHashLookup(drmHashTable, key, &value)) { entry = drmMalloc(sizeof(*entry)); entry->fd = fd; entry->f = NULL; entry->tagTable = drmHashCreate(); drmHashInsert(drmHashTable, key, entry); } else { entry = value; } return entry; } /** * Compare two busid strings * * \param first * \param second * * \return 1 if matched. * * \internal * This function compares two bus ID strings. It understands the older * PCI:b:d:f format and the newer pci:oooo:bb:dd.f format. In the format, o is * domain, b is bus, d is device, f is function. */ static int drmMatchBusID(const char *id1, const char *id2) { /* First, check if the IDs are exactly the same */ if (strcasecmp(id1, id2) == 0) return 1; /* Try to match old/new-style PCI bus IDs. */ if (strncasecmp(id1, "pci", 3) == 0) { int o1, b1, d1, f1; int o2, b2, d2, f2; int ret; ret = sscanf(id1, "pci:%04x:%02x:%02x.%d", &o1, &b1, &d1, &f1); if (ret != 4) { o1 = 0; ret = sscanf(id1, "PCI:%d:%d:%d", &b1, &d1, &f1); if (ret != 3) return 0; } ret = sscanf(id2, "pci:%04x:%02x:%02x.%d", &o2, &b2, &d2, &f2); if (ret != 4) { o2 = 0; ret = sscanf(id2, "PCI:%d:%d:%d", &b2, &d2, &f2); if (ret != 3) return 0; } if ((o1 != o2) || (b1 != b2) || (d1 != d2) || (f1 != f2)) return 0; else return 1; } return 0; } /** * Open the DRM device, creating it if necessary. * * \param dev major and minor numbers of the device. * \param minor minor number of the device. * * \return a file descriptor on success, or a negative value on error. * * \internal * Assembles the device name from \p minor and opens it, creating the device * 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) { stat_t st; char buf[64]; int fd; mode_t devmode = DRM_DEV_MODE, serv_mode; int isroot = !geteuid(); uid_t user = DRM_DEV_UID; gid_t group = DRM_DEV_GID, serv_group; sprintf(buf, DRM_DEV_NAME, DRM_DIR_NAME, minor); drmMsg("drmOpenDevice: node name is %s\n", buf); if (drm_server_info) { 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 (stat(DRM_DIR_NAME, &st)) { if (!isroot) return DRM_ERR_NOT_ROOT; mkdir(DRM_DIR_NAME, DRM_DEV_DIRMODE); chown(DRM_DIR_NAME, 0, 0); /* root:root */ chmod(DRM_DIR_NAME, DRM_DEV_DIRMODE); } /* Check if the device node exists and create it if necessary. */ if (stat(buf, &st)) { if (!isroot) return DRM_ERR_NOT_ROOT; remove(buf); mknod(buf, S_IFCHR | devmode, dev); } if (drm_server_info) { chown(buf, user, group); chmod(buf, devmode); } fd = open(buf, O_RDWR, 0); drmMsg("drmOpenDevice: open result is %d, (%s)\n", fd, fd < 0 ? strerror(errno) : "OK"); if (fd >= 0) return fd; /* Check if the device node is not what we expect it to be, and recreate it * and try again if so. */ if (st.st_rdev != dev) { if (!isroot) return DRM_ERR_NOT_ROOT; remove(buf); mknod(buf, S_IFCHR | devmode, dev); if (drm_server_info) { chown(buf, user, group); chmod(buf, devmode); } } fd = open(buf, O_RDWR, 0); drmMsg("drmOpenDevice: open result is %d, (%s)\n", fd, fd < 0 ? strerror(errno) : "OK"); if (fd >= 0) return fd; drmMsg("drmOpenDevice: Open failed\n"); remove(buf); return -errno; } /** * Open the DRM device * * \param minor device minor number. * \param create allow to create the device if set. * * \return a file descriptor on success, or a negative value on error. * * \internal * Calls drmOpenDevice() if \p create is set, otherwise assembles the device * name from \p minor and opens it. */ static int drmOpenMinor(int minor, int create) { int fd; char buf[64]; if (create) return drmOpenDevice(makedev(DRM_MAJOR, minor), minor); sprintf(buf, DRM_DEV_NAME, DRM_DIR_NAME, minor); if ((fd = open(buf, O_RDWR, 0)) >= 0) return fd; return -errno; } /** * Determine whether the DRM kernel driver has been loaded. * * \return 1 if the DRM driver is loaded, 0 otherwise. * * \internal * Determine the presence of the kernel driver by attempting to open the 0 * minor and get version information. For backward compatibility with older * Linux implementations, /proc/dri is also checked. */ int drmAvailable(void) { drmVersionPtr version; int retval = 0; int fd; if ((fd = drmOpenMinor(0, 1)) < 0) { #ifdef __linux__ /* Try proc for backward Linux compatibility */ if (!access("/proc/dri/0", R_OK)) return 1; #endif return 0; } if ((version = drmGetVersion(fd))) { retval = 1; drmFreeVersion(version); } close(fd); return retval; } /** * Open the device by bus ID. * * \param busid bus ID. * * \return a file descriptor on success, or a negative value on error. * * \internal * This function attempts to open every possible minor (up to DRM_MAX_MINOR), * comparing the device bus ID with the one supplied. * * \sa drmOpenMinor() and drmGetBusid(). */ static int drmOpenByBusid(const char *busid) { int i; int fd; const char *buf; drmSetVersion sv; drmMsg("drmOpenByBusid: Searching for BusID %s\n", busid); for (i = 0; i < DRM_MAX_MINOR; i++) { fd = drmOpenMinor(i, 1); drmMsg("drmOpenByBusid: drmOpenMinor returns %d\n", fd); if (fd >= 0) { sv.drm_di_major = 1; sv.drm_di_minor = 1; sv.drm_dd_major = -1; /* Don't care */ sv.drm_dd_minor = -1; /* Don't care */ drmSetInterfaceVersion(fd, &sv); buf = drmGetBusid(fd); drmMsg("drmOpenByBusid: drmGetBusid reports %s\n", buf); if (buf && drmMatchBusID(buf, busid)) { drmFreeBusid(buf); return fd; } if (buf) drmFreeBusid(buf); close(fd); } } return -1; } /** * Open the device by name. * * \param name driver name. * * \return a file descriptor on success, or a negative value on error. * * \internal * This function opens the first minor number that matches the driver name and * isn't already in use. If it's in use it then it will already have a bus ID * assigned. * * \sa drmOpenMinor(), drmGetVersion() and drmGetBusid(). */ static int drmOpenByName(const char *name) { int i; int fd; drmVersionPtr version; char * id; if (!drmAvailable()) { if (!drm_server_info) { return -1; } else { /* try to load the kernel module now */ if (!drm_server_info->load_module(name)) { drmMsg("[drm] failed to load kernel module \"%s\"\n", name); 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)) >= 0) { if ((version = drmGetVersion(fd))) { if (!strcmp(version->name, name)) { drmFreeVersion(version); id = drmGetBusid(fd); drmMsg("drmGetBusid returned '%s'\n", id ? id : "NULL"); if (!id || !*id) { if (id) drmFreeBusid(id); return fd; } else { drmFreeBusid(id); } } else { drmFreeVersion(version); } } close(fd); } } #ifdef __linux__ /* Backward-compatibility /proc support */ for (i = 0; i < 8; i++) { char proc_name[64], buf[512]; char *driver, *pt, *devstring; int retcode; sprintf(proc_name, "/proc/dri/%d/name", i); if ((fd = open(proc_name, 0, 0)) >= 0) { retcode = read(fd, buf, sizeof(buf)-1); close(fd); if (retcode) { buf[retcode-1] = '\0'; for (driver = pt = buf; *pt && *pt != ' '; ++pt) ; if (*pt) { /* Device is next */ *pt = '\0'; if (!strcmp(driver, name)) { /* Match */ for (devstring = ++pt; *pt && *pt != ' '; ++pt) ; if (*pt) { /* Found busid */ return drmOpenByBusid(++pt); } else { /* No busid */ return drmOpenDevice(strtol(devstring, NULL, 0),i); } } } } } } #endif return -1; } /** * Open the DRM device. * * 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. * * \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 drmOpen(const char *name, const char *busid) { if (!drmAvailable() && name != NULL && drm_server_info) { /* try to load the kernel */ 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); if (fd >= 0) return fd; } if (name) return drmOpenByName(name); return -1; } /** * Free the version information returned by drmGetVersion(). * * \param v pointer to the version information. * * \internal * It frees the memory pointed by \p %v as well as all the non-null strings * pointers in it. */ void drmFreeVersion(drmVersionPtr v) { if (!v) return; drmFree(v->name); drmFree(v->date); drmFree(v->desc); drmFree(v); } /** * Free the non-public version information returned by the kernel. * * \param v pointer to the version information. * * \internal * Used by drmGetVersion() to free the memory pointed by \p %v as well as all * the non-null strings pointers in it. */ static void drmFreeKernelVersion(drm_version_t *v) { if (!v) return; drmFree(v->name); drmFree(v->date); drmFree(v->desc); drmFree(v); } /** * Copy version information. * * \param d destination pointer. * \param s source pointer. * * \internal * Used by drmGetVersion() to translate the information returned by the ioctl * interface in a private structure into the public structure counterpart. */ static void drmCopyVersion(drmVersionPtr d, const drm_version_t *s) { d->version_major = s->version_major; d->version_minor = s->version_minor; d->version_patchlevel = s->version_patchlevel; d->name_len = s->name_len; d->name = drmStrdup(s->name); d->date_len = s->date_len; d->date = drmStrdup(s->date); d->desc_len = s->desc_len; d->desc = drmStrdup(s->desc); } /** * Query the driver version information. * * \param fd file descriptor. * * \return pointer to a drmVersion structure which should be freed with * drmFreeVersion(). * * \note Similar information is available via /proc/dri. * * \internal * It gets the version information via successive DRM_IOCTL_VERSION ioctls, * first with zeros to get the string lengths, and then the actually strings. * It also null-terminates them since they might not be already. */ drmVersionPtr drmGetVersion(int fd) { 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; if (ioctl(fd, DRM_IOCTL_VERSION, version)) { drmFreeKernelVersion(version); return NULL; } if (version->name_len) version->name = drmMalloc(version->name_len + 1); if (version->date_len) version->date = drmMalloc(version->date_len + 1); if (version->desc_len) version->desc = drmMalloc(version->desc_len + 1); if (ioctl(fd, DRM_IOCTL_VERSION, version)) { drmMsg("DRM_IOCTL_VERSION: %s\n", strerror(errno)); drmFreeKernelVersion(version); return NULL; } /* The results might not be null-terminated strings, so terminate them. */ if (version->name_len) version->name[version->name_len] = '\0'; if (version->date_len) version->date[version->date_len] = '\0'; if (version->desc_len) version->desc[version->desc_len] = '\0'; retval = drmMalloc(sizeof(*retval)); drmCopyVersion(retval, version); drmFreeKernelVersion(version); return retval; } /** * Get version information for the DRM user space library. * * This version number is driver independent. * * \param fd file descriptor. * * \return version information. * * \internal * This function allocates and fills a drm_version structure with a hard coded * version number. */ drmVersionPtr drmGetLibVersion(int fd) { drm_version_t *version = drmMalloc(sizeof(*version)); /* Version history: * NOTE THIS MUST NOT GO ABOVE VERSION 1.X due to drivers needing it * revision 1.0.x = original DRM interface with no drmGetLibVersion * entry point and many drm extensions * revision 1.1.x = added drmCommand entry points for device extensions * added drmGetLibVersion to identify libdrm.a version * revision 1.2.x = added drmSetInterfaceVersion * modified drmOpen to handle both busid and name * revision 1.3.x = added server + memory manager */ version->version_major = 1; version->version_minor = 3; version->version_patchlevel = 0; return (drmVersionPtr)version; } /** * Free the bus ID information. * * \param busid bus ID information string as given by drmGetBusid(). * * \internal * This function is just frees the memory pointed by \p busid. */ void drmFreeBusid(const char *busid) { drmFree((void *)busid); } /** * Get the bus ID of the device. * * \param fd file descriptor. * * \return bus ID string. * * \internal * This function gets the bus ID via successive DRM_IOCTL_GET_UNIQUE ioctls to * get the string length and data, passing the arguments in a drm_unique * structure. */ char *drmGetBusid(int fd) { drm_unique_t u; u.unique_len = 0; u.unique = NULL; if (ioctl(fd, DRM_IOCTL_GET_UNIQUE, &u)) return NULL; u.unique = drmMalloc(u.unique_len + 1); if (ioctl(fd, DRM_IOCTL_GET_UNIQUE, &u)) return NULL; u.unique[u.unique_len] = '\0'; return u.unique; } /** * Set the bus ID of the device. * * \param fd file descriptor. * \param busid bus ID string. * * \return zero on success, negative on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_SET_UNIQUE ioctl, passing * the arguments in a drm_unique structure. */ int drmSetBusid(int fd, const char *busid) { drm_unique_t u; u.unique = (char *)busid; u.unique_len = strlen(busid); if (ioctl(fd, DRM_IOCTL_SET_UNIQUE, &u)) { return -errno; } return 0; } int drmGetMagic(int fd, drm_magic_t * magic) { drm_auth_t auth; *magic = 0; if (ioctl(fd, DRM_IOCTL_GET_MAGIC, &auth)) return -errno; *magic = auth.magic; return 0; } int drmAuthMagic(int fd, drm_magic_t magic) { drm_auth_t auth; auth.magic = magic; if (ioctl(fd, DRM_IOCTL_AUTH_MAGIC, &auth)) return -errno; return 0; } /** * Specifies a range of memory that is available for mapping by a * non-root process. * * \param fd file descriptor. * \param offset usually the physical address. The actual meaning depends of * the \p type parameter. See below. * \param size of the memory in bytes. * \param type type of the memory to be mapped. * \param flags combination of several flags to modify the function actions. * \param handle will be set to a value that may be used as the offset * parameter for mmap(). * * \return zero on success or a negative value on error. * * \par Mapping the frame buffer * For the frame buffer * - \p offset will be the physical address of the start of the frame buffer, * - \p size will be the size of the frame buffer in bytes, and * - \p type will be DRM_FRAME_BUFFER. * * \par * The area mapped will be uncached. If MTRR support is available in the * kernel, the frame buffer area will be set to write combining. * * \par Mapping the MMIO register area * For the MMIO register area, * - \p offset will be the physical address of the start of the register area, * - \p size will be the size of the register area bytes, and * - \p type will be DRM_REGISTERS. * \par * The area mapped will be uncached. * * \par Mapping the SAREA * For the SAREA, * - \p offset will be ignored and should be set to zero, * - \p size will be the desired size of the SAREA in bytes, * - \p type will be DRM_SHM. * * \par * A shared memory area of the requested size will be created and locked in * kernel memory. This area may be mapped into client-space by using the handle * returned. * * \note May only be called by root. * * \internal * This function is a wrapper around the DRM_IOCTL_ADD_MAP ioctl, passing * the arguments in a drm_map structure. */ int drmAddMap(int fd, drm_handle_t offset, drmSize size, drmMapType type, drmMapFlags flags, drm_handle_t *handle) { drm_map_t map; map.offset = offset; map.size = size; map.handle = 0; map.type = type; map.flags = flags; if (ioctl(fd, DRM_IOCTL_ADD_MAP, &map)) return -errno; if (handle) *handle = (drm_handle_t)map.handle; return 0; } int drmRmMap(int fd, drm_handle_t handle) { drm_map_t map; map.handle = (void *)handle; if(ioctl(fd, DRM_IOCTL_RM_MAP, &map)) return -errno; return 0; } /** * Make buffers available for DMA transfers. * * \param fd file descriptor. * \param count number of buffers. * \param size size of each buffer. * \param flags buffer allocation flags. * \param agp_offset offset in the AGP aperture * * \return number of buffers allocated, negative on error. * * \internal * This function is a wrapper around DRM_IOCTL_ADD_BUFS ioctl. * * \sa drm_buf_desc. */ int drmAddBufs(int fd, int count, int size, drmBufDescFlags flags, int agp_offset) { drm_buf_desc_t request; request.count = count; request.size = size; request.low_mark = 0; request.high_mark = 0; request.flags = flags; request.agp_start = agp_offset; if (ioctl(fd, DRM_IOCTL_ADD_BUFS, &request)) return -errno; return request.count; } int drmMarkBufs(int fd, double low, double high) { drm_buf_info_t info; int i; info.count = 0; info.list = NULL; if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) return -EINVAL; if (!info.count) return -EINVAL; if (!(info.list = drmMalloc(info.count * sizeof(*info.list)))) return -ENOMEM; if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) { int retval = -errno; drmFree(info.list); return retval; } for (i = 0; i < info.count; i++) { info.list[i].low_mark = low * info.list[i].count; info.list[i].high_mark = high * info.list[i].count; if (ioctl(fd, DRM_IOCTL_MARK_BUFS, &info.list[i])) { int retval = -errno; drmFree(info.list); return retval; } } drmFree(info.list); return 0; } /** * Free buffers. * * \param fd file descriptor. * \param count number of buffers to free. * \param list list of buffers to be freed. * * \return zero on success, or a negative value on failure. * * \note This function is primarily used for debugging. * * \internal * This function is a wrapper around the DRM_IOCTL_FREE_BUFS ioctl, passing * the arguments in a drm_buf_free structure. */ int drmFreeBufs(int fd, int count, int *list) { drm_buf_free_t request; request.count = count; request.list = list; if (ioctl(fd, DRM_IOCTL_FREE_BUFS, &request)) return -errno; return 0; } /** * Close the device. * * \param fd file descriptor. * * \internal * This function closes the file descriptor. */ int drmClose(int fd) { unsigned long key = drmGetKeyFromFd(fd); drmHashEntry *entry = drmGetEntry(fd); drmHashDestroy(entry->tagTable); entry->fd = 0; entry->f = NULL; entry->tagTable = NULL; drmHashDelete(drmHashTable, key); drmFree(entry); return close(fd); } /** * Map a region of memory. * * \param fd file descriptor. * \param handle handle returned by drmAddMap(). * \param size size in bytes. Must match the size used by drmAddMap(). * \param address will contain the user-space virtual address where the mapping * begins. * * \return zero on success, or a negative value on failure. * * \internal * This function is a wrapper for mmap(). */ int drmMap(int fd, drm_handle_t handle, drmSize size, drmAddressPtr address) { static unsigned long pagesize_mask = 0; if (fd < 0) return -EINVAL; if (!pagesize_mask) pagesize_mask = getpagesize() - 1; size = (size + pagesize_mask) & ~pagesize_mask; *address = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, handle); if (*address == MAP_FAILED) return -errno; return 0; } /** * Unmap mappings obtained with drmMap(). * * \param address address as given by drmMap(). * \param size size in bytes. Must match the size used by drmMap(). * * \return zero on success, or a negative value on failure. * * \internal * This function is a wrapper for munmap(). */ int drmUnmap(drmAddress address, drmSize size) { return munmap(address, size); } drmBufInfoPtr drmGetBufInfo(int fd) { drm_buf_info_t info; drmBufInfoPtr retval; int i; info.count = 0; info.list = NULL; if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) return NULL; if (info.count) { if (!(info.list = drmMalloc(info.count * sizeof(*info.list)))) return NULL; if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) { drmFree(info.list); return NULL; } retval = drmMalloc(sizeof(*retval)); retval->count = info.count; retval->list = drmMalloc(info.count * sizeof(*retval->list)); for (i = 0; i < info.count; i++) { retval->list[i].count = info.list[i].count; retval->list[i].size = info.list[i].size; retval->list[i].low_mark = info.list[i].low_mark; retval->list[i].high_mark = info.list[i].high_mark; } drmFree(info.list); return retval; } return NULL; } /** * Map all DMA buffers into client-virtual space. * * \param fd file descriptor. * * \return a pointer to a ::drmBufMap structure. * * \note The client may not use these buffers until obtaining buffer indices * with drmDMA(). * * \internal * This function calls the DRM_IOCTL_MAP_BUFS ioctl and copies the returned * information about the buffers in a drm_buf_map structure into the * client-visible data structures. */ drmBufMapPtr drmMapBufs(int fd) { drm_buf_map_t bufs; drmBufMapPtr retval; int i; bufs.count = 0; bufs.list = NULL; bufs.virtual = NULL; if (ioctl(fd, DRM_IOCTL_MAP_BUFS, &bufs)) return NULL; if (!bufs.count) return NULL; if (!(bufs.list = drmMalloc(bufs.count * sizeof(*bufs.list)))) return NULL; if (ioctl(fd, DRM_IOCTL_MAP_BUFS, &bufs)) { drmFree(bufs.list); return NULL; } retval = drmMalloc(sizeof(*retval)); retval->count = bufs.count; retval->list = drmMalloc(bufs.count * sizeof(*retval->list)); for (i = 0; i < bufs.count; i++) { retval->list[i].idx = bufs.list[i].idx; retval->list[i].total = bufs.list[i].total; retval->list[i].used = 0; retval->list[i].address = bufs.list[i].address; } drmFree(bufs.list); return retval; } /** * Unmap buffers allocated with drmMapBufs(). * * \return zero on success, or negative value on failure. * * \internal * Calls munmap() for every buffer stored in \p bufs and frees the * memory allocated by drmMapBufs(). */ int drmUnmapBufs(drmBufMapPtr bufs) { int i; for (i = 0; i < bufs->count; i++) { munmap(bufs->list[i].address, bufs->list[i].total); } drmFree(bufs->list); drmFree(bufs); return 0; } #define DRM_DMA_RETRY 16 /** * Reserve DMA buffers. * * \param fd file descriptor. * \param request * * \return zero on success, or a negative value on failure. * * \internal * Assemble the arguments into a drm_dma structure and keeps issuing the * DRM_IOCTL_DMA ioctl until success or until maximum number of retries. */ int drmDMA(int fd, drmDMAReqPtr request) { drm_dma_t dma; int ret, i = 0; dma.context = request->context; dma.send_count = request->send_count; dma.send_indices = request->send_list; dma.send_sizes = request->send_sizes; dma.flags = request->flags; dma.request_count = request->request_count; dma.request_size = request->request_size; dma.request_indices = request->request_list; dma.request_sizes = request->request_sizes; dma.granted_count = 0; do { ret = ioctl( fd, DRM_IOCTL_DMA, &dma ); } while ( ret && errno == EAGAIN && i++ < DRM_DMA_RETRY ); if ( ret == 0 ) { request->granted_count = dma.granted_count; return 0; } else { return -errno; } } /** * Obtain heavyweight hardware lock. * * \param fd file descriptor. * \param context context. * \param flags flags that determine the sate of the hardware when the function * returns. * * \return always zero. * * \internal * This function translates the arguments into a drm_lock structure and issue * the DRM_IOCTL_LOCK ioctl until the lock is successfully acquired. */ int drmGetLock(int fd, drm_context_t context, drmLockFlags flags) { drm_lock_t 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; if (flags & DRM_LOCK_FLUSH_ALL) lock.flags |= _DRM_LOCK_FLUSH_ALL; if (flags & DRM_HALT_ALL_QUEUES) lock.flags |= _DRM_HALT_ALL_QUEUES; if (flags & DRM_HALT_CUR_QUEUES) lock.flags |= _DRM_HALT_CUR_QUEUES; while (ioctl(fd, DRM_IOCTL_LOCK, &lock)) ; return 0; } /** * Release the hardware lock. * * \param fd file descriptor. * \param context context. * * \return zero on success, or a negative value on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_UNLOCK ioctl, passing the * argument in a drm_lock structure. */ int drmUnlock(int fd, drm_context_t context) { drm_lock_t lock; lock.context = context; lock.flags = 0; return ioctl(fd, DRM_IOCTL_UNLOCK, &lock); } drm_context_t *drmGetReservedContextList(int fd, int *count) { drm_ctx_res_t res; drm_ctx_t *list; drm_context_t * retval; int i; res.count = 0; res.contexts = NULL; if (ioctl(fd, DRM_IOCTL_RES_CTX, &res)) return NULL; if (!res.count) return NULL; if (!(list = drmMalloc(res.count * sizeof(*list)))) return NULL; if (!(retval = drmMalloc(res.count * sizeof(*retval)))) { drmFree(list); return NULL; } res.contexts = list; if (ioctl(fd, DRM_IOCTL_RES_CTX, &res)) return NULL; for (i = 0; i < res.count; i++) retval[i] = list[i].handle; drmFree(list); *count = res.count; return retval; } void drmFreeReservedContextList(drm_context_t *pt) { drmFree(pt); } /** * Create context. * * Used by the X server during GLXContext initialization. This causes * per-context kernel-level resources to be allocated. * * \param fd file descriptor. * \param handle is set on success. To be used by the client when requesting DMA * dispatch with drmDMA(). * * \return zero on success, or a negative value on failure. * * \note May only be called by root. * * \internal * This function is a wrapper around the DRM_IOCTL_ADD_CTX ioctl, passing the * argument in a drm_ctx structure. */ int drmCreateContext(int fd, drm_context_t *handle) { drm_ctx_t ctx; ctx.flags = 0; /* Modified with functions below */ if (ioctl(fd, DRM_IOCTL_ADD_CTX, &ctx)) return -errno; *handle = ctx.handle; return 0; } int drmSwitchToContext(int fd, drm_context_t context) { drm_ctx_t ctx; ctx.handle = context; if (ioctl(fd, DRM_IOCTL_SWITCH_CTX, &ctx)) return -errno; return 0; } int drmSetContextFlags(int fd, drm_context_t context, drm_context_tFlags flags) { drm_ctx_t ctx; /* * Context preserving means that no context switches are done between DMA * buffers from one context and the next. This is suitable for use in the * X server (which promises to maintain hardware context), or in the * client-side library when buffers are swapped on behalf of two threads. */ ctx.handle = context; ctx.flags = 0; if (flags & DRM_CONTEXT_PRESERVED) ctx.flags |= _DRM_CONTEXT_PRESERVED; if (flags & DRM_CONTEXT_2DONLY) ctx.flags |= _DRM_CONTEXT_2DONLY; if (ioctl(fd, DRM_IOCTL_MOD_CTX, &ctx)) return -errno; return 0; } int drmGetContextFlags(int fd, drm_context_t context, drm_context_tFlagsPtr flags) { drm_ctx_t ctx; ctx.handle = context; if (ioctl(fd, DRM_IOCTL_GET_CTX, &ctx)) return -errno; *flags = 0; if (ctx.flags & _DRM_CONTEXT_PRESERVED) *flags |= DRM_CONTEXT_PRESERVED; if (ctx.flags & _DRM_CONTEXT_2DONLY) *flags |= DRM_CONTEXT_2DONLY; return 0; } /** * Destroy context. * * Free any kernel-level resources allocated with drmCreateContext() associated * with the context. * * \param fd file descriptor. * \param handle handle given by drmCreateContext(). * * \return zero on success, or a negative value on failure. * * \note May only be called by root. * * \internal * This function is a wrapper around the DRM_IOCTL_RM_CTX ioctl, passing the * argument in a drm_ctx structure. */ int drmDestroyContext(int fd, drm_context_t handle) { drm_ctx_t ctx; ctx.handle = handle; if (ioctl(fd, DRM_IOCTL_RM_CTX, &ctx)) return -errno; return 0; } int drmCreateDrawable(int fd, drm_drawable_t *handle) { drm_draw_t draw; if (ioctl(fd, DRM_IOCTL_ADD_DRAW, &draw)) return -errno; *handle = draw.handle; return 0; } int drmDestroyDrawable(int fd, drm_drawable_t handle) { drm_draw_t draw; draw.handle = handle; if (ioctl(fd, DRM_IOCTL_RM_DRAW, &draw)) return -errno; return 0; } int drmUpdateDrawableInfo(int fd, drm_drawable_t handle, drm_drawable_info_type_t type, unsigned int num, void *data) { drm_update_draw_t update; update.handle = handle; update.type = type; update.num = num; update.data = (unsigned long long)(unsigned long)data; if (ioctl(fd, DRM_IOCTL_UPDATE_DRAW, &update)) return -errno; return 0; } /** * Acquire the AGP device. * * Must be called before any of the other AGP related calls. * * \param fd file descriptor. * * \return zero on success, or a negative value on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_ACQUIRE ioctl. */ int drmAgpAcquire(int fd) { if (ioctl(fd, DRM_IOCTL_AGP_ACQUIRE, NULL)) return -errno; return 0; } /** * Release the AGP device. * * \param fd file descriptor. * * \return zero on success, or a negative value on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_RELEASE ioctl. */ int drmAgpRelease(int fd) { if (ioctl(fd, DRM_IOCTL_AGP_RELEASE, NULL)) return -errno; return 0; } /** * Set the AGP mode. * * \param fd file descriptor. * \param mode AGP mode. * * \return zero on success, or a negative value on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_ENABLE ioctl, passing the * argument in a drm_agp_mode structure. */ int drmAgpEnable(int fd, unsigned long mode) { drm_agp_mode_t m; m.mode = mode; if (ioctl(fd, DRM_IOCTL_AGP_ENABLE, &m)) return -errno; return 0; } /** * Allocate a chunk of AGP memory. * * \param fd file descriptor. * \param size requested memory size in bytes. Will be rounded to page boundary. * \param type type of memory to allocate. * \param address if not zero, will be set to the physical address of the * allocated memory. * \param handle on success will be set to a handle of the allocated memory. * * \return zero on success, or a negative value on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_ALLOC ioctl, passing the * arguments in a drm_agp_buffer structure. */ int drmAgpAlloc(int fd, unsigned long size, unsigned long type, unsigned long *address, drm_handle_t *handle) { drm_agp_buffer_t b; *handle = DRM_AGP_NO_HANDLE; b.size = size; b.handle = 0; b.type = type; if (ioctl(fd, DRM_IOCTL_AGP_ALLOC, &b)) return -errno; if (address != 0UL) *address = b.physical; *handle = b.handle; return 0; } /** * Free a chunk of AGP memory. * * \param fd file descriptor. * \param handle handle to the allocated memory, as given by drmAgpAllocate(). * * \return zero on success, or a negative value on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_FREE ioctl, passing the * argument in a drm_agp_buffer structure. */ int drmAgpFree(int fd, drm_handle_t handle) { drm_agp_buffer_t b; b.size = 0; b.handle = handle; if (ioctl(fd, DRM_IOCTL_AGP_FREE, &b)) return -errno; return 0; } /** * Bind a chunk of AGP memory. * * \param fd file descriptor. * \param handle handle to the allocated memory, as given by drmAgpAllocate(). * \param offset offset in bytes. It will round to page boundary. * * \return zero on success, or a negative value on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_BIND ioctl, passing the * argument in a drm_agp_binding structure. */ int drmAgpBind(int fd, drm_handle_t handle, unsigned long offset) { drm_agp_binding_t b; b.handle = handle; b.offset = offset; if (ioctl(fd, DRM_IOCTL_AGP_BIND, &b)) return -errno; return 0; } /** * Unbind a chunk of AGP memory. * * \param fd file descriptor. * \param handle handle to the allocated memory, as given by drmAgpAllocate(). * * \return zero on success, or a negative value on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_UNBIND ioctl, passing * the argument in a drm_agp_binding structure. */ int drmAgpUnbind(int fd, drm_handle_t handle) { drm_agp_binding_t b; b.handle = handle; b.offset = 0; if (ioctl(fd, DRM_IOCTL_AGP_UNBIND, &b)) return -errno; return 0; } /** * Get AGP driver major version number. * * \param fd file descriptor. * * \return major version number on success, or a negative value on failure.. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the * necessary information in a drm_agp_info structure. */ int drmAgpVersionMajor(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return -errno; return i.agp_version_major; } /** * Get AGP driver minor version number. * * \param fd file descriptor. * * \return minor version number on success, or a negative value on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the * necessary information in a drm_agp_info structure. */ int drmAgpVersionMinor(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return -errno; return i.agp_version_minor; } /** * Get AGP mode. * * \param fd file descriptor. * * \return mode on success, or zero on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the * necessary information in a drm_agp_info structure. */ unsigned long drmAgpGetMode(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.mode; } /** * Get AGP aperture base. * * \param fd file descriptor. * * \return aperture base on success, zero on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the * necessary information in a drm_agp_info structure. */ unsigned long drmAgpBase(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.aperture_base; } /** * Get AGP aperture size. * * \param fd file descriptor. * * \return aperture size on success, zero on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the * necessary information in a drm_agp_info structure. */ unsigned long drmAgpSize(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.aperture_size; } /** * Get used AGP memory. * * \param fd file descriptor. * * \return memory used on success, or zero on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the * necessary information in a drm_agp_info structure. */ unsigned long drmAgpMemoryUsed(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.memory_used; } /** * Get available AGP memory. * * \param fd file descriptor. * * \return memory available on success, or zero on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the * necessary information in a drm_agp_info structure. */ unsigned long drmAgpMemoryAvail(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.memory_allowed; } /** * Get hardware vendor ID. * * \param fd file descriptor. * * \return vendor ID on success, or zero on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the * necessary information in a drm_agp_info structure. */ unsigned int drmAgpVendorId(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.id_vendor; } /** * Get hardware device ID. * * \param fd file descriptor. * * \return zero on success, or zero on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_AGP_INFO ioctl, getting the * necessary information in a drm_agp_info structure. */ unsigned int drmAgpDeviceId(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.id_device; } int drmScatterGatherAlloc(int fd, unsigned long size, drm_handle_t *handle) { drm_scatter_gather_t sg; *handle = 0; sg.size = size; sg.handle = 0; if (ioctl(fd, DRM_IOCTL_SG_ALLOC, &sg)) return -errno; *handle = sg.handle; return 0; } int drmScatterGatherFree(int fd, drm_handle_t handle) { drm_scatter_gather_t sg; sg.size = 0; sg.handle = handle; if (ioctl(fd, DRM_IOCTL_SG_FREE, &sg)) return -errno; return 0; } /** * Wait for VBLANK. * * \param fd file descriptor. * \param vbl pointer to a drmVBlank structure. * * \return zero on success, or a negative value on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_WAIT_VBLANK ioctl. */ int drmWaitVBlank(int fd, drmVBlankPtr vbl) { int ret; do { ret = ioctl(fd, DRM_IOCTL_WAIT_VBLANK, vbl); vbl->request.type &= ~DRM_VBLANK_RELATIVE; } while (ret && errno == EINTR); return ret; } int drmError(int err, const char *label) { switch (err) { case DRM_ERR_NO_DEVICE: fprintf(stderr, "%s: no device\n", label); break; case DRM_ERR_NO_ACCESS: fprintf(stderr, "%s: no access\n", label); break; case DRM_ERR_NOT_ROOT: fprintf(stderr, "%s: not root\n", label); break; case DRM_ERR_INVALID: fprintf(stderr, "%s: invalid args\n", label); break; default: if (err < 0) err = -err; fprintf( stderr, "%s: error %d (%s)\n", label, err, strerror(err) ); break; } return 1; } /** * Install IRQ handler. * * \param fd file descriptor. * \param irq IRQ number. * * \return zero on success, or a negative value on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_CONTROL ioctl, passing the * argument in a drm_control structure. */ int drmCtlInstHandler(int fd, int irq) { drm_control_t ctl; ctl.func = DRM_INST_HANDLER; ctl.irq = irq; if (ioctl(fd, DRM_IOCTL_CONTROL, &ctl)) return -errno; return 0; } /** * Uninstall IRQ handler. * * \param fd file descriptor. * * \return zero on success, or a negative value on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_CONTROL ioctl, passing the * argument in a drm_control structure. */ int drmCtlUninstHandler(int fd) { drm_control_t ctl; ctl.func = DRM_UNINST_HANDLER; ctl.irq = 0; if (ioctl(fd, DRM_IOCTL_CONTROL, &ctl)) return -errno; return 0; } int drmFinish(int fd, int context, drmLockFlags flags) { drm_lock_t 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; if (flags & DRM_LOCK_FLUSH_ALL) lock.flags |= _DRM_LOCK_FLUSH_ALL; if (flags & DRM_HALT_ALL_QUEUES) lock.flags |= _DRM_HALT_ALL_QUEUES; if (flags & DRM_HALT_CUR_QUEUES) lock.flags |= _DRM_HALT_CUR_QUEUES; if (ioctl(fd, DRM_IOCTL_FINISH, &lock)) return -errno; return 0; } /** * Get IRQ from bus ID. * * \param fd file descriptor. * \param busnum bus number. * \param devnum device number. * \param funcnum function number. * * \return IRQ number on success, or a negative value on failure. * * \internal * This function is a wrapper around the DRM_IOCTL_IRQ_BUSID ioctl, passing the * arguments in a drm_irq_busid structure. */ int drmGetInterruptFromBusID(int fd, int busnum, int devnum, int funcnum) { drm_irq_busid_t p; p.busnum = busnum; p.devnum = devnum; p.funcnum = funcnum; if (ioctl(fd, DRM_IOCTL_IRQ_BUSID, &p)) return -errno; return p.irq; } int drmAddContextTag(int fd, drm_context_t context, void *tag) { drmHashEntry *entry = drmGetEntry(fd); if (drmHashInsert(entry->tagTable, context, tag)) { drmHashDelete(entry->tagTable, context); drmHashInsert(entry->tagTable, context, tag); } return 0; } int drmDelContextTag(int fd, drm_context_t context) { drmHashEntry *entry = drmGetEntry(fd); return drmHashDelete(entry->tagTable, context); } void *drmGetContextTag(int fd, drm_context_t context) { drmHashEntry *entry = drmGetEntry(fd); void *value; if (drmHashLookup(entry->tagTable, context, &value)) return NULL; return value; } int drmAddContextPrivateMapping(int fd, drm_context_t ctx_id, drm_handle_t handle) { drm_ctx_priv_map_t map; map.ctx_id = ctx_id; map.handle = (void *)handle; if (ioctl(fd, DRM_IOCTL_SET_SAREA_CTX, &map)) return -errno; return 0; } int drmGetContextPrivateMapping(int fd, drm_context_t ctx_id, drm_handle_t *handle) { drm_ctx_priv_map_t map; map.ctx_id = ctx_id; if (ioctl(fd, DRM_IOCTL_GET_SAREA_CTX, &map)) return -errno; if (handle) *handle = (drm_handle_t)map.handle; return 0; } int drmGetMap(int fd, int idx, drm_handle_t *offset, drmSize *size, drmMapType *type, drmMapFlags *flags, drm_handle_t *handle, int *mtrr) { drm_map_t map; map.offset = idx; if (ioctl(fd, DRM_IOCTL_GET_MAP, &map)) return -errno; *offset = map.offset; *size = map.size; *type = map.type; *flags = map.flags; *handle = (unsigned long)map.handle; *mtrr = map.mtrr; return 0; } int drmGetClient(int fd, int idx, int *auth, int *pid, int *uid, unsigned long *magic, unsigned long *iocs) { drm_client_t client; client.idx = idx; if (ioctl(fd, DRM_IOCTL_GET_CLIENT, &client)) return -errno; *auth = client.auth; *pid = client.pid; *uid = client.uid; *magic = client.magic; *iocs = client.iocs; return 0; } int drmGetStats(int fd, drmStatsT *stats) { drm_stats_t s; int i; if (ioctl(fd, DRM_IOCTL_GET_STATS, &s)) return -errno; stats->count = 0; memset(stats, 0, sizeof(*stats)); if (s.count > sizeof(stats->data)/sizeof(stats->data[0])) return -1; #define SET_VALUE \ stats->data[i].long_format = "%-20.20s"; \ stats->data[i].rate_format = "%8.8s"; \ stats->data[i].isvalue = 1; \ stats->data[i].verbose = 0 #define SET_COUNT \ stats->data[i].long_format = "%-20.20s"; \ stats->data[i].rate_format = "%5.5s"; \ stats->data[i].isvalue = 0; \ stats->data[i].mult_names = "kgm"; \ stats->data[i].mult = 1000; \ stats->data[i].verbose = 0 #define SET_BYTE \ stats->data[i].long_format = "%-20.20s"; \ stats->data[i].rate_format = "%5.5s"; \ stats->data[i].isvalue = 0; \ stats->data[i].mult_names = "KGM"; \ stats->data[i].mult = 1024; \ stats->data[i].verbose = 0 stats->count = s.count; for (i = 0; i < s.count; i++) { stats->data[i].value = s.data[i].value; switch (s.data[i].type) { case _DRM_STAT_LOCK: stats->data[i].long_name = "Lock"; stats->data[i].rate_name = "Lock"; SET_VALUE; break; case _DRM_STAT_OPENS: stats->data[i].long_name = "Opens"; stats->data[i].rate_name = "O"; SET_COUNT; stats->data[i].verbose = 1; break; case _DRM_STAT_CLOSES: stats->data[i].long_name = "Closes"; stats->data[i].rate_name = "Lock"; SET_COUNT; stats->data[i].verbose = 1; break; case _DRM_STAT_IOCTLS: stats->data[i].long_name = "Ioctls"; stats->data[i].rate_name = "Ioc/s"; SET_COUNT; break; case _DRM_STAT_LOCKS: stats->data[i].long_name = "Locks"; stats->data[i].rate_name = "Lck/s"; SET_COUNT; break; case _DRM_STAT_UNLOCKS: stats->data[i].long_name = "Unlocks"; stats->data[i].rate_name = "Unl/s"; SET_COUNT; break; case _DRM_STAT_IRQ: stats->data[i].long_name = "IRQs"; stats->data[i].rate_name = "IRQ/s"; SET_COUNT; break; case _DRM_STAT_PRIMARY: stats->data[i].long_name = "Primary Bytes"; stats->data[i].rate_name = "PB/s"; SET_BYTE; break; case _DRM_STAT_SECONDARY: stats->data[i].long_name = "Secondary Bytes"; stats->data[i].rate_name = "SB/s"; SET_BYTE; break; case _DRM_STAT_DMA: stats->data[i].long_name = "DMA"; stats->data[i].rate_name = "DMA/s"; SET_COUNT; break; case _DRM_STAT_SPECIAL: stats->data[i].long_name = "Special DMA"; stats->data[i].rate_name = "dma/s"; SET_COUNT; break; case _DRM_STAT_MISSED: stats->data[i].long_name = "Miss"; stats->data[i].rate_name = "Ms/s"; SET_COUNT; break; case _DRM_STAT_VALUE: stats->data[i].long_name = "Value"; stats->data[i].rate_name = "Value"; SET_VALUE; break; case _DRM_STAT_BYTE: stats->data[i].long_name = "Bytes"; stats->data[i].rate_name = "B/s"; SET_BYTE; break; case _DRM_STAT_COUNT: default: stats->data[i].long_name = "Count"; stats->data[i].rate_name = "Cnt/s"; SET_COUNT; break; } } return 0; } /** * Issue a set-version ioctl. * * \param fd file descriptor. * \param drmCommandIndex command index * \param data source pointer of the data to be read and written. * \param size size of the data to be read and written. * * \return zero on success, or a negative value on failure. * * \internal * It issues a read-write ioctl given by * \code DRM_COMMAND_BASE + drmCommandIndex \endcode. */ int drmSetInterfaceVersion(int fd, drmSetVersion *version) { int retcode = 0; drm_set_version_t 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; sv.drm_dd_minor = version->drm_dd_minor; if (ioctl(fd, DRM_IOCTL_SET_VERSION, &sv)) { retcode = -errno; } version->drm_di_major = sv.drm_di_major; version->drm_di_minor = sv.drm_di_minor; version->drm_dd_major = sv.drm_dd_major; version->drm_dd_minor = sv.drm_dd_minor; return retcode; } /** * Send a device-specific command. * * \param fd file descriptor. * \param drmCommandIndex command index * * \return zero on success, or a negative value on failure. * * \internal * It issues a ioctl given by * \code DRM_COMMAND_BASE + drmCommandIndex \endcode. */ int drmCommandNone(int fd, unsigned long drmCommandIndex) { void *data = NULL; /* dummy */ unsigned long request; request = DRM_IO( DRM_COMMAND_BASE + drmCommandIndex); if (ioctl(fd, request, data)) { return -errno; } return 0; } /** * Send a device-specific read command. * * \param fd file descriptor. * \param drmCommandIndex command index * \param data destination pointer of the data to be read. * \param size size of the data to be read. * * \return zero on success, or a negative value on failure. * * \internal * It issues a read ioctl given by * \code DRM_COMMAND_BASE + drmCommandIndex \endcode. */ int drmCommandRead(int fd, unsigned long drmCommandIndex, void *data, unsigned long size) { unsigned long request; request = DRM_IOC( DRM_IOC_READ, DRM_IOCTL_BASE, DRM_COMMAND_BASE + drmCommandIndex, size); if (ioctl(fd, request, data)) { return -errno; } return 0; } /** * Send a device-specific write command. * * \param fd file descriptor. * \param drmCommandIndex command index * \param data source pointer of the data to be written. * \param size size of the data to be written. * * \return zero on success, or a negative value on failure. * * \internal * It issues a write ioctl given by * \code DRM_COMMAND_BASE + drmCommandIndex \endcode. */ int drmCommandWrite(int fd, unsigned long drmCommandIndex, void *data, unsigned long size) { unsigned long request; request = DRM_IOC( DRM_IOC_WRITE, DRM_IOCTL_BASE, DRM_COMMAND_BASE + drmCommandIndex, size); if (ioctl(fd, request, data)) { return -errno; } return 0; } /** * Send a device-specific read-write command. * * \param fd file descriptor. * \param drmCommandIndex command index * \param data source pointer of the data to be read and written. * \param size size of the data to be read and written. * * \return zero on success, or a negative value on failure. * * \internal * It issues a read-write ioctl given by * \code DRM_COMMAND_BASE + drmCommandIndex \endcode. */ int drmCommandWriteRead(int fd, unsigned long drmCommandIndex, void *data, unsigned long size) { unsigned long request; request = DRM_IOC( DRM_IOC_READ|DRM_IOC_WRITE, DRM_IOCTL_BASE, DRM_COMMAND_BASE + drmCommandIndex, size); if (ioctl(fd, request, data)) { return -errno; } return 0; } /* * Valid flags are * DRM_FENCE_FLAG_EMIT * DRM_FENCE_FLAG_SHAREABLE * DRM_FENCE_MASK_DRIVER */ int drmFenceCreate(int fd, unsigned flags, int fence_class, unsigned type, drmFence *fence) { drm_fence_arg_t arg; memset(&arg, 0, sizeof(arg)); arg.flags = flags; arg.type = type; arg.fence_class = fence_class; if (ioctl(fd, DRM_IOCTL_FENCE_CREATE, &arg)) return -errno; fence->handle = arg.handle; fence->fence_class = arg.fence_class; fence->type = arg.type; fence->flags = arg.flags; fence->signaled = 0; return 0; } /* * Valid flags are * DRM_FENCE_FLAG_SHAREABLE * DRM_FENCE_MASK_DRIVER */ int drmFenceBuffers(int fd, unsigned flags, uint32_t fence_class, drmFence *fence) { drm_fence_arg_t arg; memset(&arg, 0, sizeof(arg)); arg.flags = flags; arg.fence_class = fence_class; if (ioctl(fd, DRM_IOCTL_FENCE_BUFFERS, &arg)) return -errno; fence->handle = arg.handle; fence->fence_class = arg.fence_class; fence->type = arg.type; fence->flags = arg.flags; fence->sequence = arg.sequence; fence->signaled = 0; return 0; } int drmFenceDestroy(int fd, const drmFence *fence) { drm_fence_arg_t arg; memset(&arg, 0, sizeof(arg)); arg.handle = fence->handle; if (ioctl(fd, DRM_IOCTL_FENCE_DESTROY, &arg)) return -errno; return 0; } int drmFenceReference(int fd, unsigned handle, drmFence *fence) { drm_fence_arg_t arg; memset(&arg, 0, sizeof(arg)); arg.handle = handle; if (ioctl(fd, DRM_IOCTL_FENCE_REFERENCE, &arg)) return -errno; fence->handle = arg.handle; fence->fence_class = arg.fence_class; fence->type = arg.type; fence->flags = arg.flags; fence->signaled = arg.signaled; return 0; } int drmFenceUnreference(int fd, const drmFence *fence) { drm_fence_arg_t arg; memset(&arg, 0, sizeof(arg)); arg.handle = fence->handle; if (ioctl(fd, DRM_IOCTL_FENCE_UNREFERENCE, &arg)) return -errno; return 0; } int drmFenceFlush(int fd, drmFence *fence, unsigned flush_type) { drm_fence_arg_t arg; memset(&arg, 0, sizeof(arg)); arg.handle = fence->handle; arg.type = flush_type; if (ioctl(fd, DRM_IOCTL_FENCE_FLUSH, &arg)) return -errno; fence->fence_class = arg.fence_class; fence->type = arg.type; fence->signaled = arg.signaled; return 0; } int drmFenceUpdate(int fd, drmFence *fence) { drm_fence_arg_t arg; memset(&arg, 0, sizeof(arg)); arg.handle = fence->handle; if (ioctl(fd, DRM_IOCTL_FENCE_SIGNALED, &arg)) return -errno; fence->fence_class = arg.fence_class; fence->type = arg.type; fence->signaled = arg.signaled; return 0; } int drmFenceSignaled(int fd, drmFence *fence, unsigned fenceType, int *signaled) { if ((fence->flags & DRM_FENCE_FLAG_SHAREABLE) || ((fenceType & fence->signaled) != fenceType)) { int ret = drmFenceFlush(fd, fence, fenceType); if (ret) return ret; } *signaled = ((fenceType & fence->signaled) == fenceType); return 0; } /* * Valid flags are * DRM_FENCE_FLAG_SHAREABLE * DRM_FENCE_MASK_DRIVER */ int drmFenceEmit(int fd, unsigned flags, drmFence *fence, unsigned emit_type) { drm_fence_arg_t arg; memset(&arg, 0, sizeof(arg)); arg.fence_class = fence->fence_class; arg.flags = flags; arg.handle = fence->handle; arg.type = emit_type; if (ioctl(fd, DRM_IOCTL_FENCE_EMIT, &arg)) return -errno; fence->fence_class = arg.fence_class; fence->type = arg.type; fence->signaled = arg.signaled; return 0; } /* * Valid flags are * DRM_FENCE_FLAG_WAIT_LAZY * DRM_FENCE_FLAG_WAIT_IGNORE_SIGNALS */ int drmFenceWait(int fd, unsigned flags, drmFence *fence, unsigned flush_type) { drm_fence_arg_t arg; int ret; if (flush_type == 0) { flush_type = fence->type; } if (!(fence->flags & DRM_FENCE_FLAG_SHAREABLE)) { if ((flush_type & fence->signaled) == flush_type) { return 0; } } memset(&arg, 0, sizeof(arg)); arg.handle = fence->handle; arg.type = flush_type; arg.flags = flags; do { ret = ioctl(fd, DRM_IOCTL_FENCE_WAIT, &arg); } while (ret != 0 && errno == EAGAIN); if (ret) return -errno; fence->fence_class = arg.fence_class; fence->type = arg.type; fence->signaled = arg.signaled; return 0; } static int drmAdjustListNodes(drmBOList *list) { drmBONode *node; drmMMListHead *l; int ret = 0; while(list->numCurrent < list->numTarget) { node = (drmBONode *) malloc(sizeof(*node)); if (!node) { ret = -ENOMEM; break; } list->numCurrent++; DRMLISTADD(&node->head, &list->free); } while(list->numCurrent > list->numTarget) { l = list->free.next; if (l == &list->free) break; DRMLISTDEL(l); node = DRMLISTENTRY(drmBONode, l, head); free(node); list->numCurrent--; } return ret; } void drmBOFreeList(drmBOList *list) { drmBONode *node; drmMMListHead *l; l = list->list.next; while(l != &list->list) { DRMLISTDEL(l); node = DRMLISTENTRY(drmBONode, l, head); free(node); l = list->list.next; list->numCurrent--; list->numOnList--; } l = list->free.next; while(l != &list->free) { DRMLISTDEL(l); node = DRMLISTENTRY(drmBONode, l, head); free(node); l = list->free.next; list->numCurrent--; } } int drmBOResetList(drmBOList *list) { drmMMListHead *l; int ret; ret = drmAdjustListNodes(list); if (ret) return ret; l = list->list.next; while (l != &list->list) { DRMLISTDEL(l); DRMLISTADD(l, &list->free); list->numOnList--; l = list->list.next; } return drmAdjustListNodes(list); } static drmBONode *drmAddListItem(drmBOList *list, drmBO *item, unsigned long arg0, unsigned long arg1) { drmBONode *node; drmMMListHead *l; l = list->free.next; if (l == &list->free) { node = (drmBONode *) malloc(sizeof(*node)); if (!node) { return NULL; } list->numCurrent++; } else { DRMLISTDEL(l); node = DRMLISTENTRY(drmBONode, l, head); } node->buf = item; node->arg0 = arg0; node->arg1 = arg1; DRMLISTADD(&node->head, &list->list); list->numOnList++; return node; } void *drmBOListIterator(drmBOList *list) { void *ret = list->list.next; if (ret == &list->list) return NULL; return ret; } void *drmBOListNext(drmBOList *list, void *iterator) { void *ret; drmMMListHead *l = (drmMMListHead *) iterator; ret = l->next; if (ret == &list->list) return NULL; return ret; } drmBO *drmBOListBuf(void *iterator) { drmBONode *node; drmMMListHead *l = (drmMMListHead *) iterator; node = DRMLISTENTRY(drmBONode, l, head); return node->buf; } int drmBOCreateList(int numTarget, drmBOList *list) { DRMINITLISTHEAD(&list->list); DRMINITLISTHEAD(&list->free); list->numTarget = numTarget; list->numCurrent = 0; list->numOnList = 0; return drmAdjustListNodes(list); } static void drmBOCopyReply(const struct drm_bo_info_rep *rep, drmBO *buf) { buf->handle = rep->handle; buf->flags = rep->flags; buf->size = rep->size; buf->offset = rep->offset; buf->mapHandle = rep->arg_handle; buf->mask = rep->mask; buf->start = rep->buffer_start; buf->fenceFlags = rep->fence_flags; buf->replyFlags = rep->rep_flags; buf->pageAlignment = rep->page_alignment; buf->tileInfo = rep->tile_info; buf->hwTileStride = rep->hw_tile_stride; buf->desiredTileStride = rep->desired_tile_stride; } int drmBOCreate(int fd, unsigned long start, unsigned long size, unsigned pageAlignment, void *user_buffer, drm_bo_type_t type, uint64_t mask, unsigned hint, drmBO *buf) { struct drm_bo_create_arg arg; struct drm_bo_create_req *req = &arg.d.req; struct drm_bo_info_rep *rep = &arg.d.rep; int ret; memset(buf, 0, sizeof(*buf)); memset(&arg, 0, sizeof(arg)); req->mask = mask; req->hint = hint; req->size = size; req->type = type; req->page_alignment = pageAlignment; buf->virtual = NULL; switch(type) { case drm_bo_type_dc: req->buffer_start = start; break; case drm_bo_type_user: req->buffer_start = (unsigned long) user_buffer; buf->virtual = user_buffer; break; default: return -EINVAL; } do { ret = ioctl(fd, DRM_IOCTL_BO_CREATE, &arg); } while (ret != 0 && errno == EAGAIN); if (ret) return -errno; drmBOCopyReply(rep, buf); buf->mapVirtual = NULL; buf->mapCount = 0; return 0; } int drmBODestroy(int fd, drmBO *buf) { struct drm_bo_handle_arg arg; if (buf->mapVirtual) { (void) drmUnmap(buf->mapVirtual, buf->start + buf->size); buf->mapVirtual = NULL; buf->virtual = NULL; } memset(&arg, 0, sizeof(arg)); arg.handle = buf->handle; if (ioctl(fd, DRM_IOCTL_BO_DESTROY, &arg)) return -errno; buf->handle = 0; return 0; } int drmBOReference(int fd, unsigned handle, drmBO *buf) { struct drm_bo_reference_info_arg arg; struct drm_bo_handle_arg *req = &arg.d.req; struct drm_bo_info_rep *rep = &arg.d.rep; memset(&arg, 0, sizeof(arg)); req->handle = handle; if (ioctl(fd, DRM_IOCTL_BO_REFERENCE, &arg)) return -errno; drmBOCopyReply(rep, buf); buf->type = drm_bo_type_dc; buf->mapVirtual = NULL; buf->mapCount = 0; buf->virtual = NULL; return 0; } int drmBOUnReference(int fd, drmBO *buf) { struct drm_bo_handle_arg arg; if (buf->mapVirtual) { (void) munmap(buf->mapVirtual, buf->start + buf->size); buf->mapVirtual = NULL; buf->virtual = NULL; } memset(&arg, 0, sizeof(arg)); arg.handle = buf->handle; if (ioctl(fd, DRM_IOCTL_BO_UNREFERENCE, &arg)) return -errno; buf->handle = 0; return 0; } /* * Flags can be DRM_BO_FLAG_READ, DRM_BO_FLAG_WRITE or'ed together * Hint currently be DRM_BO_HINT_DONT_BLOCK, which makes the * call return an -EBUSY if it can' immediately honor the mapping request. */ int drmBOMap(int fd, drmBO *buf, unsigned mapFlags, unsigned mapHint, void **address) { struct drm_bo_map_wait_idle_arg arg; struct drm_bo_info_req *req = &arg.d.req; struct drm_bo_info_rep *rep = &arg.d.rep; int ret = 0; /* * Make sure we have a virtual address of the buffer. */ if (!buf->virtual) { drmAddress virtual; virtual = mmap(0, buf->size + buf->start, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf->mapHandle); if (virtual == MAP_FAILED) { ret = -errno; } if (ret) return ret; buf->mapVirtual = virtual; buf->virtual = ((char *) virtual) + buf->start; } memset(&arg, 0, sizeof(arg)); req->handle = buf->handle; req->mask = mapFlags; req->hint = mapHint; /* * May hang if the buffer object is busy. * This IOCTL synchronizes the buffer. */ do { ret = ioctl(fd, DRM_IOCTL_BO_MAP, &arg); } while (ret != 0 && errno == EAGAIN); if (ret) return -errno; drmBOCopyReply(rep, buf); buf->mapFlags = mapFlags; ++buf->mapCount; *address = buf->virtual; return 0; } int drmBOUnmap(int fd, drmBO *buf) { struct drm_bo_handle_arg arg; memset(&arg, 0, sizeof(arg)); arg.handle = buf->handle; if (ioctl(fd, DRM_IOCTL_BO_UNMAP, &arg)) { return -errno; } buf->mapCount--; return 0; } int drmBOValidate(int fd, drmBO *buf, uint32_t fence_class, uint64_t flags, uint64_t mask, unsigned hint) { struct drm_bo_op_arg arg; struct drm_bo_op_req *req = &arg.d.req; struct drm_bo_arg_rep *rep = &arg.d.rep; int ret = 0; memset(&arg, 0, sizeof(arg)); req->bo_req.handle = buf->handle; req->bo_req.flags = flags; req->bo_req.mask = mask; req->bo_req.hint = hint; req->bo_req.fence_class = fence_class; req->op = drm_bo_validate; do{ ret = ioctl(fd, DRM_IOCTL_BO_OP, &arg); } while (ret && errno == EAGAIN); if (ret) return -errno; if (!arg.handled) return -EFAULT; if (rep->ret) return rep->ret; drmBOCopyReply(&rep->bo_info, buf); return 0; } int drmBOFence(int fd, drmBO *buf, unsigned flags, unsigned fenceHandle) { struct drm_bo_op_arg arg; struct drm_bo_op_req *req = &arg.d.req; struct drm_bo_arg_rep *rep = &arg.d.rep; int ret = 0; memset(&arg, 0, sizeof(arg)); req->bo_req.handle = buf->handle; req->bo_req.flags = flags; req->arg_handle = fenceHandle; req->op = drm_bo_fence; ret = ioctl(fd, DRM_IOCTL_BO_OP, &arg); if (ret) return -errno; if (!arg.handled) return -EFAULT; if (rep->ret) return rep->ret; return 0; } int drmBOInfo(int fd, drmBO *buf) { struct drm_bo_reference_info_arg arg; struct drm_bo_handle_arg *req = &arg.d.req; struct drm_bo_info_rep *rep = &arg.d.rep; int ret = 0; memset(&arg, 0, sizeof(arg)); req->handle = buf->handle; ret = ioctl(fd, DRM_IOCTL_BO_INFO, &arg); if (ret) return -errno; drmBOCopyReply(rep, buf); return 0; } int drmBOWaitIdle(int fd, drmBO *buf, unsigned hint) { struct drm_bo_map_wait_idle_arg arg; struct drm_bo_info_req *req = &arg.d.req; struct drm_bo_info_rep *rep = &arg.d.rep; int ret = 0; if ((buf->flags & DRM_BO_FLAG_SHAREABLE) || (buf->replyFlags & DRM_BO_REP_BUSY)) { memset(&arg, 0, sizeof(arg)); req->handle = buf->handle; req->hint = hint; do { ret = ioctl(fd, DRM_IOCTL_BO_WAIT_IDLE, &arg); } while (ret && errno == EAGAIN); if (ret) return -errno; drmBOCopyReply(rep, buf); } return 0; } int drmBOSetPin(int fd, drmBO *buf, int pin) { struct drm_bo_set_pin_arg arg; struct drm_bo_set_pin_req *req = &arg.d.req; struct drm_bo_info_rep *rep = &arg.d.rep; int ret = 0; memset(&arg, 0, sizeof(arg)); req->handle = buf->handle; req->pin = pin; do { ret = ioctl(fd, DRM_IOCTL_BO_SET_PIN, &arg); } while (ret && errno == EAGAIN); if (ret) return -errno; drmBOCopyReply(rep, buf); return 0; } int drmBOBusy(int fd, drmBO *buf, int *busy) { if (!(buf->flags & DRM_BO_FLAG_SHAREABLE) && !(buf->replyFlags & DRM_BO_REP_BUSY)) { *busy = 0; return 0; } else { int ret = drmBOInfo(fd, buf); if (ret) return ret; *busy = (buf->replyFlags & DRM_BO_REP_BUSY); return 0; } } int drmAddValidateItem(drmBOList *list, drmBO *buf, unsigned flags, unsigned mask, int *newItem) { drmBONode *node, *cur; drmMMListHead *l; *newItem = 0; cur = NULL; for (l = list->list.next; l != &list->list; l = l->next) { node = DRMLISTENTRY(drmBONode, l, head); if (node->buf == buf) { cur = node; break; } } if (!cur) { cur = drmAddListItem(list, buf, flags, mask); if (!cur) { drmMsg("Out of memory creating validate list node.\n"); return -ENOMEM; } *newItem = 1; cur->arg0 = flags; cur->arg1 = mask; } else { unsigned memMask = (cur->arg1 | mask) & DRM_BO_MASK_MEM; unsigned memFlags = cur->arg0 & flags & memMask; if (!memFlags) { drmMsg("Incompatible memory location requests " "on validate list.\n"); drmMsg("Previous flag: 0x%08lx, mask: 0x%08lx\n", cur->arg0, cur->arg1); drmMsg("Current flag: 0x%08lx, mask: 0x%08lx\n", flags, mask); return -EINVAL; } if (mask & cur->arg1 & ~DRM_BO_MASK_MEM & (cur->arg0 ^ flags)) { drmMsg("Incompatible buffer flag requests " "on validate list.\n"); drmMsg("Previous flag: 0x%08lx, mask: 0x%08lx\n", cur->arg0, cur->arg1); drmMsg("Current flag: 0x%08lx, mask: 0x%08lx\n", flags, mask); return -EINVAL; } cur->arg1 |= mask; cur->arg0 = memFlags | ((cur->arg0 | flags) & cur->arg1 & ~DRM_BO_MASK_MEM); } return 0; } int drmBOValidateList(int fd, drmBOList *list) { drmBONode *node; drmMMListHead *l; struct drm_bo_op_arg *arg, *first; struct drm_bo_op_req *req; struct drm_bo_arg_rep *rep; uint64_t *prevNext = NULL; drmBO *buf; int ret; first = NULL; for (l = list->list.next; l != &list->list; l = l->next) { node = DRMLISTENTRY(drmBONode, l, head); arg = &node->bo_arg; req = &arg->d.req; if (!first) first = arg; if (prevNext) *prevNext = (unsigned long) arg; memset(arg, 0, sizeof(*arg)); prevNext = &arg->next; req->bo_req.handle = node->buf->handle; req->op = drm_bo_validate; req->bo_req.flags = node->arg0; req->bo_req.hint = 0; req->bo_req.mask = node->arg1; req->bo_req.fence_class = 0; /* Backwards compat. */ } if (!first) return 0; do{ ret = ioctl(fd, DRM_IOCTL_BO_OP, first); } while (ret && errno == EAGAIN); if (ret) return -errno; for (l = list->list.next; l != &list->list; l = l->next) { node = DRMLISTENTRY(drmBONode, l, head); arg = &node->bo_arg; rep = &arg->d.rep; if (!arg->handled) { drmMsg("Unhandled request\n"); return -EFAULT; } if (rep->ret) return rep->ret; buf = node->buf; drmBOCopyReply(&rep->bo_info, buf); } return 0; } int drmBOFenceList(int fd, drmBOList *list, unsigned fenceHandle) { drmBONode *node; drmMMListHead *l; struct drm_bo_op_arg *arg, *first; struct drm_bo_op_req *req; struct drm_bo_arg_rep *rep; uint64_t *prevNext = NULL; drmBO *buf; unsigned fence_flags; int ret; first = NULL; for (l = list->list.next; l != &list->list; l = l->next) { node = DRMLISTENTRY(drmBONode, l, head); arg = &node->bo_arg; req = &arg->d.req; if (!first) first = arg; if (prevNext) *prevNext = (unsigned long) arg; memset(arg, 0, sizeof(*arg)); prevNext = &arg->next; req->bo_req.handle = node->buf->handle; req->op = drm_bo_fence; req->bo_req.mask = node->arg0; req->arg_handle = fenceHandle; } if (!first) return 0; ret = ioctl(fd, DRM_IOCTL_BO_OP, first); if (ret) return -errno; for (l = list->list.next; l != &list->list; l = l->next) { node = DRMLISTENTRY(drmBONode, l, head); arg = &node->bo_arg; rep = &arg->d.rep; if (!arg->handled) return -EFAULT; if (rep->ret) return rep->ret; drmBOCopyReply(&rep->bo_info, node->buf); } return 0; } int drmMMInit(int fd, unsigned long pOffset, unsigned long pSize, unsigned memType) { struct drm_mm_init_arg arg; memset(&arg, 0, sizeof(arg)); arg.magic = DRM_BO_INIT_MAGIC; arg.major = DRM_BO_INIT_MAJOR; arg.minor = DRM_BO_INIT_MINOR; arg.p_offset = pOffset; arg.p_size = pSize; arg.mem_type = memType; if (ioctl(fd, DRM_IOCTL_MM_INIT, &arg)) return -errno; return 0; } int drmMMTakedown(int fd, unsigned memType) { struct drm_mm_type_arg arg; memset(&arg, 0, sizeof(arg)); arg.mem_type = memType; if (ioctl(fd, DRM_IOCTL_MM_TAKEDOWN, &arg)) return -errno; return 0; } int drmMMLock(int fd, unsigned memType) { struct drm_mm_type_arg arg; int ret; memset(&arg, 0, sizeof(arg)); arg.mem_type = memType; do{ ret = ioctl(fd, DRM_IOCTL_MM_LOCK, &arg); } while (ret && errno == EAGAIN); return (ret) ? -errno : 0; } int drmMMUnlock(int fd, unsigned memType) { struct drm_mm_type_arg arg; int ret; memset(&arg, 0, sizeof(arg)); arg.mem_type = memType; do{ ret = ioctl(fd, DRM_IOCTL_MM_UNLOCK, &arg); } while (ret && errno == EAGAIN); return (ret) ? -errno : 0; } #define DRM_MAX_FDS 16 static struct { char *BusID; int fd; int refcount; } connection[DRM_MAX_FDS]; static int nr_fds = 0; int drmOpenOnce(void *unused, const char *BusID, int *newlyopened) { int i; int fd; for (i = 0; i < nr_fds; i++) if (strcmp(BusID, connection[i].BusID) == 0) { connection[i].refcount++; *newlyopened = 0; return connection[i].fd; } fd = drmOpen(unused, BusID); 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; *newlyopened = 1; if (0) fprintf(stderr, "saved connection %d for %s %d\n", nr_fds, connection[nr_fds].BusID, strcmp(BusID, connection[nr_fds].BusID)); nr_fds++; return fd; } void drmCloseOnce(int fd) { int i; for (i = 0; i < nr_fds; i++) { if (fd == connection[i].fd) { if (--connection[i].refcount == 0) { drmClose(connection[i].fd); free(connection[i].BusID); if (i < --nr_fds) connection[i] = connection[nr_fds]; return; } } } }