From 6d05f5b23edc562edfc279d238bea3b532ed572d Mon Sep 17 00:00:00 2001 From: LI Qingwu Date: Thu, 4 Jul 2024 11:46:27 +0200 Subject: [PATCH 01/21] build: cleanup mesonbuild convert Remove the autotools configure.ac and cleanup the .gitignore file. While on it update the README.rst to include the required compile steps and provide a version bump. Signed-off-by: LI Qingwu Signed-off-by: Marco Felsch --- .gitignore | 14 -------------- Makefile.am | 23 ----------------------- README.rst | 15 +++++++++++++++ configure.ac | 13 ------------- meson.build | 2 +- 5 files changed, 16 insertions(+), 51 deletions(-) delete mode 100644 Makefile.am delete mode 100644 configure.ac diff --git a/.gitignore b/.gitignore index 4582877..796b96d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1 @@ -/*.o -/aclocal.m4 -/autom4te.cache /build -/compile -/config.log -/config.status -/configure -/depcomp -/.deps -/install-sh -/Makefile -/Makefile.in -/missing -/platsch diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index d149ae0..0000000 --- a/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ -EXTRA_DIST = README.rst LICENSE - -sbin_PROGRAMS = platsch - -platsch_SOURCES = platsch.c -platsch_CFLAGS = $(LIBDRM_CFLAGS) -platsch_LDADD = $(LIBDRM_LIBS) - -CLEANFILES = \ - $(DIST_ARCHIVES) - -DISTCLEAN = \ - config.log \ - config.status \ - Makefile - -MAINTAINERCLEANFILES = \ - aclocal.m4 \ - configure \ - depcomp \ - install-sh \ - Makefile.in \ - missing diff --git a/README.rst b/README.rst index e318120..79dc7d4 100644 --- a/README.rst +++ b/README.rst @@ -141,3 +141,18 @@ By adding a Signed-off-by line (e.g. using ``git commit -s``) saying:: (using your real name and e-mail address), you state that your contributions are in line with the DCO. + +Compiling Instructions +---------------------- + +.. code-block:: shell + + meson setup build + meson compile -C build + +To ensure fast startup, ``platsch`` prefers using static libraries: + +.. code-block:: shell + + meson setup -Dprefer_static=true build + meson compile -C build diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 18878db..0000000 --- a/configure.ac +++ /dev/null @@ -1,13 +0,0 @@ -AC_PREREQ([2.69]) -AC_INIT([platsch], [2019.12.0], [oss-tools@pengutronix.de]) -AC_CONFIG_SRCDIR([platsch.c]) -AM_INIT_AUTOMAKE([foreign dist-xz]) - -AC_PROG_CC -AC_PROG_MAKE_SET - -PKG_CHECK_MODULES([LIBDRM], [libdrm >= 2.4.112]) - -AC_CONFIG_FILES([Makefile]) - -AC_OUTPUT diff --git a/meson.build b/meson.build index e8e85e2..8392348 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'platsch', 'c', - version : '2019.12.0', + version : '2024.06.0', default_options : [ 'warning_level=2', ], From 455ad8ea1c153d2c68371e4736c0e708cde33da6 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 31 Jul 2024 20:09:53 +0200 Subject: [PATCH 02/21] platsch: fix -Wcalloc-transposed-args warning The warning was detected with GCC 14.1.1 Signed-off-by: Marco Felsch --- platsch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platsch.c b/platsch.c index ea190a4..67c843a 100644 --- a/platsch.c +++ b/platsch.c @@ -692,7 +692,7 @@ int main(int argc, char *argv[]) goto sleep; } - initsargv = calloc(sizeof(argv[0]), argc + 1); + initsargv = calloc(argc + 1, sizeof(argv[0])); if (!initsargv) { error("failed to allocate argv for init\n"); return EXIT_FAILURE; From 08b74d41d98a5bba34b00c4f041f1ad43b6a6658 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 31 Jul 2024 19:48:12 +0200 Subject: [PATCH 03/21] platsch: make local functions static Limit the scope to the current file. Signed-off-by: Marco Felsch --- platsch.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platsch.c b/platsch.c index 67c843a..25a482d 100644 --- a/platsch.c +++ b/platsch.c @@ -56,7 +56,7 @@ static const struct platsch_format platsch_formats[] = { { DRM_FORMAT_XRGB8888, 32, "XRGB8888" }, }; -void redirect_stdfd(void) +static void redirect_stdfd(void) { int devnull = open("/dev/null", O_RDWR, 0); @@ -74,7 +74,7 @@ void redirect_stdfd(void) close(devnull); } -ssize_t readfull(int fd, void *buf, size_t count) +static ssize_t readfull(int fd, void *buf, size_t count) { ssize_t ret = 0, err; @@ -112,7 +112,7 @@ struct modeset_dev { uint32_t crtc_id; }; -void draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) +static void draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) { int fd_src; char *filename; From 6b53f9481a9fd1310e0edc8722adce8c601e38cf Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 31 Jul 2024 19:49:25 +0200 Subject: [PATCH 04/21] platsch: reorder custom data type declaration Move struct modeset_dev declaration to the file top to make it easier to find it. This aligns the code more to the kernel coding style. Signed-off-by: Marco Felsch --- platsch.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/platsch.c b/platsch.c index 25a482d..a9f888c 100644 --- a/platsch.c +++ b/platsch.c @@ -56,6 +56,24 @@ static const struct platsch_format platsch_formats[] = { { DRM_FORMAT_XRGB8888, 32, "XRGB8888" }, }; +struct modeset_dev { + struct modeset_dev *next; + + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t size; + const struct platsch_format *format; + uint32_t handle; + void *map; + + bool setmode; + drmModeModeInfo mode; + uint32_t fb_id; + uint32_t conn_id; + uint32_t crtc_id; +}; + static void redirect_stdfd(void) { int devnull = open("/dev/null", O_RDWR, 0); @@ -94,24 +112,6 @@ static ssize_t readfull(int fd, void *buf, size_t count) return ret; } -struct modeset_dev { - struct modeset_dev *next; - - uint32_t width; - uint32_t height; - uint32_t stride; - uint32_t size; - const struct platsch_format *format; - uint32_t handle; - void *map; - - bool setmode; - drmModeModeInfo mode; - uint32_t fb_id; - uint32_t conn_id; - uint32_t crtc_id; -}; - static void draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) { int fd_src; From 789bd2502e13a5c7f4e5949ffa2fa6b2a5887082 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 1 Aug 2024 12:20:12 +0200 Subject: [PATCH 05/21] platsch: add platsch_ctx support Introduce struct platsch_ctx to bundle the required state. At the moment it is just an container for the modeset_dev list but it will be extended in the upcoming commits. This commit also drops the usage of the gloabl modeset_list variable usage. Signed-off-by: Marco Felsch --- platsch.c | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/platsch.c b/platsch.c index a9f888c..3ae4c2c 100644 --- a/platsch.c +++ b/platsch.c @@ -74,6 +74,10 @@ struct modeset_dev { uint32_t crtc_id; }; +struct platsch_ctx { + struct modeset_dev *modeset_list; +}; + static void redirect_stdfd(void) { int devnull = open("/dev/null", O_RDWR, 0); @@ -157,10 +161,8 @@ static void draw_buffer(struct modeset_dev *dev, const char *dir, const char *ba return; } -static struct modeset_dev *modeset_list = NULL; - -static int drmprepare_crtc(int fd, drmModeRes *res, drmModeConnector *conn, - struct modeset_dev *dev) +static int drmprepare_crtc(struct platsch_ctx *ctx, int fd, drmModeRes *res, + drmModeConnector *conn, struct modeset_dev *dev) { drmModeEncoder *enc; int i, j; @@ -186,7 +188,7 @@ static int drmprepare_crtc(int fd, drmModeRes *res, drmModeConnector *conn, crtc_id = enc->crtc_id; bool in_use = false; - for (iter = modeset_list; iter; iter = iter->next) { + for (iter = ctx->modeset_list; iter; iter = iter->next) { if (iter->crtc_id == crtc_id) { in_use = true; break; @@ -234,7 +236,7 @@ static int drmprepare_crtc(int fd, drmModeRes *res, drmModeConnector *conn, /* check that no other device already uses this CRTC */ crtc_id = res->crtcs[j]; - for (iter = modeset_list; iter; iter = iter->next) { + for (iter = ctx->modeset_list; iter; iter = iter->next) { if (iter->crtc_id == crtc_id) { in_use = true; break; @@ -449,7 +451,8 @@ static int set_env_connector_mode(drmModeConnector *conn, return ret; } -static int drmprepare_connector(int fd, drmModeRes *res, drmModeConnector *conn, +static int drmprepare_connector(struct platsch_ctx *ctx, int fd, + drmModeRes *res, drmModeConnector *conn, struct modeset_dev *dev) { int ret; @@ -476,7 +479,7 @@ static int drmprepare_connector(int fd, drmModeRes *res, drmModeConnector *conn, conn->connector_id, dev->width, dev->height, dev->format->name); /* find a crtc for this connector */ - ret = drmprepare_crtc(fd, res, conn, dev); + ret = drmprepare_crtc(ctx, fd, res, conn, dev); if (ret) { error("no valid crtc for connector #%u\n", conn->connector_id); return ret; @@ -493,12 +496,13 @@ static int drmprepare_connector(int fd, drmModeRes *res, drmModeConnector *conn, return 0; } -static int drmprepare(int fd) +static int drmprepare(struct platsch_ctx *ctx, int fd) { drmModeRes *res; drmModeConnector *conn; int i; struct modeset_dev *dev; + bool root_node = true; int ret; /* retrieve resources */ @@ -534,7 +538,7 @@ static int drmprepare(int fd) memset(dev, 0, sizeof(*dev)); dev->conn_id = conn->connector_id; - ret = drmprepare_connector(fd, res, conn, dev); + ret = drmprepare_connector(ctx, fd, res, conn, dev); if (ret) { if (ret != -ENOENT) { error("Cannot setup device for connector #%u: %m\n", @@ -547,8 +551,10 @@ static int drmprepare(int fd) /* free connector data and link device into global list */ drmModeFreeConnector(conn); - dev->next = modeset_list; - modeset_list = dev; + dev->next = root_node ? NULL : ctx->modeset_list; + ctx->modeset_list = dev; + + root_node = false; } /* free resources again */ @@ -576,6 +582,7 @@ int main(int argc, char *argv[]) { char **initsargv; int drmfd; + struct platsch_ctx *ctx; struct modeset_dev *iter; bool pid1 = getpid() == 1; const char *dir = "/usr/share/platsch"; @@ -617,6 +624,12 @@ int main(int argc, char *argv[]) } } + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + error("Cannot allocate memory for platsch_ctx\n"); + exit(1); + } + for (i = 0; i < 64; i++) { struct drm_mode_card_res res = {0}; char *drmdev; @@ -649,10 +662,10 @@ int main(int argc, char *argv[]) } } - ret = drmprepare(drmfd); + ret = drmprepare(ctx, drmfd); assert(!ret); - for (iter = modeset_list; iter; iter = iter->next) { + for (iter = ctx->modeset_list; iter; iter = iter->next) { /* draw first then set the mode */ draw_buffer(iter, dir, base); @@ -679,6 +692,8 @@ int main(int argc, char *argv[]) if (ret) error("Failed to drop master on drm device\n"); + free(ctx); + execinit: if (pid1) { ret = fork(); From 1f1b65be2c5caebc945ee6a6d0886f10be0119ef Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 1 Aug 2024 12:24:05 +0200 Subject: [PATCH 06/21] platsch: add drmfd to platsch_ctx Save the drmfd file descriptor within the platsch_ctx and make use of it. This prepares the code base for the upcoming application/library split. Signed-off-by: Marco Felsch --- platsch.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/platsch.c b/platsch.c index 3ae4c2c..39e0a20 100644 --- a/platsch.c +++ b/platsch.c @@ -76,6 +76,7 @@ struct modeset_dev { struct platsch_ctx { struct modeset_dev *modeset_list; + int drmfd; }; static void redirect_stdfd(void) @@ -161,7 +162,7 @@ static void draw_buffer(struct modeset_dev *dev, const char *dir, const char *ba return; } -static int drmprepare_crtc(struct platsch_ctx *ctx, int fd, drmModeRes *res, +static int drmprepare_crtc(struct platsch_ctx *ctx, drmModeRes *res, drmModeConnector *conn, struct modeset_dev *dev) { drmModeEncoder *enc; @@ -173,7 +174,7 @@ static int drmprepare_crtc(struct platsch_ctx *ctx, int fd, drmModeRes *res, if (conn->encoder_id) { debug("connector #%d uses encoder #%d\n", conn->connector_id, conn->encoder_id); - enc = drmModeGetEncoder(fd, conn->encoder_id); + enc = drmModeGetEncoder(ctx->drmfd, conn->encoder_id); assert(enc); assert(enc->encoder_id == conn->encoder_id); } else { @@ -218,7 +219,7 @@ static int drmprepare_crtc(struct platsch_ctx *ctx, int fd, drmModeRes *res, * but let's be safe), iterate all other available encoders to find a * matching CRTC. */ for (i = 0; i < conn->count_encoders; ++i) { - enc = drmModeGetEncoder(fd, conn->encoders[i]); + enc = drmModeGetEncoder(ctx->drmfd, conn->encoders[i]); if (!enc) { error("Cannot retrieve encoder %u: %m\n", conn->encoders[i]); @@ -451,9 +452,8 @@ static int set_env_connector_mode(drmModeConnector *conn, return ret; } -static int drmprepare_connector(struct platsch_ctx *ctx, int fd, - drmModeRes *res, drmModeConnector *conn, - struct modeset_dev *dev) +static int drmprepare_connector(struct platsch_ctx *ctx, drmModeRes *res, + drmModeConnector *conn, struct modeset_dev *dev) { int ret; @@ -479,14 +479,14 @@ static int drmprepare_connector(struct platsch_ctx *ctx, int fd, conn->connector_id, dev->width, dev->height, dev->format->name); /* find a crtc for this connector */ - ret = drmprepare_crtc(ctx, fd, res, conn, dev); + ret = drmprepare_crtc(ctx, res, conn, dev); if (ret) { error("no valid crtc for connector #%u\n", conn->connector_id); return ret; } /* create a framebuffer for this CRTC */ - ret = modeset_create_fb(fd, dev); + ret = modeset_create_fb(ctx->drmfd, dev); if (ret) { error("cannot create framebuffer for connector #%u\n", conn->connector_id); @@ -496,7 +496,7 @@ static int drmprepare_connector(struct platsch_ctx *ctx, int fd, return 0; } -static int drmprepare(struct platsch_ctx *ctx, int fd) +static int drmprepare(struct platsch_ctx *ctx) { drmModeRes *res; drmModeConnector *conn; @@ -506,7 +506,7 @@ static int drmprepare(struct platsch_ctx *ctx, int fd) int ret; /* retrieve resources */ - res = drmModeGetResources(fd); + res = drmModeGetResources(ctx->drmfd); if (!res) { error("cannot retrieve DRM resources: %m\n"); return -errno; @@ -517,7 +517,7 @@ static int drmprepare(struct platsch_ctx *ctx, int fd) /* iterate all connectors */ for (i = 0; i < res->count_connectors; ++i) { /* get information for each connector */ - conn = drmModeGetConnector(fd, res->connectors[i]); + conn = drmModeGetConnector(ctx->drmfd, res->connectors[i]); if (!conn) { error("Cannot retrieve DRM connector #%u: %m\n", res->connectors[i]); @@ -538,7 +538,7 @@ static int drmprepare(struct platsch_ctx *ctx, int fd) memset(dev, 0, sizeof(*dev)); dev->conn_id = conn->connector_id; - ret = drmprepare_connector(ctx, fd, res, conn, dev); + ret = drmprepare_connector(ctx, res, conn, dev); if (ret) { if (ret != -ENOENT) { error("Cannot setup device for connector #%u: %m\n", @@ -658,11 +658,12 @@ int main(int argc, char *argv[]) continue; } else { /* Device found */ + ctx->drmfd = drmfd; break; } } - ret = drmprepare(ctx, drmfd); + ret = drmprepare(ctx); assert(!ret); for (iter = ctx->modeset_list; iter; iter = iter->next) { @@ -673,14 +674,14 @@ int main(int argc, char *argv[]) if (iter->setmode) { debug("set crtc\n"); - ret = drmModeSetCrtc(drmfd, iter->crtc_id, iter->fb_id, + ret = drmModeSetCrtc(ctx->drmfd, iter->crtc_id, iter->fb_id, 0, 0, &iter->conn_id, 1, &iter->mode); if (ret) error("Cannot set CRTC for connector #%u: %m\n", iter->conn_id); } else { debug("page flip\n"); - ret = drmModePageFlip(drmfd, iter->crtc_id, iter->fb_id, + ret = drmModePageFlip(ctx->drmfd, iter->crtc_id, iter->fb_id, 0, NULL); if (ret) error("Page flip failed on connector #%u: %m\n", @@ -688,7 +689,7 @@ int main(int argc, char *argv[]) } } - ret = drmDropMaster(drmfd); + ret = drmDropMaster(ctx->drmfd); if (ret) error("Failed to drop master on drm device\n"); From b9dd501f54bea2dfb0f5c94118f242e9526c7f57 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 1 Aug 2024 13:41:18 +0200 Subject: [PATCH 07/21] platsch: add dir and base to platsch_ctx Store the dir path and base name within the platsch_ctx. The strdup() is not needed at the moment but prepares the code for the application/library split too. Signed-off-by: Marco Felsch --- platsch.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/platsch.c b/platsch.c index 39e0a20..8bd3677 100644 --- a/platsch.c +++ b/platsch.c @@ -77,6 +77,8 @@ struct modeset_dev { struct platsch_ctx { struct modeset_dev *modeset_list; int drmfd; + char *dir; + char *base; }; static void redirect_stdfd(void) @@ -629,6 +631,8 @@ int main(int argc, char *argv[]) error("Cannot allocate memory for platsch_ctx\n"); exit(1); } + ctx->dir = strdup(dir); + ctx->base = strdup(base); for (i = 0; i < 64; i++) { struct drm_mode_card_res res = {0}; @@ -669,7 +673,7 @@ int main(int argc, char *argv[]) for (iter = ctx->modeset_list; iter; iter = iter->next) { /* draw first then set the mode */ - draw_buffer(iter, dir, base); + draw_buffer(iter, ctx->dir, ctx->base); if (iter->setmode) { debug("set crtc\n"); @@ -693,6 +697,8 @@ int main(int argc, char *argv[]) if (ret) error("Failed to drop master on drm device\n"); + free(ctx->dir); + free(ctx->base); free(ctx); execinit: From 3837c57e896505bfc31c91e387724dadddce4466 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 31 Jul 2024 20:51:00 +0200 Subject: [PATCH 08/21] platsch: add platsch_create_ctx Add platsch_create_ctx() to bundle the initialization. This prepares the code base for the upcoming application/library split. Signed-off-by: Marco Felsch --- platsch.c | 112 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 46 deletions(-) diff --git a/platsch.c b/platsch.c index 8bd3677..f9efea7 100644 --- a/platsch.c +++ b/platsch.c @@ -564,6 +564,68 @@ static int drmprepare(struct platsch_ctx *ctx) return 0; } +static struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base) +{ + struct platsch_ctx *ctx; + int drmfd; + int ret; + int i; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + error("Cannot allocate memory for platsch_ctx\n"); + return NULL; + } + ctx->dir = strdup(dir); + ctx->base = strdup(base); + + for (i = 0; i < 64; i++) { + struct drm_mode_card_res res = {0}; + char *drmdev; + + /* + * XXX: Maybe use drmOpen instead? + * (Where should name/busid come from?) + * XXX: Loop through drm devices to find one with connectors. + */ + ret = asprintf(&drmdev, DRM_DEV_NAME, DRM_DIR_NAME, i); + if (ret < 0) { + error("Huh, failed to allocate device name buffer\n"); + goto err_out; + } + + drmfd = open(drmdev, O_RDWR | O_CLOEXEC, 0); + free(drmdev); + if (drmfd < 0) { + error("Failed to open drm device: %m\n"); + goto err_out; + } + + ret = drmIoctl(drmfd, DRM_IOCTL_MODE_GETRESOURCES, &res); + if (ret < 0) { + close(drmfd); + continue; + } else { + /* Device found */ + ctx->drmfd = drmfd; + break; + } + } + + ret = drmprepare(ctx); + if (ret) + goto err_out; + + return ctx; + +err_out: + free(ctx->dir); + free(ctx->base); + free(ctx); + + return NULL; +} + static struct option longopts[] = { { "help", no_argument, 0, 'h' }, @@ -583,14 +645,13 @@ static void usage(const char *prog) int main(int argc, char *argv[]) { char **initsargv; - int drmfd; struct platsch_ctx *ctx; struct modeset_dev *iter; bool pid1 = getpid() == 1; const char *dir = "/usr/share/platsch"; const char *base = "splash"; const char *env; - int ret = 0, c, i; + int ret = 0, c; env = getenv("platsch_directory"); if (env) @@ -626,49 +687,9 @@ int main(int argc, char *argv[]) } } - ctx = calloc(1, sizeof(*ctx)); - if (!ctx) { - error("Cannot allocate memory for platsch_ctx\n"); - exit(1); - } - ctx->dir = strdup(dir); - ctx->base = strdup(base); - - for (i = 0; i < 64; i++) { - struct drm_mode_card_res res = {0}; - char *drmdev; - - /* - * XXX: Maybe use drmOpen instead? - * (Where should name/busid come from?) - * XXX: Loop through drm devices to find one with connectors. - */ - ret = asprintf(&drmdev, DRM_DEV_NAME, DRM_DIR_NAME, i); - if (ret < 0) { - error("Huh, failed to allocate device name buffer\n"); - goto execinit; - } - - drmfd = open(drmdev, O_RDWR | O_CLOEXEC, 0); - free(drmdev); - if (drmfd < 0) { - error("Failed to open drm device: %m\n"); - goto execinit; - } - - ret = drmIoctl(drmfd, DRM_IOCTL_MODE_GETRESOURCES, &res); - if (ret < 0) { - close(drmfd); - continue; - } else { - /* Device found */ - ctx->drmfd = drmfd; - break; - } - } - - ret = drmprepare(ctx); - assert(!ret); + ctx = platsch_create_ctx(dir, base); + if (!ctx) + return EXIT_FAILURE; for (iter = ctx->modeset_list; iter; iter = iter->next) { @@ -701,7 +722,6 @@ int main(int argc, char *argv[]) free(ctx->base); free(ctx); -execinit: if (pid1) { ret = fork(); if (ret < 0) { From 98a51d5259a1d8879c2de6b17bb566b5f3df2961 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 31 Jul 2024 21:14:00 +0200 Subject: [PATCH 09/21] platsch: add platsch_draw helper Add platsch_draw() to bundle the draw step. This prepares the code base for the upcoming application/library split. Signed-off-by: Marco Felsch --- platsch.c | 53 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/platsch.c b/platsch.c index f9efea7..eebdaa4 100644 --- a/platsch.c +++ b/platsch.c @@ -626,6 +626,35 @@ static struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base) return NULL; } +static void platsch_draw(struct platsch_ctx *ctx) +{ + struct modeset_dev *iter; + int ret; + + for (iter = ctx->modeset_list; iter; iter = iter->next) { + + /* draw first then set the mode */ + draw_buffer(iter, ctx->dir, ctx->base); + + if (iter->setmode) { + debug("set crtc\n"); + + ret = drmModeSetCrtc(ctx->drmfd, iter->crtc_id, iter->fb_id, + 0, 0, &iter->conn_id, 1, &iter->mode); + if (ret) + error("Cannot set CRTC for connector #%u: %m\n", + iter->conn_id); + } else { + debug("page flip\n"); + ret = drmModePageFlip(ctx->drmfd, iter->crtc_id, iter->fb_id, + 0, NULL); + if (ret) + error("Page flip failed on connector #%u: %m\n", + iter->conn_id); + } + } +} + static struct option longopts[] = { { "help", no_argument, 0, 'h' }, @@ -646,7 +675,6 @@ int main(int argc, char *argv[]) { char **initsargv; struct platsch_ctx *ctx; - struct modeset_dev *iter; bool pid1 = getpid() == 1; const char *dir = "/usr/share/platsch"; const char *base = "splash"; @@ -691,28 +719,7 @@ int main(int argc, char *argv[]) if (!ctx) return EXIT_FAILURE; - for (iter = ctx->modeset_list; iter; iter = iter->next) { - - /* draw first then set the mode */ - draw_buffer(iter, ctx->dir, ctx->base); - - if (iter->setmode) { - debug("set crtc\n"); - - ret = drmModeSetCrtc(ctx->drmfd, iter->crtc_id, iter->fb_id, - 0, 0, &iter->conn_id, 1, &iter->mode); - if (ret) - error("Cannot set CRTC for connector #%u: %m\n", - iter->conn_id); - } else { - debug("page flip\n"); - ret = drmModePageFlip(ctx->drmfd, iter->crtc_id, iter->fb_id, - 0, NULL); - if (ret) - error("Page flip failed on connector #%u: %m\n", - iter->conn_id); - } - } + platsch_draw(ctx); ret = drmDropMaster(ctx->drmfd); if (ret) From 3d649656f6ce7801b27b2f5de65c2809d5a86a98 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 31 Jul 2024 21:24:02 +0200 Subject: [PATCH 10/21] platsch: add platsch_destroy_ctx helper Add platsch_destroy_ctx() to bundle the final step. While on it add the missing modeset_dev free which is important once we split this part into a library. This prepares the code base for the upcoming application/library split. Signed-off-by: Marco Felsch --- platsch.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/platsch.c b/platsch.c index eebdaa4..8235e55 100644 --- a/platsch.c +++ b/platsch.c @@ -655,6 +655,25 @@ static void platsch_draw(struct platsch_ctx *ctx) } } +static void platsch_destroy_ctx(struct platsch_ctx *ctx) +{ + struct modeset_dev *mode, *next; + int ret; + + ret = drmDropMaster(ctx->drmfd); + if (ret) + error("Failed to drop master on drm device\n"); + + for (mode = ctx->modeset_list; mode;) { + next = mode->next; + free(mode); + mode = next; + } + free(ctx->dir); + free(ctx->base); + free(ctx); +} + static struct option longopts[] = { { "help", no_argument, 0, 'h' }, @@ -721,13 +740,7 @@ int main(int argc, char *argv[]) platsch_draw(ctx); - ret = drmDropMaster(ctx->drmfd); - if (ret) - error("Failed to drop master on drm device\n"); - - free(ctx->dir); - free(ctx->base); - free(ctx); + platsch_destroy_ctx(ctx); if (pid1) { ret = fork(); From 311797844a70e268b67bb13f3aac8cacd6c94d9d Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 1 Aug 2024 13:59:17 +0200 Subject: [PATCH 11/21] platsch: move dir and base default into platsch_init_ctx Move the default value handling out of the application main() function into platsch_init_ctx(). This prepares the code base for the upcoming application/library split. Signed-off-by: Marco Felsch --- platsch.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/platsch.c b/platsch.c index 8235e55..f604ed4 100644 --- a/platsch.c +++ b/platsch.c @@ -576,7 +576,13 @@ static struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base) error("Cannot allocate memory for platsch_ctx\n"); return NULL; } + + if (!dir) + dir = "/usr/share/platsch"; ctx->dir = strdup(dir); + + if (!base) + base = "splash"; ctx->base = strdup(base); for (i = 0; i < 64; i++) { @@ -695,8 +701,8 @@ int main(int argc, char *argv[]) char **initsargv; struct platsch_ctx *ctx; bool pid1 = getpid() == 1; - const char *dir = "/usr/share/platsch"; - const char *base = "splash"; + const char *dir = NULL; + const char *base = NULL; const char *env; int ret = 0, c; From 72c77ef7ad721350c9a96f388133d2f32031d478 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 1 Aug 2024 14:06:22 +0200 Subject: [PATCH 12/21] platsch: make use of calloc Use calloc which set the memory automatically to zero to remove the memset() call. Signed-off-by: Marco Felsch --- platsch.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/platsch.c b/platsch.c index f604ed4..0c86046 100644 --- a/platsch.c +++ b/platsch.c @@ -531,13 +531,12 @@ static int drmprepare(struct platsch_ctx *ctx) drmModeGetConnectorTypeName(conn->connector_type)); /* create a device structure */ - dev = malloc(sizeof(*dev)); + dev = calloc(1, sizeof(*dev)); if (!dev) { error("Cannot allocate memory for connector #%u: %m\n", res->connectors[i]); continue; } - memset(dev, 0, sizeof(*dev)); dev->conn_id = conn->connector_id; ret = drmprepare_connector(ctx, res, conn, dev); From 98492a4f6db0dcd6cd64fa423f95ab5d38295028 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 1 Aug 2024 14:57:04 +0200 Subject: [PATCH 13/21] platsch: split into platsch and libplatsch Split the single platsch.c into platsch.c and libplatsch.c. The later can be used by other projects to build custom bootsplash applications once we support building the library. During the split no changes are made except for making: struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base); void platsch_destroy_ctx(struct platsch_ctx *ctx); void platsch_draw(struct platsch_ctx *ctx); public. Signed-off-by: Marco Felsch --- libplatsch.c | 666 +++++++++++++++++++++++++++++++++++++++++++++++++++ libplatsch.h | 10 + meson.build | 2 +- platsch.c | 629 +----------------------------------------------- 4 files changed, 678 insertions(+), 629 deletions(-) create mode 100644 libplatsch.c create mode 100644 libplatsch.h diff --git a/libplatsch.c b/libplatsch.c new file mode 100644 index 0000000..745b2e4 --- /dev/null +++ b/libplatsch.c @@ -0,0 +1,666 @@ +/* + * Copyright (C) 2019 Pengutronix, Uwe Kleine-König + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Some code parts base on example code written in 2012 by David Herrmann + * and dedicated to the Public Domain. It was found + * in 2019 on + * https://raw.githubusercontent.com/dvdhrm/docs/master/drm-howto/modeset.c + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "libplatsch.h" + +#define debug(fmt, ...) printf("%s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__) +#define error(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a)) + +struct platsch_format { + uint32_t format; + uint32_t bpp; + const char *name; +}; + +static const struct platsch_format platsch_formats[] = { + { DRM_FORMAT_RGB565, 16, "RGB565" }, /* default */ + { DRM_FORMAT_XRGB8888, 32, "XRGB8888" }, +}; + +struct modeset_dev { + struct modeset_dev *next; + + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t size; + const struct platsch_format *format; + uint32_t handle; + void *map; + + bool setmode; + drmModeModeInfo mode; + uint32_t fb_id; + uint32_t conn_id; + uint32_t crtc_id; +}; + +struct platsch_ctx { + struct modeset_dev *modeset_list; + int drmfd; + char *dir; + char *base; +}; + +static ssize_t readfull(int fd, void *buf, size_t count) +{ + ssize_t ret = 0, err; + + while (count > 0) { + err = read(fd, buf, count); + if (err < 0) + return err; + else if (err > 0) { + buf += err; + count -= err; + ret += err; + } else { + return ret; + } + } + + return ret; +} + +static void draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) +{ + int fd_src; + char *filename; + ssize_t size; + int ret; + + /* + * make it easy and load a raw file in the right format instead of + * opening an (say) PNG and convert the image data to the right format. + */ + ret = asprintf(&filename, "%s/%s-%ux%u-%s.bin", + dir, base, dev->width, dev->height, dev->format->name); + if (ret < 0) { + error("Failed to allocate filename buffer\n"); + return; + } + + fd_src = open(filename, O_RDONLY | O_CLOEXEC); + if (fd_src < 0) { + error("Failed to open %s: %m\n", filename); + goto out; + } + + size = readfull(fd_src, dev->map, dev->size); + if (size < dev->size) { + if (size < 0) + error("Failed to read from %s: %m\n", filename); + else + error("Could only read %zd/%u bytes from %s\n", + size, dev->size, filename); + } + + ret = close(fd_src); + if (ret < 0) { + /* Nothing we can do about this, so just warn */ + error("Failed to close image file\n"); + } + +out: + free(filename); + + return; +} + +static int drmprepare_crtc(struct platsch_ctx *ctx, drmModeRes *res, + drmModeConnector *conn, struct modeset_dev *dev) +{ + drmModeEncoder *enc; + int i, j; + uint32_t crtc_id; + struct modeset_dev *iter; + + /* first try the currently connected encoder+crtc */ + if (conn->encoder_id) { + debug("connector #%d uses encoder #%d\n", conn->connector_id, + conn->encoder_id); + enc = drmModeGetEncoder(ctx->drmfd, conn->encoder_id); + assert(enc); + assert(enc->encoder_id == conn->encoder_id); + } else { + debug("connector #%d has no active encoder\n", + conn->connector_id); + enc = NULL; + dev->setmode = 1; + } + + if (enc) { + if (enc->crtc_id) { + crtc_id = enc->crtc_id; + bool in_use = false; + + for (iter = ctx->modeset_list; iter; iter = iter->next) { + if (iter->crtc_id == crtc_id) { + in_use = true; + break; + } + } + + if (!in_use) { + debug("encoder #%d uses crtc #%d\n", + enc->encoder_id, enc->crtc_id); + drmModeFreeEncoder(enc); + dev->crtc_id = crtc_id; + return 0; + } else { + debug("encoder #%d used crtc #%d, but that's in use\n", + enc->encoder_id, iter->crtc_id); + } + } else { + debug("encoder #%d doesn't have an active crtc\n", + enc->encoder_id); + } + + drmModeFreeEncoder(enc); + } + + /* If the connector is not currently bound to an encoder or if the + * encoder+crtc is already used by another connector (actually unlikely + * but let's be safe), iterate all other available encoders to find a + * matching CRTC. */ + for (i = 0; i < conn->count_encoders; ++i) { + enc = drmModeGetEncoder(ctx->drmfd, conn->encoders[i]); + if (!enc) { + error("Cannot retrieve encoder %u: %m\n", + conn->encoders[i]); + continue; + } + assert(enc->encoder_id == conn->encoders[i]); + + /* iterate all global CRTCs */ + for (j = 0; j < res->count_crtcs; ++j) { + bool in_use = false; + + /* check whether this CRTC works with the encoder */ + if (!(enc->possible_crtcs & (1 << j))) + continue; + + /* check that no other device already uses this CRTC */ + crtc_id = res->crtcs[j]; + for (iter = ctx->modeset_list; iter; iter = iter->next) { + if (iter->crtc_id == crtc_id) { + in_use = true; + break; + } + } + + /* we have found a CRTC, so save it and return */ + if (!in_use) { + debug("encoder #%d will use crtc #%d\n", + enc->encoder_id, crtc_id); + drmModeFreeEncoder(enc); + dev->crtc_id = crtc_id; + return 0; + } + + } + drmModeFreeEncoder(enc); + } + + error("Cannot find suitable CRTC for connector #%u\n", + conn->connector_id); + return -ENOENT; +} + +static int modeset_create_fb(int fd, struct modeset_dev *dev) +{ + struct drm_mode_create_dumb creq; + struct drm_mode_destroy_dumb dreq; + struct drm_mode_map_dumb mreq; + int ret; + + /* create dumb buffer */ + memset(&creq, 0, sizeof(creq)); + creq.width = dev->width; + creq.height = dev->height; + creq.bpp = dev->format->bpp; + ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); + if (ret < 0) { + error("Cannot create dumb buffer: %m\n"); + return -errno; + } + dev->stride = creq.pitch; + dev->size = creq.size; + dev->handle = creq.handle; + + /* create framebuffer object for the dumb-buffer */ + ret = drmModeAddFB2(fd, dev->width, dev->height, + dev->format->format, + (uint32_t[4]){ dev->handle, }, + (uint32_t[4]){ dev->stride, }, + (uint32_t[4]){ 0, }, + &dev->fb_id, 0); + if (ret) { + ret = -errno; + error("Cannot create framebuffer: %m\n"); + goto err_destroy; + } + + /* prepare buffer for memory mapping */ + memset(&mreq, 0, sizeof(mreq)); + mreq.handle = dev->handle; + ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); + if (ret) { + ret = -errno; + error("Cannot get mmap offset: %m\n"); + goto err_fb; + } + + /* perform actual memory mapping */ + dev->map = mmap(0, dev->size, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, mreq.offset); + if (dev->map == MAP_FAILED) { + ret = -errno; + error("Cannot mmap dumb buffer: %m\n"); + goto err_fb; + } + + /* + * Clear the framebuffer. Normally it's overwritten later with some + * image data, but in case this fails, initialize to all-black. + */ + memset(dev->map, 0x0, dev->size); + + return 0; + +err_fb: + drmModeRmFB(fd, dev->fb_id); +err_destroy: + memset(&dreq, 0, sizeof(dreq)); + dreq.handle = dev->handle; + drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return ret; +} + +/* Returns lowercase connector type names with '_' for '-' */ +static char *get_normalized_conn_type_name(uint32_t connector_type) +{ + int i; + const char *connector_name; + char *normalized_name; + + connector_name = drmModeGetConnectorTypeName(connector_type); + if (!connector_name) + return NULL; + + normalized_name = strdup(connector_name); + + for (i = 0; normalized_name[i]; i++) { + normalized_name[i] = tolower(normalized_name[i]); + if (normalized_name[i] == '-') + normalized_name[i] = '_'; + } + + return normalized_name; +} + +static const struct platsch_format *platsch_format_find(const char *name) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(platsch_formats); i++) + if (!strcmp(platsch_formats[i].name, name)) + return &platsch_formats[i]; + + return NULL; +} + +static int set_env_connector_mode(drmModeConnector *conn, + struct modeset_dev *dev) +{ + int ret, i = 0; + u_int32_t width = 0, height = 0; + const char *mode; + char *connector_type_name, *mode_env_name = NULL, fmt_specifier[32] = ""; + const struct platsch_format *format = NULL; + + connector_type_name = get_normalized_conn_type_name(conn->connector_type); + if (!connector_type_name) { + error("could not look up name for connector type %u\n", + conn->connector_type); + goto fallback; + } + + ret = asprintf(&mode_env_name, "platsch_%s%u_mode", + connector_type_name, conn->connector_type_id); + free(connector_type_name); + if (ret < 0) { + error("failed to allocate platsch env mode variable\n"); + return -ENOMEM; + } + + /* check for connector mode configuration in environment */ + debug("looking up %s env variable\n", mode_env_name); + mode = getenv(mode_env_name); + if (!mode) + goto fallback; + + /* format suffix is optional */ + ret = sscanf(mode, "%ux%u@%s", &width, &height, fmt_specifier); + if (ret < 2) { + error("error while scanning %s for mode\n", mode_env_name); + ret = -EFAULT; + goto err_out; + } + + /* use first mode matching given resolution */ + for (i = 0; i < conn->count_modes; i++) { + drmModeModeInfo mode = conn->modes[i]; + if (mode.hdisplay == width && mode.vdisplay == height) { + memcpy(&dev->mode, &mode, sizeof(dev->mode)); + dev->width = width; + dev->height = height; + break; + } + } + + if (i == conn->count_modes) { + error("no mode available matching %ux%u\n", width, height); + ret = -ENOENT; + goto err_out; + } + + format = platsch_format_find(fmt_specifier); + if (!format) { + if (strlen(fmt_specifier)) + error("unknown format specifier %s\n", fmt_specifier); + goto fallback_format; + } + + dev->format = format; + + free(mode_env_name); + + return 0; + +fallback: + memcpy(&dev->mode, &conn->modes[0], sizeof(dev->mode)); + dev->width = conn->modes[0].hdisplay; + dev->height = conn->modes[0].vdisplay; + debug("using default mode for connector #%u\n", conn->connector_id); + +fallback_format: + dev->format = &platsch_formats[0]; + debug("using default format %s for connector #%u\n", dev->format->name, + conn->connector_id); + + ret = 0; + +err_out: + free(mode_env_name); + + return ret; +} + +static int drmprepare_connector(struct platsch_ctx *ctx, drmModeRes *res, + drmModeConnector *conn, struct modeset_dev *dev) +{ + int ret; + + /* check if a monitor is connected */ + if (conn->connection != DRM_MODE_CONNECTED) { + error("Ignoring unused connector #%u\n", conn->connector_id); + return -ENOENT; + } + + /* check if there is at least one valid mode */ + if (conn->count_modes == 0) { + error("no valid mode for connector #%u\n", conn->connector_id); + return -EFAULT; + } + + /* configure mode information in our device structure */ + ret = set_env_connector_mode(conn, dev); + if (ret) { + error("no valid mode for connector #%u\n", conn->connector_id); + return ret; + } + debug("mode for connector #%u is %ux%u@%s\n", + conn->connector_id, dev->width, dev->height, dev->format->name); + + /* find a crtc for this connector */ + ret = drmprepare_crtc(ctx, res, conn, dev); + if (ret) { + error("no valid crtc for connector #%u\n", conn->connector_id); + return ret; + } + + /* create a framebuffer for this CRTC */ + ret = modeset_create_fb(ctx->drmfd, dev); + if (ret) { + error("cannot create framebuffer for connector #%u\n", + conn->connector_id); + return ret; + } + + return 0; +} + +static int drmprepare(struct platsch_ctx *ctx) +{ + drmModeRes *res; + drmModeConnector *conn; + int i; + struct modeset_dev *dev; + bool root_node = true; + int ret; + + /* retrieve resources */ + res = drmModeGetResources(ctx->drmfd); + if (!res) { + error("cannot retrieve DRM resources: %m\n"); + return -errno; + } + + debug("Found %d connectors\n", res->count_connectors); + + /* iterate all connectors */ + for (i = 0; i < res->count_connectors; ++i) { + /* get information for each connector */ + conn = drmModeGetConnector(ctx->drmfd, res->connectors[i]); + if (!conn) { + error("Cannot retrieve DRM connector #%u: %m\n", + res->connectors[i]); + continue; + } + assert(conn->connector_id == res->connectors[i]); + + debug("Connector #%u has type %s\n", conn->connector_id, + drmModeGetConnectorTypeName(conn->connector_type)); + + /* create a device structure */ + dev = calloc(1, sizeof(*dev)); + if (!dev) { + error("Cannot allocate memory for connector #%u: %m\n", + res->connectors[i]); + continue; + } + dev->conn_id = conn->connector_id; + + ret = drmprepare_connector(ctx, res, conn, dev); + if (ret) { + if (ret != -ENOENT) { + error("Cannot setup device for connector #%u: %m\n", + res->connectors[i]); + } + free(dev); + drmModeFreeConnector(conn); + continue; + } + + /* free connector data and link device into global list */ + drmModeFreeConnector(conn); + dev->next = root_node ? NULL : ctx->modeset_list; + ctx->modeset_list = dev; + + root_node = false; + } + + /* free resources again */ + drmModeFreeResources(res); + return 0; +} + +/************************* Public API ****************************/ + +void platsch_draw(struct platsch_ctx *ctx) +{ + struct modeset_dev *iter; + int ret; + + for (iter = ctx->modeset_list; iter; iter = iter->next) { + + /* draw first then set the mode */ + draw_buffer(iter, ctx->dir, ctx->base); + + if (iter->setmode) { + debug("set crtc\n"); + + ret = drmModeSetCrtc(ctx->drmfd, iter->crtc_id, iter->fb_id, + 0, 0, &iter->conn_id, 1, &iter->mode); + if (ret) + error("Cannot set CRTC for connector #%u: %m\n", + iter->conn_id); + } else { + debug("page flip\n"); + ret = drmModePageFlip(ctx->drmfd, iter->crtc_id, iter->fb_id, + 0, NULL); + if (ret) + error("Page flip failed on connector #%u: %m\n", + iter->conn_id); + } + } +} + +struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base) +{ + struct platsch_ctx *ctx; + int drmfd; + int ret; + int i; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + error("Cannot allocate memory for platsch_ctx\n"); + return NULL; + } + + if (!dir) + dir = "/usr/share/platsch"; + ctx->dir = strdup(dir); + + if (!base) + base = "splash"; + ctx->base = strdup(base); + + for (i = 0; i < 64; i++) { + struct drm_mode_card_res res = {0}; + char *drmdev; + + /* + * XXX: Maybe use drmOpen instead? + * (Where should name/busid come from?) + * XXX: Loop through drm devices to find one with connectors. + */ + ret = asprintf(&drmdev, DRM_DEV_NAME, DRM_DIR_NAME, i); + if (ret < 0) { + error("Huh, failed to allocate device name buffer\n"); + goto err_out; + } + + drmfd = open(drmdev, O_RDWR | O_CLOEXEC, 0); + free(drmdev); + if (drmfd < 0) { + error("Failed to open drm device: %m\n"); + goto err_out; + } + + ret = drmIoctl(drmfd, DRM_IOCTL_MODE_GETRESOURCES, &res); + if (ret < 0) { + close(drmfd); + continue; + } else { + /* Device found */ + ctx->drmfd = drmfd; + break; + } + } + + ret = drmprepare(ctx); + if (ret) + goto err_out; + + return ctx; + +err_out: + free(ctx->dir); + free(ctx->base); + free(ctx); + + return NULL; +} + +void platsch_destroy_ctx(struct platsch_ctx *ctx) +{ + struct modeset_dev *mode, *next; + int ret; + + ret = drmDropMaster(ctx->drmfd); + if (ret) + error("Failed to drop master on drm device\n"); + + for (mode = ctx->modeset_list; mode;) { + next = mode->next; + free(mode); + mode = next; + } + free(ctx->dir); + free(ctx->base); + free(ctx); +} diff --git a/libplatsch.h b/libplatsch.h new file mode 100644 index 0000000..00aacd2 --- /dev/null +++ b/libplatsch.h @@ -0,0 +1,10 @@ +#ifndef __LIBPLATSCH_H__ +#define __LIBPLATSCH_H__ + +struct platsch_ctx; + +void platsch_draw(struct platsch_ctx *ctx); +struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base); +void platsch_destroy_ctx(struct platsch_ctx *ctx); + +#endif /* __LIBPLATSCH_H__ */ diff --git a/meson.build b/meson.build index 8392348..54122f4 100644 --- a/meson.build +++ b/meson.build @@ -12,7 +12,7 @@ libdrm_dep = dependency('libdrm', version : '>=2.4.112') executable( 'platsch', - sources : ['platsch.c'], + sources : ['platsch.c', 'libplatsch.c'], dependencies : [libdrm_dep], install : true, install_dir : get_option('sbindir'), diff --git a/platsch.c b/platsch.c index 0c86046..6670d5c 100644 --- a/platsch.c +++ b/platsch.c @@ -19,11 +19,7 @@ * https://raw.githubusercontent.com/dvdhrm/docs/master/drm-howto/modeset.c */ -#define _GNU_SOURCE - #include -#include -#include #include #include #include @@ -31,56 +27,13 @@ #include #include #include -#include -#include -#include #include -#include -#include -#include +#include "libplatsch.h" #define debug(fmt, ...) printf("%s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__) #define error(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a)) - -struct platsch_format { - uint32_t format; - uint32_t bpp; - const char *name; -}; - -static const struct platsch_format platsch_formats[] = { - { DRM_FORMAT_RGB565, 16, "RGB565" }, /* default */ - { DRM_FORMAT_XRGB8888, 32, "XRGB8888" }, -}; - -struct modeset_dev { - struct modeset_dev *next; - - uint32_t width; - uint32_t height; - uint32_t stride; - uint32_t size; - const struct platsch_format *format; - uint32_t handle; - void *map; - - bool setmode; - drmModeModeInfo mode; - uint32_t fb_id; - uint32_t conn_id; - uint32_t crtc_id; -}; - -struct platsch_ctx { - struct modeset_dev *modeset_list; - int drmfd; - char *dir; - char *base; -}; - static void redirect_stdfd(void) { int devnull = open("/dev/null", O_RDWR, 0); @@ -99,586 +52,6 @@ static void redirect_stdfd(void) close(devnull); } -static ssize_t readfull(int fd, void *buf, size_t count) -{ - ssize_t ret = 0, err; - - while (count > 0) { - err = read(fd, buf, count); - if (err < 0) - return err; - else if (err > 0) { - buf += err; - count -= err; - ret += err; - } else { - return ret; - } - } - - return ret; -} - -static void draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) -{ - int fd_src; - char *filename; - ssize_t size; - int ret; - - /* - * make it easy and load a raw file in the right format instead of - * opening an (say) PNG and convert the image data to the right format. - */ - ret = asprintf(&filename, "%s/%s-%ux%u-%s.bin", - dir, base, dev->width, dev->height, dev->format->name); - if (ret < 0) { - error("Failed to allocate filename buffer\n"); - return; - } - - fd_src = open(filename, O_RDONLY | O_CLOEXEC); - if (fd_src < 0) { - error("Failed to open %s: %m\n", filename); - goto out; - } - - size = readfull(fd_src, dev->map, dev->size); - if (size < dev->size) { - if (size < 0) - error("Failed to read from %s: %m\n", filename); - else - error("Could only read %zd/%u bytes from %s\n", - size, dev->size, filename); - } - - ret = close(fd_src); - if (ret < 0) { - /* Nothing we can do about this, so just warn */ - error("Failed to close image file\n"); - } - -out: - free(filename); - - return; -} - -static int drmprepare_crtc(struct platsch_ctx *ctx, drmModeRes *res, - drmModeConnector *conn, struct modeset_dev *dev) -{ - drmModeEncoder *enc; - int i, j; - uint32_t crtc_id; - struct modeset_dev *iter; - - /* first try the currently connected encoder+crtc */ - if (conn->encoder_id) { - debug("connector #%d uses encoder #%d\n", conn->connector_id, - conn->encoder_id); - enc = drmModeGetEncoder(ctx->drmfd, conn->encoder_id); - assert(enc); - assert(enc->encoder_id == conn->encoder_id); - } else { - debug("connector #%d has no active encoder\n", - conn->connector_id); - enc = NULL; - dev->setmode = 1; - } - - if (enc) { - if (enc->crtc_id) { - crtc_id = enc->crtc_id; - bool in_use = false; - - for (iter = ctx->modeset_list; iter; iter = iter->next) { - if (iter->crtc_id == crtc_id) { - in_use = true; - break; - } - } - - if (!in_use) { - debug("encoder #%d uses crtc #%d\n", - enc->encoder_id, enc->crtc_id); - drmModeFreeEncoder(enc); - dev->crtc_id = crtc_id; - return 0; - } else { - debug("encoder #%d used crtc #%d, but that's in use\n", - enc->encoder_id, iter->crtc_id); - } - } else { - debug("encoder #%d doesn't have an active crtc\n", - enc->encoder_id); - } - - drmModeFreeEncoder(enc); - } - - /* If the connector is not currently bound to an encoder or if the - * encoder+crtc is already used by another connector (actually unlikely - * but let's be safe), iterate all other available encoders to find a - * matching CRTC. */ - for (i = 0; i < conn->count_encoders; ++i) { - enc = drmModeGetEncoder(ctx->drmfd, conn->encoders[i]); - if (!enc) { - error("Cannot retrieve encoder %u: %m\n", - conn->encoders[i]); - continue; - } - assert(enc->encoder_id == conn->encoders[i]); - - /* iterate all global CRTCs */ - for (j = 0; j < res->count_crtcs; ++j) { - bool in_use = false; - - /* check whether this CRTC works with the encoder */ - if (!(enc->possible_crtcs & (1 << j))) - continue; - - /* check that no other device already uses this CRTC */ - crtc_id = res->crtcs[j]; - for (iter = ctx->modeset_list; iter; iter = iter->next) { - if (iter->crtc_id == crtc_id) { - in_use = true; - break; - } - } - - /* we have found a CRTC, so save it and return */ - if (!in_use) { - debug("encoder #%d will use crtc #%d\n", - enc->encoder_id, crtc_id); - drmModeFreeEncoder(enc); - dev->crtc_id = crtc_id; - return 0; - } - - } - drmModeFreeEncoder(enc); - } - - error("Cannot find suitable CRTC for connector #%u\n", - conn->connector_id); - return -ENOENT; -} - -static int modeset_create_fb(int fd, struct modeset_dev *dev) -{ - struct drm_mode_create_dumb creq; - struct drm_mode_destroy_dumb dreq; - struct drm_mode_map_dumb mreq; - int ret; - - /* create dumb buffer */ - memset(&creq, 0, sizeof(creq)); - creq.width = dev->width; - creq.height = dev->height; - creq.bpp = dev->format->bpp; - ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); - if (ret < 0) { - error("Cannot create dumb buffer: %m\n"); - return -errno; - } - dev->stride = creq.pitch; - dev->size = creq.size; - dev->handle = creq.handle; - - /* create framebuffer object for the dumb-buffer */ - ret = drmModeAddFB2(fd, dev->width, dev->height, - dev->format->format, - (uint32_t[4]){ dev->handle, }, - (uint32_t[4]){ dev->stride, }, - (uint32_t[4]){ 0, }, - &dev->fb_id, 0); - if (ret) { - ret = -errno; - error("Cannot create framebuffer: %m\n"); - goto err_destroy; - } - - /* prepare buffer for memory mapping */ - memset(&mreq, 0, sizeof(mreq)); - mreq.handle = dev->handle; - ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); - if (ret) { - ret = -errno; - error("Cannot get mmap offset: %m\n"); - goto err_fb; - } - - /* perform actual memory mapping */ - dev->map = mmap(0, dev->size, PROT_READ | PROT_WRITE, MAP_SHARED, - fd, mreq.offset); - if (dev->map == MAP_FAILED) { - ret = -errno; - error("Cannot mmap dumb buffer: %m\n"); - goto err_fb; - } - - /* - * Clear the framebuffer. Normally it's overwritten later with some - * image data, but in case this fails, initialize to all-black. - */ - memset(dev->map, 0x0, dev->size); - - return 0; - -err_fb: - drmModeRmFB(fd, dev->fb_id); -err_destroy: - memset(&dreq, 0, sizeof(dreq)); - dreq.handle = dev->handle; - drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); - return ret; -} - -/* Returns lowercase connector type names with '_' for '-' */ -static char *get_normalized_conn_type_name(uint32_t connector_type) -{ - int i; - const char *connector_name; - char *normalized_name; - - connector_name = drmModeGetConnectorTypeName(connector_type); - if (!connector_name) - return NULL; - - normalized_name = strdup(connector_name); - - for (i = 0; normalized_name[i]; i++) { - normalized_name[i] = tolower(normalized_name[i]); - if (normalized_name[i] == '-') - normalized_name[i] = '_'; - } - - return normalized_name; -} - -static const struct platsch_format *platsch_format_find(const char *name) -{ - unsigned i; - - for (i = 0; i < ARRAY_SIZE(platsch_formats); i++) - if (!strcmp(platsch_formats[i].name, name)) - return &platsch_formats[i]; - - return NULL; -} - -static int set_env_connector_mode(drmModeConnector *conn, - struct modeset_dev *dev) -{ - int ret, i = 0; - u_int32_t width = 0, height = 0; - const char *mode; - char *connector_type_name, *mode_env_name = NULL, fmt_specifier[32] = ""; - const struct platsch_format *format = NULL; - - connector_type_name = get_normalized_conn_type_name(conn->connector_type); - if (!connector_type_name) { - error("could not look up name for connector type %u\n", - conn->connector_type); - goto fallback; - } - - ret = asprintf(&mode_env_name, "platsch_%s%u_mode", - connector_type_name, conn->connector_type_id); - free(connector_type_name); - if (ret < 0) { - error("failed to allocate platsch env mode variable\n"); - return -ENOMEM; - } - - /* check for connector mode configuration in environment */ - debug("looking up %s env variable\n", mode_env_name); - mode = getenv(mode_env_name); - if (!mode) - goto fallback; - - /* format suffix is optional */ - ret = sscanf(mode, "%ux%u@%s", &width, &height, fmt_specifier); - if (ret < 2) { - error("error while scanning %s for mode\n", mode_env_name); - ret = -EFAULT; - goto err_out; - } - - /* use first mode matching given resolution */ - for (i = 0; i < conn->count_modes; i++) { - drmModeModeInfo mode = conn->modes[i]; - if (mode.hdisplay == width && mode.vdisplay == height) { - memcpy(&dev->mode, &mode, sizeof(dev->mode)); - dev->width = width; - dev->height = height; - break; - } - } - - if (i == conn->count_modes) { - error("no mode available matching %ux%u\n", width, height); - ret = -ENOENT; - goto err_out; - } - - format = platsch_format_find(fmt_specifier); - if (!format) { - if (strlen(fmt_specifier)) - error("unknown format specifier %s\n", fmt_specifier); - goto fallback_format; - } - - dev->format = format; - - free(mode_env_name); - - return 0; - -fallback: - memcpy(&dev->mode, &conn->modes[0], sizeof(dev->mode)); - dev->width = conn->modes[0].hdisplay; - dev->height = conn->modes[0].vdisplay; - debug("using default mode for connector #%u\n", conn->connector_id); - -fallback_format: - dev->format = &platsch_formats[0]; - debug("using default format %s for connector #%u\n", dev->format->name, - conn->connector_id); - - ret = 0; - -err_out: - free(mode_env_name); - - return ret; -} - -static int drmprepare_connector(struct platsch_ctx *ctx, drmModeRes *res, - drmModeConnector *conn, struct modeset_dev *dev) -{ - int ret; - - /* check if a monitor is connected */ - if (conn->connection != DRM_MODE_CONNECTED) { - error("Ignoring unused connector #%u\n", conn->connector_id); - return -ENOENT; - } - - /* check if there is at least one valid mode */ - if (conn->count_modes == 0) { - error("no valid mode for connector #%u\n", conn->connector_id); - return -EFAULT; - } - - /* configure mode information in our device structure */ - ret = set_env_connector_mode(conn, dev); - if (ret) { - error("no valid mode for connector #%u\n", conn->connector_id); - return ret; - } - debug("mode for connector #%u is %ux%u@%s\n", - conn->connector_id, dev->width, dev->height, dev->format->name); - - /* find a crtc for this connector */ - ret = drmprepare_crtc(ctx, res, conn, dev); - if (ret) { - error("no valid crtc for connector #%u\n", conn->connector_id); - return ret; - } - - /* create a framebuffer for this CRTC */ - ret = modeset_create_fb(ctx->drmfd, dev); - if (ret) { - error("cannot create framebuffer for connector #%u\n", - conn->connector_id); - return ret; - } - - return 0; -} - -static int drmprepare(struct platsch_ctx *ctx) -{ - drmModeRes *res; - drmModeConnector *conn; - int i; - struct modeset_dev *dev; - bool root_node = true; - int ret; - - /* retrieve resources */ - res = drmModeGetResources(ctx->drmfd); - if (!res) { - error("cannot retrieve DRM resources: %m\n"); - return -errno; - } - - debug("Found %d connectors\n", res->count_connectors); - - /* iterate all connectors */ - for (i = 0; i < res->count_connectors; ++i) { - /* get information for each connector */ - conn = drmModeGetConnector(ctx->drmfd, res->connectors[i]); - if (!conn) { - error("Cannot retrieve DRM connector #%u: %m\n", - res->connectors[i]); - continue; - } - assert(conn->connector_id == res->connectors[i]); - - debug("Connector #%u has type %s\n", conn->connector_id, - drmModeGetConnectorTypeName(conn->connector_type)); - - /* create a device structure */ - dev = calloc(1, sizeof(*dev)); - if (!dev) { - error("Cannot allocate memory for connector #%u: %m\n", - res->connectors[i]); - continue; - } - dev->conn_id = conn->connector_id; - - ret = drmprepare_connector(ctx, res, conn, dev); - if (ret) { - if (ret != -ENOENT) { - error("Cannot setup device for connector #%u: %m\n", - res->connectors[i]); - } - free(dev); - drmModeFreeConnector(conn); - continue; - } - - /* free connector data and link device into global list */ - drmModeFreeConnector(conn); - dev->next = root_node ? NULL : ctx->modeset_list; - ctx->modeset_list = dev; - - root_node = false; - } - - /* free resources again */ - drmModeFreeResources(res); - return 0; -} - -static struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base) -{ - struct platsch_ctx *ctx; - int drmfd; - int ret; - int i; - - ctx = calloc(1, sizeof(*ctx)); - if (!ctx) { - error("Cannot allocate memory for platsch_ctx\n"); - return NULL; - } - - if (!dir) - dir = "/usr/share/platsch"; - ctx->dir = strdup(dir); - - if (!base) - base = "splash"; - ctx->base = strdup(base); - - for (i = 0; i < 64; i++) { - struct drm_mode_card_res res = {0}; - char *drmdev; - - /* - * XXX: Maybe use drmOpen instead? - * (Where should name/busid come from?) - * XXX: Loop through drm devices to find one with connectors. - */ - ret = asprintf(&drmdev, DRM_DEV_NAME, DRM_DIR_NAME, i); - if (ret < 0) { - error("Huh, failed to allocate device name buffer\n"); - goto err_out; - } - - drmfd = open(drmdev, O_RDWR | O_CLOEXEC, 0); - free(drmdev); - if (drmfd < 0) { - error("Failed to open drm device: %m\n"); - goto err_out; - } - - ret = drmIoctl(drmfd, DRM_IOCTL_MODE_GETRESOURCES, &res); - if (ret < 0) { - close(drmfd); - continue; - } else { - /* Device found */ - ctx->drmfd = drmfd; - break; - } - } - - ret = drmprepare(ctx); - if (ret) - goto err_out; - - return ctx; - -err_out: - free(ctx->dir); - free(ctx->base); - free(ctx); - - return NULL; -} - -static void platsch_draw(struct platsch_ctx *ctx) -{ - struct modeset_dev *iter; - int ret; - - for (iter = ctx->modeset_list; iter; iter = iter->next) { - - /* draw first then set the mode */ - draw_buffer(iter, ctx->dir, ctx->base); - - if (iter->setmode) { - debug("set crtc\n"); - - ret = drmModeSetCrtc(ctx->drmfd, iter->crtc_id, iter->fb_id, - 0, 0, &iter->conn_id, 1, &iter->mode); - if (ret) - error("Cannot set CRTC for connector #%u: %m\n", - iter->conn_id); - } else { - debug("page flip\n"); - ret = drmModePageFlip(ctx->drmfd, iter->crtc_id, iter->fb_id, - 0, NULL); - if (ret) - error("Page flip failed on connector #%u: %m\n", - iter->conn_id); - } - } -} - -static void platsch_destroy_ctx(struct platsch_ctx *ctx) -{ - struct modeset_dev *mode, *next; - int ret; - - ret = drmDropMaster(ctx->drmfd); - if (ret) - error("Failed to drop master on drm device\n"); - - for (mode = ctx->modeset_list; mode;) { - next = mode->next; - free(mode); - mode = next; - } - free(ctx->dir); - free(ctx->base); - free(ctx); -} - static struct option longopts[] = { { "help", no_argument, 0, 'h' }, From c58b35f03c9cc0deeec9115dd7c0e7c3cfa84222 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 1 Aug 2024 17:27:24 +0200 Subject: [PATCH 14/21] build: build libplatsch as dedicated library This adds the support for building platsch and libplatsch. Platsch uses the static library to not cause any performance regression. By this commit the conversion from a standalone application into an application and library is complete. Signed-off-by: Marco Felsch --- meson.build | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 54122f4..2e33ddb 100644 --- a/meson.build +++ b/meson.build @@ -10,10 +10,29 @@ project( libdrm_dep = dependency('libdrm', version : '>=2.4.112') +install_headers('libplatsch.h') + +platsch_lib = both_libraries( + 'platsch', + version : '0.1', + sources : ['libplatsch.c'], + dependencies : [libdrm_dep], + install : true +) + +pkg_mod = import('pkgconfig') +pkg_mod.generate( + name : 'libplatsch', + version : '0.1', + filebase : 'libplatsch', + description : 'A Library to build custom bootsplash applications.', + libraries : platsch_lib +) + executable( 'platsch', - sources : ['platsch.c', 'libplatsch.c'], - dependencies : [libdrm_dep], + sources : ['platsch.c'], + link_with : platsch_lib.get_static_lib(), install : true, install_dir : get_option('sbindir'), ) From f0f0c4196a9c7dd514f9d623746d52710e5bbe77 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 7 Aug 2024 09:22:47 +0200 Subject: [PATCH 15/21] libplatsch: platsch_destroy_ctx: drop master only if we are the master Add drmIsMaster() before dropping the master to not cause confusion due to the error message print. Signed-off-by: Marco Felsch --- libplatsch.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libplatsch.c b/libplatsch.c index 745b2e4..1e68e08 100644 --- a/libplatsch.c +++ b/libplatsch.c @@ -651,9 +651,11 @@ void platsch_destroy_ctx(struct platsch_ctx *ctx) struct modeset_dev *mode, *next; int ret; - ret = drmDropMaster(ctx->drmfd); - if (ret) - error("Failed to drop master on drm device\n"); + if (drmIsMaster(ctx->drmfd)) { + ret = drmDropMaster(ctx->drmfd); + if (ret) + error("Failed to drop master on drm device\n"); + } for (mode = ctx->modeset_list; mode;) { next = mode->next; From 346fa3ce874dde8fe774830011d3eb636cc1aa28 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Mon, 5 Aug 2024 11:16:29 +0200 Subject: [PATCH 16/21] libplatsch: add platsch_alloc_ctx/platsch_init_ctx The platsch_create_ctx() allocates and init the ctx in one call but sometimes applications need to add custom data in between both steps. Therefore add platsch_alloc_ctx and platsch_init_ctx. Signed-off-by: Marco Felsch --- libplatsch.c | 27 +++++++++++++++++++++++---- libplatsch.h | 3 +++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/libplatsch.c b/libplatsch.c index 1e68e08..e0c94e1 100644 --- a/libplatsch.c +++ b/libplatsch.c @@ -579,6 +579,24 @@ void platsch_draw(struct platsch_ctx *ctx) } struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base) +{ + struct platsch_ctx *ctx; + int ret; + + ctx = platsch_alloc_ctx(dir, base); + if (!ctx) + return NULL; + + ret = platsch_init_ctx(ctx); + if (ret) { + platsch_destroy_ctx(ctx); + return NULL; + } + + return ctx; +} + +struct platsch_ctx *platsch_alloc_ctx(const char *dir, const char *base) { struct platsch_ctx *ctx; int drmfd; @@ -632,10 +650,6 @@ struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base) } } - ret = drmprepare(ctx); - if (ret) - goto err_out; - return ctx; err_out: @@ -646,6 +660,11 @@ struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base) return NULL; } +int platsch_init_ctx(struct platsch_ctx *ctx) +{ + return drmprepare(ctx); +} + void platsch_destroy_ctx(struct platsch_ctx *ctx) { struct modeset_dev *mode, *next; diff --git a/libplatsch.h b/libplatsch.h index 00aacd2..6c371e2 100644 --- a/libplatsch.h +++ b/libplatsch.h @@ -5,6 +5,9 @@ struct platsch_ctx; void platsch_draw(struct platsch_ctx *ctx); struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base); +struct platsch_ctx *platsch_alloc_ctx(const char *dir, const char *base); +int platsch_init_ctx(struct platsch_ctx *ctx); + void platsch_destroy_ctx(struct platsch_ctx *ctx); #endif /* __LIBPLATSCH_H__ */ From b1d182aaf5d86cdebc152a673d3034fdd7bd6380 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Mon, 5 Aug 2024 11:24:26 +0200 Subject: [PATCH 17/21] libplatsch: add support for custom draw operations This adds the support to register a custom draw operation callback which will be called during platsch_draw(). The callback has all the necessary information about the framebuffer and a reference to the framebuffer itself to update content. The information will be freed after the callback has finished, therfore the content needs to be copied by the application. Signed-off-by: Marco Felsch --- libplatsch.c | 33 ++++++++++++++++++++++++++++++++- libplatsch.h | 17 +++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/libplatsch.c b/libplatsch.c index e0c94e1..9bf1426 100644 --- a/libplatsch.c +++ b/libplatsch.c @@ -81,6 +81,8 @@ struct platsch_ctx { int drmfd; char *dir; char *base; + custom_draw_cb custom_draw_buffer_cb; + void *custom_draw_priv; }; static ssize_t readfull(int fd, void *buf, size_t count) @@ -148,6 +150,22 @@ static void draw_buffer(struct modeset_dev *dev, const char *dir, const char *ba return; } +static void platsch_custom_draw_buffer(struct platsch_ctx *ctx, + struct modeset_dev *mode) +{ + struct platsch_draw_buf buf = { + .width = mode->width, + .height = mode->height, + .stride = mode->stride, + .size = mode->size, + .format = mode->format->format, + .fb_id = mode->fb_id, + .fb = mode->map, + }; + + ctx->custom_draw_buffer_cb(&buf, ctx->custom_draw_priv); +} + static int drmprepare_crtc(struct platsch_ctx *ctx, drmModeRes *res, drmModeConnector *conn, struct modeset_dev *dev) { @@ -557,7 +575,10 @@ void platsch_draw(struct platsch_ctx *ctx) for (iter = ctx->modeset_list; iter; iter = iter->next) { /* draw first then set the mode */ - draw_buffer(iter, ctx->dir, ctx->base); + if (ctx->custom_draw_buffer_cb) + platsch_custom_draw_buffer(ctx, iter); + else + draw_buffer(iter, ctx->dir, ctx->base); if (iter->setmode) { debug("set crtc\n"); @@ -578,6 +599,16 @@ void platsch_draw(struct platsch_ctx *ctx) } } +void platsch_register_custom_draw_cb(struct platsch_ctx *ctx, custom_draw_cb cb, + void *priv) +{ + if (!ctx) + return; + + ctx->custom_draw_buffer_cb = cb; + ctx->custom_draw_priv = priv; +} + struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base) { struct platsch_ctx *ctx; diff --git a/libplatsch.h b/libplatsch.h index 6c371e2..b25d08f 100644 --- a/libplatsch.h +++ b/libplatsch.h @@ -1,9 +1,26 @@ #ifndef __LIBPLATSCH_H__ #define __LIBPLATSCH_H__ +#include + struct platsch_ctx; +struct platsch_draw_buf { + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t size; + uint32_t format; + uint32_t fb_id; + void *fb; +}; + +typedef void (*custom_draw_cb)(struct platsch_draw_buf *buf, void *priv); + void platsch_draw(struct platsch_ctx *ctx); +void platsch_register_custom_draw_cb(struct platsch_ctx *ctx, + custom_draw_cb cb, void *priv); + struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base); struct platsch_ctx *platsch_alloc_ctx(const char *dir, const char *base); int platsch_init_ctx(struct platsch_ctx *ctx); From 8b4abe627aece53fe2d38e5f096d8e3ffd979830 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Mon, 5 Aug 2024 11:29:43 +0200 Subject: [PATCH 18/21] libplatsch: disable setmode once set by platsch_draw Disable the setmode flag once it was set succefully since all further platsch_draw() calls only need an page flip operation. Signed-off-by: Marco Felsch --- libplatsch.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libplatsch.c b/libplatsch.c index 9bf1426..438257b 100644 --- a/libplatsch.c +++ b/libplatsch.c @@ -588,6 +588,8 @@ void platsch_draw(struct platsch_ctx *ctx) if (ret) error("Cannot set CRTC for connector #%u: %m\n", iter->conn_id); + else + iter->setmode = 0; } else { debug("page flip\n"); ret = drmModePageFlip(ctx->drmfd, iter->crtc_id, iter->fb_id, From c3350a9d21938ac67f9edf489e6d05f4cb36ba0c Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Mon, 5 Aug 2024 11:37:47 +0200 Subject: [PATCH 19/21] libplatsch: align draw_buffer with platsch_custom_draw_buffer Rename the function to platsch_draw_buffer and align the function params to make it identical to the platsch_custom_draw_buffer function. Signed-off-by: Marco Felsch --- libplatsch.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libplatsch.c b/libplatsch.c index 438257b..6424cd9 100644 --- a/libplatsch.c +++ b/libplatsch.c @@ -105,8 +105,10 @@ static ssize_t readfull(int fd, void *buf, size_t count) return ret; } -static void draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) +static void platsch_draw_buffer(struct platsch_ctx *ctx, struct modeset_dev *dev) { + const char *base = ctx->base; + const char *dir = ctx->dir; int fd_src; char *filename; ssize_t size; @@ -146,8 +148,6 @@ static void draw_buffer(struct modeset_dev *dev, const char *dir, const char *ba out: free(filename); - - return; } static void platsch_custom_draw_buffer(struct platsch_ctx *ctx, @@ -578,7 +578,7 @@ void platsch_draw(struct platsch_ctx *ctx) if (ctx->custom_draw_buffer_cb) platsch_custom_draw_buffer(ctx, iter); else - draw_buffer(iter, ctx->dir, ctx->base); + platsch_draw_buffer(ctx, iter); if (iter->setmode) { debug("set crtc\n"); From a5c7b1d0d055b83644748618b6c44a137679c2b3 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Mon, 5 Aug 2024 11:43:52 +0200 Subject: [PATCH 20/21] libplatsch: pass platsch_ctx to modeset_create_fb Pass the whole contect to the helper function to align it with the code base. No functional change. Signed-off-by: Marco Felsch --- libplatsch.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libplatsch.c b/libplatsch.c index 6424cd9..357cdc0 100644 --- a/libplatsch.c +++ b/libplatsch.c @@ -266,11 +266,12 @@ static int drmprepare_crtc(struct platsch_ctx *ctx, drmModeRes *res, return -ENOENT; } -static int modeset_create_fb(int fd, struct modeset_dev *dev) +static int modeset_create_fb(struct platsch_ctx *ctx, struct modeset_dev *dev) { struct drm_mode_create_dumb creq; struct drm_mode_destroy_dumb dreq; struct drm_mode_map_dumb mreq; + int fd = ctx->drmfd; int ret; /* create dumb buffer */ @@ -490,7 +491,7 @@ static int drmprepare_connector(struct platsch_ctx *ctx, drmModeRes *res, } /* create a framebuffer for this CRTC */ - ret = modeset_create_fb(ctx->drmfd, dev); + ret = modeset_create_fb(ctx, dev); if (ret) { error("cannot create framebuffer for connector #%u\n", conn->connector_id); From c1b108b8349b640f35c4a31b34b8c9e771c1b7e8 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 7 Aug 2024 09:00:03 +0200 Subject: [PATCH 21/21] libplatsch: add proper API export macro At the moment we rely on the 'default' visibility which was okay since all global functions are API functions. This is error prone as one could expose symbols by accident. Therefore change the behavior by setting the default visibility to 'hidden' and introducing a new macro to export selected API functions. Signed-off-by: Marco Felsch --- libplatsch.h | 20 +++++++++++++------- meson.build | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/libplatsch.h b/libplatsch.h index b25d08f..f65500a 100644 --- a/libplatsch.h +++ b/libplatsch.h @@ -3,6 +3,12 @@ #include +#if __GNUC__ >= 4 +# define LIBPLATSCH_API __attribute__((visibility ("default"))) +#else +# define LIBPLATSCH_API +#endif + struct platsch_ctx; struct platsch_draw_buf { @@ -17,14 +23,14 @@ struct platsch_draw_buf { typedef void (*custom_draw_cb)(struct platsch_draw_buf *buf, void *priv); -void platsch_draw(struct platsch_ctx *ctx); -void platsch_register_custom_draw_cb(struct platsch_ctx *ctx, - custom_draw_cb cb, void *priv); +LIBPLATSCH_API void platsch_draw(struct platsch_ctx *ctx); +LIBPLATSCH_API void platsch_register_custom_draw_cb(struct platsch_ctx *ctx, + custom_draw_cb cb, void *priv); -struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base); -struct platsch_ctx *platsch_alloc_ctx(const char *dir, const char *base); -int platsch_init_ctx(struct platsch_ctx *ctx); +LIBPLATSCH_API struct platsch_ctx *platsch_create_ctx(const char *dir, const char *base); +LIBPLATSCH_API struct platsch_ctx *platsch_alloc_ctx(const char *dir, const char *base); +LIBPLATSCH_API int platsch_init_ctx(struct platsch_ctx *ctx); -void platsch_destroy_ctx(struct platsch_ctx *ctx); +LIBPLATSCH_API void platsch_destroy_ctx(struct platsch_ctx *ctx); #endif /* __LIBPLATSCH_H__ */ diff --git a/meson.build b/meson.build index 2e33ddb..08b2b43 100644 --- a/meson.build +++ b/meson.build @@ -16,6 +16,7 @@ platsch_lib = both_libraries( 'platsch', version : '0.1', sources : ['libplatsch.c'], + gnu_symbol_visibility : 'hidden', dependencies : [libdrm_dep], install : true )