mirror of
https://github.com/LibreELEC/LibreELEC.tv
synced 2025-09-24 19:46:01 +07:00
282 lines
8.2 KiB
Diff
282 lines
8.2 KiB
Diff
From 15c9ea76e000b860749d9c27e0d374cd0bcbe40a Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= <jefflessard3@gmail.com>
|
|
Date: Sat, 30 Aug 2025 20:41:39 -0400
|
|
Subject: [PATCH 43/54] TEST: auxdisplay: linedisp: support attribute
|
|
attachment to auxdisplay devices
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Enable linedisp library integration into existing kernel devices (like LED
|
|
class) to provide a uniform 7-segment userspace API without creating
|
|
separate child devices, meeting the consistent interface while maintaining
|
|
coherent device hierarchies.
|
|
|
|
This allows uniform 7-segment API across all drivers while solving device
|
|
proliferation and fragmented userspace interfaces.
|
|
|
|
The provided attributes appear in two locations depending on usage:
|
|
1. On linedisp.N child devices (legacy linedisp_register())
|
|
2. On the parent auxdisplay device (new linedisp_attach())
|
|
Functionality is identical in both modes.
|
|
|
|
Existing consumers of linedisp_register() are unaffected. The new API
|
|
enables drivers like TM16XX to integrate 7-segment display functionality
|
|
seamlessly within their LED class device hierarchy.
|
|
|
|
Signed-off-by: Jean-François Lessard <jefflessard3@gmail.com>
|
|
---
|
|
drivers/auxdisplay/line-display.c | 160 +++++++++++++++++++++++++++++-
|
|
drivers/auxdisplay/line-display.h | 4 +
|
|
2 files changed, 161 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c
|
|
index abeed8812088..1176d46f0b24 100644
|
|
--- a/drivers/auxdisplay/line-display.c
|
|
+++ b/drivers/auxdisplay/line-display.c
|
|
@@ -6,20 +6,23 @@
|
|
* Author: Paul Burton <paul.burton@mips.com>
|
|
*
|
|
* Copyright (C) 2021 Glider bv
|
|
+ * Copyright (C) 2025 Jean-François Lessard
|
|
*/
|
|
|
|
#ifndef CONFIG_PANEL_BOOT_MESSAGE
|
|
#include <generated/utsrelease.h>
|
|
#endif
|
|
|
|
-#include <linux/container_of.h>
|
|
+#include <linux/cleanup.h>
|
|
#include <linux/device.h>
|
|
#include <linux/export.h>
|
|
#include <linux/idr.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/kstrtox.h>
|
|
+#include <linux/list.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
+#include <linux/spinlock.h>
|
|
#include <linux/string.h>
|
|
#include <linux/sysfs.h>
|
|
#include <linux/timer.h>
|
|
@@ -31,9 +34,72 @@
|
|
|
|
#define DEFAULT_SCROLL_RATE (HZ / 2)
|
|
|
|
+struct linedisp_attachment {
|
|
+ struct list_head list;
|
|
+ struct device *device;
|
|
+ struct linedisp *linedisp;
|
|
+ bool owns_device; /* true for child device mode, false for attached mode */
|
|
+};
|
|
+
|
|
+static LIST_HEAD(linedisp_attachments);
|
|
+static DEFINE_SPINLOCK(linedisp_attachments_lock);
|
|
+
|
|
+static int create_attachment(struct device *dev, struct linedisp *linedisp, bool owns_device)
|
|
+{
|
|
+ struct linedisp_attachment *attachment;
|
|
+
|
|
+ attachment = kzalloc(sizeof(*attachment), GFP_KERNEL);
|
|
+ if (!attachment)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ attachment->device = dev;
|
|
+ attachment->linedisp = linedisp;
|
|
+ attachment->owns_device = owns_device;
|
|
+
|
|
+ guard(spinlock)(&linedisp_attachments_lock);
|
|
+ list_add(&attachment->list, &linedisp_attachments);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct linedisp *delete_attachment(struct device *dev, bool owns_device)
|
|
+{
|
|
+ struct linedisp_attachment *attachment;
|
|
+ struct linedisp *linedisp;
|
|
+
|
|
+ guard(spinlock)(&linedisp_attachments_lock);
|
|
+
|
|
+ list_for_each_entry(attachment, &linedisp_attachments, list) {
|
|
+ if (attachment->device == dev &&
|
|
+ attachment->owns_device == owns_device)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (list_entry_is_head(attachment, &linedisp_attachments, list))
|
|
+ return NULL;
|
|
+
|
|
+ linedisp = attachment->linedisp;
|
|
+ list_del(&attachment->list);
|
|
+ kfree(attachment);
|
|
+
|
|
+ return linedisp;
|
|
+}
|
|
+
|
|
static struct linedisp *to_linedisp(struct device *dev)
|
|
{
|
|
- return container_of(dev, struct linedisp, dev);
|
|
+ struct linedisp_attachment *attachment;
|
|
+
|
|
+ guard(spinlock)(&linedisp_attachments_lock);
|
|
+
|
|
+ list_for_each_entry(attachment, &linedisp_attachments, list) {
|
|
+ if (attachment->device == dev)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (list_entry_is_head(attachment, &linedisp_attachments, list))
|
|
+ return NULL;
|
|
+
|
|
+ return attachment->linedisp;
|
|
}
|
|
|
|
static inline bool should_scroll(struct linedisp *linedisp)
|
|
@@ -349,6 +415,87 @@ static int linedisp_init_map(struct linedisp *linedisp)
|
|
#define LINEDISP_INIT_TEXT "Linux " UTS_RELEASE " "
|
|
#endif
|
|
|
|
+/**
|
|
+ * linedisp_attach - attach a character line display
|
|
+ * @linedisp: pointer to character line display structure
|
|
+ * @dev: pointer of the device to attach to
|
|
+ * @num_chars: the number of characters that can be displayed
|
|
+ * @ops: character line display operations
|
|
+ *
|
|
+ * Return: zero on success, else a negative error code.
|
|
+ */
|
|
+int linedisp_attach(struct linedisp *linedisp, struct device *dev,
|
|
+ unsigned int num_chars, const struct linedisp_ops *ops)
|
|
+{
|
|
+ int err;
|
|
+
|
|
+ memset(linedisp, 0, sizeof(*linedisp));
|
|
+ linedisp->ops = ops;
|
|
+ linedisp->num_chars = num_chars;
|
|
+ linedisp->scroll_rate = DEFAULT_SCROLL_RATE;
|
|
+
|
|
+ linedisp->buf = kzalloc(linedisp->num_chars, GFP_KERNEL);
|
|
+ if (!linedisp->buf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* initialise a character mapping, if required */
|
|
+ err = linedisp_init_map(linedisp);
|
|
+ if (err)
|
|
+ goto out_free_buf;
|
|
+
|
|
+ /* initialise a timer for scrolling the message */
|
|
+ timer_setup(&linedisp->timer, linedisp_scroll, 0);
|
|
+
|
|
+ err = create_attachment(dev, linedisp, false);
|
|
+ if (err)
|
|
+ goto out_del_timer;
|
|
+
|
|
+ /* add attribute groups to target device */
|
|
+ err = device_add_groups(dev, linedisp_groups);
|
|
+ if (err)
|
|
+ goto out_del_attach;
|
|
+
|
|
+ /* display a default message */
|
|
+ err = linedisp_display(linedisp, LINEDISP_INIT_TEXT, -1);
|
|
+ if (err)
|
|
+ goto out_rem_groups;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_rem_groups:
|
|
+ device_remove_groups(dev, linedisp_groups);
|
|
+out_del_attach:
|
|
+ delete_attachment(dev, false);
|
|
+out_del_timer:
|
|
+ timer_delete_sync(&linedisp->timer);
|
|
+out_free_buf:
|
|
+ kfree(linedisp->buf);
|
|
+ return err;
|
|
+}
|
|
+EXPORT_SYMBOL_NS_GPL(linedisp_attach, "LINEDISP");
|
|
+
|
|
+/**
|
|
+ * linedisp_detach - detach a character line display
|
|
+ * @dev: pointer of the device to detach from, that was previously
|
|
+ * attached with linedisp_attach()
|
|
+ */
|
|
+void linedisp_detach(struct device *dev)
|
|
+{
|
|
+ struct linedisp *linedisp = delete_attachment(dev, false);
|
|
+
|
|
+ if (!linedisp)
|
|
+ return;
|
|
+
|
|
+ timer_delete_sync(&linedisp->timer);
|
|
+
|
|
+ device_remove_groups(dev, linedisp_groups);
|
|
+
|
|
+ kfree(linedisp->map);
|
|
+ kfree(linedisp->message);
|
|
+ kfree(linedisp->buf);
|
|
+}
|
|
+EXPORT_SYMBOL_NS_GPL(linedisp_detach, "LINEDISP");
|
|
+
|
|
/**
|
|
* linedisp_register - register a character line display
|
|
* @linedisp: pointer to character line display structure
|
|
@@ -391,10 +538,14 @@ int linedisp_register(struct linedisp *linedisp, struct device *parent,
|
|
/* initialise a timer for scrolling the message */
|
|
timer_setup(&linedisp->timer, linedisp_scroll, 0);
|
|
|
|
- err = device_add(&linedisp->dev);
|
|
+ err = create_attachment(&linedisp->dev, linedisp, true);
|
|
if (err)
|
|
goto out_del_timer;
|
|
|
|
+ err = device_add(&linedisp->dev);
|
|
+ if (err)
|
|
+ goto out_del_attach;
|
|
+
|
|
/* display a default message */
|
|
err = linedisp_display(linedisp, LINEDISP_INIT_TEXT, -1);
|
|
if (err)
|
|
@@ -404,6 +555,8 @@ int linedisp_register(struct linedisp *linedisp, struct device *parent,
|
|
|
|
out_del_dev:
|
|
device_del(&linedisp->dev);
|
|
+out_del_attach:
|
|
+ delete_attachment(&linedisp->dev, true);
|
|
out_del_timer:
|
|
timer_delete_sync(&linedisp->timer);
|
|
out_put_device:
|
|
@@ -420,6 +573,7 @@ EXPORT_SYMBOL_NS_GPL(linedisp_register, "LINEDISP");
|
|
void linedisp_unregister(struct linedisp *linedisp)
|
|
{
|
|
device_del(&linedisp->dev);
|
|
+ delete_attachment(&linedisp->dev, true);
|
|
timer_delete_sync(&linedisp->timer);
|
|
put_device(&linedisp->dev);
|
|
}
|
|
diff --git a/drivers/auxdisplay/line-display.h b/drivers/auxdisplay/line-display.h
|
|
index 4348d7a2f69a..36853b639711 100644
|
|
--- a/drivers/auxdisplay/line-display.h
|
|
+++ b/drivers/auxdisplay/line-display.h
|
|
@@ -6,6 +6,7 @@
|
|
* Author: Paul Burton <paul.burton@mips.com>
|
|
*
|
|
* Copyright (C) 2021 Glider bv
|
|
+ * Copyright (C) 2025 Jean-François Lessard
|
|
*/
|
|
|
|
#ifndef _LINEDISP_H
|
|
@@ -81,6 +82,9 @@ struct linedisp {
|
|
unsigned int id;
|
|
};
|
|
|
|
+int linedisp_attach(struct linedisp *linedisp, struct device *dev,
|
|
+ unsigned int num_chars, const struct linedisp_ops *ops);
|
|
+void linedisp_detach(struct device *dev);
|
|
int linedisp_register(struct linedisp *linedisp, struct device *parent,
|
|
unsigned int num_chars, const struct linedisp_ops *ops);
|
|
void linedisp_unregister(struct linedisp *linedisp);
|
|
--
|
|
2.34.1
|
|
|