summaryrefslogtreecommitdiff
path: root/libdrm/xf86drmRandom.c
blob: 9e1e9ee2c34940a9f85a9b39c1cf7352ec855519 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/* xf86drmRandom.c -- "Minimal Standard" PRNG Implementation
 * Created: Mon Apr 19 08:28:13 1999 by faith@precisioninsight.com
 *
 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 * 
 * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
 *
 * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/xf86drmRandom.c,v 1.4 2000/06/17 00:03:34 martin Exp $
 *
 * DESCRIPTION
 *
 * This file contains a simple, straightforward implementation of the Park
 * & Miller "Minimal Standard" PRNG [PM88, PMS93], which is a Lehmer
 * multiplicative linear congruential generator (MLCG) with a period of
 * 2^31-1.
 *
 * This implementation is intended to provide a reliable, portable PRNG
 * that is suitable for testing a hash table implementation and for
 * implementing skip lists.
 *
 * FUTURE ENHANCEMENTS
 *
 * If initial seeds are not selected randomly, two instances of the PRNG
 * can be correlated.  [Knuth81, pp. 32-33] describes a shuffling technique
 * that can eliminate this problem.
 *
 * If PRNGs are used for simulation, the period of the current
 * implementation may be too short.  [LE88] discusses methods of combining
 * MLCGs to produce much longer periods, and suggests some alternative
 * values for A and M.  [LE90 and Sch92] also provide information on
 * long-period PRNGs.
 *
 * REFERENCES
 *
 * [Knuth81] Donald E. Knuth. The Art of Computer Programming.  Volume 2:
 * Seminumerical Algorithms.  Reading, Massachusetts: Addison-Wesley, 1981.
 *
 * [LE88] Pierre L'Ecuyer. "Efficient and Portable Combined Random Number
 * Generators".  CACM 31(6), June 1988, pp. 742-774.
 *
 * [LE90] Pierre L'Ecuyer. "Random Numbers for Simulation". CACM 33(10,
 * October 1990, pp. 85-97.
 *
 * [PM88] Stephen K. Park and Keith W. Miller. "Random Number Generators:
 * Good Ones are Hard to Find". CACM 31(10), October 1988, pp. 1192-1201.
 *
 * [Sch92] Bruce Schneier. "Pseudo-Ransom Sequence Generator for 32-Bit
 * CPUs".  Dr. Dobb's Journal 17(2), February 1992, pp. 34, 37-38, 40.
 *
 * [PMS93] Stephen K. Park, Keith W. Miller, and Paul K. Stockmeyer.  In
 * "Technical Correspondence: Remarks on Choosing and Implementing Random
 * Number Generators". CACM 36(7), July 1993, pp. 105-110.
 *
 */

#define RANDOM_MAIN 0

#if RANDOM_MAIN
# include <stdio.h>
# include <stdlib.h>
#else
# include "xf86drm.h"
# ifdef XFree86LOADER
#  include "xf86.h"
#  include "xf86_ansic.h"
# else
#  include <stdio.h>
#  include <stdlib.h>
# endif
#endif

#define N(x)  drm##x

#define RANDOM_MAGIC 0xfeedbeef
#define RANDOM_DEBUG 0

#if RANDOM_MAIN
#define RANDOM_ALLOC malloc
#define RANDOM_FREE  free
#else
#define RANDOM_ALLOC drmMalloc
#define RANDOM_FREE  drmFree
#endif

typedef struct RandomState {
    unsigned long magic;
    unsigned long a;
    unsigned long m;
    unsigned long q;		/* m div a */
    unsigned long r;		/* m mod a */
    unsigned long check;
    long          seed;
} RandomState;

#if RANDOM_MAIN
extern void          *N(RandomCreate)(unsigned long seed);
extern int           N(RandomDestroy)(void *state);
extern unsigned long N(Random)(void *state);
extern double        N(RandomDouble)(void *state);
#endif

void *N(RandomCreate)(unsigned long seed)
{
    RandomState  *state;

    state           = RANDOM_ALLOC(sizeof(*state));
    if (!state) return NULL;
    state->magic    = RANDOM_MAGIC;
#if 0
				/* Park & Miller, October 1988 */
    state->a        = 16807;
    state->m        = 2147483647;
    state->check    = 1043618065; /* After 10000 iterations */
#else
				/* Park, Miller, and Stockmeyer, July 1993 */
    state->a        = 48271;
    state->m        = 2147483647;
    state->check    = 399268537; /* After 10000 iterations */
#endif
    state->q        = state->m / state->a;
    state->r        = state->m % state->a;

    state->seed     = seed;
				/* Check for illegal boundary conditions,
                                   and choose closest legal value. */
    if (state->seed <= 0)        state->seed = 1;
    if (state->seed >= state->m) state->seed = state->m - 1;

    return state;
}

int N(RandomDestroy)(void *state)
{
    RANDOM_FREE(state);
    return 0;
}

unsigned long N(Random)(void *state)
{
    RandomState   *s = (RandomState *)state;
    long          hi;
    long          lo;

    hi      = s->seed / s->q;
    lo      = s->seed % s->q;
    s->seed = s->a * lo - s->r * hi;
    if (s->seed <= 0) s->seed += s->m;

    return s->seed;
}

double N(RandomDouble)(void *state)
{
    RandomState *s = (RandomState *)state;
    
    return (double)N(Random)(state)/(double)s->m;
}

#if RANDOM_MAIN
static void check_period(long seed)
{
    unsigned long count = 0;
    unsigned long initial;
    void          *state;
    
    state = N(RandomCreate)(seed);
    initial = N(Random)(state);
    ++count;
    while (initial != N(Random)(state)) {
	if (!++count) break;
    }
    printf("With seed of %10ld, period = %10lu (0x%08lx)\n",
	   seed, count, count);
    N(RandomDestroy)(state);
}

int main(void)
{
    RandomState   *state;
    int           i;
    unsigned long rand;

    state = N(RandomCreate)(1);
    for (i = 0; i < 10000; i++) {
	rand = N(Random)(state);
    }
    printf("After 10000 iterations: %lu (%lu expected): %s\n",
	   rand, state->check,
	   rand - state->check ? "*INCORRECT*" : "CORRECT");
    N(RandomDestroy)(state);

    printf("Checking periods...\n");
    check_period(1);
    check_period(2);
    check_period(31415926);
    
    return 0;
}
#endif
'#n827'>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
/**
 * \file drm_drv.h 
 * Generic driver template
 *
 * \author Rickard E. (Rik) Faith <faith@valinux.com>
 * \author Gareth Hughes <gareth@valinux.com>
 *
 * To use this template, you must at least define the following (samples
 * given for the MGA driver):
 *
 * \code
 * #define DRIVER_AUTHOR	"VA Linux Systems, Inc."
 *
 * #define DRIVER_NAME		"mga"
 * #define DRIVER_DESC		"Matrox G200/G400"
 * #define DRIVER_DATE		"20001127"
 *
 * #define DRIVER_MAJOR		2
 * #define DRIVER_MINOR		0
 * #define DRIVER_PATCHLEVEL	2
 *
 * #define DRIVER_IOCTL_COUNT	DRM_ARRAY_SIZE( mga_ioctls )
 *
 * #define DRM(x)		mga_##x
 * \endcode
 */

/*
 * 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.
 */

#ifndef DRIVER_IOCTLS
#define DRIVER_IOCTLS
#endif

static void __exit drm_cleanup( drm_device_t *dev );

#ifndef MODULE
/** Use an additional macro to avoid preprocessor troubles */
#define DRM_OPTIONS_FUNC DRM(options)
/**
 * Called by the kernel to parse command-line options passed via the
 * boot-loader (e.g., LILO).  It calls the insmod option routine,
 * parse_options().
 */
static int __init DRM(options)( char *str )
{
	DRM(parse_options)( str );
	return 1;
}

__setup( DRIVER_NAME "=", DRM_OPTIONS_FUNC );
#undef DRM_OPTIONS_FUNC
#endif

int DRM(fb_loaded) = 0;

struct file_operations	DRM(fops) = {
	.owner   = THIS_MODULE,
	.open	 = DRM(open),
	.flush	 = DRM(flush),
	.release = DRM(release),
	.ioctl	 = DRM(ioctl),
	.mmap	 = DRM(mmap),
	.fasync  = DRM(fasync),
	.poll	 = DRM(poll),
	.read	 = DRM(read),
};

/** Ioctl table */
drm_ioctl_desc_t		  DRM(ioctls)[] = {
	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]       = { DRM(version),     0, 0 },
	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]    = { DRM(getunique),   0, 0 },
	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]     = { DRM(getmagic),    0, 0 },
	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]     = { DRM(irq_by_busid), 0, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAP)]       = { DRM(getmap),      0, 0 },
	[DRM_IOCTL_NR(DRM_IOCTL_GET_CLIENT)]    = { DRM(getclient),   0, 0 },
	[DRM_IOCTL_NR(DRM_IOCTL_GET_STATS)]     = { DRM(getstats),    0, 0 },
	[DRM_IOCTL_NR(DRM_IOCTL_SET_VERSION)]   = { DRM(setversion),  0, 1 },

	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]    = { DRM(setunique),   1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]         = { DRM(noop),        1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]       = { DRM(noop),        1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]    = { DRM(authmagic),   1, 1 },
	
	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]       = { DRM(addmap),      1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_RM_MAP)]        = { DRM(rmmap),       1, 0 },

	[DRM_IOCTL_NR(DRM_IOCTL_SET_SAREA_CTX)] = { DRM(setsareactx), 1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_GET_SAREA_CTX)] = { DRM(getsareactx), 1, 0 },

	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]       = { DRM(addctx),      1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]        = { DRM(rmctx),       1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]       = { DRM(modctx),      1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]       = { DRM(getctx),      1, 0 },
	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]    = { DRM(switchctx),   1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]       = { DRM(newctx),      1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]       = { DRM(resctx),      1, 0 },

	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]      = { DRM(adddraw),     1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]       = { DRM(rmdraw),      1, 1 },

	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	        = { DRM(lock),        1, 0 },
	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]        = { DRM(unlock),      1, 0 },

	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]        = { DRM(noop),      1, 0 },

	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]      = { DRM(addbufs),     1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]     = { DRM(markbufs),    1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]     = { DRM(infobufs),    1, 0 },
	[DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]      = { DRM(mapbufs),     1, 0 },
	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]     = { DRM(freebufs),    1, 0 },
	/* The DRM_IOCTL_DMA ioctl should be defined by the driver. */

	[DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]       = { DRM(control),     1, 1 },

#if __OS_HAS_AGP
	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)]   = { DRM(agp_acquire), 1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)]   = { DRM(agp_release), 1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]    = { DRM(agp_enable),  1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]      = { DRM(agp_info),    1, 0 },
	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]     = { DRM(agp_alloc),   1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]      = { DRM(agp_free),    1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]      = { DRM(agp_bind),    1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]    = { DRM(agp_unbind),  1, 1 },
#endif

	[DRM_IOCTL_NR(DRM_IOCTL_SG_ALLOC)]      = { DRM(sg_alloc),    1, 1 },
	[DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)]       = { DRM(sg_free),     1, 1 },

	[DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)]   = { DRM(wait_vblank), 0, 0 },

	DRIVER_IOCTLS
};

#define DRIVER_IOCTL_COUNT	DRM_ARRAY_SIZE( DRM(ioctls) )

static int DRM(setup)( drm_device_t *dev )
{
	int i;

	if (dev->fn_tbl.presetup)
		dev->fn_tbl.presetup(dev);

	atomic_set( &dev->ioctl_count, 0 );
	atomic_set( &dev->vma_count, 0 );
	dev->buf_use = 0;
	atomic_set( &dev->buf_alloc, 0 );

	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
	{
		i = DRM(dma_setup)( dev );
		if ( i < 0 )
			return i;
	}
	
	for ( i = 0 ; i < DRM_ARRAY_SIZE(dev->counts) ; i++ )
		atomic_set( &dev->counts[i], 0 );

	for ( i = 0 ; i < DRM_HASH_SIZE ; i++ ) {
		dev->magiclist[i].head = NULL;
		dev->magiclist[i].tail = NULL;
	}

	dev->ctxlist = DRM(alloc)(sizeof(*dev->ctxlist),
				  DRM_MEM_CTXLIST);
	if(dev->ctxlist == NULL) return -ENOMEM;
	memset(dev->ctxlist, 0, sizeof(*dev->ctxlist));
	INIT_LIST_HEAD(&dev->ctxlist->head);

	dev->vmalist = NULL;
	dev->sigdata.lock = dev->lock.hw_lock = NULL;
	init_waitqueue_head( &dev->lock.lock_queue );
	dev->queue_count = 0;
	dev->queue_reserved = 0;
	dev->queue_slots = 0;
	dev->queuelist = NULL;
	dev->irq_enabled = 0;
	dev->context_flag = 0;
	dev->interrupt_flag = 0;
	dev->dma_flag = 0;
	dev->last_context = 0;
	dev->last_switch = 0;
	dev->last_checked = 0;
	init_waitqueue_head( &dev->context_wait );
	dev->if_version = 0;

	dev->ctx_start = 0;
	dev->lck_start = 0;

	dev->buf_rp = dev->buf;
	dev->buf_wp = dev->buf;
	dev->buf_end = dev->buf + DRM_BSZ;
	dev->buf_async = NULL;
	init_waitqueue_head( &dev->buf_readers );
	init_waitqueue_head( &dev->buf_writers );

	DRM_DEBUG( "\n" );

	/*
	 * The kernel's context could be created here, but is now created
	 * in drm_dma_enqueue.	This is more resource-efficient for
	 * hardware that does not do DMA, but may mean that
	 * drm_select_queue fails between the time the interrupt is
	 * initialized and the time the queues are initialized.
	 */
	if (dev->fn_tbl.postsetup)
		dev->fn_tbl.postsetup(dev);

	return 0;
}


/**
 * Take down the DRM device.
 *
 * \param dev DRM device structure.
 *
 * Frees every resource in \p dev.
 *
 * \sa drm_device and setup().
 */
static int DRM(takedown)( drm_device_t *dev )
{
	drm_magic_entry_t *pt, *next;
	drm_map_t *map;
	drm_map_list_t *r_list;
	struct list_head *list, *list_next;
	drm_vma_entry_t *vma, *vma_next;
	int i;

	DRM_DEBUG( "\n" );

	if (dev->fn_tbl.pretakedown)
		dev->fn_tbl.pretakedown(dev);

	if ( dev->irq_enabled ) DRM(irq_uninstall)( dev );

	down( &dev->struct_sem );
	del_timer( &dev->timer );

	if ( dev->devname ) {
		DRM(free)( dev->devname, strlen( dev->devname ) + 1,
			   DRM_MEM_DRIVER );
		dev->devname = NULL;
	}

	if ( dev->unique ) {
		DRM(free)( dev->unique, strlen( dev->unique ) + 1,
			   DRM_MEM_DRIVER );
		dev->unique = NULL;
		dev->unique_len = 0;
	}
				/* Clear pid list */
	for ( i = 0 ; i < DRM_HASH_SIZE ; i++ ) {
		for ( pt = dev->magiclist[i].head ; pt ; pt = next ) {
			next = pt->next;
			DRM(free)( pt, sizeof(*pt), DRM_MEM_MAGIC );
		}
		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
	}

				/* Clear AGP information */
	if (drm_core_has_AGP(dev) && dev->agp) {
		drm_agp_mem_t *entry;
		drm_agp_mem_t *nexte;

				/* Remove AGP resources, but leave dev->agp
                                   intact until drv_cleanup is called. */
		for ( entry = dev->agp->memory ; entry ; entry = nexte ) {
			nexte = entry->next;
			if ( entry->bound ) DRM(unbind_agp)( entry->memory );
			DRM(free_agp)( entry->memory, entry->pages );
			DRM(free)( entry, sizeof(*entry), DRM_MEM_AGPLISTS );
		}
		dev->agp->memory = NULL;

		if ( dev->agp->acquired ) DRM(agp_do_release)();

		dev->agp->acquired = 0;
		dev->agp->enabled  = 0;
	}

				/* Clear vma list (only built for debugging) */
	if ( dev->vmalist ) {
		for ( vma = dev->vmalist ; vma ; vma = vma_next ) {
			vma_next = vma->next;
			DRM(free)( vma, sizeof(*vma), DRM_MEM_VMAS );
		}
		dev->vmalist = NULL;
	}

	if( dev->maplist ) {
		list_for_each_safe( list, list_next, &dev->maplist->head ) {
			r_list = (drm_map_list_t *)list;

			if ( ( map = r_list->map ) ) {
				switch ( map->type ) {
				case _DRM_REGISTERS:
				case _DRM_FRAME_BUFFER:
					continue;
					
				case _DRM_SHM:
					vfree(map->handle);
					break;

				case _DRM_AGP:
					/* Do nothing here, because this is all
					 * handled in the AGP/GART driver.
					 */
					break;
				case _DRM_SCATTER_GATHER:
					/* Handle it */
					if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg) {
						DRM(sg_cleanup)(dev->sg);
						dev->sg = NULL;
					}
					break;
				}
				DRM(free)(map, sizeof(*map), DRM_MEM_MAPS);
			}
			list_del( list );
			DRM(free)(r_list, sizeof(*r_list), DRM_MEM_MAPS);
 		}
 	}

	
	if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE) && dev->queuelist) {
		for ( i = 0 ; i < dev->queue_count ; i++ ) {

			if ( dev->queuelist[i] ) {
				DRM(free)( dev->queuelist[i],
					  sizeof(*dev->queuelist[0]),
					  DRM_MEM_QUEUES );
				dev->queuelist[i] = NULL;
			}
		}
		DRM(free)( dev->queuelist,
			  dev->queue_slots * sizeof(*dev->queuelist),
			  DRM_MEM_QUEUES );
		dev->queuelist = NULL;
	}
	dev->queue_count = 0;

	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
		DRM(dma_takedown)( dev );

	if ( dev->lock.hw_lock ) {
		dev->sigdata.lock = dev->lock.hw_lock = NULL; /* SHM removed */
		dev->lock.filp = NULL;
		wake_up_interruptible( &dev->lock.lock_queue );
	}
	up( &dev->struct_sem );

	return 0;
}

static void DRM(init_fn_table)(struct drm_device *dev)
{
	dev->fn_tbl.reclaim_buffers = DRM(core_reclaim_buffers);
	dev->fn_tbl.get_map_ofs = DRM(core_get_map_ofs);
	dev->fn_tbl.get_reg_ofs = DRM(core_get_reg_ofs);
}

#include "drm_pciids.h"

static struct pci_device_id DRM(pciidlist)[] = {
	DRM(PCI_IDS)
};

