drm: Branch 'master' - 24 commits

[email protected] (Emil Velikov)
Newsgroups gmane.comp.video.dri.patches
Message-ID <[email protected]>
 Makefile.am            |    3 
 exynos/exynos_fimg2d.c |  376 ++++++++++++++++++++++--------------
 exynos/exynos_fimg2d.h |   14 -
 tests/Makefile.am      |    1 
 tests/drmdevice.c      |  112 ++++++++++
 util_math.h            |    1 
 xf86atomic.h           |    2 
 xf86drm.c              |  500 +++++++++++++++++++++++++++++--------------------
 xf86drm.h              |    3 
 xf86drmMode.c          |   17 +
 10 files changed, 672 insertions(+), 357 deletions(-)

New commits:
commit df21b293e9cf550ec8d6a3e49461350dbdf14260
Author: Tobias Jakobi <[email protected]>
Date:   Tue Sep 8 17:22:34 2015 +0200

    exynos/fimg2d: remove g2d_context from public header
    
    All functions from the public API only operation on
    struct g2d_context*, so this shouldn't break too much.
    
    Make the context private since we don't want the
    user to modify its content directly. Also remove
    the defines that were only used for fields of
    g2d_context.
    
    Signed-off-by: Tobias Jakobi <[email protected]>

diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
index 9746c21..e734144 100644
--- a/exynos/exynos_fimg2d.c
+++ b/exynos/exynos_fimg2d.c
@@ -44,6 +44,21 @@
 
 #define MSG_PREFIX "exynos/fimg2d: "
 
+#define G2D_MAX_CMD_NR		64
+#define G2D_MAX_GEM_CMD_NR	64
+#define G2D_MAX_CMD_LIST_NR	64
+
+struct g2d_context {
+	int				fd;
+	unsigned int			major;
+	unsigned int			minor;
+	struct drm_exynos_g2d_cmd	cmd[G2D_MAX_CMD_NR];
+	struct drm_exynos_g2d_cmd	cmd_buf[G2D_MAX_GEM_CMD_NR];
+	unsigned int			cmd_nr;
+	unsigned int			cmd_buf_nr;
+	unsigned int			cmdlist_nr;
+};
+
 enum g2d_base_addr_reg {
 	g2d_dst = 0,
 	g2d_src
diff --git a/exynos/exynos_fimg2d.h b/exynos/exynos_fimg2d.h
index 9db0c88..4aa1568 100644
--- a/exynos/exynos_fimg2d.h
+++ b/exynos/exynos_fimg2d.h
@@ -13,9 +13,6 @@
 #ifndef _FIMG2D_H_
 #define _FIMG2D_H_
 
-#define G2D_MAX_CMD_NR		64
-#define G2D_MAX_GEM_CMD_NR	64
-#define G2D_MAX_CMD_LIST_NR	64
 #define G2D_PLANE_MAX_NR	2
 
 enum e_g2d_color_mode {
@@ -289,16 +286,7 @@ struct g2d_image {
 	void				*mapped_ptr[G2D_PLANE_MAX_NR];
 };
 
-struct g2d_context {
-	int				fd;
-	unsigned int			major;
-	unsigned int			minor;
-	struct drm_exynos_g2d_cmd	cmd[G2D_MAX_CMD_NR];
-	struct drm_exynos_g2d_cmd	cmd_buf[G2D_MAX_GEM_CMD_NR];
-	unsigned int			cmd_nr;
-	unsigned int			cmd_buf_nr;
-	unsigned int			cmdlist_nr;
-};
+struct g2d_context;
 
 struct g2d_context *g2d_init(int fd);
 void g2d_fini(struct g2d_context *ctx);
commit ce4a4f6e21d4112f0b88c973283b586e1fccddd9
Author: Tobias Jakobi <[email protected]>
Date:   Tue Sep 8 17:22:33 2015 +0200

    exynos/fimg2d: add message prefix
    
    Add a prefix to the messages printed to the console via
    printf() and fprintf() so that one can easily see where
    the message comes from.
    
    Signed-off-by: Tobias Jakobi <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
index 5873fe7..9746c21 100644
--- a/exynos/exynos_fimg2d.c
+++ b/exynos/exynos_fimg2d.c
@@ -42,6 +42,8 @@
 
 #define MIN(a, b)	((a) < (b) ? (a) : (b))
 
+#define MSG_PREFIX "exynos/fimg2d: "
+
 enum g2d_base_addr_reg {
 	g2d_dst = 0,
 	g2d_src
@@ -255,7 +257,7 @@ static int g2d_flush(struct g2d_context *ctx)
 		return 0;
 
 	if (ctx->cmdlist_nr >= G2D_MAX_CMD_LIST_NR) {
-		fprintf(stderr, "Overflow cmdlist.\n");
+		fprintf(stderr, MSG_PREFIX "command list overflow.\n");
 		return -EINVAL;
 	}
 
@@ -271,7 +273,7 @@ static int g2d_flush(struct g2d_context *ctx)
 
 	ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST, &cmdlist);
 	if (ret < 0) {
-		fprintf(stderr, "failed to set cmdlist.\n");
+		fprintf(stderr, MSG_PREFIX "failed to set cmdlist.\n");
 		return ret;
 	}
 
@@ -293,7 +295,7 @@ struct g2d_context *g2d_init(int fd)
 
 	ctx = calloc(1, sizeof(*ctx));
 	if (!ctx) {
-		fprintf(stderr, "failed to allocate context.\n");
+		fprintf(stderr, MSG_PREFIX "failed to allocate context.\n");
 		return NULL;
 	}
 
@@ -301,7 +303,7 @@ struct g2d_context *g2d_init(int fd)
 
 	ret = drmIoctl(fd, DRM_IOCTL_EXYNOS_G2D_GET_VER, &ver);
 	if (ret < 0) {
-		fprintf(stderr, "failed to get version.\n");
+		fprintf(stderr, MSG_PREFIX "failed to get version.\n");
 		free(ctx);
 		return NULL;
 	}
@@ -309,7 +311,7 @@ struct g2d_context *g2d_init(int fd)
 	ctx->major = ver.major;
 	ctx->minor = ver.minor;
 
-	printf("g2d version(%d.%d).\n", ctx->major, ctx->minor);
+	printf(MSG_PREFIX "G2D version (%d.%d).\n", ctx->major, ctx->minor);
 	return ctx;
 }
 
@@ -335,7 +337,7 @@ int g2d_exec(struct g2d_context *ctx)
 
 	ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_EXEC, &exec);
 	if (ret < 0) {
-		fprintf(stderr, "failed to execute.\n");
+		fprintf(stderr, MSG_PREFIX "failed to execute.\n");
 		return ret;
 	}
 
@@ -436,7 +438,7 @@ g2d_copy(struct g2d_context *ctx, struct g2d_image *src,
 	h = MIN(src_h, dst_h);
 
 	if (w <= 0 || h <= 0) {
-		fprintf(stderr, "invalid width or height.\n");
+		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
 		return -EINVAL;
 	}
 
@@ -532,7 +534,7 @@ g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
 		dst_h = dst->height - dst_y;
 
 	if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
-		fprintf(stderr, "invalid width or height.\n");
+		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
 		return -EINVAL;
 	}
 
@@ -633,17 +635,17 @@ g2d_blend(struct g2d_context *ctx, struct g2d_image *src,
 	h = MIN(src_h, dst_h);
 
 	if (w <= 0 || h <= 0) {
-		fprintf(stderr, "invalid width or height.\n");
+		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
 		return -EINVAL;
 	}
 
 	if (!g2d_validate_select_mode(src->select_mode)) {
-		fprintf(stderr , "invalid select mode for source.\n");
+		fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n");
 		return -EINVAL;
 	}
 
 	if (!g2d_validate_blending_op(op)) {
-		fprintf(stderr , "unsupported blending operation.\n");
+		fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n");
 		return -EINVAL;
 	}
 
@@ -752,17 +754,17 @@ g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
 		dst_h = dst->height - dst_y;
 
 	if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
-		fprintf(stderr, "invalid width or height.\n");
+		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
 		return -EINVAL;
 	}
 
 	if (!g2d_validate_select_mode(src->select_mode)) {
-		fprintf(stderr , "invalid select mode for source.\n");
+		fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n");
 		return -EINVAL;
 	}
 
 	if (!g2d_validate_blending_op(op)) {
-		fprintf(stderr , "unsupported blending operation.\n");
+		fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n");
 		return -EINVAL;
 	}
 
commit bf666b5e999236ec6406afd60ca35a465c6b3262
Author: Tobias Jakobi <[email protected]>
Date:   Tue Sep 8 17:22:32 2015 +0200

    exynos/fimg2d: make g2d_add_cmd() less heavy
    
    The function currently checks for each added command
    if an overflow of the corresponding command buffers
    occurs, but none of the callers ever checks the
    return value.
    
    Since all callers are now converted to use
    g2d_check_space() simplify the function.
    
    (1) The overflow checks become asserts, so they're only
        active for debug builds. This is fine since
        g2d_add_cmd() is not part of the public API.
    
    (2) Switch the return value to void.
    
    (3) Explicitly state that the caller has to check
        buffer space before calling g2d_add_cmd().
    
    Signed-off-by: Tobias Jakobi <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
index fbc77a3..5873fe7 100644
--- a/exynos/exynos_fimg2d.c
+++ b/exynos/exynos_fimg2d.c
@@ -18,6 +18,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
+#include <assert.h>
 
 #include <sys/mman.h>
 #include <linux/stddef.h>
