blob: 06eaf3b91e086390f6d70b3710690f50d7c73bed [file] [log] [blame]
Jose Fonsecad2443b22003-05-27 00:37:33 +00001/**
2 * \file drm_drv.h
3 * Generic driver template
4 *
5 * \author Rickard E. (Rik) Faith <faith@valinux.com>
6 * \author Gareth Hughes <gareth@valinux.com>
7 *
8 * To use this template, you must at least define the following (samples
9 * given for the MGA driver):
10 *
11 * \code
12 * #define DRIVER_AUTHOR "VA Linux Systems, Inc."
13 *
14 * #define DRIVER_NAME "mga"
15 * #define DRIVER_DESC "Matrox G200/G400"
16 * #define DRIVER_DATE "20001127"
17 *
18 * #define DRIVER_MAJOR 2
19 * #define DRIVER_MINOR 0
20 * #define DRIVER_PATCHLEVEL 2
21 *
22 * #define DRIVER_IOCTL_COUNT DRM_ARRAY_SIZE( mga_ioctls )
23 *
24 * #define DRM(x) mga_##x
25 * \endcode
26 */
27
28/*
Gareth Hughes36047532001-02-15 08:12:14 +000029 * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com
30 *
31 * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
32 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
33 * All Rights Reserved.
34 *
35 * Permission is hereby granted, free of charge, to any person obtaining a
36 * copy of this software and associated documentation files (the "Software"),
37 * to deal in the Software without restriction, including without limitation
38 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
39 * and/or sell copies of the Software, and to permit persons to whom the
40 * Software is furnished to do so, subject to the following conditions:
41 *
42 * The above copyright notice and this permission notice (including the next
43 * paragraph) shall be included in all copies or substantial portions of the
44 * Software.
45 *
46 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
47 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
48 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
49 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
50 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
51 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
52 * OTHER DEALINGS IN THE SOFTWARE.
Gareth Hughes36047532001-02-15 08:12:14 +000053 */
54
55#ifndef __MUST_HAVE_AGP
56#define __MUST_HAVE_AGP 0
57#endif
58#ifndef __HAVE_CTX_BITMAP
59#define __HAVE_CTX_BITMAP 0
60#endif
Eric Anholt2950f9e2003-10-17 05:13:48 +000061#ifndef __HAVE_IRQ
62#define __HAVE_IRQ 0
Gareth Hughes36047532001-02-15 08:12:14 +000063#endif
64#ifndef __HAVE_DMA_QUEUE
65#define __HAVE_DMA_QUEUE 0
66#endif
67#ifndef __HAVE_MULTIPLE_DMA_QUEUES
68#define __HAVE_MULTIPLE_DMA_QUEUES 0
69#endif
70#ifndef __HAVE_DMA_SCHEDULE
71#define __HAVE_DMA_SCHEDULE 0
72#endif
73#ifndef __HAVE_DMA_FLUSH
74#define __HAVE_DMA_FLUSH 0
75#endif
76#ifndef __HAVE_DMA_READY
77#define __HAVE_DMA_READY 0
78#endif
79#ifndef __HAVE_DMA_QUIESCENT
80#define __HAVE_DMA_QUIESCENT 0
81#endif
82#ifndef __HAVE_RELEASE
83#define __HAVE_RELEASE 0
84#endif
85#ifndef __HAVE_COUNTERS
86#define __HAVE_COUNTERS 0
87#endif
Alan Hourihaneb804c092001-04-30 16:18:22 +000088#ifndef __HAVE_SG
89#define __HAVE_SG 0
90#endif
Keith Whitwell36696392003-07-11 15:27:55 +000091/* __HAVE_KERNEL_CTX_SWITCH isn't used by any of the drm modules in
92 * the DRI cvs tree, but it is required by the kernel tree's sparc
93 * driver.
94 */
95#ifndef __HAVE_KERNEL_CTX_SWITCH
96#define __HAVE_KERNEL_CTX_SWITCH 0
97#endif
Keith Whitwelle15b0b62003-04-23 23:42:29 +000098#ifndef __HAVE_DRIVER_FOPS_READ
99#define __HAVE_DRIVER_FOPS_READ 0
100#endif
101#ifndef __HAVE_DRIVER_FOPS_POLL
102#define __HAVE_DRIVER_FOPS_POLL 0
103#endif
Gareth Hughes36047532001-02-15 08:12:14 +0000104
105#ifndef DRIVER_PREINIT
106#define DRIVER_PREINIT()
107#endif
108#ifndef DRIVER_POSTINIT
109#define DRIVER_POSTINIT()
110#endif
111#ifndef DRIVER_PRERELEASE
112#define DRIVER_PRERELEASE()
113#endif
114#ifndef DRIVER_PRETAKEDOWN
115#define DRIVER_PRETAKEDOWN()
116#endif
Jeff Hartmann51e38d92001-08-07 18:15:10 +0000117#ifndef DRIVER_POSTCLEANUP
118#define DRIVER_POSTCLEANUP()
119#endif
120#ifndef DRIVER_PRESETUP
121#define DRIVER_PRESETUP()
122#endif
123#ifndef DRIVER_POSTSETUP
124#define DRIVER_POSTSETUP()
125#endif
Gareth Hughes36047532001-02-15 08:12:14 +0000126#ifndef DRIVER_IOCTLS
127#define DRIVER_IOCTLS
128#endif
Michel Daenzer2655ccd2003-11-04 00:46:05 +0000129#ifndef DRIVER_OPEN_HELPER
130#define DRIVER_OPEN_HELPER( priv, dev )
131#endif
Jeff Hartmann51e38d92001-08-07 18:15:10 +0000132#ifndef DRIVER_FOPS
Jeff Hartmann51e38d92001-08-07 18:15:10 +0000133#define DRIVER_FOPS \
134static struct file_operations DRM(fops) = { \
Rik Faith977b4202002-08-06 18:00:57 +0000135 .owner = THIS_MODULE, \
136 .open = DRM(open), \
137 .flush = DRM(flush), \
138 .release = DRM(release), \
139 .ioctl = DRM(ioctl), \
140 .mmap = DRM(mmap), \
Rik Faith977b4202002-08-06 18:00:57 +0000141 .fasync = DRM(fasync), \
Keith Whitwelle15b0b62003-04-23 23:42:29 +0000142 .poll = DRM(poll), \
143 .read = DRM(read), \
Jeff Hartmann51e38d92001-08-07 18:15:10 +0000144}
Jeff Hartmann51e38d92001-08-07 18:15:10 +0000145#endif
146
Alan Hourihane74ef13f2002-07-05 08:31:11 +0000147#ifndef MODULE
Jose Fonsecad2443b22003-05-27 00:37:33 +0000148/** Use an additional macro to avoid preprocessor troubles */
Alan Hourihane74ef13f2002-07-05 08:31:11 +0000149#define DRM_OPTIONS_FUNC DRM(options)
Jose Fonsecad2443b22003-05-27 00:37:33 +0000150/**
151 * Called by the kernel to parse command-line options passed via the
152 * boot-loader (e.g., LILO). It calls the insmod option routine,
153 * parse_options().
154 */
Alan Hourihane74ef13f2002-07-05 08:31:11 +0000155static int __init DRM(options)( char *str )
156{
157 DRM(parse_options)( str );
158 return 1;
159}
160
161__setup( DRIVER_NAME "=", DRM_OPTIONS_FUNC );
162#undef DRM_OPTIONS_FUNC
163#endif
Gareth Hughes36047532001-02-15 08:12:14 +0000164
Eric Anholtff584762003-10-17 03:14:39 +0000165#define MAX_DEVICES 4
166static drm_device_t DRM(device)[MAX_DEVICES];
David Dawes56bd9c22001-07-30 19:59:39 +0000167static int DRM(numdevs) = 0;
Gareth Hughes36047532001-02-15 08:12:14 +0000168
Jeff Hartmann51e38d92001-08-07 18:15:10 +0000169DRIVER_FOPS;
Gareth Hughes36047532001-02-15 08:12:14 +0000170
Jose Fonsecad2443b22003-05-27 00:37:33 +0000171/** Ioctl table */
Kevin E Martin74e19a42001-03-14 22:22:50 +0000172static drm_ioctl_desc_t DRM(ioctls)[] = {
173 [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { DRM(version), 0, 0 },
174 [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { DRM(getunique), 0, 0 },
175 [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { DRM(getmagic), 0, 0 },
Eric Anholt66c9e302003-11-05 08:13:52 +0000176#if __HAVE_IRQ
177 [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { DRM(irq_by_busid), 0, 1 },
178#endif
Kevin E Martin74e19a42001-03-14 22:22:50 +0000179 [DRM_IOCTL_NR(DRM_IOCTL_GET_MAP)] = { DRM(getmap), 0, 0 },
180 [DRM_IOCTL_NR(DRM_IOCTL_GET_CLIENT)] = { DRM(getclient), 0, 0 },
181 [DRM_IOCTL_NR(DRM_IOCTL_GET_STATS)] = { DRM(getstats), 0, 0 },
Eric Anholt06cb1322003-10-23 02:23:31 +0000182 [DRM_IOCTL_NR(DRM_IOCTL_SET_VERSION)] = { DRM(setversion), 0, 1 },
Gareth Hughes36047532001-02-15 08:12:14 +0000183
Kevin E Martin74e19a42001-03-14 22:22:50 +0000184 [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { DRM(setunique), 1, 1 },
Keith Whitwell928c25d2003-04-22 12:07:24 +0000185 [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { DRM(noop), 1, 1 },
186 [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { DRM(noop), 1, 1 },
Kevin E Martin74e19a42001-03-14 22:22:50 +0000187 [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { DRM(authmagic), 1, 1 },
Gareth Hughes36047532001-02-15 08:12:14 +0000188
Kevin E Martin74e19a42001-03-14 22:22:50 +0000189 [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { DRM(addmap), 1, 1 },
190 [DRM_IOCTL_NR(DRM_IOCTL_RM_MAP)] = { DRM(rmmap), 1, 0 },
Gareth Hughes36047532001-02-15 08:12:14 +0000191
Alan Hourihaneb804c092001-04-30 16:18:22 +0000192#if __HAVE_CTX_BITMAP
Kevin E Martin74e19a42001-03-14 22:22:50 +0000193 [DRM_IOCTL_NR(DRM_IOCTL_SET_SAREA_CTX)] = { DRM(setsareactx), 1, 1 },
194 [DRM_IOCTL_NR(DRM_IOCTL_GET_SAREA_CTX)] = { DRM(getsareactx), 1, 0 },
Alan Hourihaneb804c092001-04-30 16:18:22 +0000195#endif
Gareth Hughes36047532001-02-15 08:12:14 +0000196
Kevin E Martin74e19a42001-03-14 22:22:50 +0000197 [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { DRM(addctx), 1, 1 },
198 [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { DRM(rmctx), 1, 1 },
199 [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { DRM(modctx), 1, 1 },
200 [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { DRM(getctx), 1, 0 },
201 [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { DRM(switchctx), 1, 1 },
202 [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { DRM(newctx), 1, 1 },
203 [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { DRM(resctx), 1, 0 },
Gareth Hughes36047532001-02-15 08:12:14 +0000204
Kevin E Martin74e19a42001-03-14 22:22:50 +0000205 [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { DRM(adddraw), 1, 1 },
206 [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { DRM(rmdraw), 1, 1 },
207
208 [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { DRM(lock), 1, 0 },
209 [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { DRM(unlock), 1, 0 },
Keith Whitwell928c25d2003-04-22 12:07:24 +0000210
211#if __HAVE_DMA_FLUSH
212 /* Gamma only, really */
Kevin E Martin74e19a42001-03-14 22:22:50 +0000213 [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { DRM(finish), 1, 0 },
Keith Whitwell928c25d2003-04-22 12:07:24 +0000214#else
215 [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { DRM(noop), 1, 0 },
216#endif
Gareth Hughes36047532001-02-15 08:12:14 +0000217
218#if __HAVE_DMA
Kevin E Martin74e19a42001-03-14 22:22:50 +0000219 [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { DRM(addbufs), 1, 1 },
220 [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { DRM(markbufs), 1, 1 },
221 [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { DRM(infobufs), 1, 0 },
222 [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { DRM(mapbufs), 1, 0 },
223 [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { DRM(freebufs), 1, 0 },
Eric Anholt2950f9e2003-10-17 05:13:48 +0000224 /* The DRM_IOCTL_DMA ioctl should be defined by the driver. */
225#endif
226#if __HAVE_IRQ || __HAVE_DMA
Kevin E Martin74e19a42001-03-14 22:22:50 +0000227 [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { DRM(control), 1, 1 },
Gareth Hughes36047532001-02-15 08:12:14 +0000228#endif
Gareth Hughes36047532001-02-15 08:12:14 +0000229
230#if __REALLY_HAVE_AGP
Jose Fonseca93522f62003-06-19 00:09:52 +0000231 [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { DRM(agp_acquire), 1, 1 },
232 [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { DRM(agp_release), 1, 1 },
233 [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = { DRM(agp_enable), 1, 1 },
234 [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = { DRM(agp_info), 1, 0 },
235 [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = { DRM(agp_alloc), 1, 1 },
236 [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { DRM(agp_free), 1, 1 },
237 [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { DRM(agp_bind), 1, 1 },
238 [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { DRM(agp_unbind), 1, 1 },
Gareth Hughes36047532001-02-15 08:12:14 +0000239#endif
240
Alan Hourihaneb804c092001-04-30 16:18:22 +0000241#if __HAVE_SG
Jose Fonseca93522f62003-06-19 00:09:52 +0000242 [DRM_IOCTL_NR(DRM_IOCTL_SG_ALLOC)] = { DRM(sg_alloc), 1, 1 },
243 [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)] = { DRM(sg_free), 1, 1 },
Alan Hourihaneb804c092001-04-30 16:18:22 +0000244#endif
245
Michel Daenzer55acd0d2002-09-25 17:18:19 +0000246#if __HAVE_VBL_IRQ
247 [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)] = { DRM(wait_vblank), 0, 0 },
248#endif
249
Gareth Hughes36047532001-02-15 08:12:14 +0000250 DRIVER_IOCTLS
251};
252
253#define DRIVER_IOCTL_COUNT DRM_ARRAY_SIZE( DRM(ioctls) )
254
255#ifdef MODULE
256static char *drm_opts = NULL;
257#endif
258
259MODULE_AUTHOR( DRIVER_AUTHOR );
260MODULE_DESCRIPTION( DRIVER_DESC );
261MODULE_PARM( drm_opts, "s" );
Alan Hourihaneca820fc2001-09-25 09:32:16 +0000262MODULE_LICENSE("GPL and additional rights");
Gareth Hughes36047532001-02-15 08:12:14 +0000263
Gareth Hughes36047532001-02-15 08:12:14 +0000264static int DRM(setup)( drm_device_t *dev )
265{
266 int i;
267
Jeff Hartmann51e38d92001-08-07 18:15:10 +0000268 DRIVER_PRESETUP();
Gareth Hughes36047532001-02-15 08:12:14 +0000269 atomic_set( &dev->ioctl_count, 0 );
270 atomic_set( &dev->vma_count, 0 );
271 dev->buf_use = 0;
272 atomic_set( &dev->buf_alloc, 0 );
273
274#if __HAVE_DMA
275 i = DRM(dma_setup)( dev );
276 if ( i < 0 )
277 return i;
278#endif
279
280 dev->counters = 6 + __HAVE_COUNTERS;
281 dev->types[0] = _DRM_STAT_LOCK;
282 dev->types[1] = _DRM_STAT_OPENS;
283 dev->types[2] = _DRM_STAT_CLOSES;
284 dev->types[3] = _DRM_STAT_IOCTLS;
285 dev->types[4] = _DRM_STAT_LOCKS;
286 dev->types[5] = _DRM_STAT_UNLOCKS;
287#ifdef __HAVE_COUNTER6
288 dev->types[6] = __HAVE_COUNTER6;
289#endif
290#ifdef __HAVE_COUNTER7
291 dev->types[7] = __HAVE_COUNTER7;
292#endif
293#ifdef __HAVE_COUNTER8
294 dev->types[8] = __HAVE_COUNTER8;
295#endif
296#ifdef __HAVE_COUNTER9
297 dev->types[9] = __HAVE_COUNTER9;
298#endif
299#ifdef __HAVE_COUNTER10
300 dev->types[10] = __HAVE_COUNTER10;
301#endif
302#ifdef __HAVE_COUNTER11
303 dev->types[11] = __HAVE_COUNTER11;
304#endif
305#ifdef __HAVE_COUNTER12
306 dev->types[12] = __HAVE_COUNTER12;
307#endif
308#ifdef __HAVE_COUNTER13
309 dev->types[13] = __HAVE_COUNTER13;
310#endif
311#ifdef __HAVE_COUNTER14
312 dev->types[14] = __HAVE_COUNTER14;
313#endif
314#ifdef __HAVE_COUNTER15
315 dev->types[14] = __HAVE_COUNTER14;
316#endif
317
318 for ( i = 0 ; i < DRM_ARRAY_SIZE(dev->counts) ; i++ )
319 atomic_set( &dev->counts[i], 0 );
320
321 for ( i = 0 ; i < DRM_HASH_SIZE ; i++ ) {
322 dev->magiclist[i].head = NULL;
323 dev->magiclist[i].tail = NULL;
324 }
Kevin E Martin74e19a42001-03-14 22:22:50 +0000325
326 dev->maplist = DRM(alloc)(sizeof(*dev->maplist),
327 DRM_MEM_MAPS);
328 if(dev->maplist == NULL) return -ENOMEM;
329 memset(dev->maplist, 0, sizeof(*dev->maplist));
330 INIT_LIST_HEAD(&dev->maplist->head);
Kevin E Martin74e19a42001-03-14 22:22:50 +0000331
Keith Whitwell82157572004-02-20 22:55:12 +0000332 dev->ctxlist = DRM(alloc)(sizeof(*dev->ctxlist),
333 DRM_MEM_CTXLIST);
334 if(dev->ctxlist == NULL) return -ENOMEM;
335 memset(dev->ctxlist, 0, sizeof(*dev->ctxlist));
336 INIT_LIST_HEAD(&dev->ctxlist->head);
337
Gareth Hughes36047532001-02-15 08:12:14 +0000338 dev->vmalist = NULL;
Michel Daenzercfa1a912002-09-21 23:18:54 +0000339 dev->sigdata.lock = dev->lock.hw_lock = NULL;
Gareth Hughes36047532001-02-15 08:12:14 +0000340 init_waitqueue_head( &dev->lock.lock_queue );
341 dev->queue_count = 0;
342 dev->queue_reserved = 0;
343 dev->queue_slots = 0;
344 dev->queuelist = NULL;
Eric Anholt66c9e302003-11-05 08:13:52 +0000345 dev->irq_enabled = 0;
Gareth Hughes36047532001-02-15 08:12:14 +0000346 dev->context_flag = 0;
347 dev->interrupt_flag = 0;
348 dev->dma_flag = 0;
349 dev->last_context = 0;
350 dev->last_switch = 0;
351 dev->last_checked = 0;
Gareth Hughes36047532001-02-15 08:12:14 +0000352 init_waitqueue_head( &dev->context_wait );
Eric Anholt66c9e302003-11-05 08:13:52 +0000353 dev->if_version = 0;
Gareth Hughes36047532001-02-15 08:12:14 +0000354
355 dev->ctx_start = 0;
356 dev->lck_start = 0;
357
358 dev->buf_rp = dev->buf;
359 dev->buf_wp = dev->buf;
360 dev->buf_end = dev->buf + DRM_BSZ;
361 dev->buf_async = NULL;
362 init_waitqueue_head( &dev->buf_readers );
363 init_waitqueue_head( &dev->buf_writers );
364
365 DRM_DEBUG( "\n" );
366
Jose Fonsecad2443b22003-05-27 00:37:33 +0000367 /*
368 * The kernel's context could be created here, but is now created
Gareth Hughes36047532001-02-15 08:12:14 +0000369 * in drm_dma_enqueue. This is more resource-efficient for
370 * hardware that does not do DMA, but may mean that
371 * drm_select_queue fails between the time the interrupt is
372 * initialized and the time the queues are initialized.
373 */
Jeff Hartmann51e38d92001-08-07 18:15:10 +0000374 DRIVER_POSTSETUP();
Gareth Hughes36047532001-02-15 08:12:14 +0000375 return 0;
376}
377
378
Jose Fonsecad2443b22003-05-27 00:37:33 +0000379/**
380 * Take down the DRM device.
381 *
382 * \param dev DRM device structure.
383 *
384 * Frees every resource in \p dev.
385 *
386 * \sa drm_device and setup().
387 */
Gareth Hughes36047532001-02-15 08:12:14 +0000388static int DRM(takedown)( drm_device_t *dev )
389{
390 drm_magic_entry_t *pt, *next;
391 drm_map_t *map;
Kevin E Martin74e19a42001-03-14 22:22:50 +0000392 drm_map_list_t *r_list;
Jeff Hartmann51e38d92001-08-07 18:15:10 +0000393 struct list_head *list, *list_next;
Gareth Hughes36047532001-02-15 08:12:14 +0000394 drm_vma_entry_t *vma, *vma_next;
395 int i;
396
397 DRM_DEBUG( "\n" );
398
399 DRIVER_PRETAKEDOWN();
Eric Anholt2950f9e2003-10-17 05:13:48 +0000400#if __HAVE_IRQ
Eric Anholt66c9e302003-11-05 08:13:52 +0000401 if ( dev->irq_enabled ) DRM(irq_uninstall)( dev );
Gareth Hughes36047532001-02-15 08:12:14 +0000402#endif
403
404 down( &dev->struct_sem );
405 del_timer( &dev->timer );
406
407 if ( dev->devname ) {
408 DRM(free)( dev->devname, strlen( dev->devname ) + 1,
409 DRM_MEM_DRIVER );
410 dev->devname = NULL;
411 }
412
413 if ( dev->unique ) {
414 DRM(free)( dev->unique, strlen( dev->unique ) + 1,
415 DRM_MEM_DRIVER );
416 dev->unique = NULL;
417 dev->unique_len = 0;
418 }
419 /* Clear pid list */
420 for ( i = 0 ; i < DRM_HASH_SIZE ; i++ ) {
421 for ( pt = dev->magiclist[i].head ; pt ; pt = next ) {
422 next = pt->next;
423 DRM(free)( pt, sizeof(*pt), DRM_MEM_MAGIC );
424 }
425 dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
426 }
427
Jose Fonseca93522f62003-06-19 00:09:52 +0000428#if __REALLY_HAVE_AGP
429 /* Clear AGP information */
430 if ( dev->agp ) {
431 drm_agp_mem_t *entry;
432 drm_agp_mem_t *nexte;
433
434 /* Remove AGP resources, but leave dev->agp
435 intact until drv_cleanup is called. */
436 for ( entry = dev->agp->memory ; entry ; entry = nexte ) {
437 nexte = entry->next;
438 if ( entry->bound ) DRM(unbind_agp)( entry->memory );
439 DRM(free_agp)( entry->memory, entry->pages );
440 DRM(free)( entry, sizeof(*entry), DRM_MEM_AGPLISTS );
441 }
442 dev->agp->memory = NULL;
443
444 if ( dev->agp->acquired ) DRM(agp_do_release)();
445
446 dev->agp->acquired = 0;
447 dev->agp->enabled = 0;
448 }
449#endif
450
Gareth Hughes36047532001-02-15 08:12:14 +0000451 /* Clear vma list (only built for debugging) */
452 if ( dev->vmalist ) {
453 for ( vma = dev->vmalist ; vma ; vma = vma_next ) {
454 vma_next = vma->next;
455 DRM(free)( vma, sizeof(*vma), DRM_MEM_VMAS );
456 }
457 dev->vmalist = NULL;
458 }
459
Kevin E Martin74e19a42001-03-14 22:22:50 +0000460 if( dev->maplist ) {
Michel Daenzerda168672003-08-06 11:46:21 +0000461 list_for_each_safe( list, list_next, &dev->maplist->head ) {
Kevin E Martin74e19a42001-03-14 22:22:50 +0000462 r_list = (drm_map_list_t *)list;
Kevin E Martin74e19a42001-03-14 22:22:50 +0000463
Michel Daenzerda168672003-08-06 11:46:21 +0000464 if ( ( map = r_list->map ) ) {
465 switch ( map->type ) {
466 case _DRM_REGISTERS:
467 case _DRM_FRAME_BUFFER:
Gareth Hughes36047532001-02-15 08:12:14 +0000468#if __REALLY_HAVE_MTRR
Michel Daenzerda168672003-08-06 11:46:21 +0000469 if ( map->mtrr >= 0 ) {
470 int retcode;
471 retcode = mtrr_del( map->mtrr,
472 map->offset,
473 map->size );
474 DRM_DEBUG( "mtrr_del=%d\n", retcode );
475 }
Gareth Hughes36047532001-02-15 08:12:14 +0000476#endif
Michel Daenzerda168672003-08-06 11:46:21 +0000477 DRM(ioremapfree)( map->handle, map->size, dev );
478 break;
479 case _DRM_SHM:
480 vfree(map->handle);
481 break;
Kevin E Martin74e19a42001-03-14 22:22:50 +0000482
Michel Daenzerda168672003-08-06 11:46:21 +0000483 case _DRM_AGP:
484 /* Do nothing here, because this is all
485 * handled in the AGP/GART driver.
486 */
487 break;
488 case _DRM_SCATTER_GATHER:
489 /* Handle it, but do nothing, if HAVE_SG
490 * isn't defined.
491 */
Alan Hourihaneb804c092001-04-30 16:18:22 +0000492#if __HAVE_SG
Michel Daenzerda168672003-08-06 11:46:21 +0000493 if(dev->sg) {
494 DRM(sg_cleanup)(dev->sg);
495 dev->sg = NULL;
496 }
Alan Hourihaneb804c092001-04-30 16:18:22 +0000497#endif
Michel Daenzerda168672003-08-06 11:46:21 +0000498 break;
499 }
500 DRM(free)(map, sizeof(*map), DRM_MEM_MAPS);
Gareth Hughes36047532001-02-15 08:12:14 +0000501 }
Michel Daenzerda168672003-08-06 11:46:21 +0000502 list_del( list );
503 DRM(free)(r_list, sizeof(*r_list), DRM_MEM_MAPS);
Kevin E Martin74e19a42001-03-14 22:22:50 +0000504 }
505 DRM(free)(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS);
Gareth Hughes36047532001-02-15 08:12:14 +0000506 dev->maplist = NULL;
Kevin E Martin74e19a42001-03-14 22:22:50 +0000507 }
Gareth Hughes36047532001-02-15 08:12:14 +0000508
509#if __HAVE_DMA_QUEUE || __HAVE_MULTIPLE_DMA_QUEUES
510 if ( dev->queuelist ) {
511 for ( i = 0 ; i < dev->queue_count ; i++ ) {
Keith Whitwell928c25d2003-04-22 12:07:24 +0000512#if __HAVE_DMA_WAITLIST
Gareth Hughes36047532001-02-15 08:12:14 +0000513 DRM(waitlist_destroy)( &dev->queuelist[i]->waitlist );
Keith Whitwell928c25d2003-04-22 12:07:24 +0000514#endif
Gareth Hughes36047532001-02-15 08:12:14 +0000515 if ( dev->queuelist[i] ) {
516 DRM(free)( dev->queuelist[i],
517 sizeof(*dev->queuelist[0]),
518 DRM_MEM_QUEUES );
519 dev->queuelist[i] = NULL;
520 }
521 }
522 DRM(free)( dev->queuelist,
523 dev->queue_slots * sizeof(*dev->queuelist),
524 DRM_MEM_QUEUES );
525 dev->queuelist = NULL;
526 }
527 dev->queue_count = 0;
528#endif
529
530#if __HAVE_DMA
531 DRM(dma_takedown)( dev );
532#endif
533 if ( dev->lock.hw_lock ) {
Michel Daenzercfa1a912002-09-21 23:18:54 +0000534 dev->sigdata.lock = dev->lock.hw_lock = NULL; /* SHM removed */
Keith Whitwell1728bc62003-03-28 14:27:37 +0000535 dev->lock.filp = 0;
Gareth Hughes36047532001-02-15 08:12:14 +0000536 wake_up_interruptible( &dev->lock.lock_queue );
537 }
538 up( &dev->struct_sem );
539
540 return 0;
541}
542
Eric Anholtff584762003-10-17 03:14:39 +0000543static drm_pci_id_list_t DRM(pciidlist)[] = {
544 DRIVER_PCI_IDS
545};
546
Eric Anholtb79d1b32003-10-23 05:56:13 +0000547static int DRM(probe)(struct pci_dev *pdev)
David Dawes56bd9c22001-07-30 19:59:39 +0000548{
Eric Anholtff584762003-10-17 03:14:39 +0000549 drm_device_t *dev;
550#if __HAVE_CTX_BITMAP
551 int retcode;
David Dawes56bd9c22001-07-30 19:59:39 +0000552#endif
Eric Anholtff584762003-10-17 03:14:39 +0000553 int i;
554 char *desc = NULL;
David Dawes56bd9c22001-07-30 19:59:39 +0000555
556 DRM_DEBUG( "\n" );
557
Eric Anholtff584762003-10-17 03:14:39 +0000558 for (i = 0; DRM(pciidlist)[i].vendor != 0; i++) {
559 if ((DRM(pciidlist)[i].vendor == pdev->vendor) &&
560 (DRM(pciidlist)[i].device == pdev->device)) {
561 desc = DRM(pciidlist)[i].name;
David Dawes56bd9c22001-07-30 19:59:39 +0000562 }
563 }
Eric Anholtff584762003-10-17 03:14:39 +0000564 if (desc == NULL)
565 return -ENODEV;
566
567 if (DRM(numdevs) >= MAX_DEVICES)
568 return -ENODEV;
569
570 dev = &(DRM(device)[DRM(numdevs)]);
571
572 memset( (void *)dev, 0, sizeof(*dev) );
573 dev->count_lock = SPIN_LOCK_UNLOCKED;
574 init_timer( &dev->timer );
575 sema_init( &dev->struct_sem, 1 );
Keith Whitwell82157572004-02-20 22:55:12 +0000576 sema_init( &dev->ctxlist_sem, 1 );
Eric Anholtff584762003-10-17 03:14:39 +0000577
578 if ((dev->minor = DRM(stub_register)(DRIVER_NAME, &DRM(fops),dev)) < 0)
579 return -EPERM;
580 dev->device = MKDEV(DRM_MAJOR, dev->minor );
581 dev->name = DRIVER_NAME;
Eric Anholt66c9e302003-11-05 08:13:52 +0000582
Eric Anholtff584762003-10-17 03:14:39 +0000583 dev->pdev = pdev;
584#ifdef __alpha__
585 dev->hose = pdev->sysdata;
Eric Anholt66c9e302003-11-05 08:13:52 +0000586 dev->pci_domain = dev->hose->bus->number;
587#else
588 dev->pci_domain = 0;
David Dawes56bd9c22001-07-30 19:59:39 +0000589#endif
Eric Anholt66c9e302003-11-05 08:13:52 +0000590 dev->pci_bus = pdev->bus->number;
591 dev->pci_slot = PCI_SLOT(pdev->devfn);
592 dev->pci_func = PCI_FUNC(pdev->devfn);
593 dev->irq = pdev->irq;
Eric Anholtff584762003-10-17 03:14:39 +0000594
595 DRIVER_PREINIT();
596
597#if __REALLY_HAVE_AGP
598 dev->agp = DRM(agp_init)();
599#if __MUST_HAVE_AGP
600 if ( dev->agp == NULL ) {
601 DRM_ERROR( "Cannot initialize the agpgart module.\n" );
602 DRM(stub_unregister)(dev->minor);
603 DRM(takedown)( dev );
Dave Airlie1bc75762004-04-08 12:09:10 +0000604 return -EINVAL;
Eric Anholtff584762003-10-17 03:14:39 +0000605 }
606#endif
607#if __REALLY_HAVE_MTRR
608 if (dev->agp)
609 dev->agp->agp_mtrr = mtrr_add( dev->agp->agp_info.aper_base,
610 dev->agp->agp_info.aper_size*1024*1024,
611 MTRR_TYPE_WRCOMB,
612 1 );
613#endif
614#endif
615
616#if __HAVE_CTX_BITMAP
617 retcode = DRM(ctxbitmap_init)( dev );
618 if( retcode ) {
619 DRM_ERROR( "Cannot allocate memory for context bitmap.\n" );
620 DRM(stub_unregister)(dev->minor);
621 DRM(takedown)( dev );
622 return retcode;
623 }
624#endif
625 DRM(numdevs)++; /* no errors, mark it reserved */
626
Eric Anholtb79d1b32003-10-23 05:56:13 +0000627 DRM_INFO( "Initialized %s %d.%d.%d %s on minor %d: %s\n",
Eric Anholtff584762003-10-17 03:14:39 +0000628 DRIVER_NAME,
629 DRIVER_MAJOR,
630 DRIVER_MINOR,
631 DRIVER_PATCHLEVEL,
632 DRIVER_DATE,
Eric Anholtb79d1b32003-10-23 05:56:13 +0000633 dev->minor,
634 desc );
Eric Anholtff584762003-10-17 03:14:39 +0000635
636 DRIVER_POSTINIT();
637
638 return 0;
David Dawes56bd9c22001-07-30 19:59:39 +0000639}
640
Eric Anholtff584762003-10-17 03:14:39 +0000641
Jose Fonsecad2443b22003-05-27 00:37:33 +0000642/**
643 * Module initialization. Called via init_module at module load time, or via
Gareth Hughes36047532001-02-15 08:12:14 +0000644 * linux/init/main.c (this is not currently supported).
Jose Fonsecad2443b22003-05-27 00:37:33 +0000645 *
646 * \return zero on success or a negative number on failure.
647 *
Eric Anholtff584762003-10-17 03:14:39 +0000648 * Initializes an array of drm_device structures, and attempts to
Jose Fonsecad2443b22003-05-27 00:37:33 +0000649 * initialize all available devices, using consecutive minors, registering the
650 * stubs and initializing the AGP device.
651 *
652 * Expands the \c DRIVER_PREINIT and \c DRIVER_POST_INIT macros before and
653 * after the initialization for driver customization.
Gareth Hughes36047532001-02-15 08:12:14 +0000654 */
655static int __init drm_init( void )
656{
Jon Smirle4a2a9c2003-10-24 17:40:54 +0000657 struct pci_dev *pdev = NULL;
Eric Anholtb79d1b32003-10-23 05:56:13 +0000658
Gareth Hughes36047532001-02-15 08:12:14 +0000659 DRM_DEBUG( "\n" );
660
Gareth Hughes36047532001-02-15 08:12:14 +0000661#ifdef MODULE
662 DRM(parse_options)( drm_opts );
663#endif
David Dawes56bd9c22001-07-30 19:59:39 +0000664
Gareth Hughes36047532001-02-15 08:12:14 +0000665 DRM(mem_init)();
666
Jon Smirle4a2a9c2003-10-24 17:40:54 +0000667 while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) {
Eric Anholtb79d1b32003-10-23 05:56:13 +0000668 DRM(probe)(pdev);
669 }
670 return 0;
Gareth Hughes36047532001-02-15 08:12:14 +0000671}
672
Jose Fonsecad2443b22003-05-27 00:37:33 +0000673/**
674 * Called via cleanup_module() at module unload time.
675 *
676 * Cleans up all DRM device, calling takedown().
677 *
678 * \sa drm_init().
Gareth Hughes36047532001-02-15 08:12:14 +0000679 */
680static void __exit drm_cleanup( void )
681{
David Dawes56bd9c22001-07-30 19:59:39 +0000682 drm_device_t *dev;
683 int i;
Gareth Hughes36047532001-02-15 08:12:14 +0000684
685 DRM_DEBUG( "\n" );
686
David Dawes56bd9c22001-07-30 19:59:39 +0000687 for (i = DRM(numdevs) - 1; i >= 0; i--) {
688 dev = &(DRM(device)[i]);
Eric Anholtff584762003-10-17 03:14:39 +0000689 if ( DRM(stub_unregister)(dev->minor) ) {
David Dawes56bd9c22001-07-30 19:59:39 +0000690 DRM_ERROR( "Cannot unload module\n" );
691 } else {
Eric Anholtff584762003-10-17 03:14:39 +0000692 DRM_DEBUG("minor %d unregistered\n", dev->minor);
David Dawes56bd9c22001-07-30 19:59:39 +0000693 if (i == 0) {
694 DRM_INFO( "Module unloaded\n" );
695 }
696 }
Gareth Hughes36047532001-02-15 08:12:14 +0000697#if __HAVE_CTX_BITMAP
David Dawes56bd9c22001-07-30 19:59:39 +0000698 DRM(ctxbitmap_cleanup)( dev );
Gareth Hughes36047532001-02-15 08:12:14 +0000699#endif
700
701#if __REALLY_HAVE_AGP && __REALLY_HAVE_MTRR
Jeff Hartmannb6923b32001-08-08 16:10:47 +0000702 if ( dev->agp && dev->agp->agp_mtrr >= 0) {
David Dawes56bd9c22001-07-30 19:59:39 +0000703 int retval;
704 retval = mtrr_del( dev->agp->agp_mtrr,
Gareth Hughes36047532001-02-15 08:12:14 +0000705 dev->agp->agp_info.aper_base,
706 dev->agp->agp_info.aper_size*1024*1024 );
David Dawes56bd9c22001-07-30 19:59:39 +0000707 DRM_DEBUG( "mtrr_del=%d\n", retval );
708 }
Gareth Hughes36047532001-02-15 08:12:14 +0000709#endif
710
David Dawes56bd9c22001-07-30 19:59:39 +0000711 DRM(takedown)( dev );
Gareth Hughes36047532001-02-15 08:12:14 +0000712
713#if __REALLY_HAVE_AGP
Jose Fonseca93522f62003-06-19 00:09:52 +0000714 if ( dev->agp ) {
715 DRM(agp_uninit)();
716 DRM(free)( dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS );
717 dev->agp = NULL;
718 }
Gareth Hughes36047532001-02-15 08:12:14 +0000719#endif
David Dawes56bd9c22001-07-30 19:59:39 +0000720 }
Jeff Hartmann51e38d92001-08-07 18:15:10 +0000721 DRIVER_POSTCLEANUP();
David Dawes56bd9c22001-07-30 19:59:39 +0000722 DRM(numdevs) = 0;
Gareth Hughes36047532001-02-15 08:12:14 +0000723}
724
725module_init( drm_init );
726module_exit( drm_cleanup );
727
728
Jose Fonsecad2443b22003-05-27 00:37:33 +0000729/**
730 * Get version information
731 *
732 * \param inode device inode.
733 * \param filp file pointer.
734 * \param cmd command.
735 * \param arg user argument, pointing to a drm_version structure.
736 * \return zero on success or negative number on failure.
737 *
738 * Fills in the version information in \p arg.
739 */
Gareth Hughes36047532001-02-15 08:12:14 +0000740int DRM(version)( struct inode *inode, struct file *filp,
741 unsigned int cmd, unsigned long arg )
742{
743 drm_version_t version;
744 int len;
745
746 if ( copy_from_user( &version,
747 (drm_version_t *)arg,
748 sizeof(version) ) )
749 return -EFAULT;
750
751#define DRM_COPY( name, value ) \
752 len = strlen( value ); \
753 if ( len > name##_len ) len = name##_len; \
754 name##_len = strlen( value ); \
755 if ( len && name ) { \
756 if ( copy_to_user( name, value, len ) ) \
757 return -EFAULT; \
758 }
759
760 version.version_major = DRIVER_MAJOR;
761 version.version_minor = DRIVER_MINOR;
762 version.version_patchlevel = DRIVER_PATCHLEVEL;
763
764 DRM_COPY( version.name, DRIVER_NAME );
765 DRM_COPY( version.date, DRIVER_DATE );
766 DRM_COPY( version.desc, DRIVER_DESC );
767
768 if ( copy_to_user( (drm_version_t *)arg,
769 &version,
770 sizeof(version) ) )
771 return -EFAULT;
772 return 0;
773}
774
Jose Fonsecad2443b22003-05-27 00:37:33 +0000775/**
776 * Open file.
777 *
778 * \param inode device inode
779 * \param filp file pointer.
780 * \return zero on success or a negative number on failure.
781 *
782 * Searches the DRM device with the same minor number, calls open_helper(), and
783 * increments the device open count. If the open count was previous at zero,
784 * i.e., it's the first that the device is open, then calls setup().
785 */
Gareth Hughes36047532001-02-15 08:12:14 +0000786int DRM(open)( struct inode *inode, struct file *filp )
787{
David Dawes56bd9c22001-07-30 19:59:39 +0000788 drm_device_t *dev = NULL;
Gareth Hughes36047532001-02-15 08:12:14 +0000789 int retcode = 0;
David Dawes56bd9c22001-07-30 19:59:39 +0000790 int i;
791
792 for (i = 0; i < DRM(numdevs); i++) {
Eric Anholtff584762003-10-17 03:14:39 +0000793 if (minor(inode->i_rdev) == DRM(device)[i].minor) {
David Dawes56bd9c22001-07-30 19:59:39 +0000794 dev = &(DRM(device)[i]);
795 break;
796 }
797 }
798 if (!dev) {
799 return -ENODEV;
800 }
Gareth Hughes36047532001-02-15 08:12:14 +0000801
Gareth Hughes36047532001-02-15 08:12:14 +0000802 retcode = DRM(open_helper)( inode, filp, dev );
803 if ( !retcode ) {
Gareth Hughes36047532001-02-15 08:12:14 +0000804 atomic_inc( &dev->counts[_DRM_STAT_OPENS] );
805 spin_lock( &dev->count_lock );
806 if ( !dev->open_count++ ) {
807 spin_unlock( &dev->count_lock );
808 return DRM(setup)( dev );
809 }
810 spin_unlock( &dev->count_lock );
811 }
812
813 return retcode;
814}
815
Jose Fonsecad2443b22003-05-27 00:37:33 +0000816/**
817 * Release file.
818 *
819 * \param inode device inode
820 * \param filp file pointer.
821 * \return zero on success or a negative number on failure.
822 *
823 * If the hardware lock is held then free it, and take it again for the kernel
824 * context since it's necessary to reclaim buffers. Unlink the file private
825 * data from its list and free it. Decreases the open count and if it reaches
826 * zero calls takedown().
827 */
Gareth Hughes36047532001-02-15 08:12:14 +0000828int DRM(release)( struct inode *inode, struct file *filp )
829{
830 drm_file_t *priv = filp->private_data;
831 drm_device_t *dev;
832 int retcode = 0;
833
834 lock_kernel();
835 dev = priv->dev;
836
837 DRM_DEBUG( "open_count = %d\n", dev->open_count );
838
839 DRIVER_PRERELEASE();
840
841 /* ========================================================
842 * Begin inline drm_release
843 */
844
Alan Hourihaneb3eb34e2003-03-25 11:36:43 +0000845 DRM_DEBUG( "pid = %d, device = 0x%lx, open_count = %d\n",
846 current->pid, (long)dev->device, dev->open_count );
Gareth Hughes36047532001-02-15 08:12:14 +0000847
David Dawesc2d7ff12003-04-17 18:41:28 +0000848 if ( priv->lock_count && dev->lock.hw_lock &&
Gareth Hughes36047532001-02-15 08:12:14 +0000849 _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) &&
Keith Whitwell1728bc62003-03-28 14:27:37 +0000850 dev->lock.filp == filp ) {
851 DRM_DEBUG( "File %p released, freeing lock for context %d\n",
852 filp,
853 _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) );
Gareth Hughes36047532001-02-15 08:12:14 +0000854#if __HAVE_RELEASE
855 DRIVER_RELEASE();
856#endif
857 DRM(lock_free)( dev, &dev->lock.hw_lock->lock,
858 _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) );
859
860 /* FIXME: may require heavy-handed reset of
861 hardware at this point, possibly
862 processed via a callback to the X
863 server. */
864 }
865#if __HAVE_RELEASE
David Dawesc2d7ff12003-04-17 18:41:28 +0000866 else if ( priv->lock_count && dev->lock.hw_lock ) {
Gareth Hughes36047532001-02-15 08:12:14 +0000867 /* The lock is required to reclaim buffers */
868 DECLARE_WAITQUEUE( entry, current );
Keith Whitwell1728bc62003-03-28 14:27:37 +0000869
Gareth Hughes36047532001-02-15 08:12:14 +0000870 add_wait_queue( &dev->lock.lock_queue, &entry );
871 for (;;) {
872 current->state = TASK_INTERRUPTIBLE;
873 if ( !dev->lock.hw_lock ) {
874 /* Device has been unregistered */
875 retcode = -EINTR;
876 break;
877 }
878 if ( DRM(lock_take)( &dev->lock.hw_lock->lock,
879 DRM_KERNEL_CONTEXT ) ) {
Keith Whitwell1728bc62003-03-28 14:27:37 +0000880 dev->lock.filp = filp;
Gareth Hughes36047532001-02-15 08:12:14 +0000881 dev->lock.lock_time = jiffies;
882 atomic_inc( &dev->counts[_DRM_STAT_LOCKS] );
883 break; /* Got lock */
884 }
885 /* Contention */
Gareth Hughes36047532001-02-15 08:12:14 +0000886 schedule();
887 if ( signal_pending( current ) ) {
888 retcode = -ERESTARTSYS;
889 break;
890 }
891 }
892 current->state = TASK_RUNNING;
893 remove_wait_queue( &dev->lock.lock_queue, &entry );
894 if( !retcode ) {
895 DRIVER_RELEASE();
896 DRM(lock_free)( dev, &dev->lock.hw_lock->lock,
897 DRM_KERNEL_CONTEXT );
898 }
899 }
Gareth Hughes01a14782001-02-16 05:24:06 +0000900#elif __HAVE_DMA
Keith Whitwell1728bc62003-03-28 14:27:37 +0000901 DRM(reclaim_buffers)( filp );
Gareth Hughes36047532001-02-15 08:12:14 +0000902#endif
903
904 DRM(fasync)( -1, filp, 0 );
905
Keith Whitwell82157572004-02-20 22:55:12 +0000906 down( &dev->ctxlist_sem );
907 if ( !list_empty( &dev->ctxlist->head ) ) {
908 drm_ctx_list_t *pos, *n;
909
910 list_for_each_entry_safe( pos, n, &dev->ctxlist->head, head ) {
911 if ( pos->tag == priv &&
912 pos->handle != DRM_KERNEL_CONTEXT ) {
913#ifdef DRIVER_CTX_DTOR
914 DRIVER_CTX_DTOR(pos->handle);
915#endif
Jon Smirl0b6f1552004-03-16 00:52:24 +0000916#if __HAVE_CTX_BITMAP
Keith Whitwell82157572004-02-20 22:55:12 +0000917 DRM(ctxbitmap_free)( dev, pos->handle );
Jon Smirl0b6f1552004-03-16 00:52:24 +0000918#endif
Keith Whitwell82157572004-02-20 22:55:12 +0000919 list_del( &pos->head );
920 DRM(free)( pos, sizeof(*pos), DRM_MEM_CTXLIST );
921 }
922 }
923 }
924 up( &dev->ctxlist_sem );
925
Gareth Hughes36047532001-02-15 08:12:14 +0000926 down( &dev->struct_sem );
927 if ( priv->remove_auth_on_close == 1 ) {
928 drm_file_t *temp = dev->file_first;
929 while ( temp ) {
930 temp->authenticated = 0;
931 temp = temp->next;
932 }
933 }
934 if ( priv->prev ) {
935 priv->prev->next = priv->next;
936 } else {
937 dev->file_first = priv->next;
938 }
939 if ( priv->next ) {
940 priv->next->prev = priv->prev;
941 } else {
942 dev->file_last = priv->prev;
943 }
944 up( &dev->struct_sem );
Keith Whitwell1728bc62003-03-28 14:27:37 +0000945
Gareth Hughes36047532001-02-15 08:12:14 +0000946 DRM(free)( priv, sizeof(*priv), DRM_MEM_FILES );
947
948 /* ========================================================
949 * End inline drm_release
950 */
951
Gareth Hughes36047532001-02-15 08:12:14 +0000952 atomic_inc( &dev->counts[_DRM_STAT_CLOSES] );
953 spin_lock( &dev->count_lock );
954 if ( !--dev->open_count ) {
955 if ( atomic_read( &dev->ioctl_count ) || dev->blocked ) {
956 DRM_ERROR( "Device busy: %d %d\n",
957 atomic_read( &dev->ioctl_count ),
958 dev->blocked );
959 spin_unlock( &dev->count_lock );
960 unlock_kernel();
961 return -EBUSY;
962 }
963 spin_unlock( &dev->count_lock );
964 unlock_kernel();
965 return DRM(takedown)( dev );
966 }
967 spin_unlock( &dev->count_lock );
968
969 unlock_kernel();
Keith Whitwell1728bc62003-03-28 14:27:37 +0000970
Gareth Hughes36047532001-02-15 08:12:14 +0000971 return retcode;
972}
973
Jose Fonsecad2443b22003-05-27 00:37:33 +0000974/**
975 * Called whenever a process performs an ioctl on /dev/drm.
976 *
977 * \param inode device inode.
978 * \param filp file pointer.
979 * \param cmd command.
980 * \param arg user argument.
981 * \return zero on success or negative number on failure.
982 *
983 * Looks up the ioctl function in the ::ioctls table, checking for root
984 * previleges if so required, and dispatches to the respective function.
Gareth Hughes36047532001-02-15 08:12:14 +0000985 */
986int DRM(ioctl)( struct inode *inode, struct file *filp,
987 unsigned int cmd, unsigned long arg )
988{
989 drm_file_t *priv = filp->private_data;
990 drm_device_t *dev = priv->dev;
991 drm_ioctl_desc_t *ioctl;
992 drm_ioctl_t *func;
993 int nr = DRM_IOCTL_NR(cmd);
994 int retcode = 0;
995
996 atomic_inc( &dev->ioctl_count );
997 atomic_inc( &dev->counts[_DRM_STAT_IOCTLS] );
998 ++priv->ioctl_count;
999
Alan Hourihaneb3eb34e2003-03-25 11:36:43 +00001000 DRM_DEBUG( "pid=%d, cmd=0x%02x, nr=0x%02x, dev 0x%lx, auth=%d\n",
1001 current->pid, cmd, nr, (long)dev->device,
1002 priv->authenticated );
Gareth Hughes36047532001-02-15 08:12:14 +00001003
1004 if ( nr >= DRIVER_IOCTL_COUNT ) {
1005 retcode = -EINVAL;
1006 } else {
1007 ioctl = &DRM(ioctls)[nr];
1008 func = ioctl->func;
1009
1010 if ( !func ) {
1011 DRM_DEBUG( "no function\n" );
1012 retcode = -EINVAL;
1013 } else if ( ( ioctl->root_only && !capable( CAP_SYS_ADMIN ) )||
1014 ( ioctl->auth_needed && !priv->authenticated ) ) {
1015 retcode = -EACCES;
1016 } else {
1017 retcode = func( inode, filp, cmd, arg );
1018 }
1019 }
1020
1021 atomic_dec( &dev->ioctl_count );
1022 return retcode;
1023}
1024
Jose Fonsecad2443b22003-05-27 00:37:33 +00001025/**
1026 * Lock ioctl.
1027 *
1028 * \param inode device inode.
1029 * \param filp file pointer.
1030 * \param cmd command.
1031 * \param arg user argument, pointing to a drm_lock structure.
1032 * \return zero on success or negative number on failure.
1033 *
1034 * Add the current task to the lock wait queue, and attempt to take to lock.
1035 */
Gareth Hughes36047532001-02-15 08:12:14 +00001036int DRM(lock)( struct inode *inode, struct file *filp,
1037 unsigned int cmd, unsigned long arg )
1038{
1039 drm_file_t *priv = filp->private_data;
1040 drm_device_t *dev = priv->dev;
1041 DECLARE_WAITQUEUE( entry, current );
1042 drm_lock_t lock;
1043 int ret = 0;
1044#if __HAVE_MULTIPLE_DMA_QUEUES
1045 drm_queue_t *q;
1046#endif
Gareth Hughes36047532001-02-15 08:12:14 +00001047
David Dawesc2d7ff12003-04-17 18:41:28 +00001048 ++priv->lock_count;
1049
Gareth Hughes36047532001-02-15 08:12:14 +00001050 if ( copy_from_user( &lock, (drm_lock_t *)arg, sizeof(lock) ) )
1051 return -EFAULT;
1052
1053 if ( lock.context == DRM_KERNEL_CONTEXT ) {
1054 DRM_ERROR( "Process %d using kernel context %d\n",
1055 current->pid, lock.context );
1056 return -EINVAL;
1057 }
1058
1059 DRM_DEBUG( "%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
1060 lock.context, current->pid,
1061 dev->lock.hw_lock->lock, lock.flags );
1062
1063#if __HAVE_DMA_QUEUE
1064 if ( lock.context < 0 )
1065 return -EINVAL;
1066#elif __HAVE_MULTIPLE_DMA_QUEUES
1067 if ( lock.context < 0 || lock.context >= dev->queue_count )
1068 return -EINVAL;
1069 q = dev->queuelist[lock.context];
1070#endif
1071
1072#if __HAVE_DMA_FLUSH
1073 ret = DRM(flush_block_and_flush)( dev, lock.context, lock.flags );
1074#endif
1075 if ( !ret ) {
1076 add_wait_queue( &dev->lock.lock_queue, &entry );
1077 for (;;) {
1078 current->state = TASK_INTERRUPTIBLE;
1079 if ( !dev->lock.hw_lock ) {
1080 /* Device has been unregistered */
1081 ret = -EINTR;
1082 break;
1083 }
1084 if ( DRM(lock_take)( &dev->lock.hw_lock->lock,
1085 lock.context ) ) {
Keith Whitwell1728bc62003-03-28 14:27:37 +00001086 dev->lock.filp = filp;
Gareth Hughes36047532001-02-15 08:12:14 +00001087 dev->lock.lock_time = jiffies;
1088 atomic_inc( &dev->counts[_DRM_STAT_LOCKS] );
1089 break; /* Got lock */
1090 }
1091
1092 /* Contention */
1093 schedule();
1094 if ( signal_pending( current ) ) {
1095 ret = -ERESTARTSYS;
1096 break;
1097 }
1098 }
1099 current->state = TASK_RUNNING;
1100 remove_wait_queue( &dev->lock.lock_queue, &entry );
1101 }
1102
1103#if __HAVE_DMA_FLUSH
1104 DRM(flush_unblock)( dev, lock.context, lock.flags ); /* cleanup phase */
1105#endif
1106
1107 if ( !ret ) {
1108 sigemptyset( &dev->sigmask );
1109 sigaddset( &dev->sigmask, SIGSTOP );
1110 sigaddset( &dev->sigmask, SIGTSTP );
1111 sigaddset( &dev->sigmask, SIGTTIN );
1112 sigaddset( &dev->sigmask, SIGTTOU );
1113 dev->sigdata.context = lock.context;
1114 dev->sigdata.lock = dev->lock.hw_lock;
1115 block_all_signals( DRM(notifier),
1116 &dev->sigdata, &dev->sigmask );
1117
1118#if __HAVE_DMA_READY
1119 if ( lock.flags & _DRM_LOCK_READY ) {
1120 DRIVER_DMA_READY();
1121 }
1122#endif
1123#if __HAVE_DMA_QUIESCENT
1124 if ( lock.flags & _DRM_LOCK_QUIESCENT ) {
1125 DRIVER_DMA_QUIESCENT();
1126 }
1127#endif
Keith Whitwell36696392003-07-11 15:27:55 +00001128 /* __HAVE_KERNEL_CTX_SWITCH isn't used by any of the
1129 * drm modules in the DRI cvs tree, but it is required
1130 * by the Sparc driver.
1131 */
1132#if __HAVE_KERNEL_CTX_SWITCH
1133 if ( dev->last_context != lock.context ) {
1134 DRM(context_switch)(dev, dev->last_context,
1135 lock.context);
1136 }
1137#endif
Gareth Hughes36047532001-02-15 08:12:14 +00001138 }
1139
1140 DRM_DEBUG( "%d %s\n", lock.context, ret ? "interrupted" : "has lock" );
1141
Gareth Hughes36047532001-02-15 08:12:14 +00001142 return ret;
1143}
1144
Jose Fonsecad2443b22003-05-27 00:37:33 +00001145/**
1146 * Unlock ioctl.
1147 *
1148 * \param inode device inode.
1149 * \param filp file pointer.
1150 * \param cmd command.
1151 * \param arg user argument, pointing to a drm_lock structure.
1152 * \return zero on success or negative number on failure.
1153 *
1154 * Transfer and free the lock.
1155 */
Gareth Hughes36047532001-02-15 08:12:14 +00001156int DRM(unlock)( struct inode *inode, struct file *filp,
1157 unsigned int cmd, unsigned long arg )
1158{
1159 drm_file_t *priv = filp->private_data;
1160 drm_device_t *dev = priv->dev;
1161 drm_lock_t lock;
1162
1163 if ( copy_from_user( &lock, (drm_lock_t *)arg, sizeof(lock) ) )
1164 return -EFAULT;
1165
1166 if ( lock.context == DRM_KERNEL_CONTEXT ) {
1167 DRM_ERROR( "Process %d using kernel context %d\n",
1168 current->pid, lock.context );
1169 return -EINVAL;
1170 }
1171
1172 atomic_inc( &dev->counts[_DRM_STAT_UNLOCKS] );
1173
Keith Whitwell36696392003-07-11 15:27:55 +00001174 /* __HAVE_KERNEL_CTX_SWITCH isn't used by any of the drm
1175 * modules in the DRI cvs tree, but it is required by the
1176 * Sparc driver.
1177 */
1178#if __HAVE_KERNEL_CTX_SWITCH
1179 /* We no longer really hold it, but if we are the next
1180 * agent to request it then we should just be able to
1181 * take it immediately and not eat the ioctl.
1182 */
1183 dev->lock.filp = 0;
1184 {
1185 __volatile__ unsigned int *plock = &dev->lock.hw_lock->lock;
1186 unsigned int old, new, prev, ctx;
1187
1188 ctx = lock.context;
1189 do {
1190 old = *plock;
1191 new = ctx;
1192 prev = cmpxchg(plock, old, new);
1193 } while (prev != old);
1194 }
1195 wake_up_interruptible(&dev->lock.lock_queue);
1196#else
Gareth Hughes36047532001-02-15 08:12:14 +00001197 DRM(lock_transfer)( dev, &dev->lock.hw_lock->lock,
1198 DRM_KERNEL_CONTEXT );
1199#if __HAVE_DMA_SCHEDULE
1200 DRM(dma_schedule)( dev, 1 );
1201#endif
1202
Keith Whitwell2daf1472003-07-08 17:10:13 +00001203 if ( DRM(lock_free)( dev, &dev->lock.hw_lock->lock,
1204 DRM_KERNEL_CONTEXT ) ) {
Keith Whitwell36696392003-07-11 15:27:55 +00001205 DRM_ERROR( "\n" );
Gareth Hughes36047532001-02-15 08:12:14 +00001206 }
Keith Whitwell36696392003-07-11 15:27:55 +00001207#endif /* !__HAVE_KERNEL_CTX_SWITCH */
Gareth Hughes36047532001-02-15 08:12:14 +00001208
1209 unblock_all_signals();
1210 return 0;
1211}