int DRM(fill_in_dev)(drm_device_t *dev, struct pci_dev *pdev, const struct pci_device_id *ent)
{
	int retcode;

	dev->count_lock = SPIN_LOCK_UNLOCKED;
	init_timer( &dev->timer );
	sema_init( &dev->struct_sem, 1 );
	sema_init( &dev->ctxlist_sem, 1 );

	dev->name   = DRIVER_NAME;
	dev->fops   = &DRM(fops);
	dev->pdev   = pdev;

#ifdef __alpha__
	dev->hose   = pdev->sysdata;
	dev->pci_domain = dev->hose->bus->number;
#else
	dev->pci_domain = 0;
#endif
	dev->pci_bus = pdev->bus->number;
	dev->pci_slot = PCI_SLOT(pdev->devfn);
	dev->pci_func = PCI_FUNC(pdev->devfn);
	dev->irq = pdev->irq;

	dev->maplist = DRM(calloc)(1, sizeof(*dev->maplist), DRM_MEM_MAPS);
	if(dev->maplist == NULL) return -ENOMEM;
	INIT_LIST_HEAD(&dev->maplist->head);

	/* dev_priv_size can be changed by a driver in driver_register_fns */
	dev->dev_priv_size = sizeof(u32);
	
	/* the DRM has 6 counters */
	dev->counters = 6;
	dev->types[0] = _DRM_STAT_LOCK;
	dev->types[1] = _DRM_STAT_OPENS;
	dev->types[2] = _DRM_STAT_CLOSES;
	dev->types[3] = _DRM_STAT_IOCTLS;
	dev->types[4] = _DRM_STAT_LOCKS;
	dev->types[5] = _DRM_STAT_UNLOCKS;

	DRM(init_fn_table)(dev);

	DRM(driver_register_fns)(dev);
	
	if (dev->fn_tbl.preinit)
		if ((retcode = dev->fn_tbl.preinit(dev, ent->driver_data)))
			goto error_out_unreg;

	if (drm_core_has_AGP(dev)) {
		dev->agp = DRM(agp_init)();
		if (drm_core_check_feature(dev, DRIVER_REQUIRE_AGP) && (dev->agp == NULL)) {
			DRM_ERROR( "Cannot initialize the agpgart module.\n" );
			retcode = -EINVAL;
			goto error_out_unreg;
		}
		

		if (drm_core_has_MTRR(dev)) {
			if (dev->agp)
				dev->agp->agp_mtrr = mtrr_add( dev->agp->agp_info.aper_base,
							       dev->agp->agp_info.aper_size*1024*1024,
							       MTRR_TYPE_WRCOMB,
							       1 );
		}
	}

	retcode = DRM(ctxbitmap_init)( dev );
	if( retcode ) {
		DRM_ERROR( "Cannot allocate memory for context bitmap.\n" );
		goto error_out_unreg;
	}

	dev->device = MKDEV(DRM_MAJOR, dev->minor );

	DRM_INFO( "Initialized %s %d.%d.%d %s on minor %d: %s\n",
		DRIVER_NAME,
		DRIVER_MAJOR,
		DRIVER_MINOR,
		DRIVER_PATCHLEVEL,
		DRIVER_DATE,
		dev->minor,
		pci_pretty_name(pdev)
		);

	/* drivers add secondary heads here if needed */
	if (dev->fn_tbl.postinit)
		if ((retcode = dev->fn_tbl.postinit(dev, ent->driver_data)))
			goto error_out_unreg;

	return 0;

 error_out_unreg:
	DRM(takedown)(dev);
	return retcode;
}

static void __exit drm_cleanup_pci(struct pci_dev *pdev)
{
	drm_device_t *dev = pci_get_drvdata(pdev);
	
	pci_set_drvdata(pdev, NULL);
	pci_release_regions(pdev);
	if (dev)
		drm_cleanup(dev);
}

static struct pci_driver drm_driver = {
	.name          = DRIVER_NAME,
	.id_table      = DRM(pciidlist),
	.probe         = DRM(probe),
	.remove        = __devexit_p(drm_cleanup_pci),
};

#ifdef MODULE
static char *drm_opts = NULL;
#endif
MODULE_PARM( drm_opts, "s" );

/**
 * Module initialization. Called via init_module at module load time, or via
 * linux/init/main.c (this is not currently supported).
 *
 * \return zero on success or a negative number on failure.
 *
 * Initializes an array of drm_device structures, and attempts to
 * initialize all available devices, using consecutive minors, registering the
 * stubs and initializing the AGP device.
 * 
 * Expands the \c DRIVER_PREINIT and \c DRIVER_POST_INIT macros before and
 * after the initialization for driver customization.
 */
static int __init drm_init( void )
{
	struct pci_dev *pdev;
	struct pci_device_id *pid;
	int i;
	
	DRM_DEBUG( "\n" );

#ifdef MODULE
	DRM(parse_options)( drm_opts );
#endif

	DRM(mem_init)();
	
	for (i=0; (DRM(pciidlist)[i].vendor != 0) && !DRM(fb_loaded); i++) {
		pid = &DRM(pciidlist[i]);
		
		pdev = NULL;
		/* pass back in pdev to account for multiple identical cards */
		while ((pdev = pci_get_subsys(pid->vendor, pid->device, pid->subvendor, pid->subdevice, pdev))) {
			/* is there already a driver loaded, or (short circuit saves work) */
			/* does something like VesaFB have control of the memory region? */
			if (pci_dev_driver(pdev) || pci_request_regions(pdev, "DRM scan")) {
				/* go into stealth mode */
				DRM(fb_loaded) = 1;
				pci_dev_put(pdev);
				break;
			}
			/* no fbdev or vesadev, put things back and wait for normal probe */
			pci_release_regions(pdev);
		}
	}
	
	if (DRM(fb_loaded) == 0)
		pci_register_driver(&drm_driver);
	else {
		for (i=0; DRM(pciidlist)[i].vendor != 0; i++) {
			pid = &DRM(pciidlist[i]);
			
			pdev = NULL;
			/* pass back in pdev to account for multiple identical cards */
			while ((pdev = pci_get_subsys(pid->vendor, pid->device, pid->subvendor, pid->subdevice, pdev))) {
				/* stealth mode requires a manual probe */
				pci_dev_get(pdev);
				DRM(probe)(pdev, &DRM(pciidlist[i]));
			}
		}
		DRM_INFO("Used old pci detect: framebuffer loaded\n");
	}
	return 0;
}