@@ -172,8 +173,11 @@ static int g2d_validate_blending_op(
  * @ctx: a pointer to g2d_context structure.
  * @cmd: command data.
  * @value: value data.
+ *
+ * The caller has to make sure that the commands buffers have enough space
+ * left to hold the command. Use g2d_check_space() to ensure this.
  */
-static int g2d_add_cmd(struct g2d_context *ctx, unsigned long cmd,
+static void g2d_add_cmd(struct g2d_context *ctx, unsigned long cmd,
 			unsigned long value)
 {
 	switch (cmd & ~(G2D_BUF_USERPTR)) {
@@ -183,28 +187,20 @@ static int g2d_add_cmd(struct g2d_context *ctx, unsigned long cmd,
 	case DST_PLANE2_BASE_ADDR_REG:
 	case PAT_BASE_ADDR_REG:
 	case MASK_BASE_ADDR_REG:
-		if (ctx->cmd_buf_nr >= G2D_MAX_GEM_CMD_NR) {
-			fprintf(stderr, "Overflow cmd_gem size.\n");
-			return -EINVAL;
-		}
+		assert(ctx->cmd_buf_nr < G2D_MAX_GEM_CMD_NR);
 
 		ctx->cmd_buf[ctx->cmd_buf_nr].offset = cmd;
 		ctx->cmd_buf[ctx->cmd_buf_nr].data = value;
 		ctx->cmd_buf_nr++;
 		break;
 	default:
-		if (ctx->cmd_nr >= G2D_MAX_CMD_NR) {
-			fprintf(stderr, "Overflow cmd size.\n");
-			return -EINVAL;
-		}
+		assert(ctx->cmd_nr < G2D_MAX_CMD_NR);
 
 		ctx->cmd[ctx->cmd_nr].offset = cmd;
 		ctx->cmd[ctx->cmd_nr].data = value;
 		ctx->cmd_nr++;
 		break;
 	}
-
-	return 0;
 }
 
 /*
commit 6a4479da71e93ef5141d9c493c235c2beb64170a
Author: Tobias Jakobi <[email protected]>
Date:   Tue Sep 8 17:22:31 2015 +0200

    exynos/fimg2d: remove superfluous initialization of g2d_point_val
    
    The g2d_point_val union consists of two coordinates of 16
    bits. Whenever this union is used though, both coordinates
    are explicitly set. Hence prior initialization is unnecessary.
    
    Signed-off-by: Tobias Jakobi <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
index d7cbb24..fbc77a3 100644
--- a/exynos/exynos_fimg2d.c
+++ b/exynos/exynos_fimg2d.c
@@ -380,15 +380,12 @@ g2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img,
 	if (y + h > img->height)
 		h = img->height - y;
 
-	pt.val = 0;
 	pt.data.x = x;
 	pt.data.y = y;
 	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
 
-	pt.val = 0;
 	pt.data.x = x + w;
 	pt.data.y = y + h;
-
 	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
 
 	g2d_add_cmd(ctx, SF_COLOR_REG, img->color);
@@ -460,20 +457,16 @@ g2d_copy(struct g2d_context *ctx, struct g2d_image *src,
 	g2d_add_base_addr(ctx, src, g2d_src);
 	g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
 
-	pt.val = 0;
 	pt.data.x = src_x;
 	pt.data.y = src_y;
 	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
-	pt.val = 0;
 	pt.data.x = src_x + w;
 	pt.data.y = src_y + h;
 	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
 
-	pt.val = 0;
 	pt.data.x = dst_x;
 	pt.data.y = dst_y;
 	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
-	pt.val = 0;
 	pt.data.x = dst_x + w;
 	pt.data.y = dst_y + h;
 	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
@@ -581,20 +574,16 @@ g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
 		g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y);
 	}
 
-	pt.val = 0;
 	pt.data.x = src_x;
 	pt.data.y = src_y;
 	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
-	pt.val = 0;
 	pt.data.x = src_x + src_w;
 	pt.data.y = src_y + src_h;
 	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
 
-	pt.val = 0;
 	pt.data.x = dst_x;
 	pt.data.y = dst_y;
 	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
-	pt.val = 0;
 	pt.data.x = dst_x + dst_w;
 	pt.data.y = dst_y + dst_h;
 	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
@@ -700,20 +689,16 @@ g2d_blend(struct g2d_context *ctx, struct g2d_image *src,
 	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
 	g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
 
-	pt.val = 0;
 	pt.data.x = src_x;
 	pt.data.y = src_y;
 	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
-	pt.val = 0;
 	pt.data.x = src_x + w;
 	pt.data.y = src_y + h;
 	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
 
-	pt.val = 0;
 	pt.data.x = dst_x;
 	pt.data.y = dst_y;
 	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
-	pt.val = 0;
 	pt.data.x = dst_x + w;
 	pt.data.y = dst_y + h;
 	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
@@ -829,20 +814,16 @@ g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
 	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
 	g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
 
-	pt.val = 0;
 	pt.data.x = src_x;
 	pt.data.y = src_y;
 	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
-	pt.val = 0;
 	pt.data.x = src_x + src_w;
 	pt.data.y = src_y + src_h;
 	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
 
-	pt.val = 0;
 	pt.data.x = dst_x;
 	pt.data.y = dst_y;
 	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
-	pt.val = 0;
 	pt.data.x = dst_x + dst_w;
 	pt.data.y = dst_y + dst_h;
 	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
commit 76d1c6b15725f1e7de3c60b0398639e06bf295be
Author: Tobias Jakobi <[email protected]>
Date:   Tue Sep 8 17:22:30 2015 +0200

    exynos/fimg2d: remove default case from g2d_get_blend_op()
    
    We now validate the blending mode via g2d_validate_mode()
    prior to feeding it to g2d_get_blend_op().
    
    Signed-off-by: Tobias Jakobi <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
index 9d9359a..d7cbb24 100644
--- a/exynos/exynos_fimg2d.c
+++ b/exynos/exynos_fimg2d.c
@@ -64,6 +64,11 @@ static unsigned int g2d_get_blend_op(enum e_g2d_op op)
 
 	val.val = 0;
 
+	/*
+	 * The switch statement is missing the default branch since
+	 * we assume that the caller checks the blending operation
+	 * via g2d_validate_blending_op() first.
+	 */
 	switch (op) {
 	case G2D_OP_CLEAR:
 	case G2D_OP_DISJOINT_CLEAR:
@@ -91,11 +96,6 @@ static unsigned int g2d_get_blend_op(enum e_g2d_op op)
 		SET_BF(val, G2D_COEFF_MODE_SRC_ALPHA, 0, 0, 0,
 				G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0);
 		break;
-	default:
-		fprintf(stderr, "Not support operation(%d).\n", op);
-		SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, G2D_COEFF_MODE_ZERO,
-				0, 0, 0);
-		break;
 	}
 
 	return val.val;
commit e3c97d1a2473cd06a964599d10465f1e6a4d9015
Author: Tobias Jakobi <[email protected]>
Date:   Tue Sep 8 17:22:29 2015 +0200

    exynos/fimg2d: add g2d_validate_xyz() functions
    
    The G2D headers define a number of modes through enums
    (like e.g. color, select, repeat, etc.).
    
    This introduces g2d_validate_select_mode() and
    g2d_validate_blending_op() which validate a
    select mode or blending operation respectively.
    
    Use this together with g2d_check_space() in
    g2d_{blend,scale_and_blend}().
    
    For this we move parameter validation to the top and
    also validate the select mode of the source image and
    the requested blending operation before starting
    command submission.
    
    Signed-off-by: Tobias Jakobi <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
index 2e04f4a..9d9359a 100644
--- a/exynos/exynos_fimg2d.c
+++ b/exynos/exynos_fimg2d.c
@@ -119,6 +119,54 @@ static unsigned int g2d_check_space(const struct g2d_context *ctx,
 }
 
 /*
+ * g2d_validate_select_mode - validate select mode.
+ *
+ * @mode: the mode to validate
+ *
+ * Returns zero for an invalid mode and one otherwise.
+ */
+static int g2d_validate_select_mode(
+	enum e_g2d_select_mode mode)
+{
+	switch (mode) {
+	case G2D_SELECT_MODE_NORMAL:
+	case G2D_SELECT_MODE_FGCOLOR:
+	case G2D_SELECT_MODE_BGCOLOR:
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * g2d_validate_blending_op - validate blending operation.
+ *
+ * @operation: the operation to validate
+ *
+ * Returns zero for an invalid mode and one otherwise.
+ */
+static int g2d_validate_blending_op(
+	enum e_g2d_op operation)
+{
+	switch (operation) {
+	case G2D_OP_CLEAR:
+	case G2D_OP_SRC:
+	case G2D_OP_DST:
+	case G2D_OP_OVER:
+	case G2D_OP_INTERPOLATE:
+	case G2D_OP_DISJOINT_CLEAR:
+	case G2D_OP_DISJOINT_SRC:
+	case G2D_OP_DISJOINT_DST:
+	case G2D_OP_CONJOINT_CLEAR:
+	case G2D_OP_CONJOINT_SRC:
+	case G2D_OP_CONJOINT_DST:
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
  * g2d_add_cmd - set given command and value to user side command buffer.
  *
  * @ctx: a pointer to g2d_context structure.
@@ -579,7 +627,45 @@ g2d_blend(struct g2d_context *ctx, struct g2d_image *src,
 	union g2d_point_val pt;
 	union g2d_bitblt_cmd_val bitblt;
 	union g2d_blend_func_val blend;
-	unsigned int src_w = 0, src_h = 0, dst_w = 0, dst_h = 0;
+	unsigned int gem_space;
+	unsigned int src_w, src_h, dst_w, dst_h;
+
+	src_w = w;
+	src_h = h;
+	if (src_x + w > src->width)
+		src_w = src->width - src_x;
+	if (src_y + h > src->height)
+		src_h = src->height - src_y;
+
+	dst_w = w;
+	dst_h = h;
+	if (dst_x + w > dst->width)
+		dst_w = dst->width - dst_x;
+	if (dst_y + h > dst->height)
+		dst_h = dst->height - dst_y;
+
+	w = MIN(src_w, dst_w);
+	h = MIN(src_h, dst_h);
+
+	if (w <= 0 || h <= 0) {
+		fprintf(stderr, "invalid width or height.\n");
+		return -EINVAL;
+	}
+
+	if (!g2d_validate_select_mode(src->select_mode)) {
+		fprintf(stderr , "invalid select mode for source.\n");
+		return -EINVAL;
+	}
+
+	if (!g2d_validate_blending_op(op)) {
+		fprintf(stderr , "unsupported blending operation.\n");
+		return -EINVAL;
+	}
+
+	gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1;
+
+	if (g2d_check_space(ctx, 12, gem_space))
+		return -ENOSPC;
 
 	bitblt.val = 0;
 	blend.val = 0;
@@ -607,32 +693,6 @@ g2d_blend(struct g2d_context *ctx, struct g2d_image *src,
 	case G2D_SELECT_MODE_BGCOLOR:
 		g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
 		break;
-	default:
-		fprintf(stderr , "failed to set src.\n");
-		return -EINVAL;
-	}
-
-	src_w = w;
-	src_h = h;
-	if (src_x + w > src->width)
-		src_w = src->width - src_x;
-	if (src_y + h > src->height)
-		src_h = src->height - src_y;
-
-	dst_w = w;
-	dst_h = h;
-	if (dst_x + w > dst->width)
-		dst_w = dst->width - dst_x;
-	if (dst_y + h > dst->height)
-		dst_h = dst->height - dst_y;
-
-	w = MIN(src_w, dst_w);
-	h = MIN(src_h, dst_h);
-
-	if (w <= 0 || h <= 0) {
-		fprintf(stderr, "invalid width or height.\n");
-		g2d_reset(ctx);
-		return -EINVAL;
 	}
 
 	bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE;
@@ -689,9 +749,47 @@ g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
 	union g2d_point_val pt;
 	union g2d_bitblt_cmd_val bitblt;
 	union g2d_blend_func_val blend;
-	unsigned int scale;
+	unsigned int scale, gem_space;
 	unsigned int scale_x, scale_y;
 
+	if (src_w == dst_w && src_h == dst_h)
+		scale = 0;
+	else {
+		scale = 1;
+		scale_x = g2d_get_scaling(src_w, dst_w);
+		scale_y = g2d_get_scaling(src_h, dst_h);
+	}
+
+	if (src_x + src_w > src->width)
+		src_w = src->width - src_x;
+	if (src_y + src_h > src->height)
+		src_h = src->height - src_y;
+
+	if (dst_x + dst_w > dst->width)
+		dst_w = dst->width - dst_x;
+	if (dst_y + dst_h > dst->height)
+		dst_h = dst->height - dst_y;
+
+	if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
+		fprintf(stderr, "invalid width or height.\n");
+		return -EINVAL;
+	}
+
+	if (!g2d_validate_select_mode(src->select_mode)) {
+		fprintf(stderr , "invalid select mode for source.\n");
+		return -EINVAL;
+	}
+
+	if (!g2d_validate_blending_op(op)) {
+		fprintf(stderr , "unsupported blending operation.\n");
+		return -EINVAL;
+	}
+
+	gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1;
+
+	if (g2d_check_space(ctx, 12 + scale * 3, gem_space))
+		return -ENOSPC;
+
 	bitblt.val = 0;
 	blend.val = 0;
 
@@ -718,33 +816,6 @@ g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
 	case G2D_SELECT_MODE_BGCOLOR:
 		g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
 		break;
-	default:
-		fprintf(stderr , "failed to set src.\n");
-		return -EINVAL;
-	}
-
-	if (src_w == dst_w && src_h == dst_h)
-		scale = 0;
-	else {
-		scale = 1;
-		scale_x = g2d_get_scaling(src_w, dst_w);
-		scale_y = g2d_get_scaling(src_h, dst_h);
-	}
-
-	if (src_x + src_w > src->width)
-		src_w = src->width - src_x;
-	if (src_y + src_h > src->height)
-		src_h = src->height - src_y;
-
-	if (dst_x + dst_w > dst->width)
-		dst_w = dst->width - dst_x;
-	if (dst_y + dst_h > dst->height)
-		dst_h = dst->height - dst_y;
-
-	if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
-		fprintf(stderr, "invalid width or height.\n");
-		g2d_reset(ctx);
-		return -EINVAL;
 	}
 
 	if (scale) {
commit 2362c88251d4bba0bb76d2a25f233004c6f99134
Author: Tobias Jakobi <[email protected]>
Date:   Tue Sep 8 17:22:28 2015 +0200

    exynos/fimg2d: add g2d_check_space()
    
    This is going to be used to check if the command buffers have
    enough space left prior to actual submission of the commands.
    
    Use this in g2d_{solid_fill,copy,copy_with_scale}().
    
    For this the parameter validation before buffer space
    checking so that we can exit early if it fails.
    Also don't reset the G2D context in this situation since
    the buffers are not partially submitted anymore.
    
    The repeat mode in g2d_copy_with_scale() is checked first
    to make computation of space easier.
    
    Signed-off-by: Tobias Jakobi <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
index 85b2317..2e04f4a 100644
--- a/exynos/exynos_fimg2d.c
+++ b/exynos/exynos_fimg2d.c
@@ -102,6 +102,23 @@ static unsigned int g2d_get_blend_op(enum e_g2d_op op)
 }
 
 /*
+ * g2d_check_space - check if command buffers have enough space left.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @num_cmds: number of (regular) commands.
+ * @num_gem_cmds: number of GEM commands.
+ */
+static unsigned int g2d_check_space(const struct g2d_context *ctx,
+	unsigned int num_cmds, unsigned int num_gem_cmds)
+{
+	if (ctx->cmd_nr + num_cmds >= G2D_MAX_CMD_NR ||
+	    ctx->cmd_buf_nr + num_gem_cmds >= G2D_MAX_GEM_CMD_NR)
+		return 1;
+	else
+		return 0;
+}
+
+/*
  * g2d_add_cmd - set given command and value to user side command buffer.
  *
  * @ctx: a pointer to g2d_context structure.
@@ -302,6 +319,9 @@ g2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img,
 	union g2d_bitblt_cmd_val bitblt;
 	union g2d_point_val pt;
 
+	if (g2d_check_space(ctx, 7, 1))
+		return -ENOSPC;
+
 	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
 	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode);
 	g2d_add_base_addr(ctx, img, g2d_dst);
@@ -355,17 +375,7 @@ g2d_copy(struct g2d_context *ctx, struct g2d_image *src,
 {
 	union g2d_rop4_val rop4;
 	union g2d_point_val pt;
-	unsigned int src_w = 0, src_h = 0, dst_w = 0, dst_h = 0;
-
-	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
-	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
-	g2d_add_base_addr(ctx, dst, g2d_dst);
-	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
-
-	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
-	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
-	g2d_add_base_addr(ctx, src, g2d_src);
-	g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
+	unsigned int src_w, src_h, dst_w, dst_h;
 
 	src_w = w;
 	src_h = h;
@@ -386,10 +396,22 @@ g2d_copy(struct g2d_context *ctx, struct g2d_image *src,
 
 	if (w <= 0 || h <= 0) {
 		fprintf(stderr, "invalid width or height.\n");
-		g2d_reset(ctx);
 		return -EINVAL;
 	}
 
+	if (g2d_check_space(ctx, 11, 2))
+		return -ENOSPC;
+
+	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
+	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
+	g2d_add_base_addr(ctx, dst, g2d_dst);
+	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
+
+	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
+	g2d_add_base_addr(ctx, src, g2d_src);
+	g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
+
 	pt.val = 0;
 	pt.data.x = src_x;
 	pt.data.y = src_y;
@@ -445,23 +467,12 @@ g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
 {
 	union g2d_rop4_val rop4;
 	union g2d_point_val pt;
-	unsigned int scale;
+	unsigned int scale, repeat_pad;
 	unsigned int scale_x, scale_y;
 
-	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
-	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
-	g2d_add_base_addr(ctx, dst, g2d_dst);
-	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
-
-	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
-	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
-
-	g2d_add_cmd(ctx, SRC_REPEAT_MODE_REG, src->repeat_mode);
-	if (src->repeat_mode == G2D_REPEAT_MODE_PAD)
-		g2d_add_cmd(ctx, SRC_PAD_VALUE_REG, dst->color);
-
-	g2d_add_base_addr(ctx, src, g2d_src);
-	g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
+	/* Sanitize this parameter to facilitate space computation below. */
+	if (negative)
+		negative = 1;
 
 	if (src_w == dst_w && src_h == dst_h)
 		scale = 0;
@@ -471,6 +482,8 @@ g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
 		scale_y = g2d_get_scaling(src_h, dst_h);
 	}
 
+	repeat_pad = src->repeat_mode == G2D_REPEAT_MODE_PAD ? 1 : 0;
+
 	if (src_x + src_w > src->width)
 		src_w = src->width - src_x;
 	if (src_y + src_h > src->height)
@@ -483,21 +496,37 @@ g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
 
 	if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
 		fprintf(stderr, "invalid width or height.\n");
-		g2d_reset(ctx);
 		return -EINVAL;
 	}
 
+	if (g2d_check_space(ctx, 12 + scale * 3 + negative + repeat_pad, 2))
+		return -ENOSPC;
+
+	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
+	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
+	g2d_add_base_addr(ctx, dst, g2d_dst);
+	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
+
+	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
+
+	g2d_add_cmd(ctx, SRC_REPEAT_MODE_REG, src->repeat_mode);
+	if (repeat_pad)
+		g2d_add_cmd(ctx, SRC_PAD_VALUE_REG, dst->color);
+
+	g2d_add_base_addr(ctx, src, g2d_src);
+	g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
+
+	rop4.val = 0;
+	rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
+
 	if (negative) {
 		g2d_add_cmd(ctx, BG_COLOR_REG, 0x00FFFFFF);
-		rop4.val = 0;
-		rop4.data.unmasked_rop3 = G2D_ROP3_SRC^G2D_ROP3_DST;
-		g2d_add_cmd(ctx, ROP4_REG, rop4.val);
-	} else {
-		rop4.val = 0;
-		rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
-		g2d_add_cmd(ctx, ROP4_REG, rop4.val);
+		rop4.data.unmasked_rop3 ^= G2D_ROP3_DST;
 	}
 
+	g2d_add_cmd(ctx, ROP4_REG, rop4.val);
+
 	if (scale) {
 		g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR);
 		g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x);
commit 9017619714eb432fa9bb1e94139cc2ad40fc7001
Author: Tobias Jakobi <[email protected]>
Date:   Tue Sep 8 17:22:27 2015 +0200

    exynos/fimg2d: simplify base address submission in g2d_scale_and_blend()
    
    Use g2d_add_base_addr() for source and destination base
    address just like all other calls.
    
    Signed-off-by: Tobias Jakobi <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
index 4a88e0c..85b2317 100644
--- a/exynos/exynos_fimg2d.c
+++ b/exynos/exynos_fimg2d.c
@@ -672,12 +672,7 @@ g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
 		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
 
 	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
-	if (dst->buf_type == G2D_IMGBUF_USERPTR)
-		g2d_add_cmd(ctx, DST_BASE_ADDR_REG | G2D_BUF_USERPTR,
-				(unsigned long)&dst->user_ptr[0]);
-	else
-		g2d_add_cmd(ctx, DST_BASE_ADDR_REG, dst->bo[0]);
-
+	g2d_add_base_addr(ctx, dst, g2d_dst);
 	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
 
 	g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode);
