summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux-core/i810_dma.c12
-rw-r--r--linux-core/i810_drv.c4
-rw-r--r--linux-core/mga_drv.c89
-rw-r--r--linux/i810_dma.c12
-rw-r--r--linux/i810_drv.c4
-rw-r--r--linux/mga_dma.c9
-rw-r--r--linux/mga_drv.c89
-rw-r--r--linux/mga_drv.h1
8 files changed, 170 insertions, 50 deletions
diff --git a/linux-core/i810_dma.c b/linux-core/i810_dma.c
index 30fda5b8..6ac4d2ee 100644
--- a/linux-core/i810_dma.c
+++ b/linux-core/i810_dma.c
@@ -714,17 +714,17 @@ void i810_reclaim_buffers(drm_device_t *dev, pid_t pid)
if (!dma) return;
if(dev->dev_private == NULL) return;
-
+ if(dma->buflist == NULL) return;
i810_flush_queue(dev);
for (i = 0; i < dma->buf_count; i++) {
drm_buf_t *buf = dma->buflist[ i ];
drm_i810_buf_priv_t *buf_priv = buf->dev_private;
-
- if (buf->pid == pid) {
- /* Only buffers that need to get reclaimed ever
- * get set to free */
- if(buf_priv == NULL) return;
+
+ /* Only buffers that need to get reclaimed ever
+ * get set to free
+ */
+ if (buf->pid == pid && buf_priv) {
cmpxchg(buf_priv->in_use,
I810_BUF_USED, I810_BUF_FREE);
}
diff --git a/linux-core/i810_drv.c b/linux-core/i810_drv.c
index d3b35c49..f1c83250 100644
--- a/linux-core/i810_drv.c
+++ b/linux-core/i810_drv.c
@@ -499,7 +499,7 @@ int i810_release(struct inode *inode, struct file *filp)
DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
current->pid, dev->device, dev->open_count);
- if (_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+ if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
&& dev->lock.pid == current->pid) {
i810_reclaim_buffers(dev, priv->pid);
DRM_ERROR("Process %d dead, freeing lock for context %d\n",
@@ -513,7 +513,7 @@ int i810_release(struct inode *inode, struct file *filp)
hardware at this point, possibly
processed via a callback to the X
server. */
- } else {
+ } else if (dev->lock.hw_lock) {
/* The lock is required to reclaim buffers */
DECLARE_WAITQUEUE(entry, current);
add_wait_queue(&dev->lock.lock_queue, &entry);
diff --git a/linux-core/mga_drv.c b/linux-core/mga_drv.c
index 48041354..82dda64d 100644
--- a/linux-core/mga_drv.c
+++ b/linux-core/mga_drv.c
@@ -510,27 +510,86 @@ int mga_release(struct inode *inode, struct file *filp)
drm_device_t *dev = priv->dev;
int retcode = 0;
- DRM_DEBUG("open_count = %d\n", dev->open_count);
- if (!(retcode = drm_release(inode, filp))) {
- MOD_DEC_USE_COUNT;
- atomic_inc(&dev->total_close);
- spin_lock(&dev->count_lock);
- if (!--dev->open_count) {
- if (atomic_read(&dev->ioctl_count) || dev->blocked) {
- DRM_ERROR("Device busy: %d %d\n",
- atomic_read(&dev->ioctl_count),
- dev->blocked);
- spin_unlock(&dev->count_lock);
- return -EBUSY;
+ DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+ current->pid, dev->device, dev->open_count);
+
+ if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+ && dev->lock.pid == current->pid) {
+ mga_reclaim_buffers(dev, priv->pid);
+ DRM_ERROR("Process %d dead, freeing lock for context %d\n",
+ current->pid,
+ _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+ drm_lock_free(dev,
+ &dev->lock.hw_lock->lock,
+ _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+
+ /* FIXME: may require heavy-handed reset of
+ hardware at this point, possibly
+ processed via a callback to the X
+ server. */
+ } else if (dev->lock.hw_lock) {
+ /* The lock is required to reclaim buffers */
+ DECLARE_WAITQUEUE(entry, current);
+ add_wait_queue(&dev->lock.lock_queue, &entry);
+ for (;;) {
+ if (!dev->lock.hw_lock) {
+ /* Device has been unregistered */
+ retcode = -EINTR;
+ break;
+ }
+ if (drm_lock_take(&dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT)) {
+ dev->lock.pid = priv->pid;
+ dev->lock.lock_time = jiffies;
+ atomic_inc(&dev->total_locks);
+ break; /* Got lock */
+ }
+ /* Contention */
+ atomic_inc(&dev->total_sleeps);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (signal_pending(current)) {
+ retcode = -ERESTARTSYS;
+ break;
}
- spin_unlock(&dev->count_lock);
- return mga_takedown(dev);
}
- spin_unlock(&dev->count_lock);
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev->lock.lock_queue, &entry);
+ if(!retcode) {
+ mga_reclaim_buffers(dev, priv->pid);
+ drm_lock_free(dev, &dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT);
+ }
+ }
+ drm_fasync(-1, filp, 0);
+
+ down(&dev->struct_sem);
+ if (priv->prev) priv->prev->next = priv->next;
+ else dev->file_first = priv->next;
+ if (priv->next) priv->next->prev = priv->prev;
+ else dev->file_last = priv->prev;
+ up(&dev->struct_sem);
+
+ drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+ MOD_DEC_USE_COUNT;
+ atomic_inc(&dev->total_close);
+ spin_lock(&dev->count_lock);
+ if (!--dev->open_count) {
+ if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+ DRM_ERROR("Device busy: %d %d\n",
+ atomic_read(&dev->ioctl_count),
+ dev->blocked);
+ spin_unlock(&dev->count_lock);
+ return -EBUSY;
+ }
+ spin_unlock(&dev->count_lock);
+ return mga_takedown(dev);
}
+ spin_unlock(&dev->count_lock);
return retcode;
}
+
/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
int mga_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
diff --git a/linux/i810_dma.c b/linux/i810_dma.c
index 30fda5b8..6ac4d2ee 100644
--- a/linux/i810_dma.c
+++ b/linux/i810_dma.c
@@ -714,17 +714,17 @@ void i810_reclaim_buffers(drm_device_t *dev, pid_t pid)
if (!dma) return;
if(dev->dev_private == NULL) return;
-
+ if(dma->buflist == NULL) return;
i810_flush_queue(dev);
for (i = 0; i < dma->buf_count; i++) {
drm_buf_t *buf = dma->buflist[ i ];
drm_i810_buf_priv_t *buf_priv = buf->dev_private;
-
- if (buf->pid == pid) {
- /* Only buffers that need to get reclaimed ever
- * get set to free */
- if(buf_priv == NULL) return;
+
+ /* Only buffers that need to get reclaimed ever
+ * get set to free
+ */
+ if (buf->pid == pid && buf_priv) {
cmpxchg(buf_priv->in_use,
I810_BUF_USED, I810_BUF_FREE);
}
diff --git a/linux/i810_drv.c b/linux/i810_drv.c
index d3b35c49..f1c83250 100644
--- a/linux/i810_drv.c
+++ b/linux/i810_drv.c
@@ -499,7 +499,7 @@ int i810_release(struct inode *inode, struct file *filp)
DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
current->pid, dev->device, dev->open_count);
- if (_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+ if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
&& dev->lock.pid == current->pid) {
i810_reclaim_buffers(dev, priv->pid);
DRM_ERROR("Process %d dead, freeing lock for context %d\n",
@@ -513,7 +513,7 @@ int i810_release(struct inode *inode, struct file *filp)
hardware at this point, possibly
processed via a callback to the X
server. */
- } else {
+ } else if (dev->lock.hw_lock) {
/* The lock is required to reclaim buffers */
DECLARE_WAITQUEUE(entry, current);
add_wait_queue(&dev->lock.lock_queue, &entry);
diff --git a/linux/mga_dma.c b/linux/mga_dma.c
index 59cbad6b..371706f4 100644
--- a/linux/mga_dma.c
+++ b/linux/mga_dma.c
@@ -1016,6 +1016,7 @@ void mga_reclaim_buffers(drm_device_t *dev, pid_t pid)
if (!dma) return;
if(dev->dev_private == NULL) return;
+ if(dma->buflist == NULL) return;
mga_flush_queue(dev);
@@ -1023,10 +1024,10 @@ void mga_reclaim_buffers(drm_device_t *dev, pid_t pid)
drm_buf_t *buf = dma->buflist[ i ];
drm_mga_buf_priv_t *buf_priv = buf->dev_private;
- if (buf->pid == pid) {
- if(buf_priv == NULL) return;
- /* Only buffers that need to get reclaimed ever
- * get set to free */
+ /* Only buffers that need to get reclaimed ever
+ * get set to free
+ */
+ if (buf->pid == pid && buf_priv) {
if(buf_priv->my_freelist->age == MGA_BUF_USED)
buf_priv->my_freelist->age = MGA_BUF_FREE;
}
diff --git a/linux/mga_drv.c b/linux/mga_drv.c
index 48041354..82dda64d 100644
--- a/linux/mga_drv.c
+++ b/linux/mga_drv.c
@@ -510,27 +510,86 @@ int mga_release(struct inode *inode, struct file *filp)
drm_device_t *dev = priv->dev;
int retcode = 0;
- DRM_DEBUG("open_count = %d\n", dev->open_count);
- if (!(retcode = drm_release(inode, filp))) {
- MOD_DEC_USE_COUNT;
- atomic_inc(&dev->total_close);
- spin_lock(&dev->count_lock);
- if (!--dev->open_count) {
- if (atomic_read(&dev->ioctl_count) || dev->blocked) {
- DRM_ERROR("Device busy: %d %d\n",
- atomic_read(&dev->ioctl_count),
- dev->blocked);
- spin_unlock(&dev->count_lock);
- return -EBUSY;
+ DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+ current->pid, dev->device, dev->open_count);
+
+ if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+ && dev->lock.pid == current->pid) {
+ mga_reclaim_buffers(dev, priv->pid);
+ DRM_ERROR("Process %d dead, freeing lock for context %d\n",
+ current->pid,
+ _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+ drm_lock_free(dev,
+ &dev->lock.hw_lock->lock,
+ _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+
+ /* FIXME: may require heavy-handed reset of
+ hardware at this point, possibly
+ processed via a callback to the X
+ server. */
+ } else if (dev->lock.hw_lock) {
+ /* The lock is required to reclaim buffers */
+ DECLARE_WAITQUEUE(entry, current);
+ add_wait_queue(&dev->lock.lock_queue, &entry);
+ for (;;) {
+ if (!dev->lock.hw_lock) {
+ /* Device has been unregistered */
+ retcode = -EINTR;
+ break;
+ }
+ if (drm_lock_take(&dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT)) {
+ dev->lock.pid = priv->pid;
+ dev->lock.lock_time = jiffies;
+ atomic_inc(&dev->total_locks);
+ break; /* Got lock */
+ }
+ /* Contention */
+ atomic_inc(&dev->total_sleeps);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (signal_pending(current)) {
+ retcode = -ERESTARTSYS;
+ break;
}
- spin_unlock(&dev->count_lock);
- return mga_takedown(dev);
}
- spin_unlock(&dev->count_lock);
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev->lock.lock_queue, &entry);
+ if(!retcode) {
+ mga_reclaim_buffers(dev, priv->pid);
+ drm_lock_free(dev, &dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT);
+ }
+ }
+ drm_fasync(-1, filp, 0);
+
+ down(&dev->struct_sem);
+ if (priv->prev) priv->prev->next = priv->next;
+ else dev->file_first = priv->next;
+ if (priv->next) priv->next->prev = priv->prev;
+ else dev->file_last = priv->prev;
+ up(&dev->struct_sem);
+
+ drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+ MOD_DEC_USE_COUNT;
+ atomic_inc(&dev->total_close);
+ spin_lock(&dev->count_lock);
+ if (!--dev->open_count) {
+ if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+ DRM_ERROR("Device busy: %d %d\n",
+ atomic_read(&dev->ioctl_count),
+ dev->blocked);
+ spin_unlock(&dev->count_lock);
+ return -EBUSY;
+ }
+ spin_unlock(&dev->count_lock);
+ return mga_takedown(dev);
}
+ spin_unlock(&dev->count_lock);
return retcode;
}
+
/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
int mga_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
diff --git a/linux/mga_drv.h b/linux/mga_drv.h
index 43deb613..801e707d 100644
--- a/linux/mga_drv.h
+++ b/linux/mga_drv.h
@@ -131,6 +131,7 @@ extern unsigned int mga_create_sync_tag(drm_device_t *dev);
extern drm_buf_t *mga_freelist_get(drm_device_t *dev);
extern int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf);
extern int mga_advance_primary(drm_device_t *dev);
+extern void mga_reclaim_buffers(drm_device_t *dev, pid_t pid);
/* mga_bufs.c */
1'>801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
/* drm_bufs.h -- Generic buffer template -*- linux-c -*-
 * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com
 *
 * Copyright 1999, 2000 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
 * VA LINUX SYSTEMS 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.
 *
 * Authors:
 *    Rickard E. (Rik) Faith <faith@valinux.com>
 *    Gareth Hughes <gareth@valinux.com>
 */

#define __NO_VERSION__
#include <linux/vmalloc.h>
#include "drmP.h"

#ifndef __HAVE_PCI_DMA
#define __HAVE_PCI_DMA		0
#endif

#ifndef __HAVE_SG
#define __HAVE_SG		0
#endif

#ifndef DRIVER_BUF_PRIV_T
#define DRIVER_BUF_PRIV_T		u32
#endif
#ifndef DRIVER_AGP_BUFFERS_MAP
#if __HAVE_AGP && __HAVE_DMA
#error "You must define DRIVER_AGP_BUFFERS_MAP()"
#else
#define DRIVER_AGP_BUFFERS_MAP( dev )	NULL
#endif
#endif

/*
 * Compute order.  Can be made faster.
 */
int DRM(order)( unsigned long size )
{
	int order;
	unsigned long tmp;

	for ( order = 0, tmp = size ; tmp >>= 1 ; ++order );

	if ( size & ~(1 << order) )
		++order;

	return order;
}

int DRM(addmap)( struct inode *inode, struct file *filp,
		 unsigned int cmd, unsigned long arg )
{
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->dev;
	drm_map_t *map;
	drm_map_list_t *list;

	if ( !(filp->f_mode & 3) ) return -EACCES; /* Require read/write */

	map = DRM(alloc)( sizeof(*map), DRM_MEM_MAPS );
	if ( !map )
		return -ENOMEM;

	if ( copy_from_user( map, (drm_map_t *)arg, sizeof(*map) ) ) {
		DRM(free)( map, sizeof(*map), DRM_MEM_MAPS );
		return -EFAULT;
	}

	/* Only allow shared memory to be removable since we only keep enough
	 * book keeping information about shared memory to allow for removal
	 * when processes fork.
	 */
	if ( (map->flags & _DRM_REMOVABLE) && map->type != _DRM_SHM ) {
		DRM(free)( map, sizeof(*map), DRM_MEM_MAPS );
		return -EINVAL;
	}
	DRM_DEBUG( "offset = 0x%08lx, size = 0x%08lx, type = %d\n",
		   map->offset, map->size, map->type );
	if ( (map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK)) ) {
		DRM(free)( map, sizeof(*map), DRM_MEM_MAPS );
		return -EINVAL;
	}
	map->mtrr   = -1;
	map->handle = 0;

	switch ( map->type ) {
	case _DRM_REGISTERS:
	case _DRM_FRAME_BUFFER:
#if !defined(__sparc__) && !defined(__alpha__)
		if ( map->offset + map->size < map->offset ||
		     map->offset < virt_to_phys(high_memory) ) {
			DRM(free)( map, sizeof(*map), DRM_MEM_MAPS );
			return -EINVAL;
		}
#endif
#ifdef __alpha__
		map->offset += dev->hose->mem_space->start;
#endif
#if __REALLY_HAVE_MTRR
		if ( map->type == _DRM_FRAME_BUFFER ||
		     (map->flags & _DRM_WRITE_COMBINING) ) {
			map->mtrr = mtrr_add( map->offset, map->size,
					      MTRR_TYPE_WRCOMB, 1 );
		}
#endif
		map->handle = DRM(ioremap)( map->offset, map->size );
		break;

	case _DRM_SHM:
		map->handle = vmalloc_32(map->size);
		DRM_DEBUG( "%ld %d %p\n",
			   map->size, DRM(order)( map->size ), map->handle );
		if ( !map->handle ) {
			DRM(free)( map, sizeof(*map), DRM_MEM_MAPS );
			return -ENOMEM;
		}
		map->offset = (unsigned long)map->handle;
		if ( map->flags & _DRM_CONTAINS_LOCK ) {
			dev->sigdata.lock =
			dev->lock.hw_lock = map->handle; /* Pointer to lock */
		}
		break;
#if __REALLY_HAVE_AGP
	case _DRM_AGP:
#ifdef __alpha__
		map->offset += dev->hose->mem_space->start;
#endif
		map->offset = map->offset + dev->agp->base;
		map->mtrr   = dev->agp->agp_mtrr; /* for getmap */
		break;
#endif
	case _DRM_SCATTER_GATHER:
		if (!dev->sg) {
			DRM(free)(map, sizeof(*map), DRM_MEM_MAPS);
			return -EINVAL;
		}
		map->offset = map->offset + dev->sg->handle;
		break;

	default:
		DRM(free)( map, sizeof(*map), DRM_MEM_MAPS );
		return -EINVAL;
	}

	list = DRM(alloc)(sizeof(*list), DRM_MEM_MAPS);
	if(!list) {
		DRM(free)(map, sizeof(*map), DRM_MEM_MAPS);
		return -EINVAL;
	}
	memset(list, 0, sizeof(*list));
	list->map = map;

	down(&dev->struct_sem);
	list_add(&list->head, &dev->maplist->head);
 	up(&dev->struct_sem);

	if ( copy_to_user( (drm_map_t *)arg, map, sizeof(*map) ) )
		return -EFAULT;
	if ( map->type != _DRM_SHM ) {
		if ( copy_to_user( &((drm_map_t *)arg)->handle,
				   &map->offset,
				   sizeof(map->offset) ) )
			return -EFAULT;
	}
	return 0;
}


/* Remove a map private from list and deallocate resources if the mapping
 * isn't in use.
 */

int DRM(rmmap)(struct inode *inode, struct file *filp,
	       unsigned int cmd, unsigned long arg)
{
	drm_file_t	*priv	= filp->private_data;
	drm_device_t	*dev	= priv->dev;
	struct list_head *list;
	drm_map_list_t *r_list = NULL;
	drm_vma_entry_t *pt, *prev;
	drm_map_t *map;
	drm_map_t request;
	int found_maps = 0;

	if (copy_from_user(&request, (drm_map_t *)arg,
			   sizeof(request))) {
		return -EFAULT;
	}

	down(&dev->struct_sem);
	list = &dev->maplist->head;
	list_for_each(list, &dev->maplist->head) {
		r_list = (drm_map_list_t *) list;

		if(r_list->map &&
		   r_list->map->handle == request.handle &&
		   r_list->map->flags & _DRM_REMOVABLE) break;
	}

	/* List has wrapped around to the head pointer, or its empty we didn't
	 * find anything.
	 */
	if(list == (&dev->maplist->head)) {
		up(&dev->struct_sem);
		return -EINVAL;
	}
	map = r_list->map;
	list_del(list);
	DRM(free)(list, sizeof(*list), DRM_MEM_MAPS);

	for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) {
		if (pt->vma->vm_private_data == map) found_maps++;
	}

	if(!found_maps) {
		switch (map->type) {
		case _DRM_REGISTERS:
		case _DRM_FRAME_BUFFER:
#if __REALLY_HAVE_MTRR
			if (map->mtrr >= 0) {
				int retcode;
				retcode = mtrr_del(map->mtrr,
						   map->offset,
						   map->size);
				DRM_DEBUG("mtrr_del = %d\n", retcode);
			}
#endif
			DRM(ioremapfree)(map->handle, map->size);
			break;
		case _DRM_SHM:
			vfree(map->handle);
			break;
		case _DRM_AGP:
		case _DRM_SCATTER_GATHER:
			break;
		}
		DRM(free)(map, sizeof(*map), DRM_MEM_MAPS);
	}
	up(&dev->struct_sem);
	return 0;
}

#if __HAVE_DMA


static void DRM(cleanup_buf_error)(drm_buf_entry_t *entry)
{
	int i;

	if (entry->seg_count) {
		for (i = 0; i < entry->seg_count; i++) {
			DRM(free_pages)(entry->seglist[i],
					entry->page_order,
					DRM_MEM_DMA);
		}
		DRM(free)(entry->seglist,
			  entry->seg_count *
			  sizeof(*entry->seglist),
			  DRM_MEM_SEGS);

		entry->seg_count = 0;
	}

   	if(entry->buf_count) {
	   	for(i = 0; i < entry->buf_count; i++) {
			if(entry->buflist[i].dev_private) {
				DRM(free)(entry->buflist[i].dev_private,
					  entry->buflist[i].dev_priv_size,
					  DRM_MEM_BUFS);
			}
		}
		DRM(free)(entry->buflist,
			  entry->buf_count *
			  sizeof(*entry->buflist),
			  DRM_MEM_BUFS);

#if __HAVE_DMA_FREELIST
	   	DRM(freelist_destroy)(&entry->freelist);
#endif

		entry->buf_count = 0;
	}
}

#if __REALLY_HAVE_AGP
int DRM(addbufs_agp)( struct inode *inode, struct file *filp,
		      unsigned int cmd, unsigned long arg )
{
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->dev;
	drm_device_dma_t *dma = dev->dma;
	drm_buf_desc_t request;
	drm_buf_entry_t *entry;
	drm_buf_t *buf;
	unsigned long offset;
	unsigned long agp_offset;
	int count;
	int order;
	int size;
	int alignment;
	int page_order;
	int total;
	int byte_count;
	int i;
	drm_buf_t **temp_buflist;

	if ( !dma ) return -EINVAL;

	if ( copy_from_user( &request, (drm_buf_desc_t *)arg,
			     sizeof(request) ) )
		return -EFAULT;

	count = request.count;
	order = DRM(order)( request.size );
	size = 1 << order;

	alignment  = (request.flags & _DRM_PAGE_ALIGN)
		? PAGE_ALIGN(size) : size;
	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
	total = PAGE_SIZE << page_order;

	byte_count = 0;
	agp_offset = dev->agp->base + request.agp_start;

	DRM_DEBUG( "count:      %d\n",  count );
	DRM_DEBUG( "order:      %d\n",  order );
	DRM_DEBUG( "size:       %d\n",  size );
	DRM_DEBUG( "agp_offset: %ld\n", agp_offset );
	DRM_DEBUG( "alignment:  %d\n",  alignment );
	DRM_DEBUG( "page_order: %d\n",  page_order );
	DRM_DEBUG( "total:      %d\n",  total );

	if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL;
	if ( dev->queue_count ) return -EBUSY; /* Not while in use */

	spin_lock( &dev->count_lock );
	if ( dev->buf_use ) {
		spin_unlock( &dev->count_lock );
		return -EBUSY;
	}
	atomic_inc( &dev->buf_alloc );
	spin_unlock( &dev->count_lock );

	down( &dev->struct_sem );
	entry = &dma->bufs[order];
	if ( entry->buf_count ) {
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -ENOMEM; /* May only call once for each order */
	}

	if (count < 0 || count > 4096) {
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -EINVAL;
	}

	entry->buflist = DRM(alloc)( count * sizeof(*entry->buflist),
				    DRM_MEM_BUFS );
	if ( !entry->buflist ) {
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -ENOMEM;
	}
	memset( entry->buflist, 0, count * sizeof(*entry->buflist) );

	entry->buf_size = size;
	entry->page_order = page_order;

	offset = 0;

	while ( entry->buf_count < count ) {
		buf          = &entry->buflist[entry->buf_count];
		buf->idx     = dma->buf_count + entry->buf_count;
		buf->total   = alignment;
		buf->order   = order;
		buf->used    = 0;

		buf->offset  = (dma->byte_count + offset);
		buf->bus_address = agp_offset + offset;
		buf->address = (void *)(agp_offset + offset);
		buf->next    = NULL;
		buf->waiting = 0;
		buf->pending = 0;
		init_waitqueue_head( &buf->dma_wait );
		buf->pid     = 0;

		buf->dev_priv_size = sizeof(DRIVER_BUF_PRIV_T);
		buf->dev_private = DRM(alloc)( sizeof(DRIVER_BUF_PRIV_T),
					       DRM_MEM_BUFS );
		if(!buf->dev_private) {
			/* Set count correctly so we free the proper amount. */
			entry->buf_count = count;
			DRM(cleanup_buf_error)(entry);
		}
		memset( buf->dev_private, 0, buf->dev_priv_size );

#if __HAVE_DMA_HISTOGRAM
		buf->time_queued = 0;
		buf->time_dispatched = 0;
		buf->time_completed = 0;
		buf->time_freed = 0;
#endif
		DRM_DEBUG( "buffer %d @ %p\n",
			   entry->buf_count, buf->address );

		offset += alignment;
		entry->buf_count++;
		byte_count += PAGE_SIZE << page_order;
	}

	DRM_DEBUG( "byte_count: %d\n", byte_count );

	temp_buflist = DRM(realloc)( dma->buflist,
				     dma->buf_count * sizeof(*dma->buflist),
				     (dma->buf_count + entry->buf_count)
				     * sizeof(*dma->buflist),
				     DRM_MEM_BUFS );
	if(!temp_buflist) {
		/* Free the entry because it isn't valid */
		DRM(cleanup_buf_error)(entry);
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -ENOMEM;
	}
	dma->buflist = temp_buflist;

	for ( i = 0 ; i < entry->buf_count ; i++ ) {
		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
	}

	dma->buf_count += entry->buf_count;
	dma->byte_count += byte_count;

	DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count );
	DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count );

#if __HAVE_DMA_FREELIST
	DRM(freelist_create)( &entry->freelist, entry->buf_count );
	for ( i = 0 ; i < entry->buf_count ; i++ ) {
		DRM(freelist_put)( dev, &entry->freelist, &entry->buflist[i] );
	}
#endif
	up( &dev->struct_sem );

	request.count = entry->buf_count;
	request.size = size;

	if ( copy_to_user( (drm_buf_desc_t *)arg, &request, sizeof(request) ) )
		return -EFAULT;

	dma->flags = _DRM_DMA_USE_AGP;

	atomic_dec( &dev->buf_alloc );
	return 0;
}
#endif /* __REALLY_HAVE_AGP */

#if __HAVE_PCI_DMA
int DRM(addbufs_pci)( struct inode *inode, struct file *filp,
		      unsigned int cmd, unsigned long arg )
{
   	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->dev;
	drm_device_dma_t *dma = dev->dma;
	drm_buf_desc_t request;
	int count;
	int order;
	int size;
	int total;
	int page_order;
	drm_buf_entry_t *entry;
	unsigned long page;
	drm_buf_t *buf;
	int alignment;
	unsigned long offset;
	int i;
	int byte_count;
	int page_count;
	unsigned long *temp_pagelist;
	drm_buf_t **temp_buflist;

	if ( !dma ) return -EINVAL;

	if ( copy_from_user( &request, (drm_buf_desc_t *)arg,
			     sizeof(request) ) )
		return -EFAULT;

	count = request.count;
	order = DRM(order)( request.size );
	size = 1 << order;

	DRM_DEBUG( "count=%d, size=%d (%d), order=%d, queue_count=%d\n",
		   request.count, request.size, size,
		   order, dev->queue_count );

	if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL;
	if ( dev->queue_count ) return -EBUSY; /* Not while in use */

	alignment = (request.flags & _DRM_PAGE_ALIGN)
		? PAGE_ALIGN(size) : size;
	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
	total = PAGE_SIZE << page_order;

	spin_lock( &dev->count_lock );
	if ( dev->buf_use ) {
		spin_unlock( &dev->count_lock );
		return -EBUSY;
	}
	atomic_inc( &dev->buf_alloc );
	spin_unlock( &dev->count_lock );

	down( &dev->struct_sem );
	entry = &dma->bufs[order];
	if ( entry->buf_count ) {
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -ENOMEM;	/* May only call once for each order */
	}

	if (count < 0 || count > 4096) {
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -EINVAL;
	}

	entry->buflist = DRM(alloc)( count * sizeof(*entry->buflist),
				    DRM_MEM_BUFS );
	if ( !entry->buflist ) {
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -ENOMEM;
	}
	memset( entry->buflist, 0, count * sizeof(*entry->buflist) );

	entry->seglist = DRM(alloc)( count * sizeof(*entry->seglist),
				    DRM_MEM_SEGS );
	if ( !entry->seglist ) {
		DRM(free)( entry->buflist,
			  count * sizeof(*entry->buflist),
			  DRM_MEM_BUFS );
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -ENOMEM;
	}
	memset( entry->seglist, 0, count * sizeof(*entry->seglist) );

	temp_pagelist = DRM(realloc)( dma->pagelist,
				      dma->page_count * sizeof(*dma->pagelist),
				      (dma->page_count + (count << page_order))
				      * sizeof(*dma->pagelist),
				      DRM_MEM_PAGES );
	if(!temp_pagelist) {
		DRM(free)( entry->buflist,
			   count * sizeof(*entry->buflist),
			   DRM_MEM_BUFS );
		DRM(free)( entry->seglist,
			   count * sizeof(*entry->seglist),
			   DRM_MEM_SEGS );
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -ENOMEM;
	}

	dma->pagelist = temp_pagelist;
	DRM_DEBUG( "pagelist: %d entries\n",
		   dma->page_count + (count << page_order) );

	entry->buf_size	= size;
	entry->page_order = page_order;
	byte_count = 0;
	page_count = 0;

	while ( entry->buf_count < count ) {
		page = DRM(alloc_pages)( page_order, DRM_MEM_DMA );
		if ( !page ) break;
		entry->seglist[entry->seg_count++] = page;
		for ( i = 0 ; i < (1 << page_order) ; i++ ) {
			DRM_DEBUG( "page %d @ 0x%08lx\n",
				   dma->page_count + page_count,
				   page + PAGE_SIZE * i );
			dma->pagelist[dma->page_count + page_count++]
				= page + PAGE_SIZE * i;
		}
		for ( offset = 0 ;
		      offset + size <= total && entry->buf_count < count ;
		      offset += alignment, ++entry->buf_count ) {
			buf	     = &entry->buflist[entry->buf_count];
			buf->idx     = dma->buf_count + entry->buf_count;
			buf->total   = alignment;
			buf->order   = order;
			buf->used    = 0;
			buf->offset  = (dma->byte_count + byte_count + offset);
			buf->address = (void *)(page + offset);
			buf->next    = NULL;
			buf->waiting = 0;
			buf->pending = 0;
			init_waitqueue_head( &buf->dma_wait );
			buf->pid     = 0;
#if __HAVE_DMA_HISTOGRAM
			buf->time_queued     = 0;
			buf->time_dispatched = 0;
			buf->time_completed  = 0;
			buf->time_freed      = 0;
#endif
			DRM_DEBUG( "buffer %d @ %p\n",
				   entry->buf_count, buf->address );
		}
		byte_count += PAGE_SIZE << page_order;
	}

	temp_buflist = DRM(realloc)( dma->buflist,
				     dma->buf_count * sizeof(*dma->buflist),
				     (dma->buf_count + entry->buf_count)
				     * sizeof(*dma->buflist),
				     DRM_MEM_BUFS );
	if(!temp_buflist) {
		/* Free the entry because it isn't valid */
		DRM(cleanup_buf_error)(entry);
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -ENOMEM;
	}
	dma->buflist = temp_buflist;

	for ( i = 0 ; i < entry->buf_count ; i++ ) {
		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
	}

	dma->buf_count += entry->buf_count;
	dma->seg_count += entry->seg_count;
	dma->page_count += entry->seg_count << page_order;
	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);

#if __HAVE_DMA_FREELIST
	DRM(freelist_create)( &entry->freelist, entry->buf_count );
	for ( i = 0 ; i < entry->buf_count ; i++ ) {
		DRM(freelist_put)( dev, &entry->freelist, &entry->buflist[i] );
	}
#endif
	up( &dev->struct_sem );

	request.count = entry->buf_count;
	request.size = size;

	if ( copy_to_user( (drm_buf_desc_t *)arg, &request, sizeof(request) ) )
		return -EFAULT;

	atomic_dec( &dev->buf_alloc );
	return 0;

}
#endif /* __HAVE_PCI_DMA */

#ifdef __HAVE_SG
int DRM(addbufs_sg)( struct inode *inode, struct file *filp,
                     unsigned int cmd, unsigned long arg )
{
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->dev;
	drm_device_dma_t *dma = dev->dma;
	drm_buf_desc_t request;
	drm_buf_entry_t *entry;
	drm_buf_t *buf;
	unsigned long offset;
	unsigned long agp_offset;
	int count;
	int order;
	int size;
	int alignment;
	int page_order;
	int total;
	int byte_count;
	int i;
	drm_buf_t **temp_buflist;

	if ( !dma ) return -EINVAL;

	if ( copy_from_user( &request, (drm_buf_desc_t *)arg,
                             sizeof(request) ) )
		return -EFAULT;

	count = request.count;
	order = DRM(order)( request.size );
	size = 1 << order;

	alignment  = (request.flags & _DRM_PAGE_ALIGN)
			? PAGE_ALIGN(size) : size;
	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
	total = PAGE_SIZE << page_order;

	byte_count = 0;
	agp_offset = request.agp_start;

	DRM_DEBUG( "count:      %d\n",  count );
	DRM_DEBUG( "order:      %d\n",  order );
	DRM_DEBUG( "size:       %d\n",  size );
	DRM_DEBUG( "agp_offset: %ld\n", agp_offset );
	DRM_DEBUG( "alignment:  %d\n",  alignment );
	DRM_DEBUG( "page_order: %d\n",  page_order );
	DRM_DEBUG( "total:      %d\n",  total );

	if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL;
	if ( dev->queue_count ) return -EBUSY; /* Not while in use */

	spin_lock( &dev->count_lock );
	if ( dev->buf_use ) {
		spin_unlock( &dev->count_lock );
		return -EBUSY;
	}
	atomic_inc( &dev->buf_alloc );
	spin_unlock( &dev->count_lock );

	down( &dev->struct_sem );
	entry = &dma->bufs[order];
	if ( entry->buf_count ) {
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -ENOMEM; /* May only call once for each order */
	}

	if (count < 0 || count > 4096) {
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -EINVAL;
	}

	entry->buflist = DRM(alloc)( count * sizeof(*entry->buflist),
				     DRM_MEM_BUFS );
	if ( !entry->buflist ) {
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -ENOMEM;
	}
	memset( entry->buflist, 0, count * sizeof(*entry->buflist) );

	entry->buf_size = size;
	entry->page_order = page_order;

	offset = 0;

	while ( entry->buf_count < count ) {
		buf          = &entry->buflist[entry->buf_count];
		buf->idx     = dma->buf_count + entry->buf_count;
		buf->total   = alignment;
		buf->order   = order;
		buf->used    = 0;

		buf->offset  = (dma->byte_count + offset);
		buf->bus_address = agp_offset + offset;
		buf->address = (void *)(agp_offset + offset + dev->sg->handle);
		buf->next    = NULL;
		buf->waiting = 0;
		buf->pending = 0;
		init_waitqueue_head( &buf->dma_wait );
		buf->pid     = 0;

		buf->dev_priv_size = sizeof(DRIVER_BUF_PRIV_T);
		buf->dev_private = DRM(alloc)( sizeof(DRIVER_BUF_PRIV_T),
					       DRM_MEM_BUFS );
		if(!buf->dev_private) {
			/* Set count correctly so we free the proper amount. */
			entry->buf_count = count;
			DRM(cleanup_buf_error)(entry);
			up( &dev->struct_sem );
			atomic_dec( &dev->buf_alloc );
			return -ENOMEM;
		}

		memset( buf->dev_private, 0, buf->dev_priv_size );

# if __HAVE_DMA_HISTOGRAM
		buf->time_queued = 0;
		buf->time_dispatched = 0;
		buf->time_completed = 0;
		buf->time_freed = 0;
# endif
		DRM_DEBUG( "buffer %d @ %p\n",
			   entry->buf_count, buf->address );

		offset += alignment;
		entry->buf_count++;
		byte_count += PAGE_SIZE << page_order;
	}

	DRM_DEBUG( "byte_count: %d\n", byte_count );

	temp_buflist = DRM(realloc)( dma->buflist,
				     dma->buf_count * sizeof(*dma->buflist),
				     (dma->buf_count + entry->buf_count)
				     * sizeof(*dma->buflist),
				     DRM_MEM_BUFS );
	if(!temp_buflist) {
		/* Free the entry because it isn't valid */
		DRM(cleanup_buf_error)(entry);
		up( &dev->struct_sem );
		atomic_dec( &dev->buf_alloc );
		return -ENOMEM;
	}
	dma->buflist = temp_buflist;

	for ( i = 0 ; i < entry->buf_count ; i++ ) {
		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
	}

	dma->buf_count += entry->buf_count;
	dma->byte_count += byte_count;

	DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count );
	DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count );

#if __HAVE_DMA_FREELIST
	DRM(freelist_create)( &entry->freelist, entry->buf_count );
	for ( i = 0 ; i < entry->buf_count ; i++ ) {
		DRM(freelist_put)( dev, &entry->freelist, &entry->buflist[i] );
	}
#endif
	up( &dev->struct_sem );

	request.count = entry->buf_count;
	request.size = size;

	if ( copy_to_user( (drm_buf_desc_t *)arg, &request, sizeof(request) ) )
		return -EFAULT;

	dma->flags = _DRM_DMA_USE_SG;

	atomic_dec( &dev->buf_alloc );
	return 0;
}
#endif /* __HAVE_SG */

int DRM(addbufs)( struct inode *inode, struct file *filp,
		  unsigned int cmd, unsigned long arg )
{
	drm_buf_desc_t request;

	if ( copy_from_user( &request, (drm_buf_desc_t *)arg,
			     sizeof(request) ) )
		return -EFAULT;

#if __REALLY_HAVE_AGP
	if ( request.flags & _DRM_AGP_BUFFER )
		return DRM(addbufs_agp)( inode, filp, cmd, arg );
	else
#endif
#if __HAVE_SG
	if ( request.flags & _DRM_SG_BUFFER )
		return DRM(addbufs_sg)( inode, filp, cmd, arg );
	else
#endif
#if __HAVE_PCI_DMA
		return DRM(addbufs_pci)( inode, filp, cmd, arg );
#else
		return -EINVAL;
#endif
}

int DRM(infobufs)( struct inode *inode, struct file *filp,
		   unsigned int cmd, unsigned long arg )
{
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->dev;
	drm_device_dma_t *dma = dev->dma;
	drm_buf_info_t request;
	int i;
	int count;

	if ( !dma ) return -EINVAL;

	spin_lock( &dev->count_lock );
	if ( atomic_read( &dev->buf_alloc ) ) {
		spin_unlock( &dev->count_lock );
		return -EBUSY;
	}
	++dev->buf_use;		/* Can't allocate more after this call */
	spin_unlock( &dev->count_lock );

	if ( copy_from_user( &request,
			     (drm_buf_info_t *)arg,
			     sizeof(request) ) )
		return -EFAULT;

	for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) {
		if ( dma->bufs[i].buf_count ) ++count;
	}

	DRM_DEBUG( "count = %d\n", count );

	if ( request.count >= count ) {
		for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) {
			if ( dma->bufs[i].buf_count ) {
				drm_buf_desc_t *to = &request.list[count];
				drm_buf_entry_t *from = &dma->bufs[i];
				drm_freelist_t *list = &dma->bufs[i].freelist;
				if ( copy_to_user( &to->count,
						   &from->buf_count,
						   sizeof(from->buf_count) ) ||
				     copy_to_user( &to->size,
						   &from->buf_size,
						   sizeof(from->buf_size) ) ||
				     copy_to_user( &to->low_mark,
						   &list->low_mark,
						   sizeof(list->low_mark) ) ||
				     copy_to_user( &to->high_mark,
						   &list->high_mark,
						   sizeof(list->high_mark) ) )
					return -EFAULT;

				DRM_DEBUG( "%d %d %d %d %d\n",
					   i,
					   dma->bufs[i].buf_count,
					   dma->bufs[i].buf_size,
					   dma->bufs[i].freelist.low_mark,
					   dma->bufs[i].freelist.high_mark );
				++count;
			}
		}
	}
	request.count = count;

	if ( copy_to_user( (drm_buf_info_t *)arg,
			   &request,
			   sizeof(request) ) )
		return -EFAULT;

	return 0;
}

