xf86drm.c revision 88dbee54ed400a3fd5594fab506518c171167805
1/* xf86drm.c -- User-level interface to DRM device
2 * Created: Tue Jan  5 08:16:21 1999 by faith@precisioninsight.com
3 *
4 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
5 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
6 * All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the next
16 * paragraph) shall be included in all copies or substantial portions of the
17 * Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 *
27 * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
28 *	    Kevin E. Martin <martin@valinux.com>
29 *
30 * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/xf86drm.c,v 1.17 2000/09/24 13:51:32 alanh Exp $
31 *
32 */
33
34#ifdef XFree86Server
35# include "xf86.h"
36# include "xf86_OSproc.h"
37# include "xf86_ansic.h"
38# include "xf86Priv.h"
39# define _DRM_MALLOC xalloc
40# define _DRM_FREE   xfree
41# ifndef XFree86LOADER
42#  include <sys/stat.h>
43#  include <sys/mman.h>
44# endif
45#else
46# include <stdio.h>
47# include <stdlib.h>
48# include <unistd.h>
49# include <string.h>
50# include <ctype.h>
51# include <fcntl.h>
52# include <errno.h>
53# include <signal.h>
54# include <sys/types.h>
55# include <sys/stat.h>
56# include <sys/ioctl.h>
57# include <sys/mman.h>
58# include <sys/time.h>
59# ifdef DRM_USE_MALLOC
60#  define _DRM_MALLOC malloc
61#  define _DRM_FREE   free
62extern int xf86InstallSIGIOHandler(int fd, void (*f)(int, void *), void *);
63extern int xf86RemoveSIGIOHandler(int fd);
64# else
65#  include <Xlibint.h>
66#  define _DRM_MALLOC Xmalloc
67#  define _DRM_FREE   Xfree
68# endif
69#endif
70
71#ifdef __alpha__
72extern unsigned long _bus_base(void);
73#define BUS_BASE _bus_base()
74#endif
75
76/* Not all systems have MAP_FAILED defined */
77#ifndef MAP_FAILED
78#define MAP_FAILED ((void *)-1)
79#endif
80
81#include "xf86drm.h"
82#include "drm.h"
83
84#ifndef DRM_MAJOR
85#define DRM_MAJOR 226		/* Linux */
86#endif
87
88#ifndef __linux__
89#undef  DRM_MAJOR
90#define DRM_MAJOR 145		/* Should set in drm.h for *BSD */
91#endif
92
93#ifndef DRM_MAX_MINOR
94#define DRM_MAX_MINOR 16
95#endif
96
97#ifdef __linux__
98#include <sys/sysmacros.h>	/* for makedev() */
99#endif
100
101#ifndef makedev
102				/* This definition needs to be changed on
103                                   some systems if dev_t is a structure.
104                                   If there is a header file we can get it
105                                   from, there would be best. */
106#define makedev(x,y)    ((dev_t)(((x) << 8) | (y)))
107#endif
108
109static void *drmHashTable = NULL; /* Context switch callbacks */
110
111typedef struct drmHashEntry {
112    int      fd;
113    void     (*f)(int, void *, void *);
114    void     *tagTable;
115} drmHashEntry;
116
117void *drmMalloc(int size)
118{
119    void *pt;
120    if ((pt = _DRM_MALLOC(size))) memset(pt, 0, size);
121    return pt;
122}
123
124void drmFree(void *pt)
125{
126    if (pt) _DRM_FREE(pt);
127}
128
129/* drmStrdup can't use strdup(3), since it doesn't call _DRM_MALLOC... */
130static char *drmStrdup(const char *s)
131{
132    char *retval = NULL;
133
134    if (s) {
135	retval = _DRM_MALLOC(strlen(s)+1);
136	strcpy(retval, s);
137    }
138    return retval;
139}
140
141
142static unsigned long drmGetKeyFromFd(int fd)
143{
144#ifdef XFree86LOADER
145    struct xf86stat st;
146#else
147    struct stat     st;
148#endif
149
150    st.st_rdev = 0;
151    fstat(fd, &st);
152    return st.st_rdev;
153}
154
155static drmHashEntry *drmGetEntry(int fd)
156{
157    unsigned long key = drmGetKeyFromFd(fd);
158    void          *value;
159    drmHashEntry  *entry;
160
161    if (!drmHashTable) drmHashTable = drmHashCreate();
162
163    if (drmHashLookup(drmHashTable, key, &value)) {
164	entry           = drmMalloc(sizeof(*entry));
165	entry->fd       = fd;
166	entry->f        = NULL;
167	entry->tagTable = drmHashCreate();
168	drmHashInsert(drmHashTable, key, entry);
169    } else {
170	entry = value;
171    }
172    return entry;
173}
174
175static int drmOpenDevice(long dev, int minor)
176{
177#ifdef XFree86LOADER
178    struct xf86stat st;
179#else
180    struct stat     st;
181#endif
182    char            buf[64];
183    int             fd;
184    mode_t          dirmode = DRM_DEV_DIRMODE;
185    mode_t          devmode = DRM_DEV_MODE;
186    int             isroot  = !geteuid();
187#if defined(XFree86Server)
188    uid_t           user    = DRM_DEV_UID;
189    gid_t           group   = DRM_DEV_GID;
190#endif
191
192#if defined(XFree86Server)
193    devmode  = xf86ConfigDRI.mode ? xf86ConfigDRI.mode : DRM_DEV_MODE;
194    dirmode  = (devmode & S_IRUSR) ? S_IXUSR : 0;
195    dirmode |= (devmode & S_IRGRP) ? S_IXGRP : 0;
196    dirmode |= (devmode & S_IROTH) ? S_IXOTH : 0;
197    dirmode |= devmode;
198    devmode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
199    group = (xf86ConfigDRI.group >= 0) ? xf86ConfigDRI.group : DRM_DEV_GID;
200#endif
201
202    if (stat(DRM_DIR_NAME, &st)) {
203	if (!isroot) return DRM_ERR_NOT_ROOT;
204	remove(DRM_DIR_NAME);
205	mkdir(DRM_DIR_NAME, dirmode);
206    }
207#if defined(XFree86Server)
208    chown(DRM_DIR_NAME, user, group);
209    chmod(DRM_DIR_NAME, dirmode);
210#endif
211
212    sprintf(buf, DRM_DEV_NAME, DRM_DIR_NAME, minor);
213    if (stat(buf, &st) || st.st_rdev != dev) {
214	if (!isroot) return DRM_ERR_NOT_ROOT;
215	remove(buf);
216	mknod(buf, S_IFCHR | devmode, dev);
217    }
218#if defined(XFree86Server)
219    chown(buf, user, group);
220    chmod(buf, devmode);
221#endif
222
223    if ((fd = open(buf, O_RDWR, 0)) >= 0) return fd;
224    remove(buf);
225    return -errno;
226}
227
228int drmOpenMinor(int minor, int create)
229{
230    int  fd;
231    char buf[64];
232
233    if (create) return drmOpenDevice(makedev(DRM_MAJOR, minor), minor);
234
235    sprintf(buf, DRM_DEV_NAME, DRM_DIR_NAME, minor);
236    if ((fd = open(buf, O_RDWR, 0)) >= 0) return fd;
237    return -errno;
238}
239
240/* drmAvailable looks for (DRM_MAJOR, 0) and returns 1 if it returns
241   information for DRM_IOCTL_VERSION.  For backward compatibility with
242   older Linux implementations, /proc/dri is also checked. */
243
244int drmAvailable(void)
245{
246    drmVersionPtr version;
247    int           retval = 0;
248    int           fd;
249
250    if ((fd = drmOpenMinor(0, 1)) < 0) {
251				/* Try proc for backward Linux compatibility */
252	if (!access("/proc/dri/0", R_OK)) return 1;
253	return 0;
254    }
255
256    if ((version = drmGetVersion(fd))) {
257	retval = 1;
258	drmFreeVersion(version);
259    }
260    close(fd);
261
262    return retval;
263}
264
265static int drmOpenByBusid(const char *busid)
266{
267    int        i;
268    int        fd;
269    const char *buf;
270
271    for (i = 0; i < DRM_MAX_MINOR; i++) {
272	if ((fd = drmOpenMinor(i, 0)) >= 0) {
273	    buf = drmGetBusid(fd);
274	    if (buf && !strcmp(buf, busid)) {
275		drmFreeBusid(buf);
276		return fd;
277	    }
278	    if (buf) drmFreeBusid(buf);
279	    close(fd);
280	}
281    }
282    return -1;
283}
284
285static int drmOpenByName(const char *name)
286{
287    int           i;
288    int           fd;
289    drmVersionPtr version;
290
291    if (!drmAvailable()) {
292#if !defined(XFree86Server)
293	return -1;
294#else
295        /* try to load the kernel module now */
296        if (!xf86LoadKernelModule(name)) {
297            ErrorF("[drm] failed to load kernel module \"%s\"\n",
298		   name);
299            return -1;
300        }
301#endif
302    }
303
304    for (i = 0; i < DRM_MAX_MINOR; i++) {
305	if ((fd = drmOpenMinor(i, 1)) >= 0) {
306	    if ((version = drmGetVersion(fd))) {
307		if (!strcmp(version->name, name)) {
308		    drmFreeVersion(version);
309		    return fd;
310		}
311		drmFreeVersion(version);
312	    }
313	}
314    }
315
316#ifdef __linux__
317				/* Backward-compatibility /proc support */
318    for (i = 0; i < 8; i++) {
319	char proc_name[64], buf[512];
320	char *driver, *pt, *devstring;
321	int  retcode;
322
323	sprintf(proc_name, "/proc/dri/%d/name", i);
324	if ((fd = open(proc_name, 0, 0)) >= 0) {
325	    retcode = read(fd, buf, sizeof(buf)-1);
326	    close(fd);
327	    if (retcode) {
328		buf[retcode-1] = '\0';
329		for (driver = pt = buf; *pt && *pt != ' '; ++pt)
330		    ;
331		if (*pt) {	/* Device is next */
332		    *pt = '\0';
333		    if (!strcmp(driver, name)) { /* Match */
334			for (devstring = ++pt; *pt && *pt != ' '; ++pt)
335			    ;
336			if (*pt) { /* Found busid */
337			    return drmOpenByBusid(++pt);
338			} else {	/* No busid */
339			    return drmOpenDevice(strtol(devstring, NULL, 0),i);
340			}
341		    }
342		}
343	    }
344	}
345    }
346#endif
347
348    return -1;
349}
350
351/* drmOpen looks up the specified name and busid, and opens the device
352   found.  The entry in /dev/dri is created if necessary (and if root).
353   A file descriptor is returned.  On error, the return value is
354   negative. */
355
356int drmOpen(const char *name, const char *busid)
357{
358
359    if (busid) return drmOpenByBusid(busid);
360    return drmOpenByName(name);
361}
362
363void drmFreeVersion(drmVersionPtr v)
364{
365    if (!v) return;
366    if (v->name) drmFree(v->name);
367    if (v->date) drmFree(v->date);
368    if (v->desc) drmFree(v->desc);
369    drmFree(v);
370}
371
372static void drmFreeKernelVersion(drm_version_t *v)
373{
374    if (!v) return;
375    if (v->name) drmFree(v->name);
376    if (v->date) drmFree(v->date);
377    if (v->desc) drmFree(v->desc);
378    drmFree(v);
379}
380
381static void drmCopyVersion(drmVersionPtr d, const drm_version_t *s)
382{
383    d->version_major      = s->version_major;
384    d->version_minor      = s->version_minor;
385    d->version_patchlevel = s->version_patchlevel;
386    d->name_len           = s->name_len;
387    d->name               = drmStrdup(s->name);
388    d->date_len           = s->date_len;
389    d->date               = drmStrdup(s->date);
390    d->desc_len           = s->desc_len;
391    d->desc               = drmStrdup(s->desc);
392}
393
394/* drmVersion obtains the version information via an ioctl.  Similar
395 * information is available via /proc/dri. */
396
397drmVersionPtr drmGetVersion(int fd)
398{
399    drmVersionPtr retval;
400    drm_version_t *version = drmMalloc(sizeof(*version));
401
402				/* First, get the lengths */
403    version->name_len    = 0;
404    version->name        = NULL;
405    version->date_len    = 0;
406    version->date        = NULL;
407    version->desc_len    = 0;
408    version->desc        = NULL;
409
410    if (ioctl(fd, DRM_IOCTL_VERSION, version)) {
411	drmFreeKernelVersion(version);
412	return NULL;
413    }
414
415				/* Now, allocate space and get the data */
416    if (version->name_len)
417	version->name    = drmMalloc(version->name_len + 1);
418    if (version->date_len)
419	version->date    = drmMalloc(version->date_len + 1);
420    if (version->desc_len)
421	version->desc    = drmMalloc(version->desc_len + 1);
422
423    if (ioctl(fd, DRM_IOCTL_VERSION, version)) {
424	drmFreeKernelVersion(version);
425	return NULL;
426    }
427
428				/* The results might not be null-terminated
429                                   strings, so terminate them. */
430
431    if (version->name_len) version->name[version->name_len] = '\0';
432    if (version->date_len) version->date[version->date_len] = '\0';
433    if (version->desc_len) version->desc[version->desc_len] = '\0';
434
435				/* Now, copy it all back into the
436                                   client-visible data structure... */
437    retval = drmMalloc(sizeof(*retval));
438    drmCopyVersion(retval, version);
439    drmFreeKernelVersion(version);
440    return retval;
441}
442
443void drmFreeBusid(const char *busid)
444{
445    drmFree((void *)busid);
446}
447
448char *drmGetBusid(int fd)
449{
450    drm_unique_t u;
451
452    u.unique_len = 0;
453    u.unique     = NULL;
454
455    if (ioctl(fd, DRM_IOCTL_GET_UNIQUE, &u)) return NULL;
456    u.unique = drmMalloc(u.unique_len + 1);
457    if (ioctl(fd, DRM_IOCTL_GET_UNIQUE, &u)) return NULL;
458    u.unique[u.unique_len] = '\0';
459    return u.unique;
460}
461
462int drmSetBusid(int fd, const char *busid)
463{
464    drm_unique_t u;
465
466    u.unique     = (char *)busid;
467    u.unique_len = strlen(busid);
468
469    if (ioctl(fd, DRM_IOCTL_SET_UNIQUE, &u)) return -errno;
470    return 0;
471}
472
473int drmGetMagic(int fd, drmMagicPtr magic)
474{
475    drm_auth_t auth;
476
477    *magic = 0;
478    if (ioctl(fd, DRM_IOCTL_GET_MAGIC, &auth)) return -errno;
479    *magic = auth.magic;
480    return 0;
481}
482
483int drmAuthMagic(int fd, drmMagic magic)
484{
485    drm_auth_t auth;
486
487    auth.magic = magic;
488    if (ioctl(fd, DRM_IOCTL_AUTH_MAGIC, &auth)) return -errno;
489    return 0;
490}
491
492int drmAddMap(int fd,
493	      drmHandle offset,
494	      drmSize size,
495	      drmMapType type,
496	      drmMapFlags flags,
497	      drmHandlePtr handle)
498{
499    drm_map_t map;
500
501    map.offset  = offset;
502#ifdef __alpha__
503    /* Make sure we add the bus_base to all but shm */
504    if (type != DRM_SHM)
505	map.offset += BUS_BASE;
506#endif
507    map.size    = size;
508    map.handle  = 0;
509    map.type    = type;
510    map.flags   = flags;
511    if (ioctl(fd, DRM_IOCTL_ADD_MAP, &map)) return -errno;
512    if (handle) *handle = (drmHandle)map.handle;
513    return 0;
514}
515
516int drmAddBufs(int fd, int count, int size, drmBufDescFlags flags,
517	       int agp_offset)
518{
519    drm_buf_desc_t request;
520
521    request.count     = count;
522    request.size      = size;
523    request.low_mark  = 0;
524    request.high_mark = 0;
525    request.flags     = flags;
526    request.agp_start = agp_offset;
527
528    if (ioctl(fd, DRM_IOCTL_ADD_BUFS, &request)) return -errno;
529    return request.count;
530}
531
532int drmMarkBufs(int fd, double low, double high)
533{
534    drm_buf_info_t info;
535    int            i;
536
537    info.count = 0;
538    info.list  = NULL;
539
540    if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) return -EINVAL;
541
542    if (!info.count) return -EINVAL;
543
544    if (!(info.list = drmMalloc(info.count * sizeof(*info.list))))
545	return -ENOMEM;
546
547    if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) {
548	int retval = -errno;
549	drmFree(info.list);
550	return retval;
551    }
552
553    for (i = 0; i < info.count; i++) {
554	info.list[i].low_mark  = low  * info.list[i].count;
555	info.list[i].high_mark = high * info.list[i].count;
556	if (ioctl(fd, DRM_IOCTL_MARK_BUFS, &info.list[i])) {
557	    int retval = -errno;
558	    drmFree(info.list);
559	    return retval;
560	}
561    }
562    drmFree(info.list);
563
564    return 0;
565}
566
567int drmFreeBufs(int fd, int count, int *list)
568{
569    drm_buf_free_t request;
570
571    request.count = count;
572    request.list  = list;
573    if (ioctl(fd, DRM_IOCTL_FREE_BUFS, &request)) return -errno;
574    return 0;
575}
576
577int drmClose(int fd)
578{
579    unsigned long key    = drmGetKeyFromFd(fd);
580    drmHashEntry  *entry = drmGetEntry(fd);
581
582    drmHashDestroy(entry->tagTable);
583    entry->fd       = 0;
584    entry->f        = NULL;
585    entry->tagTable = NULL;
586
587    drmHashDelete(drmHashTable, key);
588    drmFree(entry);
589
590    return close(fd);
591}
592
593int drmMap(int fd,
594	   drmHandle handle,
595	   drmSize size,
596	   drmAddressPtr address)
597{
598    static unsigned long pagesize_mask = 0;
599
600    if (fd < 0) return -EINVAL;
601
602    if (!pagesize_mask)
603	pagesize_mask = getpagesize() - 1;
604
605    size = (size + pagesize_mask) & ~pagesize_mask;
606
607    *address = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, handle);
608    if (*address == MAP_FAILED) return -errno;
609    return 0;
610}
611
612int drmUnmap(drmAddress address, drmSize size)
613{
614    return munmap(address, size);
615}
616
617drmBufInfoPtr drmGetBufInfo(int fd)
618{
619    drm_buf_info_t info;
620    drmBufInfoPtr  retval;
621    int            i;
622
623    info.count = 0;
624    info.list  = NULL;
625
626    if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) return NULL;
627
628    if (info.count) {
629	if (!(info.list = drmMalloc(info.count * sizeof(*info.list))))
630	    return NULL;
631
632	if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) {
633	    drmFree(info.list);
634	    return NULL;
635	}
636				/* Now, copy it all back into the
637                                   client-visible data structure... */
638	retval = drmMalloc(sizeof(*retval));
639	retval->count = info.count;
640	retval->list  = drmMalloc(info.count * sizeof(*retval->list));
641	for (i = 0; i < info.count; i++) {
642	    retval->list[i].count     = info.list[i].count;
643	    retval->list[i].size      = info.list[i].size;
644	    retval->list[i].low_mark  = info.list[i].low_mark;
645	    retval->list[i].high_mark = info.list[i].high_mark;
646	}
647	drmFree(info.list);
648	return retval;
649    }
650    return NULL;
651}
652
653drmBufMapPtr drmMapBufs(int fd)
654{
655    drm_buf_map_t bufs;
656    drmBufMapPtr  retval;
657    int           i;
658
659    bufs.count = 0;
660    bufs.list  = NULL;
661    if (ioctl(fd, DRM_IOCTL_MAP_BUFS, &bufs)) return NULL;
662
663    if (bufs.count) {
664	if (!(bufs.list = drmMalloc(bufs.count * sizeof(*bufs.list))))
665	    return NULL;
666
667	if (ioctl(fd, DRM_IOCTL_MAP_BUFS, &bufs)) {
668	    drmFree(bufs.list);
669	    return NULL;
670	}
671				/* Now, copy it all back into the
672                                   client-visible data structure... */
673	retval = drmMalloc(sizeof(*retval));
674	retval->count = bufs.count;
675	retval->list  = drmMalloc(bufs.count * sizeof(*retval->list));
676	for (i = 0; i < bufs.count; i++) {
677	    retval->list[i].idx     = bufs.list[i].idx;
678	    retval->list[i].total   = bufs.list[i].total;
679	    retval->list[i].used    = 0;
680	    retval->list[i].address = bufs.list[i].address;
681	}
682	return retval;
683    }
684    return NULL;
685}
686
687int drmUnmapBufs(drmBufMapPtr bufs)
688{
689    int i;
690
691    for (i = 0; i < bufs->count; i++) {
692	munmap(bufs->list[i].address, bufs->list[i].total);
693    }
694    return 0;
695}
696
697#define DRM_DMA_RETRY		16
698
699int drmDMA(int fd, drmDMAReqPtr request)
700{
701    drm_dma_t dma;
702    int ret, i = 0;
703
704				/* Copy to hidden structure */
705    dma.context         = request->context;
706    dma.send_count      = request->send_count;
707    dma.send_indices    = request->send_list;
708    dma.send_sizes      = request->send_sizes;
709    dma.flags           = request->flags;
710    dma.request_count   = request->request_count;
711    dma.request_size    = request->request_size;
712    dma.request_indices = request->request_list;
713    dma.request_sizes   = request->request_sizes;
714
715    do {
716	ret = ioctl( fd, DRM_IOCTL_DMA, &dma );
717    } while ( ret && errno == EAGAIN && i++ < DRM_DMA_RETRY );
718
719    if ( ret == 0 ) {
720	request->granted_count = dma.granted_count;
721	return 0;
722    } else {
723	return -errno;
724    }
725}
726
727int drmGetLock(int fd, drmContext context, drmLockFlags flags)
728{
729    drm_lock_t lock;
730
731    lock.context = context;
732    lock.flags   = 0;
733    if (flags & DRM_LOCK_READY)      lock.flags |= _DRM_LOCK_READY;
734    if (flags & DRM_LOCK_QUIESCENT)  lock.flags |= _DRM_LOCK_QUIESCENT;
735    if (flags & DRM_LOCK_FLUSH)      lock.flags |= _DRM_LOCK_FLUSH;
736    if (flags & DRM_LOCK_FLUSH_ALL)  lock.flags |= _DRM_LOCK_FLUSH_ALL;
737    if (flags & DRM_HALT_ALL_QUEUES) lock.flags |= _DRM_HALT_ALL_QUEUES;
738    if (flags & DRM_HALT_CUR_QUEUES) lock.flags |= _DRM_HALT_CUR_QUEUES;
739
740    while (ioctl(fd, DRM_IOCTL_LOCK, &lock))
741	;
742    return 0;
743}
744
745int drmUnlock(int fd, drmContext context)
746{
747    drm_lock_t lock;
748
749    lock.context = context;
750    lock.flags   = 0;
751    return ioctl(fd, DRM_IOCTL_UNLOCK, &lock);
752}
753
754drmContextPtr drmGetReservedContextList(int fd, int *count)
755{
756    drm_ctx_res_t res;
757    drm_ctx_t     *list;
758    drmContextPtr retval;
759    int           i;
760
761    res.count    = 0;
762    res.contexts = NULL;
763    if (ioctl(fd, DRM_IOCTL_RES_CTX, &res)) return NULL;
764
765    if (!res.count) return NULL;
766
767    if (!(list   = drmMalloc(res.count * sizeof(*list)))) return NULL;
768    if (!(retval = drmMalloc(res.count * sizeof(*retval)))) {
769	drmFree(list);
770	return NULL;
771    }
772
773    res.contexts = list;
774    if (ioctl(fd, DRM_IOCTL_RES_CTX, &res)) return NULL;
775
776    for (i = 0; i < res.count; i++) retval[i] = list[i].handle;
777    drmFree(list);
778
779    *count = res.count;
780    return retval;
781}
782
783void drmFreeReservedContextList(drmContextPtr pt)
784{
785    drmFree(pt);
786}
787
788int drmCreateContext(int fd, drmContextPtr handle)
789{
790    drm_ctx_t ctx;
791
792    ctx.flags = 0;	/* Modified with functions below */
793    if (ioctl(fd, DRM_IOCTL_ADD_CTX, &ctx)) return -errno;
794    *handle = ctx.handle;
795    return 0;
796}
797
798int drmSwitchToContext(int fd, drmContext context)
799{
800    drm_ctx_t ctx;
801
802    ctx.handle = context;
803    if (ioctl(fd, DRM_IOCTL_SWITCH_CTX, &ctx)) return -errno;
804    return 0;
805}
806
807int drmSetContextFlags(int fd, drmContext context, drmContextFlags flags)
808{
809    drm_ctx_t ctx;
810
811				/* Context preserving means that no context
812                                   switched are done between DMA buffers
813                                   from one context and the next.  This is
814                                   suitable for use in the X server (which
815                                   promises to maintain hardware context,
816                                   or in the client-side library when
817                                   buffers are swapped on behalf of two
818                                   threads. */
819    ctx.handle = context;
820    ctx.flags  = 0;
821    if (flags & DRM_CONTEXT_PRESERVED) ctx.flags |= _DRM_CONTEXT_PRESERVED;
822    if (flags & DRM_CONTEXT_2DONLY)    ctx.flags |= _DRM_CONTEXT_2DONLY;
823    if (ioctl(fd, DRM_IOCTL_MOD_CTX, &ctx)) return -errno;
824    return 0;
825}
826
827int drmGetContextFlags(int fd, drmContext context, drmContextFlagsPtr flags)
828{
829    drm_ctx_t ctx;
830
831    ctx.handle = context;
832    if (ioctl(fd, DRM_IOCTL_GET_CTX, &ctx)) return -errno;
833    *flags = 0;
834    if (ctx.flags & _DRM_CONTEXT_PRESERVED) *flags |= DRM_CONTEXT_PRESERVED;
835    if (ctx.flags & _DRM_CONTEXT_2DONLY)    *flags |= DRM_CONTEXT_2DONLY;
836    return 0;
837}
838
839int drmDestroyContext(int fd, drmContext handle)
840{
841    drm_ctx_t ctx;
842    ctx.handle = handle;
843    if (ioctl(fd, DRM_IOCTL_RM_CTX, &ctx)) return -errno;
844    return 0;
845}
846
847int drmCreateDrawable(int fd, drmDrawablePtr handle)
848{
849    drm_draw_t draw;
850    if (ioctl(fd, DRM_IOCTL_ADD_DRAW, &draw)) return -errno;
851    *handle = draw.handle;
852    return 0;
853}
854
855int drmDestroyDrawable(int fd, drmDrawable handle)
856{
857    drm_draw_t draw;
858    draw.handle = handle;
859    if (ioctl(fd, DRM_IOCTL_RM_DRAW, &draw)) return -errno;
860    return 0;
861}
862
863int drmAgpAcquire(int fd)
864{
865    if (ioctl(fd, DRM_IOCTL_AGP_ACQUIRE, NULL)) return -errno;
866    return 0;
867}
868
869int drmAgpRelease(int fd)
870{
871    if (ioctl(fd, DRM_IOCTL_AGP_RELEASE, NULL)) return -errno;
872    return 0;
873}
874
875int drmAgpEnable(int fd, unsigned long mode)
876{
877    drm_agp_mode_t m;
878
879    m.mode = mode;
880    if (ioctl(fd, DRM_IOCTL_AGP_ENABLE, &m)) return -errno;
881    return 0;
882}
883
884int drmAgpAlloc(int fd, unsigned long size, unsigned long type,
885		unsigned long *address, unsigned long *handle)
886{
887    drm_agp_buffer_t b;
888    *handle = 0;
889    b.size   = size;
890    b.handle = 0;
891    b.type   = type;
892    if (ioctl(fd, DRM_IOCTL_AGP_ALLOC, &b)) return -errno;
893    if (address != 0UL) *address = b.physical;
894    *handle = b.handle;
895    return 0;
896}
897
898int drmAgpFree(int fd, unsigned long handle)
899{
900    drm_agp_buffer_t b;
901
902    b.size   = 0;
903    b.handle = handle;
904    if (ioctl(fd, DRM_IOCTL_AGP_FREE, &b)) return -errno;
905    return 0;
906}
907
908int drmAgpBind(int fd, unsigned long handle, unsigned long offset)
909{
910    drm_agp_binding_t b;
911
912    b.handle = handle;
913    b.offset = offset;
914    if (ioctl(fd, DRM_IOCTL_AGP_BIND, &b)) return -errno;
915    return 0;
916}
917
918int drmAgpUnbind(int fd, unsigned long handle)
919{
920    drm_agp_binding_t b;
921
922    b.handle = handle;
923    b.offset = 0;
924    if (ioctl(fd, DRM_IOCTL_AGP_UNBIND, &b)) return -errno;
925    return 0;
926}
927
928int drmAgpVersionMajor(int fd)
929{
930    drm_agp_info_t i;
931
932    if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return -errno;
933    return i.agp_version_major;
934}
935
936int drmAgpVersionMinor(int fd)
937{
938    drm_agp_info_t i;
939
940    if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return -errno;
941    return i.agp_version_minor;
942}
943
944unsigned long drmAgpGetMode(int fd)
945{
946    drm_agp_info_t i;
947
948    if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0;
949    return i.mode;
950}
951
952unsigned long drmAgpBase(int fd)
953{
954    drm_agp_info_t i;
955
956    if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0;
957    return i.aperture_base;
958}
959
960unsigned long drmAgpSize(int fd)
961{
962    drm_agp_info_t i;
963
964    if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0;
965    return i.aperture_size;
966}
967
968unsigned long drmAgpMemoryUsed(int fd)
969{
970    drm_agp_info_t i;
971
972    if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0;
973    return i.memory_used;
974}
975
976unsigned long drmAgpMemoryAvail(int fd)
977{
978    drm_agp_info_t i;
979
980    if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0;
981    return i.memory_allowed;
982}
983
984unsigned int drmAgpVendorId(int fd)
985{
986    drm_agp_info_t i;
987
988    if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0;
989    return i.id_vendor;
990}
991
992unsigned int drmAgpDeviceId(int fd)
993{
994    drm_agp_info_t i;
995
996    if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0;
997    return i.id_device;
998}
999
1000int drmError(int err, const char *label)
1001{
1002    switch (err) {
1003    case DRM_ERR_NO_DEVICE: fprintf(stderr, "%s: no device\n", label);   break;
1004    case DRM_ERR_NO_ACCESS: fprintf(stderr, "%s: no access\n", label);   break;
1005    case DRM_ERR_NOT_ROOT:  fprintf(stderr, "%s: not root\n", label);    break;
1006    case DRM_ERR_INVALID:   fprintf(stderr, "%s: invalid args\n", label);break;
1007    default:
1008	if (err < 0) err = -err;
1009	fprintf( stderr, "%s: error %d (%s)\n", label, err, strerror(err) );
1010	break;
1011    }
1012
1013    return 1;
1014}
1015
1016int drmCtlInstHandler(int fd, int irq)
1017{
1018    drm_control_t ctl;
1019
1020    ctl.func  = DRM_INST_HANDLER;
1021    ctl.irq   = irq;
1022    if (ioctl(fd, DRM_IOCTL_CONTROL, &ctl)) return -errno;
1023    return 0;
1024}
1025
1026int drmCtlUninstHandler(int fd)
1027{
1028    drm_control_t ctl;
1029
1030    ctl.func  = DRM_UNINST_HANDLER;
1031    ctl.irq   = 0;
1032    if (ioctl(fd, DRM_IOCTL_CONTROL, &ctl)) return -errno;
1033    return 0;
1034}
1035
1036int drmFinish(int fd, int context, drmLockFlags flags)
1037{
1038    drm_lock_t lock;
1039
1040    lock.context = context;
1041    lock.flags   = 0;
1042    if (flags & DRM_LOCK_READY)      lock.flags |= _DRM_LOCK_READY;
1043    if (flags & DRM_LOCK_QUIESCENT)  lock.flags |= _DRM_LOCK_QUIESCENT;
1044    if (flags & DRM_LOCK_FLUSH)      lock.flags |= _DRM_LOCK_FLUSH;
1045    if (flags & DRM_LOCK_FLUSH_ALL)  lock.flags |= _DRM_LOCK_FLUSH_ALL;
1046    if (flags & DRM_HALT_ALL_QUEUES) lock.flags |= _DRM_HALT_ALL_QUEUES;
1047    if (flags & DRM_HALT_CUR_QUEUES) lock.flags |= _DRM_HALT_CUR_QUEUES;
1048    if (ioctl(fd, DRM_IOCTL_FINISH, &lock)) return -errno;
1049    return 0;
1050}
1051
1052int drmGetInterruptFromBusID(int fd, int busnum, int devnum, int funcnum)
1053{
1054    drm_irq_busid_t p;
1055
1056    p.busnum  = busnum;
1057    p.devnum  = devnum;
1058    p.funcnum = funcnum;
1059    if (ioctl(fd, DRM_IOCTL_IRQ_BUSID, &p)) return -errno;
1060    return p.irq;
1061}
1062
1063int drmAddContextTag(int fd, drmContext context, void *tag)
1064{
1065    drmHashEntry  *entry = drmGetEntry(fd);
1066
1067    if (drmHashInsert(entry->tagTable, context, tag)) {
1068	drmHashDelete(entry->tagTable, context);
1069	drmHashInsert(entry->tagTable, context, tag);
1070    }
1071    return 0;
1072}
1073
1074int drmDelContextTag(int fd, drmContext context)
1075{
1076    drmHashEntry  *entry = drmGetEntry(fd);
1077
1078    return drmHashDelete(entry->tagTable, context);
1079}
1080
1081void *drmGetContextTag(int fd, drmContext context)
1082{
1083    drmHashEntry  *entry = drmGetEntry(fd);
1084    void          *value;
1085
1086    if (drmHashLookup(entry->tagTable, context, &value)) return NULL;
1087
1088    return value;
1089}
1090
1091int drmGetMap(int fd, int idx, drmHandle *offset, drmSize *size,
1092	      drmMapType *type, drmMapFlags *flags, drmHandle *handle,
1093	      int *mtrr)
1094{
1095    drm_map_t map;
1096
1097    map.offset = idx;
1098    if (ioctl(fd, DRM_IOCTL_GET_MAP, &map)) return -errno;
1099    *offset = map.offset;
1100    *size   = map.size;
1101    *type   = map.type;
1102    *flags  = map.flags;
1103    *handle = (unsigned long)map.handle;
1104    *mtrr   = map.mtrr;
1105    return 0;
1106}
1107
1108int drmGetClient(int fd, int idx, int *auth, int *pid, int *uid,
1109		 unsigned long *magic, unsigned long *iocs)
1110{
1111    drm_client_t client;
1112
1113    client.idx = idx;
1114    if (ioctl(fd, DRM_IOCTL_GET_CLIENT, &client)) return -errno;
1115    *auth      = client.auth;
1116    *pid       = client.pid;
1117    *uid       = client.uid;
1118    *magic     = client.magic;
1119    *iocs      = client.iocs;
1120    return 0;
1121}
1122
1123int drmGetStats(int fd, drmStatsT *stats)
1124{
1125    drm_stats_t s;
1126    int         i;
1127
1128    if (ioctl(fd, DRM_IOCTL_GET_STATS, &s)) return -errno;
1129
1130    stats->count = 0;
1131    memset(stats, 0, sizeof(*stats));
1132    if (s.count > sizeof(stats->data)/sizeof(stats->data[0]))
1133	return -1;
1134
1135#define SET_VALUE                              \
1136    stats->data[i].long_format = "%-20.20s";   \
1137    stats->data[i].rate_format = "%8.8s";      \
1138    stats->data[i].isvalue     = 1;            \
1139    stats->data[i].verbose     = 0
1140
1141#define SET_COUNT                              \
1142    stats->data[i].long_format = "%-20.20s";   \
1143    stats->data[i].rate_format = "%5.5s";      \
1144    stats->data[i].isvalue     = 0;            \
1145    stats->data[i].mult_names  = "kgm";        \
1146    stats->data[i].mult        = 1000;         \
1147    stats->data[i].verbose     = 0
1148
1149#define SET_BYTE                               \
1150    stats->data[i].long_format = "%-20.20s";   \
1151    stats->data[i].rate_format = "%5.5s";      \
1152    stats->data[i].isvalue     = 0;            \
1153    stats->data[i].mult_names  = "KGM";        \
1154    stats->data[i].mult        = 1024;         \
1155    stats->data[i].verbose     = 0
1156
1157
1158    stats->count = s.count;
1159    for (i = 0; i < s.count; i++) {
1160	stats->data[i].value = s.data[i].value;
1161	switch (s.data[i].type) {
1162	case _DRM_STAT_LOCK:
1163	    stats->data[i].long_name = "Lock";
1164	    stats->data[i].rate_name = "Lock";
1165	    SET_VALUE;
1166	    break;
1167	case _DRM_STAT_OPENS:
1168	    stats->data[i].long_name = "Opens";
1169	    stats->data[i].rate_name = "O";
1170	    SET_COUNT;
1171	    stats->data[i].verbose   = 1;
1172	    break;
1173	case _DRM_STAT_CLOSES:
1174	    stats->data[i].long_name = "Closes";
1175	    stats->data[i].rate_name = "Lock";
1176	    SET_COUNT;
1177	    stats->data[i].verbose   = 1;
1178	    break;
1179	case _DRM_STAT_IOCTLS:
1180	    stats->data[i].long_name = "Ioctls";
1181	    stats->data[i].rate_name = "Ioc/s";
1182	    SET_COUNT;
1183	    break;
1184	case _DRM_STAT_LOCKS:
1185	    stats->data[i].long_name = "Locks";
1186	    stats->data[i].rate_name = "Lck/s";
1187	    SET_COUNT;
1188	    break;
1189	case _DRM_STAT_UNLOCKS:
1190	    stats->data[i].long_name = "Unlocks";
1191	    stats->data[i].rate_name = "Unl/s";
1192	    SET_COUNT;
1193	    break;
1194	case _DRM_STAT_IRQ:
1195	    stats->data[i].long_name = "IRQs";
1196	    stats->data[i].rate_name = "IRQ/s";
1197	    SET_COUNT;
1198	    break;
1199	case _DRM_STAT_PRIMARY:
1200	    stats->data[i].long_name = "Primary Bytes";
1201	    stats->data[i].rate_name = "PB/s";
1202	    SET_BYTE;
1203	    break;
1204	case _DRM_STAT_SECONDARY:
1205	    stats->data[i].long_name = "Secondary Bytes";
1206	    stats->data[i].rate_name = "SB/s";
1207	    SET_BYTE;
1208	    break;
1209	case _DRM_STAT_DMA:
1210	    stats->data[i].long_name = "DMA";
1211	    stats->data[i].rate_name = "DMA/s";
1212	    SET_COUNT;
1213	    break;
1214	case _DRM_STAT_SPECIAL:
1215	    stats->data[i].long_name = "Special DMA";
1216	    stats->data[i].rate_name = "dma/s";
1217	    SET_COUNT;
1218	    break;
1219	case _DRM_STAT_MISSED:
1220	    stats->data[i].long_name = "Miss";
1221	    stats->data[i].rate_name = "Ms/s";
1222	    SET_COUNT;
1223	    break;
1224	case _DRM_STAT_VALUE:
1225	    stats->data[i].long_name = "Value";
1226	    stats->data[i].rate_name = "Value";
1227	    SET_VALUE;
1228	    break;
1229	case _DRM_STAT_BYTE:
1230	    stats->data[i].long_name = "Bytes";
1231	    stats->data[i].rate_name = "B/s";
1232	    SET_BYTE;
1233	    break;
1234	case _DRM_STAT_COUNT:
1235	default:
1236	    stats->data[i].long_name = "Count";
1237	    stats->data[i].rate_name = "Cnt/s";
1238	    SET_COUNT;
1239	    break;
1240	}
1241    }
1242    return 0;
1243}
1244
1245#if defined(XFree86Server) || defined(DRM_USE_MALLOC)
1246static void drmSIGIOHandler(int interrupt, void *closure)
1247{
1248    unsigned long key;
1249    void          *value;
1250    ssize_t       count;
1251    drm_ctx_t     ctx;
1252    typedef void  (*_drmCallback)(int, void *, void *);
1253    char          buf[256];
1254    drmContext    old;
1255    drmContext    new;
1256    void          *oldctx;
1257    void          *newctx;
1258    char          *pt;
1259    drmHashEntry  *entry;
1260
1261    if (!drmHashTable) return;
1262    if (drmHashFirst(drmHashTable, &key, &value)) {
1263	entry = value;
1264	do {
1265#if 0
1266	    fprintf(stderr, "Trying %d\n", entry->fd);
1267#endif
1268	    if ((count = read(entry->fd, buf, sizeof(buf)))) {
1269		buf[count] = '\0';
1270#if 0
1271		fprintf(stderr, "Got %s\n", buf);
1272#endif
1273
1274		for (pt = buf; *pt != ' '; ++pt); /* Find first space */
1275		++pt;
1276		old    = strtol(pt, &pt, 0);
1277		new    = strtol(pt, NULL, 0);
1278		oldctx = drmGetContextTag(entry->fd, old);
1279		newctx = drmGetContextTag(entry->fd, new);
1280#if 0
1281		fprintf(stderr, "%d %d %p %p\n", old, new, oldctx, newctx);
1282#endif
1283		((_drmCallback)entry->f)(entry->fd, oldctx, newctx);
1284		ctx.handle = new;
1285		ioctl(entry->fd, DRM_IOCTL_NEW_CTX, &ctx);
1286	    }
1287	} while (drmHashNext(drmHashTable, &key, &value));
1288    }
1289}
1290
1291int drmInstallSIGIOHandler(int fd, void (*f)(int, void *, void *))
1292{
1293    drmHashEntry     *entry;
1294
1295    entry     = drmGetEntry(fd);
1296    entry->f  = f;
1297
1298    return xf86InstallSIGIOHandler(fd, drmSIGIOHandler, 0);
1299}
1300
1301int drmRemoveSIGIOHandler(int fd)
1302{
1303    drmHashEntry     *entry = drmGetEntry(fd);
1304
1305    entry->f = NULL;
1306
1307    return xf86RemoveSIGIOHandler(fd);
1308}
1309#endif
1310