@@ -685,12 +680,7 @@ g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
 
 	switch (src->select_mode) {
 	case G2D_SELECT_MODE_NORMAL:
-		if (src->buf_type == G2D_IMGBUF_USERPTR)
-			g2d_add_cmd(ctx, SRC_BASE_ADDR_REG | G2D_BUF_USERPTR,
-					(unsigned long)&src->user_ptr[0]);
-		else
-			g2d_add_cmd(ctx, SRC_BASE_ADDR_REG, src->bo[0]);
-
+		g2d_add_base_addr(ctx, src, g2d_src);
 		g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
 		break;
 	case G2D_SELECT_MODE_FGCOLOR:
commit a8dc2e75cfb2acecae7cb7aeefa20b9c92abd297
Author: Tobias Jakobi <[email protected]>
Date:   Tue Sep 8 17:22:26 2015 +0200

    exynos/fimg2d: fix empty buffer handling in g2d_flush()
    
    Empty command buffers are no error, we just don't have
    anything to do for flushing then.
    
    Signed-off-by: Tobias Jakobi <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
index 24a06d0..4a88e0c 100644
--- a/exynos/exynos_fimg2d.c
+++ b/exynos/exynos_fimg2d.c
@@ -191,7 +191,7 @@ static int g2d_flush(struct g2d_context *ctx)
 	struct drm_exynos_g2d_set_cmdlist cmdlist = {0};
 
 	if (ctx->cmd_nr == 0 && ctx->cmd_buf_nr == 0)
-		return -1;
+		return 0;
 
 	if (ctx->cmdlist_nr >= G2D_MAX_CMD_LIST_NR) {
 		fprintf(stderr, "Overflow cmdlist.\n");
commit cec65e2fcc8ddd3a2a094d446d49d7a7f52ff55d
Author: Emil Velikov <[email protected]>
Date:   Mon Sep 21 17:29:55 2015 +0100

    Fix SunOS/NetBSD atomic macro
    
    Reported-by: Evgeny Litvinenko <[email protected]>
    Signed-off-by: Emil Velikov <[email protected]>

diff --git a/xf86atomic.h b/xf86atomic.h
index 194554c..922b37d 100644
--- a/xf86atomic.h
+++ b/xf86atomic.h
@@ -93,7 +93,7 @@ typedef struct { LIBDRM_ATOMIC_TYPE atomic; } atomic_t;
 # define atomic_read(x) (int) ((x)->atomic)
 # define atomic_set(x, val) ((x)->atomic = (LIBDRM_ATOMIC_TYPE)(val))
 # define atomic_inc(x) (atomic_inc_uint (&(x)->atomic))
-# define atomic_inc_return (atomic_inc_uint_nv(&(x)->atomic))
+# define atomic_inc_return(x) (atomic_inc_uint_nv(&(x)->atomic))
 # define atomic_dec_and_test(x) (atomic_dec_uint_nv(&(x)->atomic) == 0)
 # define atomic_add(x, v) (atomic_add_int(&(x)->atomic, (v)))
 # define atomic_dec(x, v) (atomic_add_int(&(x)->atomic, -(v)))
commit 8990ed319993c5d1a354adef41c19acc662c81e8
Author: Emil Velikov <[email protected]>
Date:   Wed Sep 9 18:13:01 2015 +0100

    tests/drmdevice: add drm{Get,Free}Device() example
    
    Signed-off-by: Emil Velikov <[email protected]>
    Acked-by: Alex Deucher <[email protected]>

diff --git a/tests/drmdevice.c b/tests/drmdevice.c
index 4055f45..c336327 100644
--- a/tests/drmdevice.c
+++ b/tests/drmdevice.c
@@ -23,6 +23,9 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
 #include <xf86drm.h>
 
 
@@ -62,7 +65,8 @@ int
 main(void)
 {
     drmDevicePtr *devices;
-    int ret, max_devices;
+    drmDevicePtr device;
+    int fd, ret, max_devices;
 
     max_devices = drmGetDevices(NULL, 0);
 
@@ -84,9 +88,24 @@ main(void)
         return -1;
     }
 
-    for (int i = 0; i < ret; i++)
+    for (int i = 0; i < ret; i++) {
         print_device_info(devices[i], i);
 
+        for (int j = 0; j < DRM_NODE_MAX; j++) {
+            if (devices[i]->available_nodes & 1 << j) {
+                fd = open(devices[i]->nodes[j], O_RDONLY | O_CLOEXEC, 0);
+                if (fd < 0)
+                    continue;
+
+                if (drmGetDevice(fd, &device) == 0) {
+                    print_device_info(device, -1);
+                    drmFreeDevice(&device);
+                }
+                close(fd);
+            }
+        }
+    }
+
     drmFreeDevices(devices, ret);
     free(devices);
     return 0;
commit ccedf66b65f6ab245aa6028d7fe9eb603a121b43
Author: Emil Velikov <[email protected]>
Date:   Wed Sep 9 16:02:18 2015 +0100

    xf86drm: add drm{Get,Free}Device
    
    Similar interface to the *Devices() ones but they obtain/free the
    information of the opened device (as given by its fd).
    
    Note there is a fair bit of duplication between the two Get functions,
    and anyone interested is more than welcome to consolidate it.
    
    Signed-off-by: Emil Velikov <[email protected]>
    Acked-by: Alex Deucher <[email protected]>

diff --git a/xf86drm.c b/xf86drm.c
index aa0fbe4..789bfc2 100644
--- a/xf86drm.c
+++ b/xf86drm.c
@@ -2982,7 +2982,7 @@ static int drmParsePciDeviceInfo(const char *d_name,
 #endif
 }
 
-static void drmFreeDevice(drmDevicePtr *device)
+void drmFreeDevice(drmDevicePtr *device)
 {
     if (device == NULL)
         return;
@@ -3072,6 +3072,119 @@ static void drmFoldDuplicatedDevices(drmDevicePtr local_devices[], int count)
 }
 
 /**
+ * Get information about the opened drm device
+ *
+ * \param fd file descriptor of the drm device
+ * \param device the address of a drmDevicePtr where the information
+ *               will be allocated in stored
+ *
+ * \return zero on success, negative error code otherwise.
+ */
+int drmGetDevice(int fd, drmDevicePtr *device)
+{
+    drmDevicePtr *local_devices;
+    drmDevicePtr d;
+    DIR *sysdir;
+    struct dirent *dent;
+    struct stat sbuf;
+    char node[PATH_MAX + 1];
+    int node_type, subsystem_type;
+    int maj, min;
+    int ret, i, node_count;
+    int max_count = 16;
+
+    if (fd == -1 || device == NULL)
+        return -EINVAL;
+
+    if (fstat(fd, &sbuf))
+        return -errno;
+
+    maj = major(sbuf.st_rdev);
+    min = minor(sbuf.st_rdev);
+
+    if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
+        return -EINVAL;
+
+    subsystem_type = drmParseSubsystemType(maj, min);
+
+    local_devices = calloc(max_count, sizeof(drmDevicePtr));
+    if (local_devices == NULL)
+        return -ENOMEM;
+
+    sysdir = opendir(DRM_DIR_NAME);
+    if (!sysdir) {
+        ret = -errno;
+        goto close_sysdir;
+    }
+
+    i = 0;
+    while ((dent = readdir(sysdir))) {
+        node_type = drmGetNodeType(dent->d_name);
+        if (node_type < 0)
+            continue;
+
+        snprintf(node, PATH_MAX, "%s/%s", DRM_DIR_NAME, dent->d_name);
+        if (stat(node, &sbuf))
+            continue;
+
+        maj = major(sbuf.st_rdev);
+        min = minor(sbuf.st_rdev);
+
+        if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
+            continue;
+
+        if (drmParseSubsystemType(maj, min) != subsystem_type)
+            continue;
+
+        switch (subsystem_type) {
+        case DRM_BUS_PCI:
+            ret = drmProcessPciDevice(&d, dent->d_name, node, node_type,
+                                      maj, min, true);
+            if (ret)
+                goto free_devices;
+
+            break;
+        default:
+            fprintf(stderr, "The subsystem type is not supported yet\n");
+            break;
+        }
+
+        if (i >= max_count) {
+            drmDevicePtr *temp;
+
+            max_count += 16;
+            temp = realloc(local_devices, max_count * sizeof(drmDevicePtr));
+            if (!temp)
+                goto free_devices;
+            local_devices = temp;
+        }
+
+        local_devices[i] = d;
+        i++;
+    }
+    node_count = i;
+
+    /* Fold nodes into a single device if they share the same bus info */
+    drmFoldDuplicatedDevices(local_devices, node_count);
+
+    *device = local_devices[0];
+    for (i = 1; i < node_count && local_devices[i]; i++)
+            drmFreeDevice(&local_devices[i]);
+
+    free(local_devices);
+    closedir(sysdir);
+    return 0;
+
+free_devices:
+    drmFreeDevices(local_devices, i);
+    free(local_devices);
+
+close_sysdir:
+    closedir(sysdir);
+    return ret;
+}
+
+/**
  * Get drm devices on the system
  *
  * \param devices the array of devices with drmDevicePtr elements
diff --git a/xf86drm.h b/xf86drm.h
index e82ca84..481d882 100644
--- a/xf86drm.h
+++ b/xf86drm.h
@@ -790,6 +790,9 @@ typedef struct _drmDevice {
     } deviceinfo;
 } drmDevice, *drmDevicePtr;
 
+extern int drmGetDevice(int fd, drmDevicePtr *device);
+extern void drmFreeDevice(drmDevicePtr *device);
+
 extern int drmGetDevices(drmDevicePtr devices[], int max_devices);
 extern void drmFreeDevices(drmDevicePtr devices[], int count);
 
commit fae59d7234caf4827bf5ca74c1b706cbfb70a460
Author: Emil Velikov <[email protected]>
Date:   Wed Sep 9 17:54:34 2015 +0100

    xf86drm: split out drmProcessPciDevice and drmFoldDuplicatedDevices
    
    Will be reused in the next commit.
    
    Signed-off-by: Emil Velikov <[email protected]>
    Acked-by: Alex Deucher <[email protected]>

diff --git a/xf86drm.c b/xf86drm.c
index a783a28..aa0fbe4 100644
--- a/xf86drm.c
+++ b/xf86drm.c
@@ -36,6 +36,7 @@
 #endif
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdbool.h>
 #include <unistd.h>
 #include <string.h>
 #include <strings.h>
@@ -3001,6 +3002,75 @@ void drmFreeDevices(drmDevicePtr devices[], int count)
         drmFreeDevice(&devices[i]);
 }
 
+static int drmProcessPciDevice(drmDevicePtr *device, const char *d_name,
+                               const char *node, int node_type,
+                               int maj, int min, bool fetch_deviceinfo)
+{
+    const int max_node_str = drmGetMaxNodeName();
+    int ret, i;
+    void *addr;
+
+    addr = *device = calloc(1, sizeof(drmDevice) +
+                               (DRM_NODE_MAX *
+                                (sizeof(void *) + max_node_str)) +
+                               sizeof(drmPciBusInfo) +
+                               sizeof(drmPciDeviceInfo));
+    if (!*device)
+        return -ENOMEM;
+
+    (*device)->bustype = DRM_BUS_PCI;
+    (*device)->available_nodes = 1 << node_type;
+
+    addr += sizeof(drmDevice);
+    (*device)->nodes = addr;
+
+    addr += DRM_NODE_MAX * sizeof(void *);
+    for (i = 0; i < DRM_NODE_MAX; i++) {
+        (*device)->nodes[i] = addr;
+        addr += max_node_str;
+    }
+    memcpy((*device)->nodes[node_type], node, max_node_str);
+
+    (*device)->businfo.pci = addr;
+
+    ret = drmParsePciBusInfo(maj, min, (*device)->businfo.pci);
+    if (ret)
+        goto free_device;
+
+    // Fetch the device info if the user has requested it
+    if (fetch_deviceinfo) {
+        addr += sizeof(drmPciBusInfo);
+        (*device)->deviceinfo.pci = addr;
+
+        ret = drmParsePciDeviceInfo(d_name, (*device)->deviceinfo.pci);
+        if (ret)
+            goto free_device;
+    }
+    return 0;
+
+free_device:
+    free(*device);
+    *device = NULL;
+    return ret;
+}
+
+static void drmFoldDuplicatedDevices(drmDevicePtr local_devices[], int count)
+{
+    int node_type, i, j;
+
+    for (i = 0; i < count; i++) {
+        for (j = i + 1; j < count; j++) {
+            if (drmCompareBusInfo(local_devices[i], local_devices[j]) == 0) {
+                local_devices[i]->available_nodes |= local_devices[j]->available_nodes;
+                node_type = log2(local_devices[j]->available_nodes);
+                memcpy(local_devices[i]->nodes[node_type],
+                       local_devices[j]->nodes[node_type], drmGetMaxNodeName());
+                drmFreeDevice(&local_devices[j]);
+            }
+        }
+    }
+}
+
 /**
  * Get drm devices on the system
  *
@@ -3021,12 +3091,10 @@ int drmGetDevices(drmDevicePtr devices[], int max_devices)
     struct dirent *dent;
     struct stat sbuf;
     char node[PATH_MAX + 1];
-    const int max_node_str = drmGetMaxNodeName();
     int node_type, subsystem_type;
     int maj, min;
-    int ret, i, j, node_count, device_count;
+    int ret, i, node_count, device_count;
     int max_count = 16;
-    void *addr;
 
     local_devices = calloc(max_count, sizeof(drmDevicePtr));
     if (local_devices == NULL)
@@ -3061,44 +3129,11 @@ int drmGetDevices(drmDevicePtr devices[], int max_devices)
 
         switch (subsystem_type) {
         case DRM_BUS_PCI:
-            addr = device = calloc(1, sizeof(drmDevice) +
-                                      (DRM_NODE_MAX *
-                                       (sizeof(void *) + max_node_str)) +
-                                      sizeof(drmPciBusInfo) +
-                                      sizeof(drmPciDeviceInfo));
-            if (!device)
-                goto free_devices;
-
-            device->bustype = subsystem_type;
-            device->available_nodes = 1 << node_type;
-
-            addr += sizeof(drmDevice);
-            device->nodes = addr;
-
-            addr += DRM_NODE_MAX * sizeof(void *);
-            for (j = 0; j < DRM_NODE_MAX; j++) {
-                device->nodes[j] = addr;
-                addr += max_node_str;
-            }
-            memcpy(device->nodes[node_type], node, max_node_str);
-
-            device->businfo.pci = addr;
-
-            ret = drmParsePciBusInfo(maj, min, device->businfo.pci);
+            ret = drmProcessPciDevice(&device, dent->d_name, node, node_type,
+                                      maj, min, devices != NULL);
             if (ret)
                 goto free_devices;
 
-            // Fetch the device info if the user has requested it
-            if (devices != NULL) {
-                addr += sizeof(drmPciBusInfo);
-                device->deviceinfo.pci = addr;
-
-                ret = drmParsePciDeviceInfo(dent->d_name,
-                                            device->deviceinfo.pci);
-                if (ret)
-                    goto free_devices;
-            }
-
             break;
         default:
             fprintf(stderr, "The subsystem type is not supported yet\n");
@@ -3121,17 +3156,7 @@ int drmGetDevices(drmDevicePtr devices[], int max_devices)
     node_count = i;
 
     /* Fold nodes into a single device if they share the same bus info */
-    for (i = 0; i < node_count; i++) {
-        for (j = i + 1; j < node_count; j++) {
-            if (drmCompareBusInfo(local_devices[i], local_devices[j]) == 0) {
-                local_devices[i]->available_nodes |= local_devices[j]->available_nodes;
-                node_type = log2(local_devices[j]->available_nodes);
-                memcpy(local_devices[i]->nodes[node_type],
-                       local_devices[j]->nodes[node_type], max_node_str);
-                drmFreeDevice(&local_devices[j]);
-            }
-        }
-    }
+    drmFoldDuplicatedDevices(local_devices, node_count);
 
     device_count = 0;
     for (i = 0; i < node_count && local_devices[i]; i++) {
commit 8415a00a3f2f5cb4827b58b86e974900d3892d29
Author: Emil Velikov <[email protected]>
Date:   Mon Sep 7 18:29:05 2015 +0100

    xf86drm: warn on missing drmGetMinorNameForFD implementation
    
    Signed-off-by: Emil Velikov <[email protected]>
    Acked-by: Alex Deucher <[email protected]>

diff --git a/xf86drm.c b/xf86drm.c
index 3d03cbf..a783a28 100644
--- a/xf86drm.c
+++ b/xf86drm.c
@@ -2822,6 +2822,8 @@ static char *drmGetMinorNameForFD(int fd, int type)
 
 out_close_dir:
 	closedir(sysdir);
+#else
+#warning "Missing implementation of drmGetMinorNameForFD"
 #endif
 	return NULL;
 }
commit 291b2bb92c5fc90101417b80bbdc6c994be5fff2
Author: Emil Velikov <[email protected]>
Date:   Mon Sep 7 18:26:34 2015 +0100

    xf86drm: move ifdef __linux__ guards where needed
    
    Signed-off-by: Emil Velikov <[email protected]>
    Acked-by: Alex Deucher <[email protected]>

diff --git a/xf86drm.c b/xf86drm.c
index 8105b42..3d03cbf 100644
--- a/xf86drm.c
+++ b/xf86drm.c
@@ -2836,9 +2836,9 @@ char *drmGetRenderDeviceNameFromFd(int fd)
 	return drmGetMinorNameForFD(fd, DRM_NODE_RENDER);
 }
 
-#ifdef __linux__
 static int drmParseSubsystemType(int maj, int min)
 {
+#ifdef __linux__
     char path[PATH_MAX + 1];
     char link[PATH_MAX + 1] = "";
     char *name;
@@ -2857,10 +2857,15 @@ static int drmParseSubsystemType(int maj, int min)
         return DRM_BUS_PCI;
 
     return -EINVAL;
+#else
+#warning "Missing implementation of drmParseSubsystemType"
+    return -EINVAL;
+#endif
 }
 
 static int drmParsePciBusInfo(int maj, int min, drmPciBusInfoPtr info)
 {
+#ifdef __linux__
     char path[PATH_MAX + 1];
     char data[128];
     char *str;
@@ -2893,6 +2898,10 @@ static int drmParsePciBusInfo(int maj, int min, drmPciBusInfoPtr info)
     info->func = func;
 
     return 0;
+#else
+#warning "Missing implementation of drmParsePciBusInfo"
+    return -EINVAL;
+#endif
 }
 
 static int drmCompareBusInfo(drmDevicePtr a, drmDevicePtr b)
@@ -2942,6 +2951,7 @@ static int drmGetMaxNodeName(void)
 static int drmParsePciDeviceInfo(const char *d_name,
                                  drmPciDeviceInfoPtr device)
 {
+#ifdef __linux__
     char path[PATH_MAX + 1];
     unsigned char config[64];
     int fd, ret;
@@ -2963,6 +2973,10 @@ static int drmParsePciDeviceInfo(const char *d_name,
     device->subdevice_id = config[46] | (config[47] << 8);
 
     return 0;
+#else
+#warning "Missing implementation of drmParsePciDeviceInfo"
+    return -EINVAL;
+#endif
 }
 
 static void drmFreeDevice(drmDevicePtr *device)
@@ -3139,20 +3153,3 @@ close_sysdir:
     closedir(sysdir);
     return ret;
 }
-#else
-void drmFreeDevices(drmDevicePtr devices[], int count)
-{
-    (void)devices;
-    (void)count;
-}
-
-int drmGetDevices(drmDevicePtr devices[], int max_devices)
-{
-    (void)devices;
-    (void)max_devices;
-    return -EINVAL;
-}
-
-#warning "Missing implementation of drmGetDevices/drmFreeDevices"
-
-#endif
commit 5f68d31820315ebda9b49e8bd50d1ab605efd7fa
Author: Emil Velikov <[email protected]>
Date:   Mon Sep 7 13:54:32 2015 +0100

    xf86drm: rework drmGetDevices()
    
    Do a once off memory allocation for each drmDevice.
    
    This allows us to ease the error handling and simplify the
    de-duplication loop. As part of this we need to rework drmFreeDevice()
    such so that it frees the relevant hunks, rather than leaving that to
    the caller.
    
    Some memory stats from the drmdevice test
    
    before: 22 allocs, 22 frees, 66,922 bytes allocated
    after:   9 allocs, 9 frees, 66,436 bytes allocated
    
    Signed-off-by: Emil Velikov <[email protected]>
    Acked-by: Alex Deucher <[email protected]>

diff --git a/xf86drm.c b/xf86drm.c
index 310d1e8..8105b42 100644
--- a/xf86drm.c
+++ b/xf86drm.c
@@ -65,6 +65,8 @@
 #include "xf86drm.h"
 #include "libdrm_macros.h"
 
+#include "util_math.h"
+
 #ifdef __OpenBSD__
 #define DRM_PRIMARY_MINOR_NAME	"drm"
 #define DRM_CONTROL_MINOR_NAME	"drmC"
@@ -2928,6 +2930,15 @@ static int drmGetNodeType(const char *name)
     return -EINVAL;
 }
 
