/*
*/
/**
* \file drm_bufs.c
* Generic buffer template
*
* \author Rickard E. (Rik) Faith <faith@valinux.com>
* \author Gareth Hughes <gareth@valinux.com>
*/
/*
* Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com
*
* Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* Copyright (c) 2009, 2012, Intel Corporation.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm_io32.h"
#ifdef _LP64
#else
#error "No define for _LP64, _SYSCALL32_IMPL or _ILP32"
#endif
struct drm_local_map *map)
{
/*
* Because the kernel-userspace ABI is fixed at a 32-bit offset
* while PCI resources may live above that, we ignore the map
* offset for maps of type _DRM_FRAMEBUFFER or _DRM_REGISTERS.
* It is assumed that each driver will have only one resource of
* each type.
*/
continue;
case _DRM_SHM:
break;
return entry;
case _DRM_REGISTERS:
case _DRM_FRAME_BUFFER:
return entry;
default: /* Make gcc happy */
;
}
return entry;
}
return NULL;
}
{
if (ret < 0)
return ret;
return 0;
}
/**
* Core function to create a range of memory available for mapping by a
* non-root process.
*
* Adjusts the memory offset to its absolute value according to the mapping
* type. Adds the map to the map list drm_device::maplist. Adds MTRR's where
* applicable and if supported by the kernel.
*/
enum drm_map_flags flags,
struct drm_map_list ** maplist)
{
/* LINTED */
unsigned long user_token;
int ret;
if (!map)
return -ENOMEM;
/* Only allow shared memory to be removable since we only keep enough
* book keeping information about shared memory to allow for removal
* when processes fork.
*/
return -EINVAL;
}
DRM_DEBUG("offset = 0x%08llx, size = 0x%08lx, type = %d\n",
/* page-align _DRM_SHM maps. They are allocated here so there is no security
* hole created by that and it works around various broken drivers that use
* a non-aligned quantity to map the SAREA. --BenH
*/
return -EINVAL;
}
return -EINVAL;
}
case _DRM_REGISTERS:
case _DRM_FRAME_BUFFER:
/* Some drivers preinitialize some maps, without the X Server
* needing to be aware of it. Therefore, we just return success
* when the server tries to create a duplicate map.
*/
DRM_DEBUG("Matching maps of type %d with "
"mismatched sizes, (%ld vs %ld)\n",
}
return 0;
}
return -ENOMEM;
}
}
break;
case _DRM_SHM:
DRM_DEBUG("Matching maps of type %d with "
"mismatched sizes, (%ld vs %ld)\n",
}
return 0;
}
DRM_DEBUG("%lu %p\n",
return -ENOMEM;
}
/* Prevent a 2nd X Server from creating a 2nd lock */
return -EBUSY;
}
}
break;
case _DRM_AGP: {
if (!drm_core_has_AGP(dev)) {
return -EINVAL;
}
if (!kvaddr) {
DRM_ERROR("failed to alloc AGP aperture");
return -EPERM;
}
if (!map->umem_cookie) {
DRM_ERROR("gfxp_umem_cookie_init() failed");
return (-ENOMEM);
}
break;
}
case _DRM_GEM:
DRM_ERROR("tried to addmap GEM object\n");
break;
case _DRM_SCATTER_GATHER:
return -EINVAL;
}
if (!map->umem_cookie) {
DRM_ERROR("gfxp_umem_cookie_init() failed");
return (-ENOMEM);
}
break;
case _DRM_CONSISTENT:
return -ENOTSUP;
default:
return -EINVAL;
}
if (!list) {
return -EINVAL;
}
/* Assign a 32-bit handle */
/* We do it here so that dev->struct_mutex protects the increment */
if (ret) {
return ret;
}
return 0;
}
{
int rc;
if (!rc)
return rc;
}
/**
* Ioctl to specify a range of memory that is available for mapping by a
* non-root process.
*
* \param inode device inode.
* \param file_priv DRM file private.
* \param cmd command.
* \param arg pointer to a drm_map structure.
* \return zero on success or a negative value on error.
*
*/
/* LINTED */
{
int err;
DRM_DEBUG_DRIVER("adding map of type %d, offset: %0x, size: %0x\n",
return -EPERM;
if (err)
return err;
/* avoid a warning on 64-bit, this casting isn't very nice, but the API is set so too late */
return 0;
}
/**
* Remove a map private from list and deallocate resources if the mapping
* isn't in use.
*
* Searches the map on drm_device::maplist, removes it from the list, see if
* its being used, and free any associate resource (such as MTRR's) if it's not
* being on use.
*
* \sa drm_addmap
*/
{
/* LINTED */
int found = 0;
/* LINTED */
/* Find the list entry for the map and remove it */
found = 1;
break;
}
}
if (!found)
return -EINVAL;
case _DRM_REGISTERS:
/* FALLTHROUGH */
case _DRM_FRAME_BUFFER:
break;
case _DRM_SHM:
break;
case _DRM_AGP:
break;
case _DRM_SCATTER_GATHER:
break;
case _DRM_CONSISTENT:
break;
case _DRM_GEM:
DRM_ERROR("tried to rmmap GEM object\n");
break;
}
return 0;
}
{
int ret;
return ret;
}
/* The rmmap ioctl appears to be unnecessary. All mappings are torn down on
* the last close of the device, and this is necessary for cleanup when things
* exit uncleanly. Therefore, having userland manually remove mappings seems
* like a pointless exercise since they're going away anyway.
*
* One use case might be after addmap is allowed for normal users for SHM and
* gets used by drivers that the server doesn't need to care about. This seems
* unlikely.
*
* \param inode device inode.
* \param file_priv DRM file private.
* \param cmd command.
* \param arg pointer to a struct drm_map structure.
* \return zero on success or a negative value on error.
*/
/* LINTED */
{
int ret;
break;
}
}
/* List has wrapped around to the head pointer, or its empty we didn't
* find anything.
*/
return -EINVAL;
}
/* Register and framebuffer maps are permanent */
return 0;
}
return ret;
}
/**
* Cleanup after an error on one of the addbufs() functions.
*
* \param dev DRM device.
* \param entry buffer entry where the error occurred.
*
* Frees any pages and buffers associated with the given entry.
*/
/* LINTED */
struct drm_buf_entry * entry)
{
int i;
"drm_cleanup_buf_error: not implemented");
}
}
}
}
}
}
}
/**
* Add AGP buffers for DMA transfers.
*
* \param dev struct drm_device to which the buffers are to be added.
* \param request pointer to a struct drm_buf_desc describing the request.
* \return zero on success or a negative number on failure.
*
* After some sanity checks creates a drm_buf structure for each buffer and
* reallocates the buffer list of the same size order to accommodate the new
* buffers.
*/
/* LINTED */
{
unsigned long offset;
unsigned long agp_offset;
int count;
int order;
int size;
int alignment;
int page_order;
int total;
int byte_count;
int i;
if (!dma)
return -EINVAL;
byte_count = 0;
return -EINVAL;
return -EBUSY;
}
return -ENOMEM; /* May only call once for each order */
}
return -EINVAL;
}
return -ENOMEM;
}
offset = 0;
if (!buf->dev_private) {
/* Set count correctly so we free the proper amount. */
return -ENOMEM;
}
}
if (!temp_buflist) {
/* Free the entry because it isn't valid */
return -ENOMEM;
}
}
return 0;
}
{
unsigned long offset;
unsigned long agp_offset;
int count;
int order;
int size;
int alignment;
int page_order;
int total;
int byte_count;
int i;
return -EINVAL;
if (!dma)
return -EINVAL;
return -EPERM;
byte_count = 0;
return -EINVAL;
return -EBUSY;
}
return -ENOMEM; /* May only call once for each order */
}
return -EINVAL;
}
return -ENOMEM;
}
offset = 0;
if (!buf->dev_private) {
/* Set count correctly so we free the proper amount. */
return -ENOMEM;
}
}
if (!temp_buflist) {
/* Free the entry because it isn't valid */
return -ENOMEM;
}
}
return 0;
}
/**
* Add buffers for DMA transfers (ioctl).
*
* \param inode device inode.
* \param file_priv DRM file private.
* \param cmd command.
* \param arg pointer to a struct drm_buf_desc request.
* \return zero on success or a negative number on failure.
*
* According with the memory type specified in drm_buf_desc::flags and the
* build options, it dispatches the call either to addbufs_agp(),
* addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent
* PCI memory respectively.
*/
/* LINTED */
{
return -EINVAL;
else
return ret;
}
/**
* Get information about the buffer mappings.
*
* This was originally mean for debugging purposes, or by a sophisticated
* client library to determine how best to use the available buffers (e.g.,
* large buffers can be used for image transfer).
*
* \param inode device inode.
* \param file_priv DRM file private.
* \param cmd command.
* \param arg pointer to a drm_buf_info structure.
* \return zero on success or a negative number on failure.
*
* Increments drm_device::buf_use while holding the drm_device::count_lock
* lock, preventing of allocating more buffers after this call. Information
* about each requested buffer is then copied into user space.
*/
/* LINTED */
{
int i;
int count;
return -EINVAL;
if (!dma)
return -EINVAL;
return -EBUSY;
}
++count;
}
return -EFAULT;
DRM_DEBUG("%d %d %d %d %d\n",
i,
++count;
}
}
}
return 0;
}
/**
* Specifies a low and high water mark for buffer allocation
*
* \param inode device inode.
* \param file_priv DRM file private.
* \param cmd command.
* \param arg a pointer to a drm_buf_desc structure.
* \return zero on success or a negative number on failure.
*
* Verifies that the size order is bounded between the admissible orders and
* updates the respective drm_device_dma::bufs entry low and high water mark.
*
* \note This ioctl is deprecated and mostly never used.
*/
/* LINTED */
{
int order;
return -EINVAL;
if (!dma)
return -EINVAL;
DRM_DEBUG("%d, %d, %d\n",
return -EINVAL;
return -EINVAL;
return -EINVAL;
return 0;
}
/**
* Unreserve the buffers in list, previously reserved using drmDMA.
*
* \param inode device inode.
* \param file_priv DRM file private.
* \param cmd command.
* \param arg pointer to a drm_buf_free structure.
* \return zero on success or a negative number on failure.
*
* Calls free_buffer() for each used buffer.
* This function is primarily used for debugging.
*/
/* LINTED */
{
int i;
int idx;
return -EINVAL;
if (!dma)
return -EINVAL;
return -EFAULT;
DRM_ERROR("Index %d (of %d max)\n",
return -EINVAL;
}
DRM_ERROR("Process %d freeing buffer not owned\n",
return -EINVAL;
}
}
return 0;
}
/**
* Maps all of the DMA buffers into client-virtual space (ioctl).
*
* \param inode device inode.
* \param file_priv DRM file private.
* \param cmd command.
* \param arg pointer to a drm_buf_map structure.
* \return zero on success or a negative number on failure.
*
* Maps the AGP, SG or PCI buffer region with do_mmap(), and copies information
* about each buffer into user space. For PCI buffers, it calls do_mmap() with
* offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls
* drm_mmap_dma().
*/
/* LINTED */
{
int retcode = 0;
const int zero = 0;
unsigned long virtual;
unsigned long address;
int i;
#ifdef _MULTI_DATAMODEL
#endif
return -EINVAL;
if (!dma)
return -EINVAL;
return -EBUSY;
}
if (!map) {
goto done;
}
} else {
foff = 0;
}
DRM_ERROR("request->virtual is NULL");
goto done;
}
#ifdef _MULTI_DATAMODEL
goto done;
}
goto done;
}
goto done;
}
goto done;
}
}
} else {
#endif
goto done;
}
goto done;
}
sizeof (zero))) {
goto done;
}
goto done;
}
}
#ifdef _MULTI_DATAMODEL
}
#endif
}
done:
return retcode;
}
/**
* Compute size order. Returns the exponent of the smaller power of two which
* is greater or equal to given number.
*
* \param size size.
* \return order.
*
* \todo Can be made faster.
*/
{
int order;
unsigned long tmp;
++order;
return order;
}