/**
 * Called via cleanup_module() at module unload time.
 *
 * Cleans up all DRM device, calling takedown().
 * 
 * \sa drm_init().
 */
static void __exit drm_cleanup( drm_device_t *dev )
{
	drm_map_t *map;
	drm_map_list_t *r_list;
	struct list_head *list, *list_next;
	
	DRM_DEBUG( "\n" );
	if (!dev) {
		DRM_ERROR("cleanup called no dev\n");
		return;
	}

	DRM(takedown)(dev);

	if( dev->maplist ) {
		list_for_each_safe( list, list_next, &dev->maplist->head ) {
			r_list = (drm_map_list_t *)list;

			if ( ( map = r_list->map ) ) {
				switch ( map->type ) {
				case _DRM_REGISTERS:
					DRM(ioremapfree)( map->handle, map->size, dev );
					break;
					
				case _DRM_FRAME_BUFFER:
					if ( drm_core_has_MTRR(dev)) {
						if ( map->mtrr >= 0 ) {
							int retcode;
							retcode = mtrr_del( map->mtrr,
									    map->offset,
									    map->size );
							DRM_DEBUG( "mtrr_del=%d\n", retcode );
						}
					}
					break;
					
				case _DRM_SHM:
				case _DRM_AGP:
				case _DRM_SCATTER_GATHER:
					DRM_DEBUG("Extra maplist item\n");
					break;
				}
				DRM(free)(map, sizeof(*map), DRM_MEM_MAPS);
			}
			list_del( list );
			DRM(free)(r_list, sizeof(*r_list), DRM_MEM_MAPS);
 		}
		DRM(free)(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS);
		dev->maplist = NULL;
 	}
	if (DRM(fb_loaded)==0)
		pci_disable_device(dev->pdev);

	DRM(ctxbitmap_cleanup)( dev );

	if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) && dev->agp && dev->agp->agp_mtrr >= 0) {
		int retval;
		retval = mtrr_del( dev->agp->agp_mtrr,
				   dev->agp->agp_info.aper_base,
				   dev->agp->agp_info.aper_size*1024*1024 );
		DRM_DEBUG( "mtrr_del=%d\n", retval );
	}

	if (drm_core_has_AGP(dev) && dev->agp ) {
		DRM(agp_uninit)();
		DRM(free)( dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS );
		dev->agp = NULL;
	}
	if (dev->fn_tbl.postcleanup)
		dev->fn_tbl.postcleanup(dev);

	if ( DRM(put_minor)(dev) )
		DRM_ERROR( "Cannot unload module\n" );
}

static void __exit drm_exit (void)
{
	int i;
	drm_device_t *dev;
	drm_minor_t *minor;
	
	DRM_DEBUG( "\n" );
	if (DRM(fb_loaded)) {
		if (DRM(global)) {
			for (i = 0; DRM(global) && (i < DRM(global)->cards_limit); i++) {
				minor = &DRM(global)->minors[i];
				dev = minor->dev;
				DRM_DEBUG("fb loaded release minor %d\n", dev->minor);
				if ((minor->class == DRM_MINOR_PRIMARY) && (dev->fops == &DRM(fops))) {
					/* release the pci driver */
					if (dev->pdev)
						pci_dev_put(dev->pdev);
					drm_cleanup(dev);
				}
			}
		}
	} else
		pci_unregister_driver(&drm_driver);
	DRM_INFO( "Module unloaded\n" );
}

	
module_init( drm_init );
module_exit( drm_exit );


/**
 * Get version information
 *
 * \param inode device inode.
 * \param filp file pointer.
 * \param cmd command.
 * \param arg user argument, pointing to a drm_version structure.
 * \return zero on success or negative number on failure.
 *
 * Fills in the version information in \p arg.
 */