int DRM(markbufs)( struct inode *inode, struct file *filp,
		   unsigned int cmd, unsigned long arg )
{
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->dev;
	drm_device_dma_t *dma = dev->dma;
	drm_buf_desc_t request;
	int order;
	drm_buf_entry_t *entry;

	if ( !dma ) return -EINVAL;

	if ( copy_from_user( &request,
			     (drm_buf_desc_t *)arg,
			     sizeof(request) ) )
		return -EFAULT;

	DRM_DEBUG( "%d, %d, %d\n",
		   request.size, request.low_mark, request.high_mark );
	order = DRM(order)( request.size );
	if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL;
	entry = &dma->bufs[order];

	if ( request.low_mark < 0 || request.low_mark > entry->buf_count )
		return -EINVAL;
	if ( request.high_mark < 0 || request.high_mark > entry->buf_count )
		return -EINVAL;

	entry->freelist.low_mark  = request.low_mark;
	entry->freelist.high_mark = request.high_mark;

	return 0;
}

int DRM(freebufs)( struct inode *inode, struct file *filp,
		   unsigned int cmd, unsigned long arg )
{
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->dev;
	drm_device_dma_t *dma = dev->dma;
	drm_buf_free_t request;
	int i;
	int idx;
	drm_buf_t *buf;

	if ( !dma ) return -EINVAL;

	if ( copy_from_user( &request,
			     (drm_buf_free_t *)arg,
			     sizeof(request) ) )
		return -EFAULT;

	DRM_DEBUG( "%d\n", request.count );
	for ( i = 0 ; i < request.count ; i++ ) {
		if ( copy_from_user( &idx,
				     &request.list[i],
				     sizeof(idx) ) )
			return -EFAULT;
		if ( idx < 0 || idx >= dma->buf_count ) {
			DRM_ERROR( "Index %d (of %d max)\n",
				   idx, dma->buf_count - 1 );
			return -EINVAL;
		}
		buf = dma->buflist[idx];
		if ( buf->pid != current->pid ) {
			DRM_ERROR( "Process %d freeing buffer owned by %d\n",
				   current->pid, buf->pid );
			return -EINVAL;
		}
		DRM(free_buffer)( dev, buf );
	}

	return 0;
}

