#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <drm.h>
#include <drm_fourcc.h>
#include <xf86drm.h>
#include <xf86drmMode.h>

__attribute__((format(printf, 1, 2)))
static void kmsg(const char *format, ...)
#define KERN_EMER	"<0>"
#define KERN_ALERT	"<1>"
#define KERN_CRIT	"<2>"
#define KERN_ERR	"<3>"
#define KERN_WARNING	"<4>"
#define KERN_NOTICE	"<5>"
#define KERN_INFO	"<6>"
#define KERN_DEBUG	"<7>"
{
	va_list ap;
	FILE *file;

	file = fopen("/dev/kmsg", "w");
	if (file == NULL)
		return;

	va_start(ap, format);
	fprintf(file, "userspace: ");
	vfprintf(file, format, ap);
	va_end(ap);

	fclose(file);
}

static uint32_t dumb_create(int fd, int width, int height, int bpp)
{
	struct drm_mode_create_dumb create = {};

	create.width = width;
	create.height = height;
	create.bpp = bpp;

	create.handle = 0;
	drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);

	return create.handle;
}

static uint32_t fb_create(int fd, int width, int height, int format, int bo, int pitch)
{
	struct drm_mode_fb_cmd2 f = {};

	f.width = width;
	f.height = height;
	f.pixel_format = format;
	f.handles[0] = bo;
	f.pitches[0] = pitch;
	f.fb_id = 0;

	drmIoctl(fd, DRM_IOCTL_MODE_ADDFB2, &f);

	return f.fb_id;
}

drmModeModeInfo *connector_get_default_mode(int fd, int conn_id)
{
	drmModeConnectorPtr conn;
	int i;
 
	conn = drmModeGetConnectorCurrent(fd, conn_id);

	for (i = 0; i < conn->count_modes; i++) {
		if (conn->modes[i].type & DRM_MODE_TYPE_PREFERRED)
			return &conn->modes[i];
	}

}

static int dumb_destroy(int fd, uint32_t bo)
{
	struct drm_mode_destroy_dumb arg = {};

	arg.handle = bo;

	return drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &arg);
}

int main(int argc, char **argv)
{
	int ret;
	int fd;
	int crtc = 25;
	int conn = 29;
	int bo1, fb1, bo2, fb2;
	drmModeModeInfo *mode;

	kmsg("------------- start ----------------\n");

	fd = open("/dev/dri/card0", O_RDWR);

	ret = drmSetMaster(fd);

	kmsg("%s: pipe %d conn %d crtc %d\n", __func__, pipe, conn, crtc);

	bo1 = dumb_create(fd, 1920, 1080, 4 * 8);
	kmsg("dumb_create 1: %d\n", bo1);

	fb1 = fb_create(fd, 1920, 1080, DRM_FORMAT_XRGB8888, bo1, 1920 * 4);
	kmsg("fb_create 1: %d\n", fb1);

	mode = connector_get_default_mode(fd, conn);
	kmsg("connector_get_default_mode: %s\n", mode->name);

	ret = drmModeSetCrtc(fd,
			     crtc,
			     fb1,
			     0, 0, /* x, y */
			     &conn, /* connectors */
			     1,    /* n_connectors */
			     mode  /* mode */);
	kmsg("drmModeSetCrtc: %d\n", ret);

	ret = drmModeSetCrtc(fd,
			     crtc,
			     fb1,
			     0, 0, /* x, y */
			     NULL, /* connectors */
			     0,    /* n_connectors */
			     NULL  /* mode */);

	/* Create a second BO before destroying the first one, so it has a
	 * different DMA address
	 */
	bo2 = dumb_create(fd, 1920, 1080, 4 * 8);
	kmsg("dumb_create 2: %d\n", bo2);

	fb2 = fb_create(fd, 1920, 1080, DRM_FORMAT_XRGB8888, bo2, 1920 * 4);
	kmsg("fb_create 2: %d\n", fb2);

	/* Make sure we don't hold a reference to the first BO */
	ret = drmModeRmFB(fd, fb1);
	kmsg("drmModeRmFB: %d\n", ret);
	
	ret = dumb_destroy(fd, bo1);
	kmsg("dumb_destroy: %d\n", ret);

	/* The CRTC will be enabled with the old configuration and the VOP
	 * will try to read from the first BO.
	 */
	ret = drmModeSetCrtc(fd,
			     crtc,
			     fb2,
			     0, 0, /* x, y */
			     &conn, /* connectors */
			     1,    /* n_connectors */
			     mode  /* mode */);
	kmsg("drmModeSetCrtc: %d\n", ret);
	
	ret = drmDropMaster(fd);

	close(fd);

	kmsg("-------------- end -----------------\n");

	return 0;
}
