Re: [RFC] dma-buf: Implement test module

From: Thierry Reding
Date: Thu Dec 12 2013 - 09:43:33 EST


On Thu, Dec 12, 2013 at 03:36:29PM +0100, Thierry Reding wrote:
> This is a simple test module that can be used to allocate, export and
> delete DMA-BUF objects. It can be used to test DMA-BUF sharing in
> systems that lack a real second driver.
>
> Signed-off-by: Thierry Reding <treding@xxxxxxxxxx>
> ---
> drivers/base/Kconfig | 4 +
> drivers/base/Makefile | 1 +
> drivers/base/dma-buf-test.c | 308 ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 313 insertions(+)
> create mode 100644 drivers/base/dma-buf-test.c

And attached is a small test program that I've been using to test this
new module. It can be built with:

$ gcc -O2 -g -Wall -Werror -I/usr/include/libdrm -o dma-buf-test dma-buf-test.c -ldrm

Thierry
/*
* Copyright (C) 2013 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/ioctl.h>
#include <sys/mman.h>

#include <linux/ioctl.h>

#include <xf86drm.h>
#include <xf86drmMode.h>
#include <drm_fourcc.h>

struct dmabuf_create {
__u32 flags;
__u32 size;
};

#define DMABUF_IOCTL_BASE 'D'
#define DMABUF_IOCTL_CREATE _IOWR(DMABUF_IOCTL_BASE, 0, struct dmabuf_create)
#define DMABUF_IOCTL_DELETE _IOWR(DMABUF_IOCTL_BASE, 1, int)
#define DMABUF_IOCTL_EXPORT _IOWR(DMABUF_IOCTL_BASE, 2, int)

#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))

static const char default_dmabuf_device[] = "/dev/dmabuf";
static const char default_dri_card[] = "/dev/dri/card0";

struct screen {
int fd;

unsigned int width;
unsigned int height;
unsigned int pitch;
unsigned int depth;
unsigned int bpp;

drmModeModeInfo mode;
uint32_t fb, old_fb;
uint32_t connector;
uint32_t format;
uint32_t handle;
uint32_t crtc;
};

static int probe_connector(struct screen *screen, drmModeConnectorPtr connector)
{
drmModeEncoderPtr encoder;
drmModeCrtcPtr crtc;
drmModeFBPtr fb;

encoder = drmModeGetEncoder(screen->fd, connector->encoder_id);
if (!encoder)
return -ENODEV;

crtc = drmModeGetCrtc(screen->fd, encoder->crtc_id);
if (!crtc) {
drmModeFreeEncoder(encoder);
return -ENODEV;
}

screen->old_fb = crtc->buffer_id;

fb = drmModeGetFB(screen->fd, crtc->buffer_id);
if (!fb) {
/* TODO: create new framebuffer */
drmModeFreeEncoder(encoder);
drmModeFreeCrtc(crtc);
return -ENOSYS;
}

screen->connector = connector->connector_id;
screen->old_fb = crtc->buffer_id;
screen->crtc = encoder->crtc_id;
/* TODO: check crtc->mode_valid */
screen->mode = crtc->mode;

screen->width = fb->width;
screen->height = fb->height;
screen->pitch = fb->pitch;
screen->depth = fb->depth;
screen->bpp = fb->bpp;

drmModeFreeEncoder(encoder);
drmModeFreeCrtc(crtc);
drmModeFreeFB(fb);

return 0;
}

static int screen_open(struct screen **screenp, int fd)
{
drmModeConnectorPtr connector;
struct screen *screen;
bool found = false;
drmModeResPtr res;
unsigned int i;
int err;

if (!screenp || fd < 0)
return -EINVAL;

screen = calloc(1, sizeof(*screen));
if (!screen)
return -ENOMEM;

screen->format = DRM_FORMAT_XRGB8888;
screen->fd = fd;

res = drmModeGetResources(fd);
if (!res) {
free(screen);
return -ENOMEM;
}

for (i = 0; i < res->count_connectors; i++) {
connector = drmModeGetConnector(fd, res->connectors[i]);
if (!connector)
continue;

if (connector->connection != DRM_MODE_CONNECTED) {
drmModeFreeConnector(connector);
continue;
}

err = probe_connector(screen, connector);
if (err < 0) {
drmModeFreeConnector(connector);
continue;
}

found = true;
break;
}

drmModeFreeResources(res);

if (!found) {
free(screen);
return -ENODEV;
}

*screenp = screen;

return 0;
}

int screen_close(struct screen *screen)
{
struct drm_gem_close args;
int err;

err = drmModeSetCrtc(screen->fd, screen->crtc, screen->old_fb, 0, 0,
&screen->connector, 1, &screen->mode);
if (err < 0) {
fprintf(stderr, "drmModeSetCrtc() failed: %m\n");
return -errno;
}

err = drmModeRmFB(screen->fd, screen->fb);
if (err < 0) {
fprintf(stderr, "drmModeRmFB() failed: %m\n");
return -errno;
}

memset(&args, 0, sizeof(args));
args.handle = screen->handle;

err = ioctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &args);
if (err < 0) {
fprintf(stderr, "ioctl(DRM_IOCTL_GEM_CLOSE) failed: %m\n");
return -errno;
}