int DRM(version)( struct inode *inode, struct file *filp,
		  unsigned int cmd, unsigned long arg )
{
	drm_version_t __user *argp = (void __user *)arg;
	drm_version_t version;
	int len;

	if ( copy_from_user( &version, argp, sizeof(version) ) )
		return -EFAULT;

#define DRM_COPY( name, value )						\
	len = strlen( value );						\
	if ( len > name##_len ) len = name##_len;			\
	name##_len = strlen( value );					\
	if ( len && name ) {						\
		if ( copy_to_user( name, value, len ) )			\
			return -EFAULT;					\
	}

	version.version_major = DRIVER_MAJOR;
	version.version_minor = DRIVER_MINOR;
	version.version_patchlevel = DRIVER_PATCHLEVEL;

	DRM_COPY( version.name, DRIVER_NAME );
	DRM_COPY( version.date, DRIVER_DATE );
	DRM_COPY( version.desc, DRIVER_DESC );

	if ( copy_to_user( argp, &version, sizeof(version) ) )
		return -EFAULT;
	return 0;
}

/**
 * Open file.
 * 
 * \param inode device inode
 * \param filp file pointer.
 * \return zero on success or a negative number on failure.
 *
 * Searches the DRM device with the same minor number, calls open_helper(), and
 * increments the device open count. If the open count was previous at zero,
 * i.e., it's the first that the device is open, then calls setup().
 */
int DRM(open)( struct inode *inode, struct file *filp )
{
	drm_device_t *dev = NULL;
	int minor = iminor(inode);
	int retcode = 0;

	if (!((minor >= 0) && (minor < DRM(global)->cards_limit)))
		return -ENODEV;
		
	dev = DRM(global)->minors[minor].dev;
	if (!dev)
		return -ENODEV;

	retcode = DRM(open_helper)( inode, filp, dev );
	if ( !retcode ) {
		atomic_inc( &dev->counts[_DRM_STAT_OPENS] );
		spin_lock( &dev->count_lock );
		if ( !dev->open_count++ ) {
			spin_unlock( &dev->count_lock );
			return DRM(setup)( dev );
		}
		spin_unlock( &dev->count_lock );
	}

	return retcode;
}

/**
 * Release file.
 *
 * \param inode device inode
 * \param filp file pointer.
 * \return zero on success or a negative number on failure.
 *
 * If the hardware lock is held then free it, and take it again for the kernel
 * context since it's necessary to reclaim buffers. Unlink the file private
 * data from its list and free it. Decreases the open count and if it reaches
 * zero calls takedown().
 */
int DRM(release)( struct inode *inode, struct file *filp )
{
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev;
	int retcode = 0;

	lock_kernel();
	dev = priv->dev;

	DRM_DEBUG( "open_count = %d\n", dev->open_count );

	if (dev->fn_tbl.prerelease)
		dev->fn_tbl.prerelease(dev, filp);

	/* ========================================================
	 * Begin inline drm_release
	 */

	DRM_DEBUG( "pid = %d, device = 0x%lx, open_count = %d\n",
		   current->pid, (long)old_encode_dev(dev->device), dev->open_count );

	if ( priv->lock_count && dev->lock.hw_lock &&
	     _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) &&
	     dev->lock.filp == filp ) {
		DRM_DEBUG( "File %p released, freeing lock for context %d\n",
			filp,
			_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) );
		
		if (dev->fn_tbl.release)
			dev->fn_tbl.release(dev, filp);

		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->fn_tbl.release && priv->lock_count && dev->lock.hw_lock ) {
		/* The lock is required to reclaim buffers */
		DECLARE_WAITQUEUE( entry, current );

		add_wait_queue( &dev->lock.lock_queue, &entry );
		for (;;) {
			current->state = TASK_INTERRUPTIBLE;
			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.filp	    = filp;
				dev->lock.lock_time = jiffies;
                                atomic_inc( &dev->counts[_DRM_STAT_LOCKS] );
				break;	/* Got lock */
			}
				/* Contention */
			schedule();
			if ( signal_pending( current ) ) {
				retcode = -ERESTARTSYS;
				break;
			}
		}
		current->state = TASK_RUNNING;
		remove_wait_queue( &dev->lock.lock_queue, &entry );
		if( !retcode ) {
			if (dev->fn_tbl.release)
				dev->fn_tbl.release(dev, filp);
			DRM(lock_free)( dev, &dev->lock.hw_lock->lock,
					DRM_KERNEL_CONTEXT );
		}
	}
	
	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
	{
		dev->fn_tbl.reclaim_buffers(filp);
	}

	DRM(fasync)( -1, filp, 0 );

	down( &dev->ctxlist_sem );
	if ( !list_empty( &dev->ctxlist->head ) ) {
		drm_ctx_list_t *pos, *n;

		list_for_each_entry_safe( pos, n, &dev->ctxlist->head, head ) {
			if ( pos->tag == priv &&
			     pos->handle != DRM_KERNEL_CONTEXT ) {
				if (dev->fn_tbl.context_dtor)
					dev->fn_tbl.context_dtor(dev, pos->handle);

				DRM(ctxbitmap_free)( dev, pos->handle );

				list_del( &pos->head );
				DRM(free)( pos, sizeof(*pos), DRM_MEM_CTXLIST );
				--dev->ctx_count;
			}
		}
	}
	up( &dev->ctxlist_sem );

	down( &dev->struct_sem );
	if ( priv->remove_auth_on_close == 1 ) {
		drm_file_t *temp = dev->file_first;
		while ( temp ) {
			temp->authenticated = 0;
			temp = temp->next;
		}
	}
	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 );
	
	if (dev->fn_tbl.free_filp_priv)
		dev->fn_tbl.free_filp_priv( dev, priv );
	DRM(free)( priv, sizeof(*priv), DRM_MEM_FILES );

	/* ========================================================
	 * End inline drm_release
	 */

	atomic_inc( &dev->counts[_DRM_STAT_CLOSES] );
	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 );
			unlock_kernel();
			return -EBUSY;
		}
		spin_unlock( &dev->count_lock );
		unlock_kernel();
		return DRM(takedown)( dev );
	}
	spin_unlock( &dev->count_lock );

	unlock_kernel();

	return retcode;
}

