xf86drm.c revision e1dba5c3a73078dec24f07a6d685435677db94a4
1/* xf86drm.c -- User-level interface to DRM device
2 * Created: Tue Jan  5 08:16:21 1999 by faith@precisioninsight.com
3 * Revised: Mon Dec  6 11:34:13 1999 by faith@precisioninsight.com
4 *
5 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
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 * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/xf86drm.c,v 1.43 1999/08/04 18:14:43 faith Exp $
28 * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/xf86drm.c,v 1.5 1999/10/13 22:33:07 dawes Exp $
29 *
30 */
31
32#ifdef XFree86Server
33# include "xf86.h"
34# include "xf86_OSproc.h"
35# include "xf86_ansic.h"
36# include "xf86Priv.h"
37# define _DRM_MALLOC xalloc
38# define _DRM_FREE   xfree
39# ifndef XFree86LOADER
40#  include <sys/stat.h>
41#  include <sys/mman.h>
42# endif
43#else
44# include <stdio.h>
45# include <stdlib.h>
46# include <unistd.h>
47# include <string.h>
48# include <ctype.h>
49# include <fcntl.h>
50# include <errno.h>
51# include <signal.h>
52# include <sys/types.h>
53# include <sys/stat.h>
54# include <sys/ioctl.h>
55# include <sys/mman.h>
56# include <sys/time.h>
57# ifdef DRM_USE_MALLOC
58#  define _DRM_MALLOC malloc
59#  define _DRM_FREE   free
60extern int xf86InstallSIGIOHandler(int fd, void (*f)(int, void *), void *);
61extern int xf86RemoveSIGIOHandler(int fd);
62# else
63#  include <Xlibint.h>
64#  define _DRM_MALLOC Xmalloc
65#  define _DRM_FREE   Xfree
66# endif
67#endif
68
69/* Not all systems have MAP_FAILED defined */
70#ifndef MAP_FAILED
71#define MAP_FAILED ((void *)-1)
72#endif
73
74#include <sys/sysmacros.h>	/* for makedev() */
75#include "xf86drm.h"
76#include "drm.h"
77
78static void *drmHashTable = NULL; /* Context switch callbacks */
79
80typedef struct drmHashEntry {
81    int      fd;
82    void     (*f)(int, void *, void *);
83    void     *tagTable;
84} drmHashEntry;
85
86void *drmMalloc(int size)
87{
88    void *pt;
89    if ((pt = _DRM_MALLOC(size))) memset(pt, 0, size);
90    return pt;
91}
92
93void drmFree(void *pt)
94{
95    if (pt) _DRM_FREE(pt);
96}
97
98static char *drmStrdup(const char *s)
99{
100    return s ? strdup(s) : NULL;
101}
102
103
104static unsigned long drmGetKeyFromFd(int fd)
105{
106#ifdef XFree86LOADER
107    struct xf86stat st;
108#else
109    struct stat     st;
110#endif
111
112    st.st_rdev = 0;
113    fstat(fd, &st);
114    return st.st_rdev;
115}
116
117static drmHashEntry *drmGetEntry(int fd)
118{
119    unsigned long key = drmGetKeyFromFd(fd);
120    void          *value;
121    drmHashEntry  *entry;
122
123    if (!drmHashTable) drmHashTable = drmHashCreate();
124
125    if (drmHashLookup(drmHashTable, key, &value)) {
126	entry           = drmMalloc(sizeof(*entry));
127	entry->fd       = fd;
128	entry->f        = NULL;
129	entry->tagTable = drmHashCreate();
130	drmHashInsert(drmHashTable, key, entry);
131    } else {
132	entry = value;
133    }
134    return entry;
135}
136
137/* drm_open is used to open the /dev/drm device */
138
139static int drm_open(const char *file)
140{
141    int fd = open(file, O_RDWR, 0);
142
143    if (fd >= 0) return fd;
144    return -errno;
145}
146
147/* drmAvailable looks for /proc/drm, and returns 1 if it is present. */
148
149int drmAvailable(void)
150{
151    if (!access("/proc/graphics/0", R_OK)) return 1;
152    return 0;
153}
154
155static int drmOpenDevice(const char *path, long dev,
156			 mode_t mode, uid_t user, gid_t group)
157{
158#ifdef XFree86LOADER
159    struct xf86stat st;
160#else
161    struct stat     st;
162#endif
163
164    if (!stat(path, &st) && st.st_rdev == dev) return drm_open(path);
165
166    if (geteuid()) return DRM_ERR_NOT_ROOT;
167    remove(path);
168    if (mknod(path, S_IFCHR, dev)) {
169	remove(path);
170	return DRM_ERR_NOT_ROOT;
171    }
172    chown(path, user, group);
173    chmod(path, mode);
174    return drm_open(path);
175}
176
177static int drmOpenByBusid(const char *busid)
178{
179    int    i;
180    char   dev_name[64];
181    char   *buf;
182    int    fd;
183
184    for (i = 0; i < 8; i++) {
185	sprintf(dev_name, "/dev/graphics/card%d", i);
186	if ((fd = drm_open(dev_name)) >= 0) {
187	    buf = drmGetBusid(fd);
188	    if (buf && !strcmp(buf, busid)) {
189	      drmFreeBusid(buf);
190	      return fd;
191	    }
192	    if (buf) drmFreeBusid(buf);
193	    close(fd);
194	}
195    }
196    return -1;
197}
198
199static int drmOpenByName(const char *name)
200{
201    int    i;
202    char   proc_name[64];
203    char   dev_name[64];
204    char   buf[512];
205    mode_t mode   = DRM_DEV_MODE;
206    mode_t dirmode;
207    gid_t  group  = DRM_DEV_GID;
208    uid_t  user   = DRM_DEV_UID;
209    int    fd;
210    char   *pt;
211    char   *driver = NULL;
212    char   *devstring;
213    long   dev     = 0;
214    int    retcode;
215
216#if defined(XFree86Server)
217    mode  = xf86ConfigDRI.mode ? xf86ConfigDRI.mode : DRM_DEV_MODE;
218    group = xf86ConfigDRI.group ? xf86ConfigDRI.group : DRM_DEV_GID;
219#endif
220
221    if (!geteuid()) {
222	dirmode = mode;
223	if (dirmode & S_IRUSR) dirmode |= S_IXUSR;
224	if (dirmode & S_IRGRP) dirmode |= S_IXGRP;
225	if (dirmode & S_IROTH) dirmode |= S_IXOTH;
226	dirmode &= ~(S_IWGRP | S_IWOTH);
227	mkdir("/dev/graphics", 0);
228	chown("/dev/graphics", user, group);
229	chmod("/dev/graphics", dirmode);
230    }
231
232    for (i = 0; i < 8; i++) {
233	sprintf(proc_name, "/proc/graphics/%d/name", i);
234	sprintf(dev_name, "/dev/graphics/card%d", i);
235	if ((fd = open(proc_name, 0, 0)) >= 0) {
236	    retcode = read(fd, buf, sizeof(buf)-1);
237	    close(fd);
238	    if (retcode) {
239		buf[retcode-1] = '\0';
240		for (driver = pt = buf; *pt && *pt != ' '; ++pt)
241		    ;
242		if (*pt) {	/* Device is next */
243		    *pt = '\0';
244		    if (!strcmp(driver, name)) { /* Match */
245			for (devstring = ++pt; *pt && *pt != ' '; ++pt)
246			    ;
247			if (*pt) { /* Found busid */
248			  return drmOpenByBusid(++pt);
249			} else {	/* No busid */
250			  dev = strtol(devstring, NULL, 0);
251			  return drmOpenDevice(dev_name, dev,
252					       mode, user, group);
253			}
254		    }
255		}
256	    }
257	} else remove(dev_name);
258    }
259    return -1;
260}
261
262/* drmOpen looks up the specified name and busid, and opens the device
263   found.  The entry in /dev/graphics is created if necessary (and if root).
264   A file descriptor is returned.  On error, the return value is
265   negative. */
266
267int drmOpen(const char *name, const char *busid)
268{
269
270    if (busid) return drmOpenByBusid(busid);
271    return drmOpenByName(name);
272}
273
274void drmFreeVersion(drmVersionPtr v)
275{
276    if (!v) return;
277    if (v->name) drmFree(v->name);
278    if (v->date) drmFree(v->date);
279    if (v->desc) drmFree(v->desc);
280    drmFree(v);
281}
282
283static void drmFreeKernelVersion(drm_version_t *v)
284{
285    if (!v) return;
286    if (v->name) drmFree(v->name);
287    if (v->date) drmFree(v->date);
288    if (v->desc) drmFree(v->desc);
289    drmFree(v);
290}
291
292static void drmCopyVersion(drmVersionPtr d, drm_version_t *s)
293{
294    d->version_major      = s->version_major;
295    d->version_minor      = s->version_minor;
296    d->version_patchlevel = s->version_patchlevel;
297    d->name_len           = s->name_len;
298    d->name               = drmStrdup(s->name);
299    d->date_len           = s->date_len;
300    d->date               = drmStrdup(s->date);
301    d->desc_len           = s->desc_len;
302    d->desc               = drmStrdup(s->desc);
303}
304
305/* drmVersion obtains the version information via an ioctl.  Similar
306 * information is available via /proc/drm. */
307
308drmVersionPtr drmGetVersion(int fd)
309{
310    drmVersionPtr retval;
311    drm_version_t *version = drmMalloc(sizeof(*version));
312
313				/* First, get the lengths */
314    version->name_len    = 0;
315    version->name        = NULL;
316    version->date_len    = 0;
317    version->date        = NULL;
318    version->desc_len    = 0;
319    version->desc        = NULL;
320
321    if (ioctl(fd, DRM_IOCTL_VERSION, version)) {
322	drmFreeKernelVersion(version);
323	return NULL;
324    }
325
326				/* Now, allocate space and get the data */
327    if (version->name_len)
328	version->name    = drmMalloc(version->name_len + 1);
329    if (version->date_len)
330	version->date    = drmMalloc(version->date_len + 1);
331    if (version->desc_len)
332	version->desc    = drmMalloc(version->desc_len + 1);
333
334    if (ioctl(fd, DRM_IOCTL_VERSION, version)) {
335	drmFreeKernelVersion(version);
336	return NULL;
337    }
338
339				/* The results might not be null-terminated
340                                   strings, so terminate them. */
341
342    if (version->name_len) version->name[version->name_len] = '\0';
343    if (version->date_len) version->date[version->date_len] = '\0';
344    if (version->desc_len) version->desc[version->desc_len] = '\0';
345
346				/* Now, copy it all back into the
347                                   client-visible data structure... */
348    retval = drmMalloc(sizeof(*retval));
349    drmCopyVersion(retval, version);
350    drmFreeKernelVersion(version);
351    return retval;
352}
353
354void drmFreeBusid(const char *busid)
355{
356    drmFree((void *)busid);
357}
358
359char *drmGetBusid(int fd)
360{
361    drm_unique_t u;
362
363    u.unique_len = 0;
364    u.unique     = NULL;
365
366    if (ioctl(fd, DRM_IOCTL_GET_UNIQUE, &u)) return NULL;
367    u.unique = drmMalloc(u.unique_len + 1);
368    if (ioctl(fd, DRM_IOCTL_GET_UNIQUE, &u)) return NULL;
369    u.unique[u.unique_len] = '\0';
370    return u.unique;
371}
372
373int drmSetBusid(int fd, const char *busid)
374{
375    drm_unique_t u;
376
377    u.unique     = (char *)busid;
378    u.unique_len = strlen(busid);
379
380    if (ioctl(fd, DRM_IOCTL_SET_UNIQUE, &u)) return -errno;
381    return 0;
382}
383
384int drmGetMagic(int fd, drmMagicPtr magic)
385{
386    drm_auth_t auth;
387
388    *magic = 0;
389    if (ioctl(fd, DRM_IOCTL_GET_MAGIC, &auth)) return -errno;
390    *magic = auth.magic;
391    return 0;
392}
393
394int drmAuthMagic(int fd, drmMagic magic)
395{
396    drm_auth_t auth;
397
398    auth.magic = magic;
399    if (ioctl(fd, DRM_IOCTL_AUTH_MAGIC, &auth)) return -errno;
400    return 0;
401}
402
403int drmAddMap(int fd,
404	      drmHandle offset,
405	      drmSize size,
406	      drmMapType type,
407	      drmMapFlags flags,
408	      drmHandlePtr handle)
409{
410    drm_map_t map;
411
412    map.offset  = offset;
413    map.size    = size;
414    map.handle  = 0;
415    map.type    = type;
416    map.flags   = flags;
417    if (ioctl(fd, DRM_IOCTL_ADD_MAP, &map)) return -errno;
418    if (handle) *handle = (drmHandle)map.handle;
419    return 0;
420}
421
422int drmAddBufs(int fd, int count, int size, int flags)
423{
424    drm_buf_desc_t request;
425
426    request.count     = count;
427    request.size      = size;
428    request.low_mark  = 0;
429    request.high_mark = 0;
430    request.flags     = flags;
431    if (ioctl(fd, DRM_IOCTL_ADD_BUFS, &request)) return -errno;
432    return request.count;
433}
434
435int drmMarkBufs(int fd, double low, double high)
436{
437    drm_buf_info_t info;
438    int            i;
439
440    info.count = 0;
441    info.list  = NULL;
442
443    if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) return -EINVAL;
444
445    if (!info.count) return -EINVAL;
446
447    if (!(info.list = drmMalloc(info.count * sizeof(*info.list))))
448	return -ENOMEM;
449
450    if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) {
451	int retval = -errno;
452	drmFree(info.list);
453	return retval;
454    }
455
456    for (i = 0; i < info.count; i++) {
457	info.list[i].low_mark  = low  * info.list[i].count;
458	info.list[i].high_mark = high * info.list[i].count;
459	if (ioctl(fd, DRM_IOCTL_MARK_BUFS, &info.list[i])) {
460	    int retval = -errno;
461	    drmFree(info.list);
462	    return retval;
463	}
464    }
465    drmFree(info.list);
466
467    return 0;
468}
469
470int drmFreeBufs(int fd, int count, int *list)
471{
472    drm_buf_free_t request;
473
474    request.count = count;
475    request.list  = list;
476    if (ioctl(fd, DRM_IOCTL_FREE_BUFS, &request)) return -errno;
477    return 0;
478}
479
480int drmClose(int fd)
481{
482    unsigned long key    = drmGetKeyFromFd(fd);
483    drmHashEntry  *entry = drmGetEntry(fd);
484
485    drmHashDestroy(entry->tagTable);
486    entry->fd       = 0;
487    entry->f        = NULL;
488    entry->tagTable = NULL;
489
490    drmHashDelete(drmHashTable, key);
491    drmFree(entry);
492
493    return close(fd);
494}
495
496int drmMap(int fd,
497	   drmHandle handle,
498	   drmSize size,
499	   drmAddressPtr address)
500{
501    if (fd < 0) return -EINVAL;
502    *address = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, handle);
503    if (*address == MAP_FAILED) return -errno;
504    return 0;
505}
506
507int drmUnmap(drmAddress address, drmSize size)
508{
509    return munmap(address, size);
510}
511
512drmBufInfoPtr drmGetBufInfo(int fd)
513{
514    drm_buf_info_t info;
515    drmBufInfoPtr  retval;
516    int            i;
517
518    info.count = 0;
519    info.list  = NULL;
520
521    if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) return NULL;
522
523    if (info.count) {
524	if (!(info.list = drmMalloc(info.count * sizeof(*info.list))))
525	    return NULL;
526
527	if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) {
528	    drmFree(info.list);
529	    return NULL;
530	}
531				/* Now, copy it all back into the
532                                   client-visible data structure... */
533	retval = drmMalloc(sizeof(*retval));
534	retval->count = info.count;
535	retval->list  = drmMalloc(info.count * sizeof(*retval->list));
536	for (i = 0; i < info.count; i++) {
537	    retval->list[i].count     = info.list[i].count;
538	    retval->list[i].size      = info.list[i].size;
539	    retval->list[i].low_mark  = info.list[i].low_mark;
540	    retval->list[i].high_mark = info.list[i].high_mark;
541	}
542	drmFree(info.list);
543	return retval;
544    }
545    return NULL;
546}
547
548drmBufMapPtr drmMapBufs(int fd)
549{
550    drm_buf_map_t bufs;
551    drmBufMapPtr  retval;
552    int           i;
553
554    bufs.count = 0;
555    bufs.list  = NULL;
556    if (ioctl(fd, DRM_IOCTL_MAP_BUFS, &bufs)) return NULL;
557
558    if (bufs.count) {
559	if (!(bufs.list = drmMalloc(bufs.count * sizeof(*bufs.list))))
560	    return NULL;
561
562	if (ioctl(fd, DRM_IOCTL_MAP_BUFS, &bufs)) {
563	    drmFree(bufs.list);
564	    return NULL;
565	}
566				/* Now, copy it all back into the
567                                   client-visible data structure... */
568	retval = drmMalloc(sizeof(*retval));
569	retval->count = bufs.count;
570	retval->list  = drmMalloc(bufs.count * sizeof(*retval->list));
571	for (i = 0; i < bufs.count; i++) {
572	    retval->list[i].idx     = bufs.list[i].idx;
573	    retval->list[i].total   = bufs.list[i].total;
574	    retval->list[i].used    = 0;
575	    retval->list[i].address = bufs.list[i].address;
576	}
577	return retval;
578    }
579    return NULL;
580}
581
582int drmUnmapBufs(drmBufMapPtr bufs)
583{
584    int i;
585
586    for (i = 0; i < bufs->count; i++) {
587	munmap(bufs->list[i].address, bufs->list[i].total);
588    }
589    return 0;
590}
591
592int drmDMA(int fd, drmDMAReqPtr request)
593{
594    drm_dma_t dma;
595
596				/* Copy to hidden structure */
597    dma.context         = request->context;
598    dma.send_count      = request->send_count;
599    dma.send_indices    = request->send_list;
600    dma.send_sizes      = request->send_sizes;
601    dma.flags           = request->flags;
602    dma.request_count   = request->request_count;
603    dma.request_size    = request->request_size;
604    dma.request_indices = request->request_list;
605    dma.request_sizes   = request->request_sizes;
606    if (ioctl(fd, DRM_IOCTL_DMA, &dma)) return -errno;
607    request->granted_count = dma.granted_count;
608
609    return 0;
610}
611
612int drmGetLock(int fd, drmContext context, drmLockFlags flags)
613{
614    drm_lock_t lock;
615
616    lock.context = context;
617    lock.flags   = 0;
618    if (flags & DRM_LOCK_READY)      lock.flags |= _DRM_LOCK_READY;
619    if (flags & DRM_LOCK_QUIESCENT)  lock.flags |= _DRM_LOCK_QUIESCENT;
620    if (flags & DRM_LOCK_FLUSH)      lock.flags |= _DRM_LOCK_FLUSH;
621    if (flags & DRM_LOCK_FLUSH_ALL)  lock.flags |= _DRM_LOCK_FLUSH_ALL;
622    if (flags & DRM_HALT_ALL_QUEUES) lock.flags |= _DRM_HALT_ALL_QUEUES;
623    if (flags & DRM_HALT_CUR_QUEUES) lock.flags |= _DRM_HALT_CUR_QUEUES;
624
625    while (ioctl(fd, DRM_IOCTL_LOCK, &lock))
626	;
627    return 0;
628}
629
630int drmUnlock(int fd, drmContext context)
631{
632    drm_lock_t lock;
633
634    lock.context = context;
635    lock.flags   = 0;
636    return ioctl(fd, DRM_IOCTL_UNLOCK, &lock);
637}
638
639drmContextPtr drmGetReservedContextList(int fd, int *count)
640{
641    drm_ctx_res_t res;
642    drm_ctx_t     *list;
643    drmContextPtr retval;
644    int           i;
645
646    res.count    = 0;
647    res.contexts = NULL;
648    if (ioctl(fd, DRM_IOCTL_RES_CTX, &res)) return NULL;
649
650    if (!res.count) return NULL;
651
652    if (!(list   = drmMalloc(res.count * sizeof(*list)))) return NULL;
653    if (!(retval = drmMalloc(res.count * sizeof(*retval)))) {
654	drmFree(list);
655	return NULL;
656    }
657
658    res.contexts = list;
659    if (ioctl(fd, DRM_IOCTL_RES_CTX, &res)) return NULL;
660
661    for (i = 0; i < res.count; i++) retval[i] = list[i].handle;
662    drmFree(list);
663
664    *count = res.count;
665    return retval;
666}
667
668void drmFreeReservedContextList(drmContextPtr pt)
669{
670    drmFree(pt);
671}
672
673int drmCreateContext(int fd, drmContextPtr handle)
674{
675    drm_ctx_t ctx;
676
677    ctx.flags = 0;	/* Modified with functions below */
678    if (ioctl(fd, DRM_IOCTL_ADD_CTX, &ctx)) return -errno;
679    *handle = ctx.handle;
680    return 0;
681}
682
683int drmSwitchToContext(int fd, drmContext context)
684{
685    drm_ctx_t ctx;
686
687    ctx.handle = context;
688    if (ioctl(fd, DRM_IOCTL_SWITCH_CTX, &ctx)) return -errno;
689    return 0;
690}
691
692int drmSetContextFlags(int fd, drmContext context, drmContextFlags flags)
693{
694    drm_ctx_t ctx;
695
696				/* Context preserving means that no context
697                                   switched are done between DMA buffers
698                                   from one context and the next.  This is
699                                   suitable for use in the X server (which
700                                   promises to maintain hardware context,
701                                   or in the client-side library when
702                                   buffers are swapped on behalf of two
703                                   threads. */
704    ctx.handle = context;
705    ctx.flags  = 0;
706    if (flags & DRM_CONTEXT_PRESERVED) ctx.flags |= _DRM_CONTEXT_PRESERVED;
707    if (flags & DRM_CONTEXT_2DONLY)    ctx.flags |= _DRM_CONTEXT_2DONLY;
708    if (ioctl(fd, DRM_IOCTL_MOD_CTX, &ctx)) return -errno;
709    return 0;
710}
711
712int drmGetContextFlags(int fd, drmContext context, drmContextFlagsPtr flags)
713{
714    drm_ctx_t ctx;
715
716    ctx.handle = context;
717    if (ioctl(fd, DRM_IOCTL_GET_CTX, &ctx)) return -errno;
718    *flags = 0;
719    if (ctx.flags & _DRM_CONTEXT_PRESERVED) *flags |= DRM_CONTEXT_PRESERVED;
720    if (ctx.flags & _DRM_CONTEXT_2DONLY)    *flags |= DRM_CONTEXT_2DONLY;
721    return 0;
722}
723
724int drmDestroyContext(int fd, drmContext handle)
725{
726    drm_ctx_t ctx;
727    ctx.handle = handle;
728    if (ioctl(fd, DRM_IOCTL_RM_CTX, &ctx)) return -errno;
729    return 0;
730}
731
732int drmCreateDrawable(int fd, drmDrawablePtr handle)
733{
734    drm_draw_t draw;
735    if (ioctl(fd, DRM_IOCTL_ADD_DRAW, &draw)) return -errno;
736    *handle = draw.handle;
737    return 0;
738}
739
740int drmDestroyDrawable(int fd, drmDrawable handle)
741{
742    drm_draw_t draw;
743    draw.handle = handle;
744    if (ioctl(fd, DRM_IOCTL_RM_DRAW, &draw)) return -errno;
745    return 0;
746}
747
748int drmError(int err, const char *label)
749{
750    switch (err) {
751    case DRM_ERR_NO_DEVICE: fprintf(stderr, "%s: no device\n", label);   break;
752    case DRM_ERR_NO_ACCESS: fprintf(stderr, "%s: no access\n", label);   break;
753    case DRM_ERR_NOT_ROOT:  fprintf(stderr, "%s: not root\n", label);    break;
754    case DRM_ERR_INVALID:   fprintf(stderr, "%s: invalid args\n", label);break;
755    default:
756	if (err < 0) err = -err;
757	fprintf( stderr, "%s: error %d (%s)\n", label, err, strerror(err) );
758	break;
759    }
760
761    return 1;
762}
763
764int drmCtlInstHandler(int fd, int irq)
765{
766    drm_control_t ctl;
767
768    ctl.func  = DRM_INST_HANDLER;
769    ctl.irq   = irq;
770    if (ioctl(fd, DRM_IOCTL_CONTROL, &ctl)) return -errno;
771    return 0;
772}
773
774int drmCtlUninstHandler(int fd)
775{
776    drm_control_t ctl;
777
778    ctl.func  = DRM_UNINST_HANDLER;
779    ctl.irq   = 0;
780    if (ioctl(fd, DRM_IOCTL_CONTROL, &ctl)) return -errno;
781    return 0;
782}
783
784int drmFinish(int fd, int context, drmLockFlags flags)
785{
786    drm_lock_t lock;
787
788    lock.context = context;
789    lock.flags   = 0;
790    if (flags & DRM_LOCK_READY)      lock.flags |= _DRM_LOCK_READY;
791    if (flags & DRM_LOCK_QUIESCENT)  lock.flags |= _DRM_LOCK_QUIESCENT;
792    if (flags & DRM_LOCK_FLUSH)      lock.flags |= _DRM_LOCK_FLUSH;
793    if (flags & DRM_LOCK_FLUSH_ALL)  lock.flags |= _DRM_LOCK_FLUSH_ALL;
794    if (flags & DRM_HALT_ALL_QUEUES) lock.flags |= _DRM_HALT_ALL_QUEUES;
795    if (flags & DRM_HALT_CUR_QUEUES) lock.flags |= _DRM_HALT_CUR_QUEUES;
796    if (ioctl(fd, DRM_IOCTL_FINISH, &lock)) return -errno;
797    return 0;
798}
799
800int drmGetInterruptFromBusID(int fd, int busnum, int devnum, int funcnum)
801{
802    drm_irq_busid_t p;
803
804    p.busnum  = busnum;
805    p.devnum  = devnum;
806    p.funcnum = funcnum;
807    if (ioctl(fd, DRM_IOCTL_IRQ_BUSID, &p)) return -errno;
808    return p.irq;
809}
810
811int drmAddContextTag(int fd, drmContext context, void *tag)
812{
813    drmHashEntry  *entry = drmGetEntry(fd);
814
815    if (drmHashInsert(entry->tagTable, context, tag)) {
816	drmHashDelete(entry->tagTable, context);
817	drmHashInsert(entry->tagTable, context, tag);
818    }
819    return 0;
820}
821
822int drmDelContextTag(int fd, drmContext context)
823{
824    drmHashEntry  *entry = drmGetEntry(fd);
825
826    return drmHashDelete(entry->tagTable, context);
827}
828
829void *drmGetContextTag(int fd, drmContext context)
830{
831    drmHashEntry  *entry = drmGetEntry(fd);
832    void          *value;
833
834    if (drmHashLookup(entry->tagTable, context, &value)) return NULL;
835
836    return value;
837}
838
839#if defined(XFree86Server) || defined(DRM_USE_MALLOC)
840static void drmSIGIOHandler(int interrupt, void *closure)
841{
842    unsigned long key;
843    void          *value;
844    ssize_t       count;
845    drm_ctx_t     ctx;
846    typedef void  (*_drmCallback)(int, void *, void *);
847    char          buf[256];
848    drmContext    old;
849    drmContext    new;
850    void          *oldctx;
851    void          *newctx;
852    char          *pt;
853    drmHashEntry  *entry;
854
855    if (!drmHashTable) return;
856    if (drmHashFirst(drmHashTable, &key, &value)) {
857	entry = value;
858	do {
859#if 0
860	    fprintf(stderr, "Trying %d\n", entry->fd);
861#endif
862	    if ((count = read(entry->fd, buf, sizeof(buf)))) {
863		buf[count] = '\0';
864#if 0
865		fprintf(stderr, "Got %s\n", buf);
866#endif
867
868		for (pt = buf; *pt != ' '; ++pt); /* Find first space */
869		++pt;
870		old    = strtol(pt, &pt, 0);
871		new    = strtol(pt, NULL, 0);
872		oldctx = drmGetContextTag(entry->fd, old);
873		newctx = drmGetContextTag(entry->fd, new);
874#if 0
875		fprintf(stderr, "%d %d %p %p\n", old, new, oldctx, newctx);
876#endif
877		((_drmCallback)entry->f)(entry->fd, oldctx, newctx);
878		ctx.handle = new;
879		ioctl(entry->fd, DRM_IOCTL_NEW_CTX, &ctx);
880	    }
881	} while (drmHashNext(drmHashTable, &key, &value));
882    }
883}
884
885int drmInstallSIGIOHandler(int fd, void (*f)(int, void *, void *))
886{
887    drmHashEntry     *entry;
888
889    entry     = drmGetEntry(fd);
890    entry->f  = f;
891
892    return xf86InstallSIGIOHandler(fd, drmSIGIOHandler, 0);
893}
894
895int drmRemoveSIGIOHandler(int fd)
896{
897    drmHashEntry     *entry = drmGetEntry(fd);
898
899    entry->f = NULL;
900
901    return xf86RemoveSIGIOHandler(fd);
902}
903#endif
904