return 0;
}

int screen_import(struct screen *screen, int buf)
{
uint32_t handles[4], pitches[4], offsets[4];
int err;

err = drmPrimeFDToHandle(screen->fd, buf, &screen->handle);
if (err < 0) {
fprintf(stderr, "drmPrimeFDToHandle() failed: %m\n");
return -errno;
}

printf("DRM handle: %x\n", screen->handle);

handles[0] = screen->handle;
pitches[0] = screen->pitch;
offsets[0] = 0;

err = drmModeAddFB2(screen->fd, screen->width, screen->height,
screen->format, handles, pitches, offsets,
&screen->fb, 0);
if (err < 0) {
fprintf(stderr, "drmModeAddFB() failed: %m\n");
return -errno;
}

err = drmModeSetCrtc(screen->fd, screen->crtc, screen->fb, 0, 0,
&screen->connector, 1, &screen->mode);
if (err < 0) {
fprintf(stderr, "drmModeSetCrtc() failed: %m\n");
return -errno;
}

return 0;
}

int dmabuf_create(int fd, size_t size, int flags)
{
struct dmabuf_create args;

memset(&args, 0, sizeof(args));
args.flags = flags;
args.size = size;

return ioctl(fd, DMABUF_IOCTL_CREATE, &args);
}

int dmabuf_delete(int fd)
{
return ioctl(fd, DMABUF_IOCTL_DELETE, 0);
}

int dmabuf_export(int fd, int flags)
{
return ioctl(fd, DMABUF_IOCTL_EXPORT, flags);
}

void *dmabuf_mmap(int buf, off_t offset, size_t size)
{
void *ptr;

ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, buf, offset);
if (ptr == MAP_FAILED)
return NULL;

return ptr;
}

void dmabuf_unmap(int buf, void *ptr, size_t size)
{
munmap(ptr, size);
}

static void prepare_buffer(int buf, size_t size, struct screen *screen)
{
void *ptr;

/* TODO: handle other formats */
if (screen->format != DRM_FORMAT_XRGB8888) {
fprintf(stderr, "unsupported format: %x\n", screen->format);
return;
}

ptr = dmabuf_mmap(buf, 0, size);
if (ptr) {
uint32_t colors[2] = { 0x00ff0000, 0x000000ff };
unsigned int sx = 16;
unsigned int sy = 16;
uint32_t *fb = ptr;
unsigned int i, j;

for (j = 0; j < screen->height; j++) {
unsigned int y = j / sy;

for (i = 0; i < screen->width; i++) {
unsigned int x = i / sx;

*fb++ = colors[(x ^ y) & 1];
}
}

dmabuf_unmap(buf, ptr, size);
} else {
fprintf(stderr, "dmabuf_mmap() failed: %m\n");
}
}

int main(int argc, char *argv[])
{
const char *dmabuf, *card;
struct timeval timeout;
struct screen *screen;
int fd, err, buf, drm;
size_t size;
fd_set fds;

if (argc < 2)
dmabuf = default_dmabuf_device;
else
dmabuf = argv[1];

if (argc < 3)
card = default_dri_card;
else
card = argv[2];

fd = open(dmabuf, O_RDWR);
if (fd < 0) {
fprintf(stderr, "open() failed: %m\n");
return 1;
}

drm = open(card, O_RDWR);
if (drm < 0) {
fprintf(stderr, "open() failed: %m\n");
return 1;
}

err = drmSetMaster(drm);
if (err < 0) {
fprintf(stderr, "drmSetMaster() failed: %m\n");
return 1;
}

err = screen_open(&screen, drm);
if (err < 0) {
fprintf(stderr, "screen_open() failed: %s\n", strerror(-err));
return 1;
}

size = screen->pitch * screen->height;

err = dmabuf_create(fd, size, O_RDWR);
if (err < 0) {
fprintf(stderr, "dmabuf_create() failed: %m\n");
return 1;
}

buf = dmabuf_export(fd, 0);
if (buf < 0) {
fprintf(stderr, "dmabuf_export() failed: %m\n");
return 1;
}

printf("obtained DMA-BUF: %d\n", buf);

prepare_buffer(buf, size, screen);

err = screen_import(screen, buf);
if (err < 0) {
fprintf(stderr, "screen_import() failed: %s\n", strerror(-err));
return 1;
}

FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);

memset(&timeout, 0, sizeof(timeout));
timeout.tv_sec = 5;
timeout.tv_usec = 0;

err = select(STDIN_FILENO + 1, &fds, NULL, NULL, &timeout);
if (err < 0) {
fprintf(stderr, "select() failed: %m\n");
return 1;
}

err = screen_close(screen);
if (err < 0) {
fprintf(stderr, "screen_close() failed: %s\n", strerror(-err));
return 1;
}

err = dmabuf_delete(fd);
if (err < 0) {
fprintf(stderr, "dmabuf_delete() failed: %m\n");
return 1;
}

close(buf);

err = drmDropMaster(drm);
if (err < 0) {
fprintf(stderr, "drmDropMaster() failed: %m\n");
return 1;
}

close(drm);
close(fd);
return 0;
}

Attachment: pgp00000.pgp
Description: PGP signature