+static int drmGetMaxNodeName(void)
+{
+    return sizeof(DRM_DIR_NAME) +
+           MAX3(sizeof(DRM_PRIMARY_MINOR_NAME),
+                sizeof(DRM_CONTROL_MINOR_NAME),
+                sizeof(DRM_RENDER_MINOR_NAME)) +
+           3 /* lenght of the node number */;
+}
+
 static int drmParsePciDeviceInfo(const char *d_name,
                                  drmPciDeviceInfoPtr device)
 {
@@ -2954,20 +2965,13 @@ static int drmParsePciDeviceInfo(const char *d_name,
     return 0;
 }
 
-static void drmFreeDevice(drmDevicePtr device)
+static void drmFreeDevice(drmDevicePtr *device)
 {
-    int i;
-
     if (device == NULL)
         return;
 
-    if (device->nodes != NULL)
-        for (i = 0; i < DRM_NODE_MAX; i++)
-            free(device->nodes[i]);
-
-    free(device->nodes);
-    free(device->businfo.pci);
-    free(device->deviceinfo.pci);
+    free(*device);
+    *device = NULL;
 }
 
 void drmFreeDevices(drmDevicePtr devices[], int count)
@@ -2977,11 +2981,8 @@ void drmFreeDevices(drmDevicePtr devices[], int count)
     if (devices == NULL)
         return;
 
-    for (i = 0; i < count; i++) {
-        drmFreeDevice(devices[i]);
-        free(devices[i]);
-        devices[i] = NULL;
-    }
+    for (i = 0; i < count && devices[i] != NULL; i++)
+        drmFreeDevice(&devices[i]);
 }
 
 /**
@@ -2998,29 +2999,30 @@ void drmFreeDevices(drmDevicePtr devices[], int count)
  */
 int drmGetDevices(drmDevicePtr devices[], int max_devices)
 {
-    drmDevicePtr devs = NULL;
-    drmPciBusInfoPtr pcibus = NULL;
-    drmPciDeviceInfoPtr pcidevice = NULL;
-    DIR *sysdir = NULL;
-    struct dirent *dent = NULL;
-    struct stat sbuf = {0};
-    char node[PATH_MAX + 1] = "";
+    drmDevicePtr *local_devices;
+    drmDevicePtr device;
+    DIR *sysdir;
+    struct dirent *dent;
+    struct stat sbuf;
+    char node[PATH_MAX + 1];
+    const int max_node_str = drmGetMaxNodeName();
     int node_type, subsystem_type;
     int maj, min;
-    int ret, i = 0, j, node_count, device_count = 0;
+    int ret, i, j, node_count, device_count;
     int max_count = 16;
-    int *duplicated = NULL;
+    void *addr;
 
-    devs = calloc(max_count, sizeof(*devs));
-    if (devs == NULL)
+    local_devices = calloc(max_count, sizeof(drmDevicePtr));
+    if (local_devices == NULL)
         return -ENOMEM;
 
     sysdir = opendir(DRM_DIR_NAME);
     if (!sysdir) {
         ret = -errno;
-        goto free_locals;
+        goto close_sysdir;
     }
 
+    i = 0;
     while ((dent = readdir(sysdir))) {
         node_type = drmGetNodeType(dent->d_name);
         if (node_type < 0)
@@ -3043,115 +3045,97 @@ int drmGetDevices(drmDevicePtr devices[], int max_devices)
 
         switch (subsystem_type) {
         case DRM_BUS_PCI:
-            pcibus = calloc(1, sizeof(*pcibus));
-            if (pcibus == NULL) {
-                ret = -ENOMEM;
-                goto free_locals;
+            addr = device = calloc(1, sizeof(drmDevice) +
+                                      (DRM_NODE_MAX *
+                                       (sizeof(void *) + max_node_str)) +
+                                      sizeof(drmPciBusInfo) +
+                                      sizeof(drmPciDeviceInfo));
+            if (!device)
+                goto free_devices;
+
+            device->bustype = subsystem_type;
+            device->available_nodes = 1 << node_type;
+
+            addr += sizeof(drmDevice);
+            device->nodes = addr;
+
+            addr += DRM_NODE_MAX * sizeof(void *);
+            for (j = 0; j < DRM_NODE_MAX; j++) {
+                device->nodes[j] = addr;
+                addr += max_node_str;
             }
+            memcpy(device->nodes[node_type], node, max_node_str);
 
-            ret = drmParsePciBusInfo(maj, min, pcibus);
-            if (ret)
-                goto free_locals;
-
-            if (i >= max_count) {
-                max_count += 16;
-                devs = realloc(devs, max_count * sizeof(*devs));
-            }
+            device->businfo.pci = addr;
 
-            devs[i].businfo.pci = pcibus;
-            devs[i].bustype = subsystem_type;
-            devs[i].nodes = calloc(DRM_NODE_MAX, sizeof(char *));
-            if (devs[i].nodes == NULL) {
-                ret = -ENOMEM;
-                goto free_locals;
-            }
-            devs[i].nodes[node_type] = strdup(node);
-            if (devs[i].nodes[node_type] == NULL) {
-                ret = -ENOMEM;
-                goto free_locals;
-            }
-            devs[i].available_nodes = 1 << node_type;
+            ret = drmParsePciBusInfo(maj, min, device->businfo.pci);
+            if (ret)
+                goto free_devices;
 
+            // Fetch the device info if the user has requested it
             if (devices != NULL) {
-                pcidevice = calloc(1, sizeof(*pcidevice));
-                if (pcidevice == NULL) {
-                    ret = -ENOMEM;
-                    goto free_locals;
-                }
+                addr += sizeof(drmPciBusInfo);
+                device->deviceinfo.pci = addr;
 
-                ret = drmParsePciDeviceInfo(dent->d_name, pcidevice);
+                ret = drmParsePciDeviceInfo(dent->d_name,
+                                            device->deviceinfo.pci);
                 if (ret)
-                    goto free_locals;
-
-                devs[i].deviceinfo.pci = pcidevice;
+                    goto free_devices;
             }
+
             break;
         default:
             fprintf(stderr, "The subsystem type is not supported yet\n");
             break;
         }
-        i++;
-    }
 
-    node_count = i;
+        if (i >= max_count) {
+            drmDevicePtr *temp;
 
-    /* merge duplicated devices with same domain/bus/device/func IDs */
-    duplicated = calloc(node_count, sizeof(*duplicated));
-    if (duplicated == NULL) {
-        ret = -ENOMEM;
-        goto free_locals;
+            max_count += 16;
+            temp = realloc(local_devices, max_count * sizeof(drmDevicePtr));
+            if (!temp)
+                goto free_devices;
+            local_devices = temp;
+        }
+
+        local_devices[i] = device;
+        i++;
     }
+    node_count = i;
 
+    /* Fold nodes into a single device if they share the same bus info */
     for (i = 0; i < node_count; i++) {
-        for (j = i+1; j < node_count; j++) {
-            if (duplicated[i] || duplicated[j])
-                continue;
-            if (drmCompareBusInfo(&devs[i], &devs[j]) == 0) {
-                duplicated[j] = 1;
-                devs[i].available_nodes |= devs[j].available_nodes;
-                node_type = log2(devs[j].available_nodes);
-                devs[i].nodes[node_type] = devs[j].nodes[node_type];
-                free(devs[j].nodes);
-                free(devs[j].businfo.pci);
-                free(devs[j].deviceinfo.pci);
+        for (j = i + 1; j < node_count; j++) {
+            if (drmCompareBusInfo(local_devices[i], local_devices[j]) == 0) {
+                local_devices[i]->available_nodes |= local_devices[j]->available_nodes;
+                node_type = log2(local_devices[j]->available_nodes);
+                memcpy(local_devices[i]->nodes[node_type],
+                       local_devices[j]->nodes[node_type], max_node_str);
+                drmFreeDevice(&local_devices[j]);
             }
         }
     }
 
-    for (i = 0; i < node_count; i++) {
-        if(duplicated[i] == 0) {
-            if ((devices != NULL) && (device_count < max_devices)) {
-                devices[device_count] = calloc(1, sizeof(drmDevice));
-                if (devices[device_count] == NULL) {
-                    ret = -ENOMEM;
-                    break;
-                }
-                memcpy(devices[device_count], &devs[i], sizeof(drmDevice));
-            } else
-                drmFreeDevice(&devs[i]);
-            device_count++;
-        }
-    }
+    device_count = 0;
+    for (i = 0; i < node_count && local_devices[i]; i++) {
+        if ((devices != NULL) && (device_count < max_devices))
+            devices[device_count] = local_devices[i];
+        else
+            drmFreeDevice(&local_devices[i]);
 
-    if (i < node_count) {
-        drmFreeDevices(devices, device_count);
-        for ( ; i < node_count; i++)
-            if(duplicated[i] == 0)
-                drmFreeDevice(&devs[i]);
-    } else
-        ret = device_count;
+        device_count++;
+    }
 
-    free(duplicated);
-    free(devs);
+    free(local_devices);
     closedir(sysdir);
-    return ret;
+    return device_count;
+
+free_devices:
+    drmFreeDevices(local_devices, i);
+    free(local_devices);
 
-free_locals:
-    for (j = 0; j < i; j++)
-        drmFreeDevice(&devs[j]);
-    free(pcidevice);
-    free(pcibus);
-    free(devs);
+close_sysdir:
     closedir(sysdir);
     return ret;
 }
commit 6c88173f7ecc2470341ca96e91b6ccb0a5efbef6
Author: Emil Velikov <[email protected]>
Date:   Mon Sep 7 14:13:32 2015 +0100

    util_math: add MAX3 macro
    
    Signed-off-by: Emil Velikov <[email protected]>
    Acked-by: Alex Deucher <[email protected]>

diff --git a/util_math.h b/util_math.h
index 3bc5f64..02b15a8 100644
--- a/util_math.h
+++ b/util_math.h
@@ -26,6 +26,7 @@
 
 #define MIN2( A, B )   ( (A)<(B) ? (A) : (B) )
 #define MAX2( A, B )   ( (A)>(B) ? (A) : (B) )
+#define MAX3( A, B, C ) ((A) > (B) ? MAX2(A, C) : MAX2(B, C))
 
 #define __align_mask(value, mask)  (((value) + (mask)) & ~(mask))
 #define ALIGN(value, alignment)    __align_mask(value, (typeof(value))((alignment) - 1))
commit bc2aca9e22bf2cc82874f3e44568c684eec9c5e1
Author: Emil Velikov <[email protected]>
Date:   Mon Sep 7 13:51:54 2015 +0100

    xf86drm: rename drmSameDevice to drmCompareBusInfo
    
    Move away form the boolean name, change the return value
    appropriately and check if either argument is NULL.
    
    Signed-off-by: Emil Velikov <[email protected]>
    Acked-by: Alex Deucher <[email protected]>

diff --git a/xf86drm.c b/xf86drm.c
index dc1782d..310d1e8 100644
--- a/xf86drm.c
+++ b/xf86drm.c
@@ -2893,20 +2893,22 @@ static int drmParsePciBusInfo(int maj, int min, drmPciBusInfoPtr info)
     return 0;
 }
 
-static int drmSameDevice(drmDevicePtr a, drmDevicePtr b)
+static int drmCompareBusInfo(drmDevicePtr a, drmDevicePtr b)
 {
+    if (a == NULL || b == NULL)
+        return -1;
+
     if (a->bustype != b->bustype)
-        return 0;
+        return -1;
 
     switch (a->bustype) {
     case DRM_BUS_PCI:
-        if (memcmp(a->businfo.pci, b->businfo.pci, sizeof(drmPciBusInfo)) == 0)
-            return 1;
+        return memcmp(a->businfo.pci, b->businfo.pci, sizeof(drmPciBusInfo));
     default:
         break;
     }
 
-    return 0;
+    return -1;
 }
 
 static int drmGetNodeType(const char *name)
@@ -3104,7 +3106,7 @@ int drmGetDevices(drmDevicePtr devices[], int max_devices)
         for (j = i+1; j < node_count; j++) {
             if (duplicated[i] || duplicated[j])
                 continue;
-            if (drmSameDevice(&devs[i], &devs[j])) {
+            if (drmCompareBusInfo(&devs[i], &devs[j]) == 0) {
                 duplicated[j] = 1;
                 devs[i].available_nodes |= devs[j].available_nodes;
                 node_type = log2(devs[j].available_nodes);
commit a250fceaaa150363accaf3fb71a0e42bcecc40da
Author: Emil Velikov <[email protected]>
Date:   Mon Sep 7 12:54:27 2015 +0100

    xf86drm: move the final linux specific bits out of drmGetDevices
    
    Third and final piece of making drmGetDevices less crazy/ugly.
    
    Signed-off-by: Emil Velikov <[email protected]>
    Acked-by: Alex Deucher <[email protected]>

diff --git a/xf86drm.c b/xf86drm.c
index 1174a64..dc1782d 100644
--- a/xf86drm.c
+++ b/xf86drm.c
@@ -2835,21 +2835,23 @@ char *drmGetRenderDeviceNameFromFd(int fd)
 }
 
 #ifdef __linux__
-static int drmParseSubsystemType(const char *str)
+static int drmParseSubsystemType(int maj, int min)
 {
+    char path[PATH_MAX + 1];
     char link[PATH_MAX + 1] = "";
     char *name;
 
-    if (readlink(str, link, PATH_MAX) < 0)
-        return -EINVAL;
+    snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/subsystem",
+             maj, min);
+
+    if (readlink(path, link, PATH_MAX) < 0)
+        return -errno;
 
     name = strrchr(link, '/');
     if (!name)
         return -EINVAL;
 
-    name++;
-
-    if (strncmp(name, "pci", 3) == 0)
+    if (strncmp(name, "/pci", 4) == 0)
         return DRM_BUS_PCI;
 
     return -EINVAL;
@@ -3001,7 +3003,6 @@ int drmGetDevices(drmDevicePtr devices[], int max_devices)
     struct dirent *dent = NULL;
     struct stat sbuf = {0};
     char node[PATH_MAX + 1] = "";
-    char path[PATH_MAX + 1] = "";
     int node_type, subsystem_type;
     int maj, min;
     int ret, i = 0, j, node_count, device_count = 0;
@@ -3033,9 +3034,7 @@ int drmGetDevices(drmDevicePtr devices[], int max_devices)
         if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
             continue;
 
-        snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/subsystem",
-                 maj, min);
-        subsystem_type = drmParseSubsystemType(path);
+        subsystem_type = drmParseSubsystemType(maj, min);
 
         if (subsystem_type < 0)
             continue;
commit ef5192e9c7897c82da815a2c893b2e2562997a3a
Author: Emil Velikov <[email protected]>
Date:   Mon Sep 7 12:47:47 2015 +0100

    xf86drm: move platform details to drmParsePciDeviceInfo()
    
    As with previous commit let's try to keep drmGetDevices clean of linux
    specifics.
    
    Signed-off-by: Emil Velikov <[email protected]>
    Acked-by: Alex Deucher <[email protected]>

diff --git a/xf86drm.c b/xf86drm.c
index b4c5aa0..1174a64 100644
--- a/xf86drm.c
+++ b/xf86drm.c
@@ -2924,11 +2924,22 @@ static int drmGetNodeType(const char *name)
     return -EINVAL;
 }
 
-static int drmParsePciDeviceInfo(const unsigned char *config,
+static int drmParsePciDeviceInfo(const char *d_name,
                                  drmPciDeviceInfoPtr device)
 {
-    if (config == NULL)
-        return -EINVAL;
+    char path[PATH_MAX + 1];
+    unsigned char config[64];
+    int fd, ret;
+
+    snprintf(path, PATH_MAX, "/sys/class/drm/%s/device/config", d_name);
+    fd = open(path, O_RDONLY);
+    if (fd < 0)
+        return -errno;
+
+    ret = read(fd, config, sizeof(config));
+    close(fd);
+    if (ret < 0)
+        return -errno;
 
     device->vendor_id = config[0] | (config[1] << 8);
     device->device_id = config[2] | (config[3] << 8);
@@ -2991,10 +3002,8 @@ int drmGetDevices(drmDevicePtr devices[], int max_devices)
     struct stat sbuf = {0};
     char node[PATH_MAX + 1] = "";
     char path[PATH_MAX + 1] = "";
-    unsigned char config[64] = "";
     int node_type, subsystem_type;
     int maj, min;
-    int fd;
     int ret, i = 0, j, node_count, device_count = 0;
     int max_count = 16;
     int *duplicated = NULL;
@@ -3063,32 +3072,17 @@ int drmGetDevices(drmDevicePtr devices[], int max_devices)
             devs[i].available_nodes = 1 << node_type;
 
             if (devices != NULL) {
-                snprintf(path, PATH_MAX, "/sys/class/drm/%s/device/config",
-                         dent->d_name);
-                fd = open(path, O_RDONLY);
-                if (fd < 0) {
-                     ret = -errno;
-                     goto free_locals;
-                }
-                ret = read(fd, config, 64);
-                if (ret < 0) {
-                    ret = -errno;
-                    close(fd);
-                    goto free_locals;
-                }
-
                 pcidevice = calloc(1, sizeof(*pcidevice));
                 if (pcidevice == NULL) {
                     ret = -ENOMEM;
                     goto free_locals;
                 }
 
-                ret = drmParsePciDeviceInfo(config, pcidevice);
+                ret = drmParsePciDeviceInfo(dent->d_name, pcidevice);
                 if (ret)
                     goto free_locals;
 
                 devs[i].deviceinfo.pci = pcidevice;
-                close(fd);
             }
             break;
         default:
commit 536e0deba3829e194aafda6d9a1d9e938ba8277a
Author: Emil Velikov <[email protected]>
Date:   Mon Sep 7 12:37:57 2015 +0100

    xf86drm: flex platform specifics into drmParsePciBusInfo
    
    This will allow one to reuse the core drmGetDevices implementation on
    other platforms. Keeping all the platform specifics in ParseFoo.
    
    On the plus side this saves a bit of code :)
    
    Signed-off-by: Emil Velikov <[email protected]>
    Acked-by: Alex Deucher <[email protected]>

diff --git a/xf86drm.c b/xf86drm.c
index a5a7b41..b4c5aa0 100644
--- a/xf86drm.c
+++ b/xf86drm.c
@@ -2855,23 +2855,33 @@ static int drmParseSubsystemType(const char *str)
     return -EINVAL;
 }
 
-static int drmParsePciBusInfo(const char *str, drmPciBusInfoPtr info)
+static int drmParsePciBusInfo(int maj, int min, drmPciBusInfoPtr info)
 {
+    char path[PATH_MAX + 1];
+    char data[128];
+    char *str;
     int domain, bus, dev, func;
-    char *value;
+    int fd, ret;
 
-    if (str == NULL)
-        return -EINVAL;
+    snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/uevent", maj, min);
+    fd = open(path, O_RDONLY);
+    if (fd < 0)
+        return -errno;
 
-    value = strstr(str, "PCI_SLOT_NAME=");
-    if (value == NULL)
-        return -EINVAL;
+    ret = read(fd, data, sizeof(data));
+    close(fd);
+    if (ret < 0)
+        return -errno;
 
-    value += strlen("PCI_SLOT_NAME=");
+#define TAG "PCI_SLOT_NAME="
+    str = strstr(data, TAG);
+    if (str == NULL)
+        return -EINVAL;
 
-    if (sscanf(value, "%04x:%02x:%02x.%1u",
+    if (sscanf(str, TAG "%04x:%02x:%02x.%1u",
                &domain, &bus, &dev, &func) != 4)
         return -EINVAL;
+#undef TAG
 
     info->domain = domain;
     info->bus = bus;
@@ -2981,7 +2991,6 @@ int drmGetDevices(drmDevicePtr devices[], int max_devices)
     struct stat sbuf = {0};
     char node[PATH_MAX + 1] = "";
     char path[PATH_MAX + 1] = "";
-    char data[128] = "";
     unsigned char config[64] = "";
     int node_type, subsystem_type;
     int maj, min;
@@ -3030,22 +3039,7 @@ int drmGetDevices(drmDevicePtr devices[], int max_devices)
                 goto free_locals;
             }
 
-            snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/uevent",
-                     maj, min);
-            fd = open(path, O_RDONLY);
-            if (fd < 0) {
-                ret = -errno;
-                goto free_locals;
-            }
-            ret = read(fd, data, sizeof(data));
-            if (ret < 0) {
-                ret = -errno;
-                close(fd);
-                goto free_locals;
-            }
-
-            ret = drmParsePciBusInfo(data, pcibus);
-            close(fd);
+            ret = drmParsePciBusInfo(maj, min, pcibus);
             if (ret)
                 goto free_locals;
 
commit f098d1c130f369f059a53cda5c38b3d4ea06a0b7
Author: Emil Velikov <[email protected]>
Date:   Wed Sep 9 16:48:29 2015 +0100

    tests/drmdevice: add new 'test'
    
    A simple example of how to use/what is the new drm{Get,Free}Devices()
    interface.
    
    Signed-off-by: Emil Velikov <[email protected]>
    Acked-by: Alex Deucher <[email protected]>

diff --git a/tests/Makefile.am b/tests/Makefile.am
index a511d28..c53f4af 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -31,6 +31,7 @@ LDADD = $(top_builddir)/libdrm.la
 
 check_PROGRAMS = \
 	dristat \
+	drmdevice \
 	drmstat
 
 dristat_LDADD = -lm
diff --git a/tests/drmdevice.c b/tests/drmdevice.c
new file mode 100644
index 0000000..4055f45
--- /dev/null
+++ b/tests/drmdevice.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015 Emil Velikov <[email protected]>
+ *
+ * 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,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice 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 THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 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 <stdio.h>
+#include <stdlib.h>
+#include <xf86drm.h>
+
+
+static void
+print_device_info(drmDevicePtr device, int i)
+{
+    printf("device[%i]\n", i);
+    printf("\tavailable_nodes %04x\n", device->available_nodes);
+    printf("\tnodes\n");
+    for (int j = 0; j < DRM_NODE_MAX; j++)
+        if (device->available_nodes & 1 << j)
+            printf("\t\tnodes[%d] %s\n", j, device->nodes[j]);
+
+    printf("\tbustype %04x\n", device->bustype);
+    printf("\tbusinfo\n");
+    if (device->bustype == DRM_BUS_PCI) {
+        printf("\t\tpci\n");
+        printf("\t\t\tdomain\t%04x\n",device->businfo.pci->domain);
+        printf("\t\t\tbu\t%02x\n", device->businfo.pci->bus);
+        printf("\t\t\tde\t%02x\n", device->businfo.pci->dev);
+        printf("\t\t\tfunc\t%1u\n", device->businfo.pci->func);
+
+        printf("\tdeviceinfo\n");
+        printf("\t\tpci\n");
+        printf("\t\t\tvendor_id\t%04x\n", device->deviceinfo.pci->vendor_id);
+        printf("\t\t\tdevice_id\t%04x\n", device->deviceinfo.pci->device_id);
+        printf("\t\t\tsubvendor_id\t%04x\n", device->deviceinfo.pci->subvendor_id);
+        printf("\t\t\tsubdevice_id\t%04x\n", device->deviceinfo.pci->subdevice_id);
+        printf("\t\t\trevision_id\t%02x\n", device->deviceinfo.pci->revision_id);
+    } else {
+        printf("Unknown/unhandled bustype\n");
+    }
+    printf("\n");
+}
+
+int
+main(void)
+{
+    drmDevicePtr *devices;
+    int ret, max_devices;
+
+    max_devices = drmGetDevices(NULL, 0);
+
+    if (max_devices <= 0) {
+        printf("drmGetDevices() has returned %d\n", max_devices);
+        return -1;
+    }
+
+    devices = calloc(max_devices, sizeof(drmDevicePtr));
+    if (devices == NULL) {
+        printf("Failed to allocate memory for the drmDevicePtr array\n");
+        return -1;
+    }
+
+    ret = drmGetDevices(devices, max_devices);
+    if (ret < 0) {
+        printf("drmGetDevices() returned an error %d\n", ret);
+        free(devices);
+        return -1;
+    }
+
+    for (int i = 0; i < ret; i++)
+        print_device_info(devices[i], i);
+
+    drmFreeDevices(devices, ret);
+    free(devices);
+    return 0;
+}
commit ed3c665548c2c0400a7e9e91cb9324c9e1f29375
Author: Emil Velikov <[email protected]>
Date:   Sat Sep 5 17:20:53 2015 +0100

    xf86drmMode: smoke-test the atomic API
    
    As going through the modetest patches for atomic support I've noticed
    that if we pass NULL for the drmModeAtomicReqPtr argument we'll crash.
    
    So let's handle things appropriately if the user forgot to check the
    return value of drmModeAtomicAlloc and drmModeAtomicDuplicate or made a
    typo somewhere along the way.
    
    Cc: Ville Syrjälä <[email protected]>
    Cc: Rob Clark <[email protected]>
    Cc: Daniel Stone <[email protected]>
    Signed-off-by: Emil Velikov <[email protected]>

diff --git a/xf86drmMode.c b/xf86drmMode.c
index 23800dd..ab6b519 100644
--- a/xf86drmMode.c
+++ b/xf86drmMode.c
@@ -1189,6 +1189,9 @@ drmModeAtomicReqPtr drmModeAtomicDuplicate(drmModeAtomicReqPtr old)
 {
 	drmModeAtomicReqPtr new;
 
+	if (!old)
+		return NULL;
+
 	new = drmMalloc(sizeof *new);
 	if (!new)
 		return NULL;
@@ -1213,6 +1216,9 @@ drmModeAtomicReqPtr drmModeAtomicDuplicate(drmModeAtomicReqPtr old)
 
 int drmModeAtomicMerge(drmModeAtomicReqPtr base, drmModeAtomicReqPtr augment)
 {
+	if (!base)
+		return -EINVAL;
+
 	if (!augment || augment->cursor == 0)
 		return 0;
 
@@ -1239,12 +1245,15 @@ int drmModeAtomicMerge(drmModeAtomicReqPtr base, drmModeAtomicReqPtr augment)
 
 int drmModeAtomicGetCursor(drmModeAtomicReqPtr req)
 {
+	if (!req)
+		return -EINVAL;
 	return req->cursor;
 }
 
 void drmModeAtomicSetCursor(drmModeAtomicReqPtr req, int cursor)
 {
-	req->cursor = cursor;
+	if (req)
+		req->cursor = cursor;
 }
 
 int drmModeAtomicAddProperty(drmModeAtomicReqPtr req,
@@ -1252,6 +1261,9 @@ int drmModeAtomicAddProperty(drmModeAtomicReqPtr req,
 			     uint32_t property_id,
 			     uint64_t value)
 {
+	if (!req)
+		return -EINVAL;
+
 	if (req->cursor >= req->size_items) {
 		drmModeAtomicReqItemPtr new;
 
@@ -1309,6 +1321,9 @@ int drmModeAtomicCommit(int fd, drmModeAtomicReqPtr req, uint32_t flags,
 	int obj_idx = -1;
 	int ret = -1;
 
+	if (!req)
+		return -EINVAL;
+
 	if (req->cursor == 0)
 		return 0;
 
commit 00808a99d05929f675bb4ab680a1ffc6d0868ba4
Author: Emil Velikov <[email protected]>
Date:   Fri Sep 4 22:06:44 2015 +0100

    automake: set --enable-valgrind during make distcheck
    
    Signed-off-by: Emil Velikov <[email protected]>

diff --git a/Makefile.am b/Makefile.am
index a7a0cca..ca41508 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -37,7 +37,8 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \
 	--enable-tegra-experimental-api \
 	--enable-install-test-programs \
 	--enable-cairo-tests \
-	--enable-manpages
+	--enable-manpages \
+	--enable-valgrind
 
 pkgconfigdir = @pkgconfigdir@
 pkgconfig_DATA = libdrm.pc

------------------------------------------------------------------------------

--
_______________________________________________
Dri-patches mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/dri-patches
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.