/** 
 * Called whenever a process performs an ioctl on /dev/drm.
 *
 * \param inode device inode.
 * \param filp file pointer.
 * \param cmd command.
 * \param arg user argument.
 * \return zero on success or negative number on failure.
 *
 * Looks up the ioctl function in the ::ioctls table, checking for root
 * previleges if so required, and dispatches to the respective function.
 */
int DRM(ioctl)( 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_ioctl_desc_t *ioctl;
	drm_ioctl_t *func;
	int nr = DRM_IOCTL_NR(cmd);
	int retcode = 0;

	atomic_inc( &dev->ioctl_count );
	atomic_inc( &dev->counts[_DRM_STAT_IOCTLS] );
	++priv->ioctl_count;

	DRM_DEBUG( "pid=%d, cmd=0x%02x, nr=0x%02x, dev 0x%lx, auth=%d\n",
		   current->pid, cmd, nr, (long)old_encode_dev(dev->device), 
		   priv->authenticated );

	if ( nr >= DRIVER_IOCTL_COUNT ) {
		retcode = -EINVAL;
	} else {
		ioctl = &DRM(ioctls)[nr];
		func = ioctl->func;

		if ( !func ) {
			DRM_DEBUG( "no function\n" );
			retcode = -EINVAL;
		} else if ( ( ioctl->root_only && !capable( CAP_SYS_ADMIN ) )||
			    ( ioctl->auth_needed && !priv->authenticated ) ) {
			retcode = -EACCES;
		} else {
			retcode = func( inode, filp, cmd, arg );
		}
	}

	atomic_dec( &dev->ioctl_count );
	if (retcode) DRM_DEBUG( "ret = %x\n", retcode);
	return retcode;
}

/** 
 * Lock ioctl.
 *
 * \param inode device inode.
 * \param filp file pointer.
 * \param cmd command.
 * \param arg user argument, pointing to a drm_lock structure.
 * \return zero on success or negative number on failure.
 *
 * Add the current task to the lock wait queue, and attempt to take to lock.
 */
int DRM(lock)( 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;
        DECLARE_WAITQUEUE( entry, current );
        drm_lock_t lock;
        int ret = 0;

	++priv->lock_count;

        if ( copy_from_user( &lock, (drm_lock_t __user *)arg, sizeof(lock) ) )
		return -EFAULT;

        if ( lock.context == DRM_KERNEL_CONTEXT ) {
                DRM_ERROR( "Process %d using kernel context %d\n",
			   current->pid, lock.context );
                return -EINVAL;
        }

        DRM_DEBUG( "%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
		   lock.context, current->pid,
		   dev->lock.hw_lock->lock, lock.flags );

	if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE))
		if ( lock.context < 0 )
			return -EINVAL;

	add_wait_queue( &dev->lock.lock_queue, &entry );
	for (;;) {
		current->state = TASK_INTERRUPTIBLE;
		if ( !dev->lock.hw_lock ) {
			/* Device has been unregistered */
			ret = -EINTR;
			break;
		}
		if ( DRM(lock_take)( &dev->lock.hw_lock->lock,
				     lock.context ) ) {
			dev->lock.filp      = filp;
			dev->lock.lock_time = jiffies;
			atomic_inc( &dev->counts[_DRM_STAT_LOCKS] );
			break;  /* Got lock */
		}
		
		/* Contention */
		schedule();
		if ( signal_pending( current ) ) {
			ret = -ERESTARTSYS;
			break;
		}
	}
	current->state = TASK_RUNNING;
	remove_wait_queue( &dev->lock.lock_queue, &entry );
	
	sigemptyset( &dev->sigmask );
	sigaddset( &dev->sigmask, SIGSTOP );
	sigaddset( &dev->sigmask, SIGTSTP );
	sigaddset( &dev->sigmask, SIGTTIN );
	sigaddset( &dev->sigmask, SIGTTOU );
	dev->sigdata.context = lock.context;
	dev->sigdata.lock    = dev->lock.hw_lock;
	block_all_signals( DRM(notifier),
			   &dev->sigdata, &dev->sigmask );
	
	if (dev->fn_tbl.dma_ready && (lock.flags & _DRM_LOCK_READY))
		dev->fn_tbl.dma_ready(dev);
	
	if ( dev->fn_tbl.dma_quiescent && (lock.flags & _DRM_LOCK_QUIESCENT ))
		return dev->fn_tbl.dma_quiescent(dev);
	
	
	if ( dev->fn_tbl.kernel_context_switch && dev->last_context != lock.context ) {
		dev->fn_tbl.kernel_context_switch(dev, dev->last_context,
						  lock.context);
	}
	

        DRM_DEBUG( "%d %s\n", lock.context, ret ? "interrupted" : "has lock" );

        return ret;
}

/** 
 * Unlock ioctl.
 *
 * \param inode device inode.
 * \param filp file pointer.
 * \param cmd command.
 * \param arg user argument, pointing to a drm_lock structure.
 * \return zero on success or negative number on failure.
 *
 * Transfer and free the lock.
 */
int DRM(unlock)( 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_lock_t lock;

	if ( copy_from_user( &lock, (drm_lock_t __user *)arg, sizeof(lock) ) )
		return -EFAULT;