drm_bufs.c revision 145478
1145132Sanholt/* drm_bufs.h -- Generic buffer template -*- linux-c -*-
2145132Sanholt * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com
3145132Sanholt */
4145132Sanholt/*-
5145132Sanholt * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
6145132Sanholt * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
7145132Sanholt * All Rights Reserved.
8145132Sanholt *
9145132Sanholt * Permission is hereby granted, free of charge, to any person obtaining a
10145132Sanholt * copy of this software and associated documentation files (the "Software"),
11145132Sanholt * to deal in the Software without restriction, including without limitation
12145132Sanholt * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13145132Sanholt * and/or sell copies of the Software, and to permit persons to whom the
14145132Sanholt * Software is furnished to do so, subject to the following conditions:
15145132Sanholt *
16145132Sanholt * The above copyright notice and this permission notice (including the next
17145132Sanholt * paragraph) shall be included in all copies or substantial portions of the
18145132Sanholt * Software.
19145132Sanholt *
20145132Sanholt * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21145132Sanholt * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22145132Sanholt * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23145132Sanholt * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24145132Sanholt * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25145132Sanholt * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26145132Sanholt * OTHER DEALINGS IN THE SOFTWARE.
27145132Sanholt *
28145132Sanholt * Authors:
29145132Sanholt *    Rickard E. (Rik) Faith <faith@valinux.com>
30145132Sanholt *    Gareth Hughes <gareth@valinux.com>
31145132Sanholt *
32145132Sanholt * $FreeBSD: head/sys/dev/drm/drm_bufs.c 145478 2005-04-24 19:03:32Z anholt $
33145132Sanholt */
34145132Sanholt
35145132Sanholt#include "dev/drm/drmP.h"
36145132Sanholt
37145132Sanholt/*
38145132Sanholt * Compute order.  Can be made faster.
39145132Sanholt */
40145132Sanholtint drm_order(unsigned long size)
41145132Sanholt{
42145132Sanholt	int order;
43145132Sanholt	unsigned long tmp;
44145132Sanholt
45145132Sanholt	for ( order = 0, tmp = size ; tmp >>= 1 ; ++order );
46145132Sanholt
47145132Sanholt	if ( size & ~(1 << order) )
48145132Sanholt		++order;
49145132Sanholt
50145132Sanholt	return order;
51145132Sanholt}
52145132Sanholt
53145132Sanholtunsigned long drm_get_resource_start(drm_device_t *dev, unsigned int resource)
54145132Sanholt{
55145132Sanholt	struct resource *bsr;
56145132Sanholt	unsigned long offset;
57145132Sanholt
58145132Sanholt	resource = resource * 4 + 0x10;
59145132Sanholt
60145132Sanholt	bsr = bus_alloc_resource_any(dev->device, SYS_RES_MEMORY, &resource,
61145132Sanholt	    RF_ACTIVE | RF_SHAREABLE);
62145132Sanholt	if (bsr == NULL) {
63145132Sanholt		DRM_ERROR("Couldn't find resource 0x%x\n", resource);
64145132Sanholt		return 0;
65145132Sanholt	}
66145132Sanholt
67145132Sanholt	offset = rman_get_start(bsr);
68145132Sanholt
69145132Sanholt	bus_release_resource(dev->device, SYS_RES_MEMORY, resource, bsr);
70145132Sanholt
71145132Sanholt	return offset;
72145132Sanholt}
73145132Sanholt
74145132Sanholtunsigned long drm_get_resource_len(drm_device_t *dev, unsigned int resource)
75145132Sanholt{
76145132Sanholt	struct resource *bsr;
77145132Sanholt	unsigned long len;
78145132Sanholt
79145132Sanholt	resource = resource * 4 + 0x10;
80145132Sanholt
81145132Sanholt	bsr = bus_alloc_resource_any(dev->device, SYS_RES_MEMORY, &resource,
82145132Sanholt	    RF_ACTIVE | RF_SHAREABLE);
83145132Sanholt	if (bsr == NULL) {
84145132Sanholt		DRM_ERROR("Couldn't find resource 0x%x\n", resource);
85145132Sanholt		return ENOMEM;
86145132Sanholt	}
87145132Sanholt
88145132Sanholt	len = rman_get_size(bsr);
89145132Sanholt
90145132Sanholt	bus_release_resource(dev->device, SYS_RES_MEMORY, resource, bsr);
91145132Sanholt
92145132Sanholt	return len;
93145132Sanholt}
94145132Sanholt
95145132Sanholtint drm_initmap(drm_device_t *dev, unsigned long start, unsigned long len,
96145132Sanholt		unsigned int resource, int type, int flags)
97145132Sanholt{
98145132Sanholt	drm_local_map_t *map;
99145132Sanholt	struct resource *bsr;
100145132Sanholt
101145132Sanholt	if (type != _DRM_REGISTERS && type != _DRM_FRAME_BUFFER)
102145132Sanholt		return EINVAL;
103145132Sanholt	if (len == 0)
104145132Sanholt		return EINVAL;
105145132Sanholt
106145132Sanholt	map = malloc(sizeof(*map), M_DRM, M_ZERO | M_NOWAIT);
107145132Sanholt	if (map == NULL)
108145132Sanholt		return ENOMEM;
109145132Sanholt
110145132Sanholt	map->rid = resource * 4 + 0x10;
111145132Sanholt	bsr = bus_alloc_resource_any(dev->device, SYS_RES_MEMORY, &map->rid,
112145132Sanholt	    RF_ACTIVE | RF_SHAREABLE);
113145132Sanholt	if (bsr == NULL) {
114145132Sanholt		DRM_ERROR("Couldn't allocate %s resource\n",
115145132Sanholt		    ((type == _DRM_REGISTERS) ? "mmio" : "framebuffer"));
116145132Sanholt		free(map, M_DRM);
117145132Sanholt		return ENOMEM;
118145132Sanholt	}
119145132Sanholt
120145132Sanholt	map->kernel_owned = 1;
121145132Sanholt	map->type = type;
122145132Sanholt	map->flags = flags;
123145132Sanholt	map->bsr = bsr;
124145132Sanholt	map->bst = rman_get_bustag(bsr);
125145132Sanholt	map->bsh = rman_get_bushandle(bsr);
126145132Sanholt	map->offset = start;
127145132Sanholt	map->size = len;
128145132Sanholt
129145132Sanholt	if (type == _DRM_REGISTERS)
130145132Sanholt		map->handle = rman_get_virtual(bsr);
131145132Sanholt
132145132Sanholt	DRM_DEBUG("initmap %d,0x%x@0x%lx/0x%lx\n", map->type, map->flags,
133145132Sanholt	    map->offset, map->size);
134145132Sanholt
135145132Sanholt	if (map->flags & _DRM_WRITE_COMBINING) {
136145132Sanholt		int err;
137145132Sanholt
138145132Sanholt		err = drm_mtrr_add(map->offset, map->size, DRM_MTRR_WC);
139145132Sanholt		if (err == 0)
140145132Sanholt			map->mtrr = 1;
141145132Sanholt	}
142145132Sanholt
143145132Sanholt	DRM_LOCK();
144145132Sanholt	TAILQ_INSERT_TAIL(&dev->maplist, map, link);
145145132Sanholt	DRM_UNLOCK();
146145132Sanholt
147145132Sanholt	return 0;
148145132Sanholt}
149145132Sanholt
150145132Sanholtint drm_addmap(DRM_IOCTL_ARGS)
151145132Sanholt{
152145132Sanholt	DRM_DEVICE;
153145132Sanholt	drm_map_t request;
154145132Sanholt	drm_local_map_t *map;
155145132Sanholt	dma_addr_t bus_addr;
156145132Sanholt
157145132Sanholt	if (!(dev->flags & (FREAD|FWRITE)))
158145132Sanholt		return DRM_ERR(EACCES); /* Require read/write */
159145132Sanholt
160145132Sanholt	DRM_COPY_FROM_USER_IOCTL( request, (drm_map_t *)data, sizeof(drm_map_t) );
161145132Sanholt
162145132Sanholt	/* Only allow shared memory to be removable since we only keep enough
163145132Sanholt	 * book keeping information about shared memory to allow for removal
164145132Sanholt	 * when processes fork.
165145132Sanholt	 */
166145132Sanholt	if ((request.flags & _DRM_REMOVABLE) && request.type != _DRM_SHM)
167145132Sanholt		return EINVAL;
168145132Sanholt	if ((request.offset & PAGE_MASK) || (request.size & PAGE_MASK))
169145132Sanholt		return EINVAL;
170145132Sanholt	if (request.offset + request.size < request.offset)
171145132Sanholt		return EINVAL;
172145132Sanholt
173145132Sanholt	DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n",
174145132Sanholt	    request.offset, request.size, request.type);
175145132Sanholt
176145132Sanholt	/* Check if this is just another version of a kernel-allocated map, and
177145132Sanholt	 * just hand that back if so.
178145132Sanholt	 */
179145132Sanholt	if (request.type == _DRM_REGISTERS || request.type == _DRM_FRAME_BUFFER)
180145132Sanholt	{
181145132Sanholt		DRM_LOCK();
182145132Sanholt		TAILQ_FOREACH(map, &dev->maplist, link) {
183145132Sanholt			if (map->kernel_owned && map->type == request.type &&
184145132Sanholt			    map->offset == request.offset) {
185145132Sanholt				/* XXX: this size setting is questionable. */
186145132Sanholt				map->size = request.size;
187145132Sanholt				DRM_DEBUG("Found kernel map %d\n", request.type);
188145132Sanholt				goto done;
189145132Sanholt			}
190145132Sanholt		}
191145132Sanholt		DRM_UNLOCK();
192145132Sanholt	}
193145132Sanholt
194145132Sanholt	/* Allocate a new map structure, fill it in, and do any type-specific
195145132Sanholt	 * initialization necessary.
196145132Sanholt	 */
197145132Sanholt	map = malloc(sizeof(*map), M_DRM, M_ZERO | M_NOWAIT);
198145132Sanholt	if ( !map )
199145132Sanholt		return DRM_ERR(ENOMEM);
200145132Sanholt
201145132Sanholt	map->offset = request.offset;
202145132Sanholt	map->size = request.size;
203145132Sanholt	map->type = request.type;
204145132Sanholt	map->flags = request.flags;
205145132Sanholt
206145132Sanholt	switch ( map->type ) {
207145132Sanholt	case _DRM_REGISTERS:
208145478Sanholt		map->handle = drm_ioremap(dev, map);
209145132Sanholt		if (!(map->flags & _DRM_WRITE_COMBINING))
210145132Sanholt			break;
211145132Sanholt		/* FALLTHROUGH */
212145132Sanholt	case _DRM_FRAME_BUFFER:
213145132Sanholt		if (drm_mtrr_add(map->offset, map->size, DRM_MTRR_WC) == 0)
214145132Sanholt			map->mtrr = 1;
215145132Sanholt		break;
216145132Sanholt	case _DRM_SHM:
217145132Sanholt		map->handle = malloc(map->size, M_DRM, M_NOWAIT);
218145132Sanholt		DRM_DEBUG( "%lu %d %p\n",
219145132Sanholt			   map->size, drm_order(map->size), map->handle );
220145132Sanholt		if ( !map->handle ) {
221145132Sanholt			free(map, M_DRM);
222145132Sanholt			return DRM_ERR(ENOMEM);
223145132Sanholt		}
224145132Sanholt		map->offset = (unsigned long)map->handle;
225145132Sanholt		if ( map->flags & _DRM_CONTAINS_LOCK ) {
226145132Sanholt			/* Prevent a 2nd X Server from creating a 2nd lock */
227145132Sanholt			DRM_LOCK();
228145132Sanholt			if (dev->lock.hw_lock != NULL) {
229145132Sanholt				DRM_UNLOCK();
230145132Sanholt				free(map->handle, M_DRM);
231145132Sanholt				free(map, M_DRM);
232145132Sanholt				return DRM_ERR(EBUSY);
233145132Sanholt			}
234145132Sanholt			dev->lock.hw_lock = map->handle; /* Pointer to lock */
235145132Sanholt			DRM_UNLOCK();
236145132Sanholt		}
237145132Sanholt		break;
238145132Sanholt	case _DRM_AGP:
239145132Sanholt		map->offset += dev->agp->base;
240145132Sanholt		map->mtrr   = dev->agp->mtrr; /* for getmap */
241145132Sanholt		break;
242145132Sanholt	case _DRM_SCATTER_GATHER:
243145132Sanholt		if (!dev->sg) {
244145132Sanholt			free(map, M_DRM);
245145132Sanholt			return DRM_ERR(EINVAL);
246145132Sanholt		}
247145132Sanholt		map->offset = map->offset + dev->sg->handle;
248145132Sanholt		break;
249145132Sanholt	case _DRM_CONSISTENT:
250145132Sanholt		map->handle = drm_pci_alloc(dev, map->size, map->size,
251145132Sanholt		    0xfffffffful, &bus_addr);
252145132Sanholt		if (map->handle == NULL) {
253145132Sanholt			free(map, M_DRM);
254145132Sanholt			return ENOMEM;
255145132Sanholt		}
256145132Sanholt		map->offset = (unsigned long)bus_addr;
257145132Sanholt		break;
258145132Sanholt	default:
259145132Sanholt		free(map, M_DRM);
260145132Sanholt		return DRM_ERR(EINVAL);
261145132Sanholt	}
262145132Sanholt
263145132Sanholt	DRM_LOCK();
264145132Sanholt	TAILQ_INSERT_TAIL(&dev->maplist, map, link);
265145132Sanholt
266145132Sanholtdone:
267145132Sanholt	/* Jumped to, with lock held, when a kernel map is found. */
268145132Sanholt	request.offset = map->offset;
269145132Sanholt	request.size = map->size;
270145132Sanholt	request.type = map->type;
271145132Sanholt	request.flags = map->flags;
272145132Sanholt	request.mtrr   = map->mtrr;
273145132Sanholt	request.handle = map->handle;
274145132Sanholt	DRM_UNLOCK();
275145132Sanholt
276145132Sanholt	DRM_DEBUG("Added map %d 0x%lx/0x%lx\n", request.type, request.offset, request.size);
277145132Sanholt
278145132Sanholt	if ( request.type != _DRM_SHM ) {
279145132Sanholt		request.handle = (void *)request.offset;
280145132Sanholt	}
281145132Sanholt
282145132Sanholt	DRM_COPY_TO_USER_IOCTL( (drm_map_t *)data, request, sizeof(drm_map_t) );
283145132Sanholt
284145132Sanholt	return 0;
285145132Sanholt}
286145132Sanholt
287145132Sanholtvoid drm_remove_map(drm_device_t *dev, drm_local_map_t *map)
288145132Sanholt{
289145132Sanholt	DRM_SPINLOCK_ASSERT(&dev->dev_lock);
290145132Sanholt
291145132Sanholt	TAILQ_REMOVE(&dev->maplist, map, link);
292145132Sanholt
293145132Sanholt	switch (map->type) {
294145132Sanholt	case _DRM_REGISTERS:
295145132Sanholt		if (map->bsr == NULL)
296145132Sanholt			drm_ioremapfree(map);
297145132Sanholt		/* FALLTHROUGH */
298145132Sanholt	case _DRM_FRAME_BUFFER:
299145132Sanholt		if (map->mtrr) {
300145132Sanholt			int __unused retcode;
301145132Sanholt
302145132Sanholt			retcode = drm_mtrr_del(map->offset, map->size,
303145132Sanholt			    DRM_MTRR_WC);
304145132Sanholt			DRM_DEBUG("mtrr_del = %d\n", retcode);
305145132Sanholt		}
306145132Sanholt		break;
307145132Sanholt	case _DRM_SHM:
308145132Sanholt		free(map->handle, M_DRM);
309145132Sanholt		break;
310145132Sanholt	case _DRM_AGP:
311145132Sanholt	case _DRM_SCATTER_GATHER:
312145132Sanholt		break;
313145132Sanholt	case _DRM_CONSISTENT:
314145132Sanholt		drm_pci_free(dev, map->size, map->handle, map->offset);
315145132Sanholt		break;
316145132Sanholt	}
317145132Sanholt
318145132Sanholt	if (map->bsr != NULL) {
319145132Sanholt		bus_release_resource(dev->device, SYS_RES_MEMORY, map->rid,
320145132Sanholt		    map->bsr);
321145132Sanholt	}
322145132Sanholt
323145132Sanholt	free(map, M_DRM);
324145132Sanholt}
325145132Sanholt
326145132Sanholt/* Remove a map private from list and deallocate resources if the mapping
327145132Sanholt * isn't in use.
328145132Sanholt */
329145132Sanholt
330145132Sanholtint drm_rmmap(DRM_IOCTL_ARGS)
331145132Sanholt{
332145132Sanholt	DRM_DEVICE;
333145132Sanholt	drm_local_map_t *map;
334145132Sanholt	drm_map_t request;
335145132Sanholt
336145132Sanholt	DRM_COPY_FROM_USER_IOCTL( request, (drm_map_t *)data, sizeof(request) );
337145132Sanholt
338145132Sanholt	DRM_LOCK();
339145132Sanholt	TAILQ_FOREACH(map, &dev->maplist, link) {
340145132Sanholt		if (map->handle == request.handle &&
341145132Sanholt		    map->flags & _DRM_REMOVABLE)
342145132Sanholt			break;
343145132Sanholt	}
344145132Sanholt
345145132Sanholt	/* No match found. */
346145132Sanholt	if (map == NULL) {
347145132Sanholt		DRM_UNLOCK();
348145132Sanholt		return DRM_ERR(EINVAL);
349145132Sanholt	}
350145132Sanholt
351145132Sanholt	drm_remove_map(dev, map);
352145132Sanholt
353145132Sanholt	DRM_UNLOCK();
354145132Sanholt
355145132Sanholt	return 0;
356145132Sanholt}
357145132Sanholt
358145132Sanholt
359145132Sanholtstatic void drm_cleanup_buf_error(drm_device_t *dev, drm_buf_entry_t *entry)
360145132Sanholt{
361145132Sanholt	int i;
362145132Sanholt
363145132Sanholt	if (entry->seg_count) {
364145132Sanholt		for (i = 0; i < entry->seg_count; i++) {
365145132Sanholt			drm_pci_free(dev, entry->buf_size,
366145132Sanholt			    (void *)entry->seglist[i],
367145132Sanholt			    entry->seglist_bus[i]);
368145132Sanholt		}
369145132Sanholt		free(entry->seglist, M_DRM);
370145132Sanholt		free(entry->seglist_bus, M_DRM);
371145132Sanholt
372145132Sanholt		entry->seg_count = 0;
373145132Sanholt	}
374145132Sanholt
375145132Sanholt   	if (entry->buf_count) {
376145132Sanholt	   	for (i = 0; i < entry->buf_count; i++) {
377145132Sanholt			free(entry->buflist[i].dev_private, M_DRM);
378145132Sanholt		}
379145132Sanholt		free(entry->buflist, M_DRM);
380145132Sanholt
381145132Sanholt		entry->buf_count = 0;
382145132Sanholt	}
383145132Sanholt}
384145132Sanholt
385145132Sanholtstatic int drm_addbufs_agp(drm_device_t *dev, drm_buf_desc_t *request)
386145132Sanholt{
387145132Sanholt	drm_device_dma_t *dma = dev->dma;
388145132Sanholt	drm_buf_entry_t *entry;
389145132Sanholt	drm_buf_t *buf;
390145132Sanholt	unsigned long offset;
391145132Sanholt	unsigned long agp_offset;
392145132Sanholt	int count;
393145132Sanholt	int order;
394145132Sanholt	int size;
395145132Sanholt	int alignment;
396145132Sanholt	int page_order;
397145132Sanholt	int total;
398145132Sanholt	int byte_count;
399145132Sanholt	int i;
400145132Sanholt	drm_buf_t **temp_buflist;
401145132Sanholt
402145132Sanholt	count = request->count;
403145132Sanholt	order = drm_order(request->size);
404145132Sanholt	size = 1 << order;
405145132Sanholt
406145132Sanholt	alignment  = (request->flags & _DRM_PAGE_ALIGN)
407145132Sanholt		? round_page(size) : size;
408145132Sanholt	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
409145132Sanholt	total = PAGE_SIZE << page_order;
410145132Sanholt
411145132Sanholt	byte_count = 0;
412145132Sanholt	agp_offset = dev->agp->base + request->agp_start;
413145132Sanholt
414145132Sanholt	DRM_DEBUG( "count:      %d\n",  count );
415145132Sanholt	DRM_DEBUG( "order:      %d\n",  order );
416145132Sanholt	DRM_DEBUG( "size:       %d\n",  size );
417145132Sanholt	DRM_DEBUG( "agp_offset: 0x%lx\n", agp_offset );
418145132Sanholt	DRM_DEBUG( "alignment:  %d\n",  alignment );
419145132Sanholt	DRM_DEBUG( "page_order: %d\n",  page_order );
420145132Sanholt	DRM_DEBUG( "total:      %d\n",  total );
421145132Sanholt
422145132Sanholt	entry = &dma->bufs[order];
423145132Sanholt
424145132Sanholt	entry->buflist = malloc(count * sizeof(*entry->buflist), M_DRM,
425145132Sanholt	    M_NOWAIT | M_ZERO);
426145132Sanholt	if ( !entry->buflist ) {
427145132Sanholt		return DRM_ERR(ENOMEM);
428145132Sanholt	}
429145132Sanholt
430145132Sanholt	entry->buf_size = size;
431145132Sanholt	entry->page_order = page_order;
432145132Sanholt
433145132Sanholt	offset = 0;
434145132Sanholt
435145132Sanholt	while ( entry->buf_count < count ) {
436145132Sanholt		buf          = &entry->buflist[entry->buf_count];
437145132Sanholt		buf->idx     = dma->buf_count + entry->buf_count;
438145132Sanholt		buf->total   = alignment;
439145132Sanholt		buf->order   = order;
440145132Sanholt		buf->used    = 0;
441145132Sanholt
442145132Sanholt		buf->offset  = (dma->byte_count + offset);
443145132Sanholt		buf->bus_address = agp_offset + offset;
444145132Sanholt		buf->address = (void *)(agp_offset + offset);
445145132Sanholt		buf->next    = NULL;
446145132Sanholt		buf->pending = 0;
447145132Sanholt		buf->filp    = NULL;
448145132Sanholt
449145132Sanholt		buf->dev_priv_size = dev->dev_priv_size;
450145132Sanholt		buf->dev_private = malloc(buf->dev_priv_size, M_DRM,
451145132Sanholt		    M_NOWAIT | M_ZERO);
452145132Sanholt		if (buf->dev_private == NULL) {
453145132Sanholt			/* Set count correctly so we free the proper amount. */
454145132Sanholt			entry->buf_count = count;
455145132Sanholt			drm_cleanup_buf_error(dev, entry);
456145132Sanholt			return DRM_ERR(ENOMEM);
457145132Sanholt		}
458145132Sanholt
459145132Sanholt		offset += alignment;
460145132Sanholt		entry->buf_count++;
461145132Sanholt		byte_count += PAGE_SIZE << page_order;
462145132Sanholt	}
463145132Sanholt
464145132Sanholt	DRM_DEBUG( "byte_count: %d\n", byte_count );
465145132Sanholt
466145132Sanholt	temp_buflist = realloc(dma->buflist,
467145132Sanholt	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), M_DRM,
468145132Sanholt	    M_NOWAIT);
469145132Sanholt	if (temp_buflist == NULL) {
470145132Sanholt		/* Free the entry because it isn't valid */
471145132Sanholt		drm_cleanup_buf_error(dev, entry);
472145132Sanholt		return DRM_ERR(ENOMEM);
473145132Sanholt	}
474145132Sanholt	dma->buflist = temp_buflist;
475145132Sanholt
476145132Sanholt	for ( i = 0 ; i < entry->buf_count ; i++ ) {
477145132Sanholt		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
478145132Sanholt	}
479145132Sanholt
480145132Sanholt	dma->buf_count += entry->buf_count;
481145132Sanholt	dma->byte_count += byte_count;
482145132Sanholt
483145132Sanholt	DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count );
484145132Sanholt	DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count );
485145132Sanholt
486145132Sanholt	request->count = entry->buf_count;
487145132Sanholt	request->size = size;
488145132Sanholt
489145132Sanholt	dma->flags = _DRM_DMA_USE_AGP;
490145132Sanholt
491145132Sanholt	return 0;
492145132Sanholt}
493145132Sanholt
494145132Sanholtstatic int drm_addbufs_pci(drm_device_t *dev, drm_buf_desc_t *request)
495145132Sanholt{
496145132Sanholt	drm_device_dma_t *dma = dev->dma;
497145132Sanholt	int count;
498145132Sanholt	int order;
499145132Sanholt	int size;
500145132Sanholt	int total;
501145132Sanholt	int page_order;
502145132Sanholt	drm_buf_entry_t *entry;
503145132Sanholt	vm_offset_t vaddr;
504145132Sanholt	drm_buf_t *buf;
505145132Sanholt	int alignment;
506145132Sanholt	unsigned long offset;
507145132Sanholt	int i;
508145132Sanholt	int byte_count;
509145132Sanholt	int page_count;
510145132Sanholt	unsigned long *temp_pagelist;
511145132Sanholt	drm_buf_t **temp_buflist;
512145132Sanholt	dma_addr_t bus_addr;
513145132Sanholt
514145132Sanholt	count = request->count;
515145132Sanholt	order = drm_order(request->size);
516145132Sanholt	size = 1 << order;
517145132Sanholt
518145132Sanholt	DRM_DEBUG( "count=%d, size=%d (%d), order=%d\n",
519145132Sanholt		   request->count, request->size, size, order );
520145132Sanholt
521145132Sanholt	alignment = (request->flags & _DRM_PAGE_ALIGN)
522145132Sanholt		? round_page(size) : size;
523145132Sanholt	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
524145132Sanholt	total = PAGE_SIZE << page_order;
525145132Sanholt
526145132Sanholt	entry = &dma->bufs[order];
527145132Sanholt
528145132Sanholt	entry->buflist = malloc(count * sizeof(*entry->buflist), M_DRM,
529145132Sanholt	    M_NOWAIT | M_ZERO);
530145132Sanholt	entry->seglist = malloc(count * sizeof(*entry->seglist), M_DRM,
531145132Sanholt	    M_NOWAIT | M_ZERO);
532145132Sanholt	entry->seglist_bus = malloc(count * sizeof(*entry->seglist_bus), M_DRM,
533145132Sanholt	    M_NOWAIT | M_ZERO);
534145132Sanholt
535145132Sanholt	/* Keep the original pagelist until we know all the allocations
536145132Sanholt	 * have succeeded
537145132Sanholt	 */
538145132Sanholt	temp_pagelist = malloc((dma->page_count + (count << page_order)) *
539145132Sanholt	    sizeof(*dma->pagelist), M_DRM, M_NOWAIT);
540145132Sanholt
541145132Sanholt	if (entry->buflist == NULL || entry->seglist == NULL ||
542145132Sanholt	    entry->seglist_bus == NULL || temp_pagelist == NULL) {
543145132Sanholt		free(entry->buflist, M_DRM);
544145132Sanholt		free(entry->seglist, M_DRM);
545145132Sanholt		free(entry->seglist_bus, M_DRM);
546145132Sanholt		return DRM_ERR(ENOMEM);
547145132Sanholt	}
548145132Sanholt
549145132Sanholt	memcpy(temp_pagelist, dma->pagelist, dma->page_count *
550145132Sanholt	    sizeof(*dma->pagelist));
551145132Sanholt
552145132Sanholt	DRM_DEBUG( "pagelist: %d entries\n",
553145132Sanholt		   dma->page_count + (count << page_order) );
554145132Sanholt
555145132Sanholt	entry->buf_size	= size;
556145132Sanholt	entry->page_order = page_order;
557145132Sanholt	byte_count = 0;
558145132Sanholt	page_count = 0;
559145132Sanholt
560145132Sanholt	while ( entry->buf_count < count ) {
561145132Sanholt		vaddr = (vm_offset_t)drm_pci_alloc(dev, size, alignment,
562145132Sanholt		    0xfffffffful, &bus_addr);
563145132Sanholt		if (vaddr == 0) {
564145132Sanholt			/* Set count correctly so we free the proper amount. */
565145132Sanholt			entry->buf_count = count;
566145132Sanholt			entry->seg_count = count;
567145132Sanholt			drm_cleanup_buf_error(dev, entry);
568145132Sanholt			free(temp_pagelist, M_DRM);
569145132Sanholt			return DRM_ERR(ENOMEM);
570145132Sanholt		}
571145132Sanholt
572145132Sanholt		entry->seglist_bus[entry->seg_count] = bus_addr;
573145132Sanholt		entry->seglist[entry->seg_count++] = vaddr;
574145132Sanholt		for ( i = 0 ; i < (1 << page_order) ; i++ ) {
575145132Sanholt			DRM_DEBUG( "page %d @ 0x%08lx\n",
576145132Sanholt				   dma->page_count + page_count,
577145132Sanholt				   (long)vaddr + PAGE_SIZE * i );
578145132Sanholt			temp_pagelist[dma->page_count + page_count++] =
579145132Sanholt			    vaddr + PAGE_SIZE * i;
580145132Sanholt		}
581145132Sanholt		for ( offset = 0 ;
582145132Sanholt		      offset + size <= total && entry->buf_count < count ;
583145132Sanholt		      offset += alignment, ++entry->buf_count ) {
584145132Sanholt			buf	     = &entry->buflist[entry->buf_count];
585145132Sanholt			buf->idx     = dma->buf_count + entry->buf_count;
586145132Sanholt			buf->total   = alignment;
587145132Sanholt			buf->order   = order;
588145132Sanholt			buf->used    = 0;
589145132Sanholt			buf->offset  = (dma->byte_count + byte_count + offset);
590145132Sanholt			buf->address = (void *)(vaddr + offset);
591145132Sanholt			buf->bus_address = bus_addr + offset;
592145132Sanholt			buf->next    = NULL;
593145132Sanholt			buf->pending = 0;
594145132Sanholt			buf->filp    = NULL;
595145132Sanholt
596145132Sanholt			buf->dev_priv_size = dev->dev_priv_size;
597145132Sanholt			buf->dev_private = malloc(buf->dev_priv_size, M_DRM,
598145132Sanholt			    M_NOWAIT | M_ZERO);
599145132Sanholt			if (buf->dev_private == NULL) {
600145132Sanholt				/* Set count correctly so we free the proper amount. */
601145132Sanholt				entry->buf_count = count;
602145132Sanholt				entry->seg_count = count;
603145132Sanholt				drm_cleanup_buf_error(dev, entry);
604145132Sanholt				free(temp_pagelist, M_DRM);
605145132Sanholt				return DRM_ERR(ENOMEM);
606145132Sanholt			}
607145132Sanholt
608145132Sanholt			DRM_DEBUG( "buffer %d @ %p\n",
609145132Sanholt				   entry->buf_count, buf->address );
610145132Sanholt		}
611145132Sanholt		byte_count += PAGE_SIZE << page_order;
612145132Sanholt	}
613145132Sanholt
614145132Sanholt	temp_buflist = realloc(dma->buflist,
615145132Sanholt	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), M_DRM,
616145132Sanholt	    M_NOWAIT);
617145132Sanholt	if (temp_buflist == NULL) {
618145132Sanholt		/* Free the entry because it isn't valid */
619145132Sanholt		drm_cleanup_buf_error(dev, entry);
620145132Sanholt		free(temp_pagelist, M_DRM);
621145132Sanholt		return DRM_ERR(ENOMEM);
622145132Sanholt	}
623145132Sanholt	dma->buflist = temp_buflist;
624145132Sanholt
625145132Sanholt	for ( i = 0 ; i < entry->buf_count ; i++ ) {
626145132Sanholt		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
627145132Sanholt	}
628145132Sanholt
629145132Sanholt	/* No allocations failed, so now we can replace the orginal pagelist
630145132Sanholt	 * with the new one.
631145132Sanholt	 */
632145132Sanholt	free(dma->pagelist, M_DRM);
633145132Sanholt	dma->pagelist = temp_pagelist;
634145132Sanholt
635145132Sanholt	dma->buf_count += entry->buf_count;
636145132Sanholt	dma->seg_count += entry->seg_count;
637145132Sanholt	dma->page_count += entry->seg_count << page_order;
638145132Sanholt	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
639145132Sanholt
640145132Sanholt	request->count = entry->buf_count;
641145132Sanholt	request->size = size;
642145132Sanholt
643145132Sanholt	return 0;
644145132Sanholt
645145132Sanholt}
646145132Sanholt
647145132Sanholtstatic int drm_addbufs_sg(drm_device_t *dev, drm_buf_desc_t *request)
648145132Sanholt{
649145132Sanholt	drm_device_dma_t *dma = dev->dma;
650145132Sanholt	drm_buf_entry_t *entry;
651145132Sanholt	drm_buf_t *buf;
652145132Sanholt	unsigned long offset;
653145132Sanholt	unsigned long agp_offset;
654145132Sanholt	int count;
655145132Sanholt	int order;
656145132Sanholt	int size;
657145132Sanholt	int alignment;
658145132Sanholt	int page_order;
659145132Sanholt	int total;
660145132Sanholt	int byte_count;
661145132Sanholt	int i;
662145132Sanholt	drm_buf_t **temp_buflist;
663145132Sanholt
664145132Sanholt	count = request->count;
665145132Sanholt	order = drm_order(request->size);
666145132Sanholt	size = 1 << order;
667145132Sanholt
668145132Sanholt	alignment  = (request->flags & _DRM_PAGE_ALIGN)
669145132Sanholt		? round_page(size) : size;
670145132Sanholt	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
671145132Sanholt	total = PAGE_SIZE << page_order;
672145132Sanholt
673145132Sanholt	byte_count = 0;
674145132Sanholt	agp_offset = request->agp_start;
675145132Sanholt
676145132Sanholt	DRM_DEBUG( "count:      %d\n",  count );
677145132Sanholt	DRM_DEBUG( "order:      %d\n",  order );
678145132Sanholt	DRM_DEBUG( "size:       %d\n",  size );
679145132Sanholt	DRM_DEBUG( "agp_offset: %ld\n", agp_offset );
680145132Sanholt	DRM_DEBUG( "alignment:  %d\n",  alignment );
681145132Sanholt	DRM_DEBUG( "page_order: %d\n",  page_order );
682145132Sanholt	DRM_DEBUG( "total:      %d\n",  total );
683145132Sanholt
684145132Sanholt	entry = &dma->bufs[order];
685145132Sanholt
686145132Sanholt	entry->buflist = malloc(count * sizeof(*entry->buflist), M_DRM,
687145132Sanholt	    M_NOWAIT | M_ZERO);
688145132Sanholt	if (entry->buflist == NULL)
689145132Sanholt		return DRM_ERR(ENOMEM);
690145132Sanholt
691145132Sanholt	entry->buf_size = size;
692145132Sanholt	entry->page_order = page_order;
693145132Sanholt
694145132Sanholt	offset = 0;
695145132Sanholt
696145132Sanholt	while ( entry->buf_count < count ) {
697145132Sanholt		buf          = &entry->buflist[entry->buf_count];
698145132Sanholt		buf->idx     = dma->buf_count + entry->buf_count;
699145132Sanholt		buf->total   = alignment;
700145132Sanholt		buf->order   = order;
701145132Sanholt		buf->used    = 0;
702145132Sanholt
703145132Sanholt		buf->offset  = (dma->byte_count + offset);
704145132Sanholt		buf->bus_address = agp_offset + offset;
705145132Sanholt		buf->address = (void *)(agp_offset + offset + dev->sg->handle);
706145132Sanholt		buf->next    = NULL;
707145132Sanholt		buf->pending = 0;
708145132Sanholt		buf->filp    = NULL;
709145132Sanholt
710145132Sanholt		buf->dev_priv_size = dev->dev_priv_size;
711145132Sanholt		buf->dev_private = malloc(buf->dev_priv_size, M_DRM,
712145132Sanholt		    M_NOWAIT | M_ZERO);
713145132Sanholt		if (buf->dev_private == NULL) {
714145132Sanholt			/* Set count correctly so we free the proper amount. */
715145132Sanholt			entry->buf_count = count;
716145132Sanholt			drm_cleanup_buf_error(dev, entry);
717145132Sanholt			return DRM_ERR(ENOMEM);
718145132Sanholt		}
719145132Sanholt
720145132Sanholt		DRM_DEBUG( "buffer %d @ %p\n",
721145132Sanholt			   entry->buf_count, buf->address );
722145132Sanholt
723145132Sanholt		offset += alignment;
724145132Sanholt		entry->buf_count++;
725145132Sanholt		byte_count += PAGE_SIZE << page_order;
726145132Sanholt	}
727145132Sanholt
728145132Sanholt	DRM_DEBUG( "byte_count: %d\n", byte_count );
729145132Sanholt
730145132Sanholt	temp_buflist = realloc(dma->buflist,
731145132Sanholt	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), M_DRM,
732145132Sanholt	    M_NOWAIT);
733145132Sanholt	if (temp_buflist == NULL) {
734145132Sanholt		/* Free the entry because it isn't valid */
735145132Sanholt		drm_cleanup_buf_error(dev, entry);
736145132Sanholt		return DRM_ERR(ENOMEM);
737145132Sanholt	}
738145132Sanholt	dma->buflist = temp_buflist;
739145132Sanholt
740145132Sanholt	for ( i = 0 ; i < entry->buf_count ; i++ ) {
741145132Sanholt		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
742145132Sanholt	}
743145132Sanholt
744145132Sanholt	dma->buf_count += entry->buf_count;
745145132Sanholt	dma->byte_count += byte_count;
746145132Sanholt
747145132Sanholt	DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count );
748145132Sanholt	DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count );
749145132Sanholt
750145132Sanholt	request->count = entry->buf_count;
751145132Sanholt	request->size = size;
752145132Sanholt
753145132Sanholt	dma->flags = _DRM_DMA_USE_SG;
754145132Sanholt
755145132Sanholt	return 0;
756145132Sanholt}
757145132Sanholt
758145132Sanholtint drm_addbufs(DRM_IOCTL_ARGS)
759145132Sanholt{
760145132Sanholt	DRM_DEVICE;
761145132Sanholt	drm_buf_desc_t request;
762145132Sanholt	int err;
763145132Sanholt	int order;
764145132Sanholt
765145132Sanholt	DRM_COPY_FROM_USER_IOCTL( request, (drm_buf_desc_t *)data, sizeof(request) );
766145132Sanholt
767145132Sanholt	if (request.count < 0 || request.count > 4096)
768145132Sanholt		return DRM_ERR(EINVAL);
769145132Sanholt
770145132Sanholt	order = drm_order(request.size);
771145132Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
772145132Sanholt		return DRM_ERR(EINVAL);
773145132Sanholt
774145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
775145132Sanholt	/* No more allocations after first buffer-using ioctl. */
776145132Sanholt	if (dev->buf_use != 0) {
777145132Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
778145132Sanholt		return DRM_ERR(EBUSY);
779145132Sanholt	}
780145132Sanholt	/* No more than one allocation per order */
781145132Sanholt	if (dev->dma->bufs[order].buf_count != 0) {
782145132Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
783145132Sanholt		return DRM_ERR(ENOMEM);
784145132Sanholt	}
785145132Sanholt
786145132Sanholt	if ( request.flags & _DRM_AGP_BUFFER )
787145132Sanholt		err = drm_addbufs_agp(dev, &request);
788145132Sanholt	else
789145132Sanholt	if ( request.flags & _DRM_SG_BUFFER )
790145132Sanholt		err = drm_addbufs_sg(dev, &request);
791145132Sanholt	else
792145132Sanholt		err = drm_addbufs_pci(dev, &request);
793145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
794145132Sanholt
795145132Sanholt	DRM_COPY_TO_USER_IOCTL((drm_buf_desc_t *)data, request, sizeof(request));
796145132Sanholt
797145132Sanholt	return err;
798145132Sanholt}
799145132Sanholt
800145132Sanholtint drm_infobufs(DRM_IOCTL_ARGS)
801145132Sanholt{
802145132Sanholt	DRM_DEVICE;
803145132Sanholt	drm_device_dma_t *dma = dev->dma;
804145132Sanholt	drm_buf_info_t request;
805145132Sanholt	int i;
806145132Sanholt	int count;
807145132Sanholt	int retcode = 0;
808145132Sanholt
809145132Sanholt	DRM_COPY_FROM_USER_IOCTL( request, (drm_buf_info_t *)data, sizeof(request) );
810145132Sanholt
811145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
812145132Sanholt	++dev->buf_use;		/* Can't allocate more after this call */
813145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
814145132Sanholt
815145132Sanholt	for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) {
816145132Sanholt		if ( dma->bufs[i].buf_count ) ++count;
817145132Sanholt	}
818145132Sanholt
819145132Sanholt	DRM_DEBUG( "count = %d\n", count );
820145132Sanholt
821145132Sanholt	if ( request.count >= count ) {
822145132Sanholt		for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) {
823145132Sanholt			if ( dma->bufs[i].buf_count ) {
824145132Sanholt				drm_buf_desc_t from;
825145132Sanholt
826145132Sanholt				from.count = dma->bufs[i].buf_count;
827145132Sanholt				from.size = dma->bufs[i].buf_size;
828145132Sanholt				from.low_mark = dma->bufs[i].freelist.low_mark;
829145132Sanholt				from.high_mark = dma->bufs[i].freelist.high_mark;
830145132Sanholt
831145132Sanholt				if (DRM_COPY_TO_USER(&request.list[count], &from,
832145132Sanholt				    sizeof(drm_buf_desc_t)) != 0) {
833145132Sanholt					retcode = DRM_ERR(EFAULT);
834145132Sanholt					break;
835145132Sanholt				}
836145132Sanholt
837145132Sanholt				DRM_DEBUG( "%d %d %d %d %d\n",
838145132Sanholt					   i,
839145132Sanholt					   dma->bufs[i].buf_count,
840145132Sanholt					   dma->bufs[i].buf_size,
841145132Sanholt					   dma->bufs[i].freelist.low_mark,
842145132Sanholt					   dma->bufs[i].freelist.high_mark );
843145132Sanholt				++count;
844145132Sanholt			}
845145132Sanholt		}
846145132Sanholt	}
847145132Sanholt	request.count = count;
848145132Sanholt
849145132Sanholt	DRM_COPY_TO_USER_IOCTL( (drm_buf_info_t *)data, request, sizeof(request) );
850145132Sanholt
851145132Sanholt	return retcode;
852145132Sanholt}
853145132Sanholt
854145132Sanholtint drm_markbufs(DRM_IOCTL_ARGS)
855145132Sanholt{
856145132Sanholt	DRM_DEVICE;
857145132Sanholt	drm_device_dma_t *dma = dev->dma;
858145132Sanholt	drm_buf_desc_t request;
859145132Sanholt	int order;
860145132Sanholt
861145132Sanholt	DRM_COPY_FROM_USER_IOCTL( request, (drm_buf_desc_t *)data, sizeof(request) );
862145132Sanholt
863145132Sanholt	DRM_DEBUG( "%d, %d, %d\n",
864145132Sanholt		   request.size, request.low_mark, request.high_mark );
865145132Sanholt
866145132Sanholt
867145132Sanholt	order = drm_order(request.size);
868145132Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ||
869145132Sanholt	    request.low_mark < 0 || request.high_mark < 0) {
870145132Sanholt		return DRM_ERR(EINVAL);
871145132Sanholt	}
872145132Sanholt
873145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
874145132Sanholt	if (request.low_mark > dma->bufs[order].buf_count ||
875145132Sanholt	    request.high_mark > dma->bufs[order].buf_count) {
876145132Sanholt		return DRM_ERR(EINVAL);
877145132Sanholt	}
878145132Sanholt
879145132Sanholt	dma->bufs[order].freelist.low_mark  = request.low_mark;
880145132Sanholt	dma->bufs[order].freelist.high_mark = request.high_mark;
881145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
882145132Sanholt
883145132Sanholt	return 0;
884145132Sanholt}
885145132Sanholt
886145132Sanholtint drm_freebufs(DRM_IOCTL_ARGS)
887145132Sanholt{
888145132Sanholt	DRM_DEVICE;
889145132Sanholt	drm_device_dma_t *dma = dev->dma;
890145132Sanholt	drm_buf_free_t request;
891145132Sanholt	int i;
892145132Sanholt	int idx;
893145132Sanholt	drm_buf_t *buf;
894145132Sanholt	int retcode = 0;
895145132Sanholt
896145132Sanholt	DRM_COPY_FROM_USER_IOCTL( request, (drm_buf_free_t *)data, sizeof(request) );
897145132Sanholt
898145132Sanholt	DRM_DEBUG( "%d\n", request.count );
899145132Sanholt
900145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
901145132Sanholt	for ( i = 0 ; i < request.count ; i++ ) {
902145132Sanholt		if (DRM_COPY_FROM_USER(&idx, &request.list[i], sizeof(idx))) {
903145132Sanholt			retcode = DRM_ERR(EFAULT);
904145132Sanholt			break;
905145132Sanholt		}
906145132Sanholt		if ( idx < 0 || idx >= dma->buf_count ) {
907145132Sanholt			DRM_ERROR( "Index %d (of %d max)\n",
908145132Sanholt				   idx, dma->buf_count - 1 );
909145132Sanholt			retcode = DRM_ERR(EINVAL);
910145132Sanholt			break;
911145132Sanholt		}
912145132Sanholt		buf = dma->buflist[idx];
913145132Sanholt		if ( buf->filp != filp ) {
914145132Sanholt			DRM_ERROR("Process %d freeing buffer not owned\n",
915145132Sanholt				   DRM_CURRENTPID);
916145132Sanholt			retcode = DRM_ERR(EINVAL);
917145132Sanholt			break;
918145132Sanholt		}
919145132Sanholt		drm_free_buffer(dev, buf);
920145132Sanholt	}
921145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
922145132Sanholt
923145132Sanholt	return retcode;
924145132Sanholt}
925145132Sanholt
926145132Sanholtint drm_mapbufs(DRM_IOCTL_ARGS)
927145132Sanholt{
928145132Sanholt	DRM_DEVICE;
929145132Sanholt	drm_device_dma_t *dma = dev->dma;
930145132Sanholt	int retcode = 0;
931145132Sanholt	const int zero = 0;
932145132Sanholt	vm_offset_t address;
933145132Sanholt	struct vmspace *vms;
934145132Sanholt#ifdef __FreeBSD__
935145132Sanholt	vm_ooffset_t foff;
936145132Sanholt	vm_size_t size;
937145132Sanholt	vm_offset_t vaddr;
938145132Sanholt#elif defined(__NetBSD__) || defined(__OpenBSD__)
939145132Sanholt	struct vnode *vn;
940145132Sanholt	vm_size_t size;
941145132Sanholt	vaddr_t vaddr;
942145132Sanholt#endif /* __NetBSD__ || __OpenBSD__ */
943145132Sanholt
944145132Sanholt	drm_buf_map_t request;
945145132Sanholt	int i;
946145132Sanholt
947145132Sanholt	DRM_COPY_FROM_USER_IOCTL( request, (drm_buf_map_t *)data, sizeof(request) );
948145132Sanholt
949145132Sanholt#if defined(__NetBSD__) || defined(__OpenBSD__)
950145132Sanholt	if (!vfinddev(kdev, VCHR, &vn))
951145132Sanholt		return 0;	/* FIXME: Shouldn't this be EINVAL or something? */
952145132Sanholt#endif /* __NetBSD__ || __OpenBSD */
953145132Sanholt
954145132Sanholt#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
955145132Sanholt	vms = p->td_proc->p_vmspace;
956145132Sanholt#else
957145132Sanholt	vms = p->p_vmspace;
958145132Sanholt#endif
959145132Sanholt
960145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
961145132Sanholt	dev->buf_use++;		/* Can't allocate more after this call */
962145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
963145132Sanholt
964145132Sanholt	if (request.count < dma->buf_count)
965145132Sanholt		goto done;
966145132Sanholt
967145132Sanholt	if ((dev->use_agp && (dma->flags & _DRM_DMA_USE_AGP)) ||
968145132Sanholt	    (dev->use_sg && (dma->flags & _DRM_DMA_USE_SG))) {
969145132Sanholt		drm_local_map_t *map = dev->agp_buffer_map;
970145132Sanholt
971145132Sanholt		if (map == NULL) {
972145132Sanholt			retcode = EINVAL;
973145132Sanholt			goto done;
974145132Sanholt		}
975145132Sanholt		size = round_page(map->size);
976145132Sanholt		foff = map->offset;
977145132Sanholt	} else {
978145132Sanholt		size = round_page(dma->byte_count),
979145132Sanholt		foff = 0;
980145132Sanholt	}
981145132Sanholt
982145132Sanholt#ifdef __FreeBSD__
983145132Sanholt	vaddr = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ);
984145132Sanholt#if __FreeBSD_version >= 600023
985145132Sanholt	retcode = vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
986145132Sanholt	    VM_PROT_ALL, MAP_SHARED, OBJT_DEVICE, kdev, foff );
987145132Sanholt#else
988145132Sanholt	retcode = vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
989145132Sanholt	    VM_PROT_ALL, MAP_SHARED, SLIST_FIRST(&kdev->si_hlist), foff );
990145132Sanholt#endif
991145132Sanholt#elif defined(__NetBSD__) || defined(__OpenBSD__)
992145132Sanholt	vaddr = round_page((vaddr_t)vms->vm_daddr + MAXDSIZ);
993145132Sanholt	retcode = uvm_mmap(&vms->vm_map, &vaddr, size,
994145132Sanholt	    UVM_PROT_READ | UVM_PROT_WRITE, UVM_PROT_ALL, MAP_SHARED,
995145132Sanholt	    &vn->v_uobj, foff, p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
996145132Sanholt#endif /* __NetBSD__ || __OpenBSD */
997145132Sanholt	if (retcode)
998145132Sanholt		goto done;
999145132Sanholt
1000145132Sanholt	request.virtual = (void *)vaddr;
1001145132Sanholt
1002145132Sanholt	for ( i = 0 ; i < dma->buf_count ; i++ ) {
1003145132Sanholt		if (DRM_COPY_TO_USER(&request.list[i].idx,
1004145132Sanholt		    &dma->buflist[i]->idx, sizeof(request.list[0].idx))) {
1005145132Sanholt			retcode = EFAULT;
1006145132Sanholt			goto done;
1007145132Sanholt		}
1008145132Sanholt		if (DRM_COPY_TO_USER(&request.list[i].total,
1009145132Sanholt		    &dma->buflist[i]->total, sizeof(request.list[0].total))) {
1010145132Sanholt			retcode = EFAULT;
1011145132Sanholt			goto done;
1012145132Sanholt		}
1013145132Sanholt		if (DRM_COPY_TO_USER(&request.list[i].used, &zero,
1014145132Sanholt		    sizeof(zero))) {
1015145132Sanholt			retcode = EFAULT;
1016145132Sanholt			goto done;
1017145132Sanholt		}
1018145132Sanholt		address = vaddr + dma->buflist[i]->offset; /* *** */
1019145132Sanholt		if (DRM_COPY_TO_USER(&request.list[i].address, &address,
1020145132Sanholt		    sizeof(address))) {
1021145132Sanholt			retcode = EFAULT;
1022145132Sanholt			goto done;
1023145132Sanholt		}
1024145132Sanholt	}
1025145132Sanholt
1026145132Sanholt done:
1027145132Sanholt	request.count = dma->buf_count;
1028145132Sanholt
1029145132Sanholt	DRM_DEBUG( "%d buffers, retcode = %d\n", request.count, retcode );
1030145132Sanholt
1031145132Sanholt	DRM_COPY_TO_USER_IOCTL((drm_buf_map_t *)data, request, sizeof(request));
1032145132Sanholt
1033145132Sanholt	return DRM_ERR(retcode);
1034145132Sanholt}
1035