genode/libports/src/lib/libdrm/ioctl.cc

222 lines
7.3 KiB
C++

/*
* \brief Handler for ioctl operations on DRM device
* \author Norman Feske
* \date 2010-07-13
*/
/*
* Copyright (C) 2010-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <util/string.h>
#include <base/env.h>
#include <base/printf.h>
/* libc includes */
#include <sys/ioccom.h>
/* libc plugin interface */
#include <libc-plugin/plugin.h>
#include <libc-plugin/fd_alloc.h>
/* libdrm includes */
extern "C" {
#define virtual _virtual
#include <drm.h>
#include <i915_drm.h>
#undef virtual
}
/* GPU driver interface */
#include <gpu/driver.h>
enum { verbose_ioctl = false };
long drm_command(long request) { return (request & 0xff) - DRM_COMMAND_BASE; }
/**
* Return name of DRM command
*/
const char *command_name(long request)
{
if (IOCGROUP(request) != DRM_IOCTL_BASE)
return "<non-DRM>";
switch (drm_command(request)) {
case DRM_I915_INIT: return "DRM_I915_INIT";
case DRM_I915_FLUSH: return "DRM_I915_FLUSH";
case DRM_I915_FLIP: return "DRM_I915_FLIP";
case DRM_I915_BATCHBUFFER: return "DRM_I915_BATCHBUFFER";
case DRM_I915_IRQ_EMIT: return "DRM_I915_IRQ_EMIT";
case DRM_I915_IRQ_WAIT: return "DRM_I915_IRQ_WAIT";
case DRM_I915_GETPARAM: return "DRM_I915_GETPARAM";
case DRM_I915_SETPARAM: return "DRM_I915_SETPARAM";
case DRM_I915_ALLOC: return "DRM_I915_ALLOC";
case DRM_I915_FREE: return "DRM_I915_FREE";
case DRM_I915_INIT_HEAP: return "DRM_I915_INIT_HEAP";
case DRM_I915_CMDBUFFER: return "DRM_I915_CMDBUFFER";
case DRM_I915_DESTROY_HEAP: return "DRM_I915_DESTROY_HEAP";
case DRM_I915_SET_VBLANK_PIPE: return "DRM_I915_SET_VBLANK_PIPE";
case DRM_I915_GET_VBLANK_PIPE: return "DRM_I915_GET_VBLANK_PIPE";
case DRM_I915_VBLANK_SWAP: return "DRM_I915_VBLANK_SWAP";
case DRM_I915_HWS_ADDR: return "DRM_I915_HWS_ADDR";
case DRM_I915_GEM_INIT: return "DRM_I915_GEM_INIT";
case DRM_I915_GEM_EXECBUFFER: return "DRM_I915_GEM_EXECBUFFER";
case DRM_I915_GEM_PIN: return "DRM_I915_GEM_PIN";
case DRM_I915_GEM_UNPIN: return "DRM_I915_GEM_UNPIN";
case DRM_I915_GEM_BUSY: return "DRM_I915_GEM_BUSY";
case DRM_I915_GEM_THROTTLE: return "DRM_I915_GEM_THROTTLE";
case DRM_I915_GEM_ENTERVT: return "DRM_I915_GEM_ENTERVT";
case DRM_I915_GEM_LEAVEVT: return "DRM_I915_GEM_LEAVEVT";
case DRM_I915_GEM_CREATE: return "DRM_I915_GEM_CREATE";
case DRM_I915_GEM_PREAD: return "DRM_I915_GEM_PREAD";
case DRM_I915_GEM_PWRITE: return "DRM_I915_GEM_PWRITE";
case DRM_I915_GEM_MMAP: return "DRM_I915_GEM_MMAP";
case DRM_I915_GEM_SET_DOMAIN: return "DRM_I915_GEM_SET_DOMAIN";
case DRM_I915_GEM_SW_FINISH: return "DRM_I915_GEM_SW_FINISH";
case DRM_I915_GEM_SET_TILING: return "DRM_I915_GEM_SET_TILING";
case DRM_I915_GEM_GET_TILING: return "DRM_I915_GEM_GET_TILING";
case DRM_I915_GEM_GET_APERTURE: return "DRM_I915_GEM_GET_APERTURE";
case DRM_I915_GEM_MMAP_GTT: return "DRM_I915_GEM_MMAP_GTT";
case DRM_I915_GET_PIPE_FROM_CRTC_ID: return "DRM_I915_GET_PIPE_FROM_CRTC_ID";
case DRM_I915_GEM_MADVISE: return "DRM_I915_GEM_MADVISE";
case DRM_I915_OVERLAY_PUT_IMAGE: return "DRM_I915_OVERLAY_PUT_IMAGE";
case DRM_I915_OVERLAY_ATTRS: return "DRM_I915_OVERLAY_ATTRS";
case DRM_I915_GEM_EXECBUFFER2: return "DRM_I915_GEM_EXECBUFFER2";
default: return "<unknown>";
}
}
static void dump_ioctl(long request)
{
PDBG("ioctl(request=%lx, %s, len=%ld, cmd=%s)", request,
(request & 0xe0000000) == IOC_OUT ? "out" :
(request & 0xe0000000) == IOC_IN ? "in" :
(request & 0xe0000000) == IOC_INOUT ? "inout" : "void",
IOCPARM_LEN(request),
command_name(request));
}
namespace {
struct Plugin_context : Libc::Plugin_context { };
class Plugin : public Libc::Plugin
{
private:
Gpu_driver *_driver;
Gpu_driver::Client *_client;
/*
* Assign a priority of 1 to override libc_vfs.
*/
enum { PLUGIN_PRIORITY = 1 };
public:
Plugin(Gpu_driver *driver)
:
Libc::Plugin(PLUGIN_PRIORITY), _driver(driver), _client(0)
{
if (!_driver) {
PERR("could not initialize GPU driver");
return;
}
_client = _driver->create_client();
}
bool supports_open(const char *pathname, int flags)
{
return !Genode::strcmp(pathname, "/dev/drm");
}
Libc::File_descriptor *open(const char *pathname, int flags)
{
Plugin_context *context = new (Genode::env()->heap()) Plugin_context();
return Libc::file_descriptor_allocator()->alloc(this, context);
}
bool supports_stat(const char *path)
{
return (Genode::strcmp(path, "/dev") == 0 ||
Genode::strcmp(path, "/dev/drm") == 0);
}
int stat(const char *path, struct stat *buf)
{
if (buf)
buf->st_mode = S_IFDIR;
return 0;
}
int ioctl(Libc::File_descriptor *fd, int request, char *argp)
{
if (verbose_ioctl)
dump_ioctl(request);
if (drm_command(request) == DRM_I915_GEM_MMAP_GTT) {
drm_i915_gem_mmap_gtt *arg = (drm_i915_gem_mmap_gtt *)(argp);
arg->offset = (__u64)_driver->map_buffer_object(_client, arg->handle);
return arg->offset ? 0 : -1;
}
return _driver->ioctl(_client, drm_command(request), argp);
}
bool supports_mmap() { return true; }
/**
* Pseudo mmap specific for DRM device
*
* The original protocol between the Gallium driver and the kernel
* DRM driver is based on the interplay of the GEM_MMAP_GTT ioctl and
* mmap. First, GEM_MMAP_GTT is called with a buffer object as
* argument. The DRM driver returns a global ID called "fake offset"
* representing the buffer object. The fake offset actually refers
* to a region within the virtual 'dev->mm_private' memory map.
* The Gallium driver then passes this fake offset back to the DRM
* driver as 'offset' argument to 'mmap'. The DRM driver uses this
* argument to look up the GEM offset and set up a vm_area specific
* for the buffer object - paged by the DRM driver.
*
* We avoid this round-trip of a global ID. Instead of calling the
* 'GEM_MMAP_GTT' ioctl, we use a dedicated driver function called
* 'map_buffer_object', which simply returns the local address of
* the already mapped buffer object via the 'offset' return value.
* Hence, all 'mmap' has to do is passing back the 'offset' argument
* as return value.
*/
void *mmap(void *addr, ::size_t length, int prot, int flags,
Libc::File_descriptor *fd, ::off_t offset)
{
PDBG("\naddr=%p, length=%zd, prot=%x, flags=%x, offset=0x%lx",
addr, length, prot, flags, (long)offset);
return (void *)offset;
}
int close(Libc::File_descriptor *fd)
{
Genode::destroy(Genode::env()->heap(),
static_cast<Plugin_context *>(fd->context));
Libc::file_descriptor_allocator()->free(fd);
return 0;
}
};
}
void __attribute__((constructor)) init_drm_device_plugin()
{
static Plugin plugin(gpu_driver());
}