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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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: pgpn6hy7Swvgg.pgp
Description: PGP signature

_______________________________________________
dri-devel mailing list
dri-devel@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/dri-devel

[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux