]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/drm/drm_drv.c
Disable the drm_initmap calls in radeon_cp.c, due to them resulting in improper
[FreeBSD/FreeBSD.git] / sys / dev / drm / drm_drv.c
1 /* drm_drv.h -- Generic driver template -*- linux-c -*-
2  * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com
3  */
4 /*-
5  * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
6  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
7  * All Rights Reserved.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice (including the next
17  * paragraph) shall be included in all copies or substantial portions of the
18  * Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26  * OTHER DEALINGS IN THE SOFTWARE.
27  *
28  * Authors:
29  *    Rickard E. (Rik) Faith <faith@valinux.com>
30  *    Gareth Hughes <gareth@valinux.com>
31  *
32  * $FreeBSD$
33  */
34
35 #include "dev/drm/drmP.h"
36 #include "dev/drm/drm.h"
37
38 int drm_debug_flag = 0;
39
40 static int drm_init(device_t nbdev);
41 static void drm_cleanup(drm_device_t *dev);
42
43 #ifdef __FreeBSD__
44 #define DRIVER_SOFTC(unit) \
45         ((drm_device_t *)devclass_get_softc(drm_devclass, unit))
46
47 MODULE_VERSION(drm, 1);
48 MODULE_DEPEND(drm, agp, 1, 1, 1);
49 MODULE_DEPEND(drm, pci, 1, 1, 1);
50 #if __FreeBSD_version > 502127
51 MODULE_DEPEND(drm, mem, 1, 1, 1);
52 #endif
53 #endif /* __FreeBSD__ */
54
55 #if defined(__NetBSD__) || defined(__OpenBSD__)
56 #define DRIVER_SOFTC(unit) \
57         ((drm_device_t *)device_lookup(&drm_cd, unit))
58 #endif /* __NetBSD__ || __OpenBSD__ */
59
60 static drm_ioctl_desc_t           drm_ioctls[256] = {
61         [DRM_IOCTL_NR(DRM_IOCTL_VERSION)]       = { drm_version,     0, 0 },
62         [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]    = { drm_getunique,   0, 0 },
63         [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]     = { drm_getmagic,    0, 0 },
64         [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]     = { drm_irq_by_busid, 0, 1 },
65         [DRM_IOCTL_NR(DRM_IOCTL_GET_MAP)]       = { drm_getmap,      0, 0 },
66         [DRM_IOCTL_NR(DRM_IOCTL_GET_CLIENT)]    = { drm_getclient,   0, 0 },
67         [DRM_IOCTL_NR(DRM_IOCTL_GET_STATS)]     = { drm_getstats,    0, 0 },
68         [DRM_IOCTL_NR(DRM_IOCTL_SET_VERSION)]   = { drm_setversion,  0, 1 },
69
70         [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]    = { drm_setunique,   1, 1 },
71         [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]         = { drm_noop,        1, 1 },
72         [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]       = { drm_noop,        1, 1 },
73         [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]    = { drm_authmagic,   1, 1 },
74
75         [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]       = { drm_addmap,      1, 1 },
76         [DRM_IOCTL_NR(DRM_IOCTL_RM_MAP)]        = { drm_rmmap,       1, 0 },
77
78         [DRM_IOCTL_NR(DRM_IOCTL_SET_SAREA_CTX)] = { drm_setsareactx, 1, 1 },
79         [DRM_IOCTL_NR(DRM_IOCTL_GET_SAREA_CTX)] = { drm_getsareactx, 1, 0 },
80
81         [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]       = { drm_addctx,      1, 1 },
82         [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]        = { drm_rmctx,       1, 1 },
83         [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]       = { drm_modctx,      1, 1 },
84         [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]       = { drm_getctx,      1, 0 },
85         [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]    = { drm_switchctx,   1, 1 },
86         [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]       = { drm_newctx,      1, 1 },
87         [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]       = { drm_resctx,      1, 0 },
88
89         [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]      = { drm_adddraw,     1, 1 },
90         [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]       = { drm_rmdraw,      1, 1 },
91
92         [DRM_IOCTL_NR(DRM_IOCTL_LOCK)]          = { drm_lock,        1, 0 },
93         [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]        = { drm_unlock,      1, 0 },
94         [DRM_IOCTL_NR(DRM_IOCTL_FINISH)]        = { drm_noop,        1, 0 },
95
96         [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]      = { drm_addbufs,     1, 1 },
97         [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]     = { drm_markbufs,    1, 1 },
98         [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]     = { drm_infobufs,    1, 0 },
99         [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]      = { drm_mapbufs,     1, 0 },
100         [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]     = { drm_freebufs,    1, 0 },
101         [DRM_IOCTL_NR(DRM_IOCTL_DMA)]           = { drm_dma,         1, 0 },
102
103         [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]       = { drm_control,     1, 1 },
104
105         [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)]   = { drm_agp_acquire, 1, 1 },
106         [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)]   = { drm_agp_release, 1, 1 },
107         [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]    = { drm_agp_enable,  1, 1 },
108         [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]      = { drm_agp_info,    1, 0 },
109         [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]     = { drm_agp_alloc,   1, 1 },
110         [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]      = { drm_agp_free,    1, 1 },
111         [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]      = { drm_agp_bind,    1, 1 },
112         [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]    = { drm_agp_unbind,  1, 1 },
113
114         [DRM_IOCTL_NR(DRM_IOCTL_SG_ALLOC)]      = { drm_sg_alloc,    1, 1 },
115         [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)]       = { drm_sg_free,     1, 1 },
116
117         [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)]   = { drm_wait_vblank, 0, 0 },
118 };
119
120 const char *drm_find_description(int vendor, int device, drm_pci_id_list_t *idlist);
121
122 #ifdef __FreeBSD__
123 static struct cdevsw drm_cdevsw = {
124 #if __FreeBSD_version >= 502103
125         .d_version =    D_VERSION,
126 #endif
127         .d_open =       drm_open,
128         .d_close =      drm_close,
129         .d_read =       drm_read,
130         .d_ioctl =      drm_ioctl,
131         .d_poll =       drm_poll,
132         .d_mmap =       drm_mmap,
133         .d_name =       "drm",
134 #if __FreeBSD_version >= 502103
135         .d_flags =      D_TRACKCLOSE | D_NEEDGIANT,
136 #else
137         .d_maj =        145,
138         .d_flags =      D_TRACKCLOSE,
139 #endif
140 #if __FreeBSD_version < 500000
141         .d_bmaj =       -1
142 #endif
143 };
144
145 int drm_probe(device_t dev, drm_pci_id_list_t *idlist)
146 {
147         const char *s = NULL;
148         int vendor, device;
149
150         vendor = pci_get_vendor(dev);
151         device = pci_get_device(dev);
152
153         s = drm_find_description(vendor, device, idlist);
154         if (s != NULL) {
155                 device_set_desc(dev, s);
156                 return 0;
157         }
158
159         return ENXIO;
160 }
161
162 int drm_attach(device_t dev, drm_pci_id_list_t *idlist)
163 {
164         return drm_init(dev);
165 }
166
167 int drm_detach(device_t dev)
168 {
169         drm_cleanup(device_get_softc(dev));
170         return 0;
171 }
172
173 #ifndef DRM_DEV_NAME
174 #define DRM_DEV_NAME "drm"
175 #endif
176
177 devclass_t drm_devclass;
178
179 #elif defined(__NetBSD__) || defined(__OpenBSD__)
180
181 static struct cdevsw drm_cdevsw = {
182         drm_open,
183         drm_close,
184         drm_read,
185         nowrite,
186         drm_ioctl,
187         nostop,
188         notty,
189         drm_poll,
190         drm_mmap,
191         nokqfilter,
192         D_TTY
193 };
194
195 int drm_refcnt = 0;
196
197 #if defined(__NetBSD__) && __NetBSD_Version__ >= 106080000
198 MOD_DEV("drm", DRIVER_NAME, NULL, -1, &drm_cdevsw, CDEV_MAJOR);
199 #else
200 MOD_DEV("drm", LM_DT_CHAR, CDEV_MAJOR, &drm_cdevsw);
201 #endif
202
203 int drm_lkmentry(struct lkm_table *lkmtp, int cmd, int ver);
204 static int drm_lkmhandle(struct lkm_table *lkmtp, int cmd);
205
206 int drm_modprobe();
207 int drm_probe(struct pci_attach_args *pa);
208 void drm_attach(struct pci_attach_args *pa, dev_t kdev);
209
210 int drm_lkmentry(struct lkm_table *lkmtp, int cmd, int ver) {
211         DISPATCH(lkmtp, cmd, ver, drm_lkmhandle, drm_lkmhandle, drm_lkmhandle);
212 }
213
214 static int drm_lkmhandle(struct lkm_table *lkmtp, int cmd)
215 {
216         int j, error = 0;
217 #if defined(__NetBSD__) && (__NetBSD_Version__ > 106080000)
218         struct lkm_dev *args = lkmtp->private.lkm_dev;
219 #endif
220
221         switch(cmd) {
222         case LKM_E_LOAD:
223                 if (lkmexists(lkmtp))
224                         return EEXIST;
225
226                 if(drm_modprobe())
227                         return 0;
228
229                 return 1;
230
231         case LKM_E_UNLOAD:
232                 if (drm_refcnt > 0)
233                         return (EBUSY);
234                 break;
235         case LKM_E_STAT:
236                 break;
237
238         default:
239                 error = EIO;
240                 break;
241         }
242         
243         return error;
244 }
245
246 int drm_modprobe() {
247         struct pci_attach_args pa;
248         int error;
249
250         error = pci_find_device(&pa, drm_probe, idlist);
251         if (error != 0)
252                 drm_attach(&pa, 0);
253
254         return error;
255 }
256
257 int drm_probe(struct pci_attach_args *pa, drm_pci_id_list_t idlist)
258 {
259         const char *desc;
260
261         desc = drm_find_description(PCI_VENDOR(pa->pa_id),
262             PCI_PRODUCT(pa->pa_id), idlist);
263         if (desc != NULL) {
264                 return 1;
265         }
266
267         return 0;
268 }
269
270 void drm_attach(struct pci_attach_args *pa, dev_t kdev, 
271     drm_pci_id_list_t *idlist)
272 {
273         int i;
274         drm_device_t *dev;
275
276         config_makeroom(kdev, &drm_cd);
277         drm_cd.cd_devs[(kdev)] = malloc(sizeof(drm_device_t), M_DRM, M_WAITOK);
278         dev = DRIVER_SOFTC(kdev);
279
280         memset(dev, 0, sizeof(drm_device_t));
281         memcpy(&dev->pa, pa, sizeof(dev->pa));
282
283         DRM_INFO("%s", drm_find_description(PCI_VENDOR(pa->pa_id), PCI_PRODUCT(pa->pa_id), idlist));
284         drm_init(dev);
285 }
286
287 int drm_detach(struct device *self, int flags)
288 {
289         drm_cleanup((drm_device_t *)self);
290         return 0;
291 }
292
293 int drm_activate(struct device *self, enum devact act)
294 {
295         switch (act) {
296         case DVACT_ACTIVATE:
297                 return (EOPNOTSUPP);
298                 break;
299
300         case DVACT_DEACTIVATE:
301                 /* FIXME */
302                 break;
303         }
304         return (0);
305 }
306 #endif /* __NetBSD__ || __OpenBSD__ */
307
308 const char *drm_find_description(int vendor, int device, drm_pci_id_list_t *idlist) {
309         int i = 0;
310         
311         for (i = 0; idlist[i].vendor != 0; i++) {
312                 if ((idlist[i].vendor == vendor) &&
313                     (idlist[i].device == device)) {
314                         return idlist[i].name;
315                 }
316         }
317         return NULL;
318 }
319
320 /* Initialize the DRM on first open. */
321 static int drm_setup(drm_device_t *dev)
322 {
323         int i;
324
325         DRM_SPINLOCK_ASSERT(&dev->dev_lock);
326
327         if (dev->presetup)
328                 dev->presetup(dev);
329
330         dev->buf_use = 0;
331
332         if (dev->use_dma) {
333                 i = drm_dma_setup(dev);
334                 if (i != 0)
335                         return i;
336         }
337
338         dev->counters  = 6;
339         dev->types[0]  = _DRM_STAT_LOCK;
340         dev->types[1]  = _DRM_STAT_OPENS;
341         dev->types[2]  = _DRM_STAT_CLOSES;
342         dev->types[3]  = _DRM_STAT_IOCTLS;
343         dev->types[4]  = _DRM_STAT_LOCKS;
344         dev->types[5]  = _DRM_STAT_UNLOCKS;
345
346         for ( i = 0 ; i < DRM_ARRAY_SIZE(dev->counts) ; i++ )
347                 atomic_set( &dev->counts[i], 0 );
348
349         for ( i = 0 ; i < DRM_HASH_SIZE ; i++ ) {
350                 dev->magiclist[i].head = NULL;
351                 dev->magiclist[i].tail = NULL;
352         }
353
354         dev->lock.hw_lock = NULL;
355         dev->lock.lock_queue = 0;
356         dev->irq_enabled = 0;
357         dev->context_flag = 0;
358         dev->last_context = 0;
359         dev->if_version = 0;
360
361 #ifdef __FreeBSD__
362         dev->buf_sigio = NULL;
363 #elif defined(__NetBSD__) || defined(__OpenBSD__)
364         dev->buf_pgid = 0;
365 #endif
366
367         DRM_DEBUG( "\n" );
368
369         if (dev->postsetup)
370                 dev->postsetup(dev);
371
372         return 0;
373 }
374
375 /* Free resources associated with the DRM on the last close. */
376 static int drm_takedown(drm_device_t *dev)
377 {
378         drm_magic_entry_t *pt, *next;
379         drm_local_map_t *map, *mapsave;
380         int i;
381
382         DRM_SPINLOCK_ASSERT(&dev->dev_lock);
383
384         DRM_DEBUG( "\n" );
385
386         if (dev->pretakedown != NULL)
387                 dev->pretakedown(dev);
388
389         if (dev->irq_enabled)
390                 drm_irq_uninstall(dev);
391
392         if ( dev->unique ) {
393                 free(dev->unique, M_DRM);
394                 dev->unique = NULL;
395                 dev->unique_len = 0;
396         }
397                                 /* Clear pid list */
398         for ( i = 0 ; i < DRM_HASH_SIZE ; i++ ) {
399                 for ( pt = dev->magiclist[i].head ; pt ; pt = next ) {
400                         next = pt->next;
401                         free(pt, M_DRM);
402                 }
403                 dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
404         }
405
406                                 /* Clear AGP information */
407         if ( dev->agp ) {
408                 drm_agp_mem_t *entry;
409                 drm_agp_mem_t *nexte;
410
411                                 /* Remove AGP resources, but leave dev->agp
412                                    intact until drm_cleanup is called. */
413                 for ( entry = dev->agp->memory ; entry ; entry = nexte ) {
414                         nexte = entry->next;
415                         if ( entry->bound )
416                                 drm_agp_unbind_memory(entry->handle);
417                         drm_agp_free_memory(entry->handle);
418                         free(entry, M_DRM);
419                 }
420                 dev->agp->memory = NULL;
421
422                 if (dev->agp->acquired)
423                         drm_agp_do_release();
424
425                 dev->agp->acquired = 0;
426                 dev->agp->enabled  = 0;
427         }
428         if (dev->sg != NULL) {
429                 drm_sg_cleanup(dev->sg);
430                 dev->sg = NULL;
431         }
432
433         /* Clean up maps that weren't set up by the driver. */
434         TAILQ_FOREACH_SAFE(map, &dev->maplist, link, mapsave) {
435                 if (!map->kernel_owned)
436                         drm_remove_map(dev, map);
437         }
438
439         drm_dma_takedown(dev);
440         if ( dev->lock.hw_lock ) {
441                 dev->lock.hw_lock = NULL; /* SHM removed */
442                 dev->lock.filp = NULL;
443                 DRM_WAKEUP_INT((void *)&dev->lock.lock_queue);
444         }
445
446         return 0;
447 }
448
449 /* linux: drm_init is called via init_module at module load time, or via
450  *        linux/init/main.c (this is not currently supported).
451  * bsd:   drm_init is called via the attach function per device.
452  */
453 static int drm_init(device_t nbdev)
454 {
455         int unit;
456         drm_device_t *dev;
457         int retcode;
458         DRM_DEBUG( "\n" );
459
460 #ifdef __FreeBSD__
461         unit = device_get_unit(nbdev);
462         dev = device_get_softc(nbdev);
463
464         if (!strcmp(device_get_name(nbdev), "drmsub"))
465                 dev->device = device_get_parent(nbdev);
466         else
467                 dev->device = nbdev;
468
469         dev->devnode = make_dev(&drm_cdevsw,
470                         unit,
471                         DRM_DEV_UID,
472                         DRM_DEV_GID,
473                         DRM_DEV_MODE,
474                         "dri/card%d", unit);
475 #if __FreeBSD_version >= 500000
476         mtx_init(&dev->dev_lock, "drm device", NULL, MTX_DEF);
477 #endif
478 #elif defined(__NetBSD__) || defined(__OpenBSD__)
479         dev = nbdev;
480         unit = minor(dev->device.dv_unit);
481 #endif
482
483         dev->irq = pci_get_irq(dev->device);
484         /* XXX Fix domain number (alpha hoses) */
485         dev->pci_domain = 0;
486         dev->pci_bus = pci_get_bus(dev->device);
487         dev->pci_slot = pci_get_slot(dev->device);
488         dev->pci_func = pci_get_function(dev->device);
489
490         TAILQ_INIT(&dev->maplist);
491
492         drm_mem_init();
493 #ifdef __FreeBSD__
494         drm_sysctl_init(dev);
495 #endif
496         TAILQ_INIT(&dev->files);
497
498         if (dev->preinit != NULL) {
499                 retcode = dev->preinit(dev, 0);
500                 if (retcode != 0)
501                         goto error;
502         }
503
504         if (dev->use_agp) {
505                 if (drm_device_is_agp(dev))
506                         dev->agp = drm_agp_init();
507                 if (dev->require_agp && dev->agp == NULL) {
508                         DRM_ERROR("Card isn't AGP, or couldn't initialize "
509                             "AGP.\n");
510                         retcode = DRM_ERR(ENOMEM);
511                         goto error;
512                 }
513                 if (dev->agp != NULL) {
514                         if (drm_mtrr_add(dev->agp->info.ai_aperture_base,
515                             dev->agp->info.ai_aperture_size, DRM_MTRR_WC) == 0)
516                                 dev->agp->mtrr = 1;
517                 }
518         }
519
520         retcode = drm_ctxbitmap_init(dev);
521         if (retcode != 0) {
522                 DRM_ERROR("Cannot allocate memory for context bitmap.\n");
523                 goto error;
524         }
525         
526         DRM_INFO( "Initialized %s %d.%d.%d %s on minor %d\n",
527                 dev->driver_name,
528                 dev->driver_major,
529                 dev->driver_minor,
530                 dev->driver_patchlevel,
531                 dev->driver_date,
532                 unit );
533
534         if (dev->postinit != NULL)
535                 dev->postinit(dev, 0);
536
537         return 0;
538
539 error:
540 #ifdef __FreeBSD__
541         drm_sysctl_cleanup(dev);
542 #endif
543         DRM_LOCK();
544         drm_takedown(dev);
545         DRM_UNLOCK();
546 #ifdef __FreeBSD__
547         destroy_dev(dev->devnode);
548 #if __FreeBSD_version >= 500000
549         mtx_destroy(&dev->dev_lock);
550 #endif
551 #endif
552         return retcode;
553 }
554
555 /* linux: drm_cleanup is called via cleanup_module at module unload time.
556  * bsd:   drm_cleanup is called per device at module unload time.
557  * FIXME: NetBSD
558  */
559 static void drm_cleanup(drm_device_t *dev)
560 {
561         drm_local_map_t *map;
562
563         DRM_DEBUG( "\n" );
564
565 #ifdef __FreeBSD__
566         drm_sysctl_cleanup(dev);
567         destroy_dev(dev->devnode);
568 #endif
569
570         drm_ctxbitmap_cleanup(dev);
571
572         if (dev->agp && dev->agp->mtrr) {
573                 int __unused retcode;
574
575                 retcode = drm_mtrr_del(dev->agp->info.ai_aperture_base,
576                     dev->agp->info.ai_aperture_size, DRM_MTRR_WC);
577                 DRM_DEBUG("mtrr_del = %d", retcode);
578         }
579
580         DRM_LOCK();
581         drm_takedown(dev);
582         DRM_UNLOCK();
583
584         /* Clean up any maps left over that had been allocated by the driver. */
585         while ((map = TAILQ_FIRST(&dev->maplist)) != NULL) {
586                 drm_remove_map(dev, map);
587         }
588
589         if ( dev->agp ) {
590                 drm_agp_uninit();
591                 free(dev->agp, M_DRM);
592                 dev->agp = NULL;
593         }
594
595         if (dev->postcleanup != NULL)
596                 dev->postcleanup(dev);
597
598         drm_mem_uninit();
599 #if defined(__FreeBSD__) &&  __FreeBSD_version >= 500000
600         mtx_destroy(&dev->dev_lock);
601 #endif
602 }
603
604
605 int drm_version(DRM_IOCTL_ARGS)
606 {
607         DRM_DEVICE;
608         drm_version_t version;
609         int len;
610
611         DRM_COPY_FROM_USER_IOCTL( version, (drm_version_t *)data, sizeof(version) );
612
613 #define DRM_COPY( name, value )                                         \
614         len = strlen( value );                                          \
615         if ( len > name##_len ) len = name##_len;                       \
616         name##_len = strlen( value );                                   \
617         if ( len && name ) {                                            \
618                 if ( DRM_COPY_TO_USER( name, value, len ) )             \
619                         return DRM_ERR(EFAULT);                         \
620         }
621
622         version.version_major = dev->driver_major;
623         version.version_minor = dev->driver_minor;
624         version.version_patchlevel = dev->driver_patchlevel;
625
626         DRM_COPY(version.name, dev->driver_name);
627         DRM_COPY(version.date, dev->driver_date);
628         DRM_COPY(version.desc, dev->driver_desc);
629
630         DRM_COPY_TO_USER_IOCTL( (drm_version_t *)data, version, sizeof(version) );
631
632         return 0;
633 }
634
635 int drm_open(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p)
636 {
637         drm_device_t *dev = NULL;
638         int retcode = 0;
639
640         dev = DRIVER_SOFTC(minor(kdev));
641
642         DRM_DEBUG( "open_count = %d\n", dev->open_count );
643
644         retcode = drm_open_helper(kdev, flags, fmt, p, dev);
645
646         if ( !retcode ) {
647                 atomic_inc( &dev->counts[_DRM_STAT_OPENS] );
648                 DRM_LOCK();
649 #ifdef __FreeBSD__
650                 device_busy(dev->device);
651 #endif
652                 if ( !dev->open_count++ )
653                         retcode = drm_setup(dev);
654                 DRM_UNLOCK();
655         }
656
657         return retcode;
658 }
659
660 int drm_close(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p)
661 {
662         drm_file_t *priv;
663         DRM_DEVICE;
664         int retcode = 0;
665         DRMFILE filp = (void *)(uintptr_t)(DRM_CURRENTPID);
666         
667         DRM_DEBUG( "open_count = %d\n", dev->open_count );
668
669         DRM_LOCK();
670
671         priv = drm_find_file_by_proc(dev, p);
672         if (!priv) {
673                 DRM_UNLOCK();
674                 DRM_ERROR("can't find authenticator\n");
675                 return EINVAL;
676         }
677
678         if (dev->prerelease != NULL)
679                 dev->prerelease(dev, filp);
680
681         /* ========================================================
682          * Begin inline drm_release
683          */
684
685 #ifdef __FreeBSD__
686         DRM_DEBUG( "pid = %d, device = 0x%lx, open_count = %d\n",
687                    DRM_CURRENTPID, (long)dev->device, dev->open_count );
688 #elif defined(__NetBSD__) || defined(__OpenBSD__)
689         DRM_DEBUG( "pid = %d, device = 0x%lx, open_count = %d\n",
690                    DRM_CURRENTPID, (long)&dev->device, dev->open_count);
691 #endif
692
693         if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
694             && dev->lock.filp == filp) {
695                 DRM_DEBUG("Process %d dead, freeing lock for context %d\n",
696                           DRM_CURRENTPID,
697                           _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
698                 if (dev->release != NULL)
699                         dev->release(dev, filp);
700
701                 drm_lock_free(dev, &dev->lock.hw_lock->lock,
702                     _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
703                 
704                                 /* FIXME: may require heavy-handed reset of
705                                    hardware at this point, possibly
706                                    processed via a callback to the X
707                                    server. */
708         } else if (dev->release != NULL && dev->lock.hw_lock != NULL) {
709                 /* The lock is required to reclaim buffers */
710                 for (;;) {
711                         if ( !dev->lock.hw_lock ) {
712                                 /* Device has been unregistered */
713                                 retcode = DRM_ERR(EINTR);
714                                 break;
715                         }
716                         if (drm_lock_take(&dev->lock.hw_lock->lock,
717                             DRM_KERNEL_CONTEXT)) {
718                                 dev->lock.filp = filp;
719                                 dev->lock.lock_time = jiffies;
720                                 atomic_inc( &dev->counts[_DRM_STAT_LOCKS] );
721                                 break;  /* Got lock */
722                         }
723                                 /* Contention */
724 #if defined(__FreeBSD__) && __FreeBSD_version > 500000
725                         retcode = msleep((void *)&dev->lock.lock_queue,
726                             &dev->dev_lock, PZERO | PCATCH, "drmlk2", 0);
727 #else
728                         retcode = tsleep((void *)&dev->lock.lock_queue,
729                             PZERO | PCATCH, "drmlk2", 0);
730 #endif
731                         if (retcode)
732                                 break;
733                 }
734                 if (retcode == 0) {
735                         dev->release(dev, filp);
736                         drm_lock_free(dev, &dev->lock.hw_lock->lock,
737                             DRM_KERNEL_CONTEXT);
738                 }
739         }
740
741         if (dev->use_dma)
742                 drm_reclaim_buffers(dev, (void *)(uintptr_t)priv->pid);
743
744 #if defined (__FreeBSD__) && (__FreeBSD_version >= 500000)
745         funsetown(&dev->buf_sigio);
746 #elif defined(__FreeBSD__)
747         funsetown(dev->buf_sigio);
748 #elif defined(__NetBSD__) || defined(__OpenBSD__)
749         dev->buf_pgid = 0;
750 #endif /* __NetBSD__  || __OpenBSD__ */
751
752         if (--priv->refs == 0) {
753                 if (dev->free_filp_priv != NULL)
754                         dev->free_filp_priv(dev, priv);
755                 TAILQ_REMOVE(&dev->files, priv, link);
756                 free(priv, M_DRM);
757         }
758
759         /* ========================================================
760          * End inline drm_release
761          */
762
763         atomic_inc( &dev->counts[_DRM_STAT_CLOSES] );
764 #ifdef __FreeBSD__
765         device_unbusy(dev->device);
766 #endif
767         if (--dev->open_count == 0) {
768                 retcode = drm_takedown(dev);
769         }
770
771         DRM_UNLOCK();
772         
773         return retcode;
774 }
775
776 /* drm_ioctl is called whenever a process performs an ioctl on /dev/drm.
777  */
778 int drm_ioctl(struct cdev *kdev, u_long cmd, caddr_t data, int flags, 
779     DRM_STRUCTPROC *p)
780 {
781         DRM_DEVICE;
782         int retcode = 0;
783         drm_ioctl_desc_t *ioctl;
784         int (*func)(DRM_IOCTL_ARGS);
785         int nr = DRM_IOCTL_NR(cmd);
786         int is_driver_ioctl = 0;
787         drm_file_t *priv;
788         DRMFILE filp = (DRMFILE)(uintptr_t)DRM_CURRENTPID;
789
790         DRM_LOCK();
791         priv = drm_find_file_by_proc(dev, p);
792         DRM_UNLOCK();
793         if (priv == NULL) {
794                 DRM_ERROR("can't find authenticator\n");
795                 return EINVAL;
796         }
797
798         atomic_inc( &dev->counts[_DRM_STAT_IOCTLS] );
799         ++priv->ioctl_count;
800
801 #ifdef __FreeBSD__
802         DRM_DEBUG( "pid=%d, cmd=0x%02lx, nr=0x%02x, dev 0x%lx, auth=%d\n",
803                  DRM_CURRENTPID, cmd, nr, (long)dev->device, priv->authenticated );
804 #elif defined(__NetBSD__) || defined(__OpenBSD__)
805         DRM_DEBUG( "pid=%d, cmd=0x%02lx, nr=0x%02x, dev 0x%lx, auth=%d\n",
806                  DRM_CURRENTPID, cmd, nr, (long)&dev->device, priv->authenticated );
807 #endif
808
809         switch (cmd) {
810         case FIONBIO:
811         case FIOASYNC:
812                 return 0;
813
814 #ifdef __FreeBSD__
815         case FIOSETOWN:
816                 return fsetown(*(int *)data, &dev->buf_sigio);
817
818         case FIOGETOWN:
819 #if (__FreeBSD_version >= 500000)
820                 *(int *) data = fgetown(&dev->buf_sigio);
821 #else
822                 *(int *) data = fgetown(dev->buf_sigio);
823 #endif
824                 return 0;
825 #endif /* __FreeBSD__ */
826 #if defined(__NetBSD__) || defined(__OpenBSD__)
827         case TIOCSPGRP:
828                 dev->buf_pgid = *(int *)data;
829                 return 0;
830
831         case TIOCGPGRP:
832                 *(int *)data = dev->buf_pgid;
833                 return 0;
834 #endif /* __NetBSD__ */
835         }
836
837         if (IOCGROUP(cmd) != DRM_IOCTL_BASE) {
838                 DRM_DEBUG("Bad ioctl group 0x%x\n", (int)IOCGROUP(cmd));
839                 return EINVAL;
840         }
841
842         ioctl = &drm_ioctls[nr];
843         /* It's not a core DRM ioctl, try driver-specific. */
844         if (ioctl->func == NULL && nr >= DRM_COMMAND_BASE) {
845                 /* The array entries begin at DRM_COMMAND_BASE ioctl nr */
846                 nr -= DRM_COMMAND_BASE;
847                 if (nr > dev->max_driver_ioctl) {
848                         DRM_DEBUG("Bad driver ioctl number, 0x%x (of 0x%x)\n",
849                             nr, dev->max_driver_ioctl);
850                         return EINVAL;
851                 }
852                 ioctl = &dev->driver_ioctls[nr];
853                 is_driver_ioctl = 1;
854         }
855         func = ioctl->func;
856
857         if (func == NULL) {
858                 DRM_DEBUG( "no function\n" );
859                 return EINVAL;
860         }
861         if ((ioctl->root_only && DRM_SUSER(p)) || (ioctl->auth_needed &&
862             !priv->authenticated))
863                 return EACCES;
864
865         if (is_driver_ioctl)
866                 DRM_LOCK();
867         retcode = func(kdev, cmd, data, flags, p, filp);
868         if (is_driver_ioctl)
869                 DRM_UNLOCK();
870
871         if (retcode != 0)
872                 DRM_DEBUG("    returning %d\n", retcode);
873
874         return DRM_ERR(retcode);
875 }
876
877
878 #if DRM_LINUX
879
880 #include <sys/sysproto.h>
881
882 MODULE_DEPEND(DRIVER_NAME, linux, 1, 1, 1);
883
884 #define LINUX_IOCTL_DRM_MIN             0x6400
885 #define LINUX_IOCTL_DRM_MAX             0x64ff
886
887 static linux_ioctl_function_t drm_linux_ioctl;
888 static struct linux_ioctl_handler drm_handler = {drm_linux_ioctl, 
889     LINUX_IOCTL_DRM_MIN, LINUX_IOCTL_DRM_MAX};
890
891 SYSINIT(drm_register, SI_SUB_KLD, SI_ORDER_MIDDLE, 
892     linux_ioctl_register_handler, &drm_handler);
893 SYSUNINIT(drm_unregister, SI_SUB_KLD, SI_ORDER_MIDDLE, 
894     linux_ioctl_unregister_handler, &drm_handler);
895
896 /* The bits for in/out are switched on Linux */
897 #define LINUX_IOC_IN    IOC_OUT
898 #define LINUX_IOC_OUT   IOC_IN
899
900 static int
901 drm_linux_ioctl(DRM_STRUCTPROC *p, struct linux_ioctl_args* args)
902 {
903         int error;
904         int cmd = args->cmd;
905
906         args->cmd &= ~(LINUX_IOC_IN | LINUX_IOC_OUT);
907         if (cmd & LINUX_IOC_IN)
908                 args->cmd |= IOC_IN;
909         if (cmd & LINUX_IOC_OUT)
910                 args->cmd |= IOC_OUT;
911         
912         error = ioctl(p, (struct ioctl_args *)args);
913
914         return error;
915 }
916 #endif /* DRM_LINUX */