int DRM(mapbufs)( struct inode *inode, struct file *filp,
		  unsigned int cmd, unsigned long arg )
{
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->dev;
	drm_device_dma_t *dma = dev->dma;
	int retcode = 0;
	const int zero = 0;
	unsigned long virtual;
	unsigned long address;
	drm_buf_map_t request;
	int i;

	if ( !dma ) return -EINVAL;

	spin_lock( &dev->count_lock );
	if ( atomic_read( &dev->buf_alloc ) ) {
		spin_unlock( &dev->count_lock );
		return -EBUSY;
	}
	dev->buf_use++;		/* Can't allocate more after this call */
	spin_unlock( &dev->count_lock );

	if ( copy_from_user( &request, (drm_buf_map_t *)arg,
			     sizeof(request) ) )
		return -EFAULT;

	if ( request.count >= dma->buf_count ) {
		if ( (__HAVE_AGP && (dma->flags & _DRM_DMA_USE_AGP)) ||
		     (__HAVE_SG && (dma->flags & _DRM_DMA_USE_SG)) ) {
			drm_map_t *map = DRIVER_AGP_BUFFERS_MAP( dev );

			if ( !map ) {
				retcode = -EINVAL;
				goto done;
			}

#if LINUX_VERSION_CODE <= 0x020402
			down( &current->mm->mmap_sem );
#else
			down_write( &current->mm->mmap_sem );
#endif
			virtual = do_mmap( filp, 0, map->size,
					   PROT_READ | PROT_WRITE,
					   MAP_SHARED,
					   (unsigned long)map->offset );
#if LINUX_VERSION_CODE <= 0x020402
			up( &current->mm->mmap_sem );
#else
			up_write( &current->mm->mmap_sem );
#endif
		} else {
#if LINUX_VERSION_CODE <= 0x020402
			down( &current->mm->mmap_sem );
#else
			down_write( &current->mm->mmap_sem );
#endif
			virtual = do_mmap( filp, 0, dma->byte_count,
					   PROT_READ | PROT_WRITE,
					   MAP_SHARED, 0 );
#if LINUX_VERSION_CODE <= 0x020402
			up( &current->mm->mmap_sem );
#else
			up_write( &current->mm->mmap_sem );
#endif
		}
		if ( virtual > -1024UL ) {
			/* Real error */
			retcode = (signed long)virtual;
			goto done;
		}
		request.virtual = (void *)virtual;

		for ( i = 0 ; i < dma->buf_count ; i++ ) {
			if ( copy_to_user( &request.list[i].idx,
					   &dma->buflist[i]->idx,
					   sizeof(request.list[0].idx) ) ) {
				retcode = -EFAULT;
				goto done;
			}
			if ( copy_to_user( &request.list[i].total,
					   &dma->buflist[i]->total,
					   sizeof(request.list[0].total) ) ) {
				retcode = -EFAULT;
				goto done;
			}
			if ( copy_to_user( &request.list[i].used,
					   &zero,
					   sizeof(zero) ) ) {
				retcode = -EFAULT;
				goto done;
			}
			address = virtual + dma->buflist[i]->offset; /* *** */
			if ( copy_to_user( &request.list[i].address,
					   &address,
					   sizeof(address) ) ) {
				retcode = -EFAULT;
				goto done;
			}
		}
	}
 done:
	request.count = dma->buf_count;
	DRM_DEBUG( "%d buffers, retcode = %d\n", request.count, retcode );

	if ( copy_to_user( (drm_buf_map_t *)arg, &request, sizeof(request) ) )
		return -EFAULT;

	return retcode;
}

#endif /* __HAVE_DMA */