[Concept,32/36] video: Maintain a list of contexts in vidconsole

Message ID 20260120231814.2033069-33-sjg@u-boot.org
State New
Headers
Series video: Add multiple-context support to vidconsole (part F) |

Commit Message

Simon Glass Jan. 20, 2026, 11:17 p.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Add an alist to track all client-allocated contexts in the vidconsole.
This ensures that contexts can be properly cleaned up when the device
is removed.

Update vidconsole_ctx_new() to add newly created contexts to the list
and vidconsole_ctx_dispose() to remove them. Init the list in
vidconsole_pre_probe() and dispose of any remaining contexts in
vidconsole_pre_remove()

Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 drivers/video/vidconsole-uclass.c | 65 ++++++++++++++++++++++++-------
 include/video_console.h           |  2 +
 2 files changed, 53 insertions(+), 14 deletions(-)
  

Patch

diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c
index 83872b54b5d..4433920ecb6 100644
--- a/drivers/video/vidconsole-uclass.c
+++ b/drivers/video/vidconsole-uclass.c
@@ -712,32 +712,64 @@  int vidconsole_nominal(struct udevice *dev, const char *name, uint size,
 
 int vidconsole_ctx_new(struct udevice *dev, void **ctxp)
 {
+	struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 	struct vidconsole_ops *ops = vidconsole_get_ops(dev);
-	void *ctx;
+	void **ptr;
 	int ret;
 
 	if (!ops->ctx_new)
 		return -ENOSYS;
 
-	ret = ops->ctx_new(dev, &ctx);
-	if (ret)
+	/* reserve space first so ctx_new failure doesn't need cleanup */
+	ptr = alist_add_placeholder(&priv->ctx_list);
+	if (!ptr)
+		return -ENOMEM;
+
+	ret = ops->ctx_new(dev, ptr);
+	if (ret) {
+		priv->ctx_list.count--;
 		return ret;
-	*ctxp = ctx;
+	}
+	*ctxp = *ptr;
 
 	return 0;
 }
 
-int vidconsole_ctx_dispose(struct udevice *dev, void *ctx)
+/**
+ * vidconsole_free_ctx() - Free a context without removing it from the list
+ *
+ * This calls the driver's ctx_dispose method and frees the save_data, but
+ * does not remove the context from the alist.
+ *
+ * @dev: Vidconsole device
+ * @ctx: Context to free
+ */
+static void vidconsole_free_ctx(struct udevice *dev, struct vidconsole_ctx *ctx)
 {
 	struct vidconsole_ops *ops = vidconsole_get_ops(dev);
-	int ret;
 
-	if (!ops->ctx_dispose)
-		return -ENOSYS;
+	if (ops->ctx_dispose)
+		ops->ctx_dispose(dev, ctx);
+	free(ctx->curs.save_data);
+}
 
-	ret = ops->ctx_dispose(dev, ctx);
-	if (ret)
-		return ret;
+int vidconsole_ctx_dispose(struct udevice *dev, void *vctx)
+{
+	struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+	struct vidconsole_ctx *ctx = vctx;
+	void **ptr, **from;
+
+	if (!ctx)
+		return 0;
+
+	/* remove the context from the list */
+	alist_for_each_filter(ptr, from, &priv->ctx_list) {
+		if (*ptr != ctx)
+			*from++ = *ptr;
+	}
+	alist_update_end(&priv->ctx_list, from);
+
+	vidconsole_free_ctx(dev, ctx);
 
 	return 0;
 }
@@ -903,6 +935,9 @@  static int vidconsole_pre_probe(struct udevice *dev)
 
 	ctx->xsize_frac = VID_TO_POS(vid_priv->xsize);
 
+	alist_init_struct(&priv->ctx_list, void *);
+	alist_add(&priv->ctx_list, ctx);
+
 	return 0;
 }
 
@@ -934,10 +969,12 @@  static int vidconsole_post_probe(struct udevice *dev)
 static int vidconsole_pre_remove(struct udevice *dev)
 {
 	struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
-	struct vidconsole_ctx *ctx = priv->ctx;
+	void **ptr;
 
-	free(ctx->curs.save_data);
-	free(ctx);
+	/* free all contexts in the list, including the default ctx */
+	alist_for_each(ptr, &priv->ctx_list)
+		vidconsole_free_ctx(dev, *ptr);
+	alist_uninit(&priv->ctx_list);
 	priv->ctx = NULL;
 
 	return 0;
diff --git a/include/video_console.h b/include/video_console.h
index 6d635fd5971..8d2f0510534 100644
--- a/include/video_console.h
+++ b/include/video_console.h
@@ -166,11 +166,13 @@  struct vidconsole_uc_plat {
  *
  * @sdev:		stdio device, acting as an output sink
  * @ctx:		Per-client context (allocated by the uclass)
+ * @ctx_list:		List of additional contexts allocated by clients
  * @quiet:		Suppress all output from stdio
  */
 struct vidconsole_priv {
 	struct stdio_dev sdev;
 	struct vidconsole_ctx *ctx;
+	struct alist ctx_list;
 	bool quiet;
 };