f69eefacf7
Changes:
- Rebase onto v5.13.13
Links:
- kernel: e6d532e204
2325 lines
83 KiB
Diff
2325 lines
83 KiB
Diff
From e86e95ef7660bc45a6dce58d56d1024158649ab9 Mon Sep 17 00:00:00 2001
|
|
From: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Date: Sun, 23 May 2021 14:09:42 +0200
|
|
Subject: [PATCH] platform/surface: aggregator_registry: Consolidate node
|
|
groups for 5th- and 6th-gen devices
|
|
|
|
5th- and 6th-generation Surface devices have all SAM clients defined in
|
|
ACPI, except for the platform profile/performance mode which his handled
|
|
via the WSID (Windows Surface Integration Device). Thus, the node groups
|
|
for those devices are the same and we can just use a single one instead
|
|
of re-defining the same one over and over again.
|
|
|
|
Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Patchset: surface-sam
|
|
---
|
|
.../surface/surface_aggregator_registry.c | 47 +++++--------------
|
|
1 file changed, 12 insertions(+), 35 deletions(-)
|
|
|
|
diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
|
|
index ef83461fa536..4428c4330229 100644
|
|
--- a/drivers/platform/surface/surface_aggregator_registry.c
|
|
+++ b/drivers/platform/surface/surface_aggregator_registry.c
|
|
@@ -119,8 +119,13 @@ static const struct software_node ssam_node_hid_base_iid6 = {
|
|
.parent = &ssam_node_hub_base,
|
|
};
|
|
|
|
-/* Devices for Surface Book 2. */
|
|
-static const struct software_node *ssam_node_group_sb2[] = {
|
|
+/*
|
|
+ * Devices for 5th- and 6th-generations models:
|
|
+ * - Surface Book 2,
|
|
+ * - Surface Laptop 1 and 2,
|
|
+ * - Surface Pro 5 and 6.
|
|
+ */
|
|
+static const struct software_node *ssam_node_group_gen5[] = {
|
|
&ssam_node_root,
|
|
&ssam_node_tmp_pprof,
|
|
NULL,
|
|
@@ -142,20 +147,6 @@ static const struct software_node *ssam_node_group_sb3[] = {
|
|
NULL,
|
|
};
|
|
|
|
-/* Devices for Surface Laptop 1. */
|
|
-static const struct software_node *ssam_node_group_sl1[] = {
|
|
- &ssam_node_root,
|
|
- &ssam_node_tmp_pprof,
|
|
- NULL,
|
|
-};
|
|
-
|
|
-/* Devices for Surface Laptop 2. */
|
|
-static const struct software_node *ssam_node_group_sl2[] = {
|
|
- &ssam_node_root,
|
|
- &ssam_node_tmp_pprof,
|
|
- NULL,
|
|
-};
|
|
-
|
|
/* Devices for Surface Laptop 3 and 4. */
|
|
static const struct software_node *ssam_node_group_sl3[] = {
|
|
&ssam_node_root,
|
|
@@ -177,20 +168,6 @@ static const struct software_node *ssam_node_group_slg1[] = {
|
|
NULL,
|
|
};
|
|
|
|
-/* Devices for Surface Pro 5. */
|
|
-static const struct software_node *ssam_node_group_sp5[] = {
|
|
- &ssam_node_root,
|
|
- &ssam_node_tmp_pprof,
|
|
- NULL,
|
|
-};
|
|
-
|
|
-/* Devices for Surface Pro 6. */
|
|
-static const struct software_node *ssam_node_group_sp6[] = {
|
|
- &ssam_node_root,
|
|
- &ssam_node_tmp_pprof,
|
|
- NULL,
|
|
-};
|
|
-
|
|
/* Devices for Surface Pro 7 and Surface Pro 7+. */
|
|
static const struct software_node *ssam_node_group_sp7[] = {
|
|
&ssam_node_root,
|
|
@@ -495,10 +472,10 @@ static struct ssam_device_driver ssam_base_hub_driver = {
|
|
|
|
static const struct acpi_device_id ssam_platform_hub_match[] = {
|
|
/* Surface Pro 4, 5, and 6 (OMBR < 0x10) */
|
|
- { "MSHW0081", (unsigned long)ssam_node_group_sp5 },
|
|
+ { "MSHW0081", (unsigned long)ssam_node_group_gen5 },
|
|
|
|
/* Surface Pro 6 (OMBR >= 0x10) */
|
|
- { "MSHW0111", (unsigned long)ssam_node_group_sp6 },
|
|
+ { "MSHW0111", (unsigned long)ssam_node_group_gen5 },
|
|
|
|
/* Surface Pro 7 */
|
|
{ "MSHW0116", (unsigned long)ssam_node_group_sp7 },
|
|
@@ -507,16 +484,16 @@ static const struct acpi_device_id ssam_platform_hub_match[] = {
|
|
{ "MSHW0119", (unsigned long)ssam_node_group_sp7 },
|
|
|
|
/* Surface Book 2 */
|
|
- { "MSHW0107", (unsigned long)ssam_node_group_sb2 },
|
|
+ { "MSHW0107", (unsigned long)ssam_node_group_gen5 },
|
|
|
|
/* Surface Book 3 */
|
|
{ "MSHW0117", (unsigned long)ssam_node_group_sb3 },
|
|
|
|
/* Surface Laptop 1 */
|
|
- { "MSHW0086", (unsigned long)ssam_node_group_sl1 },
|
|
+ { "MSHW0086", (unsigned long)ssam_node_group_gen5 },
|
|
|
|
/* Surface Laptop 2 */
|
|
- { "MSHW0112", (unsigned long)ssam_node_group_sl2 },
|
|
+ { "MSHW0112", (unsigned long)ssam_node_group_gen5 },
|
|
|
|
/* Surface Laptop 3 (13", Intel) */
|
|
{ "MSHW0114", (unsigned long)ssam_node_group_sl3 },
|
|
--
|
|
2.33.0
|
|
|
|
From 88dc6f1ea08c6175a1679d1f739c2bca5d639e0b Mon Sep 17 00:00:00 2001
|
|
From: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Date: Fri, 4 Jun 2021 15:47:49 +0200
|
|
Subject: [PATCH] platform/surface: aggregator: Allow registering notifiers
|
|
without enabling events
|
|
|
|
Currently, each SSAM event notifier is directly tied to one group of
|
|
events. This makes sense as registering a notifier will automatically
|
|
take care of enabling the corresponding event group and normally drivers
|
|
only need notifications for a very limited number of events, associated
|
|
with different callbacks for each group.
|
|
|
|
However, there are rare cases, especially for debugging, when we want to
|
|
get notifications for a whole event target category instead of just a
|
|
single group of events in that category. Registering multiple notifiers,
|
|
i.e. one per group, may be infeasible due to two issues: a) we might not
|
|
know every event enable/disable specification as some events are
|
|
auto-enabled by the EC and b) forwarding this to the same callback will
|
|
lead to duplicate events as we might not know the full event
|
|
specification to perform the appropriate filtering.
|
|
|
|
This commit introduces observer-notifiers, which are notifiers that are
|
|
not tied to a specific event group and do not attempt to manage any
|
|
events. In other words, they can be registered without enabling any
|
|
event group or incrementing the corresponding reference count and just
|
|
act as silent observers, listening to all currently/previously enabled
|
|
events based on their match-specification.
|
|
|
|
Essentially, this allows us to register one single notifier for a full
|
|
event target category, meaning that we can process all events of that
|
|
target category in a single callback without duplication. Specifically,
|
|
this will be used in the cdev debug interface to forward events to
|
|
user-space via a device file from which the events can be read.
|
|
|
|
Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
|
|
Link: https://lore.kernel.org/r/20210604134755.535590-2-luzmaximilian@gmail.com
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
Patchset: surface-sam
|
|
---
|
|
.../platform/surface/aggregator/controller.c | 69 +++++++++++--------
|
|
include/linux/surface_aggregator/controller.h | 17 +++++
|
|
2 files changed, 58 insertions(+), 28 deletions(-)
|
|
|
|
diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c
|
|
index a06964aa96e7..cd3a6b77f48d 100644
|
|
--- a/drivers/platform/surface/aggregator/controller.c
|
|
+++ b/drivers/platform/surface/aggregator/controller.c
|
|
@@ -2127,9 +2127,15 @@ int ssam_ctrl_notif_d0_entry(struct ssam_controller *ctrl)
|
|
* @ctrl: The controller to register the notifier on.
|
|
* @n: The event notifier to register.
|
|
*
|
|
- * Register an event notifier and increment the usage counter of the
|
|
- * associated SAM event. If the event was previously not enabled, it will be
|
|
- * enabled during this call.
|
|
+ * Register an event notifier. Increment the usage counter of the associated
|
|
+ * SAM event if the notifier is not marked as an observer. If the event is not
|
|
+ * marked as an observer and is currently not enabled, it will be enabled
|
|
+ * during this call. If the notifier is marked as an observer, no attempt will
|
|
+ * be made at enabling any event and no reference count will be modified.
|
|
+ *
|
|
+ * Notifiers marked as observers do not need to be associated with one specific
|
|
+ * event, i.e. as long as no event matching is performed, only the event target
|
|
+ * category needs to be set.
|
|
*
|
|
* Return: Returns zero on success, %-ENOSPC if there have already been
|
|
* %INT_MAX notifiers for the event ID/type associated with the notifier block
|
|
@@ -2138,11 +2144,10 @@ int ssam_ctrl_notif_d0_entry(struct ssam_controller *ctrl)
|
|
* for the specific associated event, returns the status of the event-enable
|
|
* EC-command.
|
|
*/
|
|
-int ssam_notifier_register(struct ssam_controller *ctrl,
|
|
- struct ssam_event_notifier *n)
|
|
+int ssam_notifier_register(struct ssam_controller *ctrl, struct ssam_event_notifier *n)
|
|
{
|
|
u16 rqid = ssh_tc_to_rqid(n->event.id.target_category);
|
|
- struct ssam_nf_refcount_entry *entry;
|
|
+ struct ssam_nf_refcount_entry *entry = NULL;
|
|
struct ssam_nf_head *nf_head;
|
|
struct ssam_nf *nf;
|
|
int status;
|
|
@@ -2155,29 +2160,32 @@ int ssam_notifier_register(struct ssam_controller *ctrl,
|
|
|
|
mutex_lock(&nf->lock);
|
|
|
|
- entry = ssam_nf_refcount_inc(nf, n->event.reg, n->event.id);
|
|
- if (IS_ERR(entry)) {
|
|
- mutex_unlock(&nf->lock);
|
|
- return PTR_ERR(entry);
|
|
- }
|
|
+ if (!(n->flags & SSAM_EVENT_NOTIFIER_OBSERVER)) {
|
|
+ entry = ssam_nf_refcount_inc(nf, n->event.reg, n->event.id);
|
|
+ if (IS_ERR(entry)) {
|
|
+ mutex_unlock(&nf->lock);
|
|
+ return PTR_ERR(entry);
|
|
+ }
|
|
|
|
- ssam_dbg(ctrl, "enabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
|
|
- n->event.reg.target_category, n->event.id.target_category,
|
|
- n->event.id.instance, entry->refcount);
|
|
+ ssam_dbg(ctrl, "enabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
|
|
+ n->event.reg.target_category, n->event.id.target_category,
|
|
+ n->event.id.instance, entry->refcount);
|
|
+ }
|
|
|
|
status = ssam_nfblk_insert(nf_head, &n->base);
|
|
if (status) {
|
|
- entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
|
|
- if (entry->refcount == 0)
|
|
- kfree(entry);
|
|
+ if (entry) {
|
|
+ entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
|
|
+ if (entry->refcount == 0)
|
|
+ kfree(entry);
|
|
+ }
|
|
|
|
mutex_unlock(&nf->lock);
|
|
return status;
|
|
}
|
|
|
|
- if (entry->refcount == 1) {
|
|
- status = ssam_ssh_event_enable(ctrl, n->event.reg, n->event.id,
|
|
- n->event.flags);
|
|
+ if (entry && entry->refcount == 1) {
|
|
+ status = ssam_ssh_event_enable(ctrl, n->event.reg, n->event.id, n->event.flags);
|
|
if (status) {
|
|
ssam_nfblk_remove(&n->base);
|
|
kfree(ssam_nf_refcount_dec(nf, n->event.reg, n->event.id));
|
|
@@ -2188,7 +2196,7 @@ int ssam_notifier_register(struct ssam_controller *ctrl,
|
|
|
|
entry->flags = n->event.flags;
|
|
|
|
- } else if (entry->flags != n->event.flags) {
|
|
+ } else if (entry && entry->flags != n->event.flags) {
|
|
ssam_warn(ctrl,
|
|
"inconsistent flags when enabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n",
|
|
n->event.flags, entry->flags, n->event.reg.target_category,
|
|
@@ -2205,17 +2213,16 @@ EXPORT_SYMBOL_GPL(ssam_notifier_register);
|
|
* @ctrl: The controller the notifier has been registered on.
|
|
* @n: The event notifier to unregister.
|
|
*
|
|
- * Unregister an event notifier and decrement the usage counter of the
|
|
- * associated SAM event. If the usage counter reaches zero, the event will be
|
|
- * disabled.
|
|
+ * Unregister an event notifier. Decrement the usage counter of the associated
|
|
+ * SAM event if the notifier is not marked as an observer. If the usage counter
|
|
+ * reaches zero, the event will be disabled.
|
|
*
|
|
* Return: Returns zero on success, %-ENOENT if the given notifier block has
|
|
* not been registered on the controller. If the given notifier block was the
|
|
* last one associated with its specific event, returns the status of the
|
|
* event-disable EC-command.
|
|
*/
|
|
-int ssam_notifier_unregister(struct ssam_controller *ctrl,
|
|
- struct ssam_event_notifier *n)
|
|
+int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n)
|
|
{
|
|
u16 rqid = ssh_tc_to_rqid(n->event.id.target_category);
|
|
struct ssam_nf_refcount_entry *entry;
|
|
@@ -2236,6 +2243,13 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl,
|
|
return -ENOENT;
|
|
}
|
|
|
|
+ /*
|
|
+ * If this is an observer notifier, do not attempt to disable the
|
|
+ * event, just remove it.
|
|
+ */
|
|
+ if (n->flags & SSAM_EVENT_NOTIFIER_OBSERVER)
|
|
+ goto remove;
|
|
+
|
|
entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
|
|
if (WARN_ON(!entry)) {
|
|
/*
|
|
@@ -2260,8 +2274,7 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl,
|
|
}
|
|
|
|
if (entry->refcount == 0) {
|
|
- status = ssam_ssh_event_disable(ctrl, n->event.reg, n->event.id,
|
|
- n->event.flags);
|
|
+ status = ssam_ssh_event_disable(ctrl, n->event.reg, n->event.id, n->event.flags);
|
|
kfree(entry);
|
|
}
|
|
|
|
diff --git a/include/linux/surface_aggregator/controller.h b/include/linux/surface_aggregator/controller.h
|
|
index 0806796eabcb..cf4bb48a850e 100644
|
|
--- a/include/linux/surface_aggregator/controller.h
|
|
+++ b/include/linux/surface_aggregator/controller.h
|
|
@@ -795,6 +795,20 @@ enum ssam_event_mask {
|
|
#define SSAM_EVENT_REGISTRY_REG \
|
|
SSAM_EVENT_REGISTRY(SSAM_SSH_TC_REG, 0x02, 0x01, 0x02)
|
|
|
|
+/**
|
|
+ * enum ssam_event_notifier_flags - Flags for event notifiers.
|
|
+ * @SSAM_EVENT_NOTIFIER_OBSERVER:
|
|
+ * The corresponding notifier acts as observer. Registering a notifier
|
|
+ * with this flag set will not attempt to enable any event. Equally,
|
|
+ * unregistering will not attempt to disable any event. Note that a
|
|
+ * notifier with this flag may not even correspond to a certain event at
|
|
+ * all, only to a specific event target category. Event matching will not
|
|
+ * be influenced by this flag.
|
|
+ */
|
|
+enum ssam_event_notifier_flags {
|
|
+ SSAM_EVENT_NOTIFIER_OBSERVER = BIT(0),
|
|
+};
|
|
+
|
|
/**
|
|
* struct ssam_event_notifier - Notifier block for SSAM events.
|
|
* @base: The base notifier block with callback function and priority.
|
|
@@ -803,6 +817,7 @@ enum ssam_event_mask {
|
|
* @event.id: ID specifying the event.
|
|
* @event.mask: Flags determining how events are matched to the notifier.
|
|
* @event.flags: Flags used for enabling the event.
|
|
+ * @flags: Notifier flags (see &enum ssam_event_notifier_flags).
|
|
*/
|
|
struct ssam_event_notifier {
|
|
struct ssam_notifier_block base;
|
|
@@ -813,6 +828,8 @@ struct ssam_event_notifier {
|
|
enum ssam_event_mask mask;
|
|
u8 flags;
|
|
} event;
|
|
+
|
|
+ unsigned long flags;
|
|
};
|
|
|
|
int ssam_notifier_register(struct ssam_controller *ctrl,
|
|
--
|
|
2.33.0
|
|
|
|
From 74f47ca38affed9e9d399dec770ddefd550e043b Mon Sep 17 00:00:00 2001
|
|
From: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Date: Fri, 4 Jun 2021 15:47:50 +0200
|
|
Subject: [PATCH] platform/surface: aggregator: Allow enabling of events
|
|
without notifiers
|
|
|
|
We can already enable and disable SAM events via one of two ways: either
|
|
via a (non-observer) notifier tied to a specific event group, or a
|
|
generic event enable/disable request. In some instances, however,
|
|
neither method may be desirable.
|
|
|
|
The first method will tie the event enable request to a specific
|
|
notifier, however, when we want to receive notifications for multiple
|
|
event groups of the same target category and forward this to the same
|
|
notifier callback, we may receive duplicate events, i.e. one event per
|
|
registered notifier. The second method will bypass the internal
|
|
reference counting mechanism, meaning that a disable request will
|
|
disable the event regardless of any other client driver using it, which
|
|
may break the functionality of that driver.
|
|
|
|
To address this problem, add new functions that allow enabling and
|
|
disabling of events via the event reference counting mechanism built
|
|
into the controller, without needing to register a notifier.
|
|
|
|
This can then be used in combination with observer notifiers to process
|
|
multiple events of the same target category without duplication in the
|
|
same callback function.
|
|
|
|
Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Link: https://lore.kernel.org/r/20210604134755.535590-3-luzmaximilian@gmail.com
|
|
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
Patchset: surface-sam
|
|
---
|
|
.../platform/surface/aggregator/controller.c | 293 +++++++++++++++---
|
|
include/linux/surface_aggregator/controller.h | 8 +
|
|
2 files changed, 253 insertions(+), 48 deletions(-)
|
|
|
|
diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c
|
|
index cd3a6b77f48d..cedd0f779f7a 100644
|
|
--- a/drivers/platform/surface/aggregator/controller.c
|
|
+++ b/drivers/platform/surface/aggregator/controller.c
|
|
@@ -407,6 +407,31 @@ ssam_nf_refcount_dec(struct ssam_nf *nf, struct ssam_event_registry reg,
|
|
return NULL;
|
|
}
|
|
|
|
+/**
|
|
+ * ssam_nf_refcount_dec_free() - Decrement reference-/activation-count of the
|
|
+ * given event and free its entry if the reference count reaches zero.
|
|
+ * @nf: The notifier system reference.
|
|
+ * @reg: The registry used to enable/disable the event.
|
|
+ * @id: The event ID.
|
|
+ *
|
|
+ * Decrements the reference-/activation-count of the specified event, freeing
|
|
+ * its entry if it reaches zero.
|
|
+ *
|
|
+ * Note: ``nf->lock`` must be held when calling this function.
|
|
+ */
|
|
+static void ssam_nf_refcount_dec_free(struct ssam_nf *nf,
|
|
+ struct ssam_event_registry reg,
|
|
+ struct ssam_event_id id)
|
|
+{
|
|
+ struct ssam_nf_refcount_entry *entry;
|
|
+
|
|
+ lockdep_assert_held(&nf->lock);
|
|
+
|
|
+ entry = ssam_nf_refcount_dec(nf, reg, id);
|
|
+ if (entry && entry->refcount == 0)
|
|
+ kfree(entry);
|
|
+}
|
|
+
|
|
/**
|
|
* ssam_nf_refcount_empty() - Test if the notification system has any
|
|
* enabled/active events.
|
|
@@ -2122,6 +2147,109 @@ int ssam_ctrl_notif_d0_entry(struct ssam_controller *ctrl)
|
|
|
|
/* -- Top-level event registry interface. ----------------------------------- */
|
|
|
|
+/**
|
|
+ * ssam_nf_refcount_enable() - Enable event for reference count entry if it has
|
|
+ * not already been enabled.
|
|
+ * @ctrl: The controller to enable the event on.
|
|
+ * @entry: The reference count entry for the event to be enabled.
|
|
+ * @flags: The flags used for enabling the event on the EC.
|
|
+ *
|
|
+ * Enable the event associated with the given reference count entry if the
|
|
+ * reference count equals one, i.e. the event has not previously been enabled.
|
|
+ * If the event has already been enabled (i.e. reference count not equal to
|
|
+ * one), check that the flags used for enabling match and warn about this if
|
|
+ * they do not.
|
|
+ *
|
|
+ * This does not modify the reference count itself, which is done with
|
|
+ * ssam_nf_refcount_inc() / ssam_nf_refcount_dec().
|
|
+ *
|
|
+ * Note: ``nf->lock`` must be held when calling this function.
|
|
+ *
|
|
+ * Return: Returns zero on success. If the event is enabled by this call,
|
|
+ * returns the status of the event-enable EC command.
|
|
+ */
|
|
+static int ssam_nf_refcount_enable(struct ssam_controller *ctrl,
|
|
+ struct ssam_nf_refcount_entry *entry, u8 flags)
|
|
+{
|
|
+ const struct ssam_event_registry reg = entry->key.reg;
|
|
+ const struct ssam_event_id id = entry->key.id;
|
|
+ struct ssam_nf *nf = &ctrl->cplt.event.notif;
|
|
+ int status;
|
|
+
|
|
+ lockdep_assert_held(&nf->lock);
|
|
+
|
|
+ ssam_dbg(ctrl, "enabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
|
|
+ reg.target_category, id.target_category, id.instance, entry->refcount);
|
|
+
|
|
+ if (entry->refcount == 1) {
|
|
+ status = ssam_ssh_event_enable(ctrl, reg, id, flags);
|
|
+ if (status)
|
|
+ return status;
|
|
+
|
|
+ entry->flags = flags;
|
|
+
|
|
+ } else if (entry->flags != flags) {
|
|
+ ssam_warn(ctrl,
|
|
+ "inconsistent flags when enabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n",
|
|
+ flags, entry->flags, reg.target_category, id.target_category,
|
|
+ id.instance);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * ssam_nf_refcount_disable_free() - Disable event for reference count entry if it is
|
|
+ * no longer in use and free the corresponding entry.
|
|
+ * @ctrl: The controller to disable the event on.
|
|
+ * @entry: The reference count entry for the event to be disabled.
|
|
+ * @flags: The flags used for enabling the event on the EC.
|
|
+ *
|
|
+ * If the reference count equals zero, i.e. the event is no longer requested by
|
|
+ * any client, the event will be disabled and the corresponding reference count
|
|
+ * entry freed. The reference count entry must not be used any more after a
|
|
+ * call to this function.
|
|
+ *
|
|
+ * Also checks if the flags used for disabling the event match the flags used
|
|
+ * for enabling the event and warns if they do not (regardless of reference
|
|
+ * count).
|
|
+ *
|
|
+ * This does not modify the reference count itself, which is done with
|
|
+ * ssam_nf_refcount_inc() / ssam_nf_refcount_dec().
|
|
+ *
|
|
+ * Note: ``nf->lock`` must be held when calling this function.
|
|
+ *
|
|
+ * Return: Returns zero on success. If the event is disabled by this call,
|
|
+ * returns the status of the event-enable EC command.
|
|
+ */
|
|
+static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl,
|
|
+ struct ssam_nf_refcount_entry *entry, u8 flags)
|
|
+{
|
|
+ const struct ssam_event_registry reg = entry->key.reg;
|
|
+ const struct ssam_event_id id = entry->key.id;
|
|
+ struct ssam_nf *nf = &ctrl->cplt.event.notif;
|
|
+ int status;
|
|
+
|
|
+ lockdep_assert_held(&nf->lock);
|
|
+
|
|
+ ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
|
|
+ reg.target_category, id.target_category, id.instance, entry->refcount);
|
|
+
|
|
+ if (entry->flags != flags) {
|
|
+ ssam_warn(ctrl,
|
|
+ "inconsistent flags when disabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n",
|
|
+ flags, entry->flags, reg.target_category, id.target_category,
|
|
+ id.instance);
|
|
+ }
|
|
+
|
|
+ if (entry->refcount == 0) {
|
|
+ status = ssam_ssh_event_disable(ctrl, reg, id, flags);
|
|
+ kfree(entry);
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
/**
|
|
* ssam_notifier_register() - Register an event notifier.
|
|
* @ctrl: The controller to register the notifier on.
|
|
@@ -2166,41 +2294,26 @@ int ssam_notifier_register(struct ssam_controller *ctrl, struct ssam_event_notif
|
|
mutex_unlock(&nf->lock);
|
|
return PTR_ERR(entry);
|
|
}
|
|
-
|
|
- ssam_dbg(ctrl, "enabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
|
|
- n->event.reg.target_category, n->event.id.target_category,
|
|
- n->event.id.instance, entry->refcount);
|
|
}
|
|
|
|
status = ssam_nfblk_insert(nf_head, &n->base);
|
|
if (status) {
|
|
- if (entry) {
|
|
- entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
|
|
- if (entry->refcount == 0)
|
|
- kfree(entry);
|
|
- }
|
|
+ if (entry)
|
|
+ ssam_nf_refcount_dec_free(nf, n->event.reg, n->event.id);
|
|
|
|
mutex_unlock(&nf->lock);
|
|
return status;
|
|
}
|
|
|
|
- if (entry && entry->refcount == 1) {
|
|
- status = ssam_ssh_event_enable(ctrl, n->event.reg, n->event.id, n->event.flags);
|
|
+ if (entry) {
|
|
+ status = ssam_nf_refcount_enable(ctrl, entry, n->event.flags);
|
|
if (status) {
|
|
ssam_nfblk_remove(&n->base);
|
|
- kfree(ssam_nf_refcount_dec(nf, n->event.reg, n->event.id));
|
|
+ ssam_nf_refcount_dec_free(nf, n->event.reg, n->event.id);
|
|
mutex_unlock(&nf->lock);
|
|
synchronize_srcu(&nf_head->srcu);
|
|
return status;
|
|
}
|
|
-
|
|
- entry->flags = n->event.flags;
|
|
-
|
|
- } else if (entry && entry->flags != n->event.flags) {
|
|
- ssam_warn(ctrl,
|
|
- "inconsistent flags when enabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n",
|
|
- n->event.flags, entry->flags, n->event.reg.target_category,
|
|
- n->event.id.target_category, n->event.id.instance);
|
|
}
|
|
|
|
mutex_unlock(&nf->lock);
|
|
@@ -2247,35 +2360,20 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_not
|
|
* If this is an observer notifier, do not attempt to disable the
|
|
* event, just remove it.
|
|
*/
|
|
- if (n->flags & SSAM_EVENT_NOTIFIER_OBSERVER)
|
|
- goto remove;
|
|
-
|
|
- entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
|
|
- if (WARN_ON(!entry)) {
|
|
- /*
|
|
- * If this does not return an entry, there's a logic error
|
|
- * somewhere: The notifier block is registered, but the event
|
|
- * refcount entry is not there. Remove the notifier block
|
|
- * anyways.
|
|
- */
|
|
- status = -ENOENT;
|
|
- goto remove;
|
|
- }
|
|
-
|
|
- ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
|
|
- n->event.reg.target_category, n->event.id.target_category,
|
|
- n->event.id.instance, entry->refcount);
|
|
-
|
|
- if (entry->flags != n->event.flags) {
|
|
- ssam_warn(ctrl,
|
|
- "inconsistent flags when disabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n",
|
|
- n->event.flags, entry->flags, n->event.reg.target_category,
|
|
- n->event.id.target_category, n->event.id.instance);
|
|
- }
|
|
+ if (!(n->flags & SSAM_EVENT_NOTIFIER_OBSERVER)) {
|
|
+ entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
|
|
+ if (WARN_ON(!entry)) {
|
|
+ /*
|
|
+ * If this does not return an entry, there's a logic
|
|
+ * error somewhere: The notifier block is registered,
|
|
+ * but the event refcount entry is not there. Remove
|
|
+ * the notifier block anyways.
|
|
+ */
|
|
+ status = -ENOENT;
|
|
+ goto remove;
|
|
+ }
|
|
|
|
- if (entry->refcount == 0) {
|
|
- status = ssam_ssh_event_disable(ctrl, n->event.reg, n->event.id, n->event.flags);
|
|
- kfree(entry);
|
|
+ status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags);
|
|
}
|
|
|
|
remove:
|
|
@@ -2287,6 +2385,105 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_not
|
|
}
|
|
EXPORT_SYMBOL_GPL(ssam_notifier_unregister);
|
|
|
|
+/**
|
|
+ * ssam_controller_event_enable() - Enable the specified event.
|
|
+ * @ctrl: The controller to enable the event for.
|
|
+ * @reg: The event registry to use for enabling the event.
|
|
+ * @id: The event ID specifying the event to be enabled.
|
|
+ * @flags: The SAM event flags used for enabling the event.
|
|
+ *
|
|
+ * Increment the event reference count of the specified event. If the event has
|
|
+ * not been enabled previously, it will be enabled by this call.
|
|
+ *
|
|
+ * Note: In general, ssam_notifier_register() with a non-observer notifier
|
|
+ * should be preferred for enabling/disabling events, as this will guarantee
|
|
+ * proper ordering and event forwarding in case of errors during event
|
|
+ * enabling/disabling.
|
|
+ *
|
|
+ * Return: Returns zero on success, %-ENOSPC if the reference count for the
|
|
+ * specified event has reached its maximum, %-ENOMEM if the corresponding event
|
|
+ * entry could not be allocated. If this is the first time that this event has
|
|
+ * been enabled (i.e. the reference count was incremented from zero to one by
|
|
+ * this call), returns the status of the event-enable EC-command.
|
|
+ */
|
|
+int ssam_controller_event_enable(struct ssam_controller *ctrl,
|
|
+ struct ssam_event_registry reg,
|
|
+ struct ssam_event_id id, u8 flags)
|
|
+{
|
|
+ u16 rqid = ssh_tc_to_rqid(id.target_category);
|
|
+ struct ssam_nf *nf = &ctrl->cplt.event.notif;
|
|
+ struct ssam_nf_refcount_entry *entry;
|
|
+ int status;
|
|
+
|
|
+ if (!ssh_rqid_is_event(rqid))
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&nf->lock);
|
|
+
|
|
+ entry = ssam_nf_refcount_inc(nf, reg, id);
|
|
+ if (IS_ERR(entry)) {
|
|
+ mutex_unlock(&nf->lock);
|
|
+ return PTR_ERR(entry);
|
|
+ }
|
|
+
|
|
+ status = ssam_nf_refcount_enable(ctrl, entry, flags);
|
|
+ if (status) {
|
|
+ ssam_nf_refcount_dec_free(nf, reg, id);
|
|
+ mutex_unlock(&nf->lock);
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&nf->lock);
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(ssam_controller_event_enable);
|
|
+
|
|
+/**
|
|
+ * ssam_controller_event_disable() - Disable the specified event.
|
|
+ * @ctrl: The controller to disable the event for.
|
|
+ * @reg: The event registry to use for disabling the event.
|
|
+ * @id: The event ID specifying the event to be disabled.
|
|
+ * @flags: The flags used when enabling the event.
|
|
+ *
|
|
+ * Decrement the reference count of the specified event. If the reference count
|
|
+ * reaches zero, the event will be disabled.
|
|
+ *
|
|
+ * Note: In general, ssam_notifier_register()/ssam_notifier_unregister() with a
|
|
+ * non-observer notifier should be preferred for enabling/disabling events, as
|
|
+ * this will guarantee proper ordering and event forwarding in case of errors
|
|
+ * during event enabling/disabling.
|
|
+ *
|
|
+ * Return: Returns zero on success, %-ENOENT if the given event has not been
|
|
+ * enabled on the controller. If the reference count of the event reaches zero
|
|
+ * during this call, returns the status of the event-disable EC-command.
|
|
+ */
|
|
+int ssam_controller_event_disable(struct ssam_controller *ctrl,
|
|
+ struct ssam_event_registry reg,
|
|
+ struct ssam_event_id id, u8 flags)
|
|
+{
|
|
+ u16 rqid = ssh_tc_to_rqid(id.target_category);
|
|
+ struct ssam_nf *nf = &ctrl->cplt.event.notif;
|
|
+ struct ssam_nf_refcount_entry *entry;
|
|
+ int status = 0;
|
|
+
|
|
+ if (!ssh_rqid_is_event(rqid))
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&nf->lock);
|
|
+
|
|
+ entry = ssam_nf_refcount_dec(nf, reg, id);
|
|
+ if (!entry) {
|
|
+ mutex_unlock(&nf->lock);
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ status = ssam_nf_refcount_disable_free(ctrl, entry, flags);
|
|
+
|
|
+ mutex_unlock(&nf->lock);
|
|
+ return status;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(ssam_controller_event_disable);
|
|
+
|
|
/**
|
|
* ssam_notifier_disable_registered() - Disable events for all registered
|
|
* notifiers.
|
|
diff --git a/include/linux/surface_aggregator/controller.h b/include/linux/surface_aggregator/controller.h
|
|
index cf4bb48a850e..7965bdc669c5 100644
|
|
--- a/include/linux/surface_aggregator/controller.h
|
|
+++ b/include/linux/surface_aggregator/controller.h
|
|
@@ -838,4 +838,12 @@ int ssam_notifier_register(struct ssam_controller *ctrl,
|
|
int ssam_notifier_unregister(struct ssam_controller *ctrl,
|
|
struct ssam_event_notifier *n);
|
|
|
|
+int ssam_controller_event_enable(struct ssam_controller *ctrl,
|
|
+ struct ssam_event_registry reg,
|
|
+ struct ssam_event_id id, u8 flags);
|
|
+
|
|
+int ssam_controller_event_disable(struct ssam_controller *ctrl,
|
|
+ struct ssam_event_registry reg,
|
|
+ struct ssam_event_id id, u8 flags);
|
|
+
|
|
#endif /* _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H */
|
|
--
|
|
2.33.0
|
|
|
|
From 9526a89c84b92359d384c45ac58acd2c14cbb5bc Mon Sep 17 00:00:00 2001
|
|
From: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Date: Fri, 4 Jun 2021 15:47:51 +0200
|
|
Subject: [PATCH] platform/surface: aggregator: Update copyright
|
|
|
|
It's 2021, update the copyright accordingly.
|
|
|
|
Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
|
|
Link: https://lore.kernel.org/r/20210604134755.535590-4-luzmaximilian@gmail.com
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
Patchset: surface-sam
|
|
---
|
|
drivers/platform/surface/aggregator/Kconfig | 2 +-
|
|
drivers/platform/surface/aggregator/Makefile | 2 +-
|
|
drivers/platform/surface/aggregator/bus.c | 2 +-
|
|
drivers/platform/surface/aggregator/bus.h | 2 +-
|
|
drivers/platform/surface/aggregator/controller.c | 2 +-
|
|
drivers/platform/surface/aggregator/controller.h | 2 +-
|
|
drivers/platform/surface/aggregator/core.c | 2 +-
|
|
drivers/platform/surface/aggregator/ssh_msgb.h | 2 +-
|
|
drivers/platform/surface/aggregator/ssh_packet_layer.c | 2 +-
|
|
drivers/platform/surface/aggregator/ssh_packet_layer.h | 2 +-
|
|
drivers/platform/surface/aggregator/ssh_parser.c | 2 +-
|
|
drivers/platform/surface/aggregator/ssh_parser.h | 2 +-
|
|
drivers/platform/surface/aggregator/ssh_request_layer.c | 2 +-
|
|
drivers/platform/surface/aggregator/ssh_request_layer.h | 2 +-
|
|
drivers/platform/surface/aggregator/trace.h | 2 +-
|
|
include/linux/surface_aggregator/controller.h | 2 +-
|
|
include/linux/surface_aggregator/device.h | 2 +-
|
|
include/linux/surface_aggregator/serial_hub.h | 2 +-
|
|
18 files changed, 18 insertions(+), 18 deletions(-)
|
|
|
|
diff --git a/drivers/platform/surface/aggregator/Kconfig b/drivers/platform/surface/aggregator/Kconfig
|
|
index 3aaeea9f0433..fd6dc452f3e8 100644
|
|
--- a/drivers/platform/surface/aggregator/Kconfig
|
|
+++ b/drivers/platform/surface/aggregator/Kconfig
|
|
@@ -1,5 +1,5 @@
|
|
# SPDX-License-Identifier: GPL-2.0+
|
|
-# Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+# Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
|
|
menuconfig SURFACE_AGGREGATOR
|
|
tristate "Microsoft Surface System Aggregator Module Subsystem and Drivers"
|
|
diff --git a/drivers/platform/surface/aggregator/Makefile b/drivers/platform/surface/aggregator/Makefile
|
|
index c112e2c7112b..c8498c41e758 100644
|
|
--- a/drivers/platform/surface/aggregator/Makefile
|
|
+++ b/drivers/platform/surface/aggregator/Makefile
|
|
@@ -1,5 +1,5 @@
|
|
# SPDX-License-Identifier: GPL-2.0+
|
|
-# Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+# Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
|
|
# For include/trace/define_trace.h to include trace.h
|
|
CFLAGS_core.o = -I$(src)
|
|
diff --git a/drivers/platform/surface/aggregator/bus.c b/drivers/platform/surface/aggregator/bus.c
|
|
index a9b660af0917..0169677c243e 100644
|
|
--- a/drivers/platform/surface/aggregator/bus.c
|
|
+++ b/drivers/platform/surface/aggregator/bus.c
|
|
@@ -2,7 +2,7 @@
|
|
/*
|
|
* Surface System Aggregator Module bus and device integration.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#include <linux/device.h>
|
|
diff --git a/drivers/platform/surface/aggregator/bus.h b/drivers/platform/surface/aggregator/bus.h
|
|
index 7712baaed6a5..ed032c2cbdb2 100644
|
|
--- a/drivers/platform/surface/aggregator/bus.h
|
|
+++ b/drivers/platform/surface/aggregator/bus.h
|
|
@@ -2,7 +2,7 @@
|
|
/*
|
|
* Surface System Aggregator Module bus and device integration.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#ifndef _SURFACE_AGGREGATOR_BUS_H
|
|
diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c
|
|
index cedd0f779f7a..6646f4d6e10d 100644
|
|
--- a/drivers/platform/surface/aggregator/controller.c
|
|
+++ b/drivers/platform/surface/aggregator/controller.c
|
|
@@ -2,7 +2,7 @@
|
|
/*
|
|
* Main SSAM/SSH controller structure and functionality.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#include <linux/acpi.h>
|
|
diff --git a/drivers/platform/surface/aggregator/controller.h b/drivers/platform/surface/aggregator/controller.h
|
|
index 8297d34e7489..a0963c3562ff 100644
|
|
--- a/drivers/platform/surface/aggregator/controller.h
|
|
+++ b/drivers/platform/surface/aggregator/controller.h
|
|
@@ -2,7 +2,7 @@
|
|
/*
|
|
* Main SSAM/SSH controller structure and functionality.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#ifndef _SURFACE_AGGREGATOR_CONTROLLER_H
|
|
diff --git a/drivers/platform/surface/aggregator/core.c b/drivers/platform/surface/aggregator/core.c
|
|
index 8dc2c267bcd6..5d780e55f4a1 100644
|
|
--- a/drivers/platform/surface/aggregator/core.c
|
|
+++ b/drivers/platform/surface/aggregator/core.c
|
|
@@ -7,7 +7,7 @@
|
|
* Handles communication via requests as well as enabling, disabling, and
|
|
* relaying of events.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#include <linux/acpi.h>
|
|
diff --git a/drivers/platform/surface/aggregator/ssh_msgb.h b/drivers/platform/surface/aggregator/ssh_msgb.h
|
|
index 1221f642dda1..e562958ffdf0 100644
|
|
--- a/drivers/platform/surface/aggregator/ssh_msgb.h
|
|
+++ b/drivers/platform/surface/aggregator/ssh_msgb.h
|
|
@@ -2,7 +2,7 @@
|
|
/*
|
|
* SSH message builder functions.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#ifndef _SURFACE_AGGREGATOR_SSH_MSGB_H
|
|
diff --git a/drivers/platform/surface/aggregator/ssh_packet_layer.c b/drivers/platform/surface/aggregator/ssh_packet_layer.c
|
|
index 15d96eac6811..5e08049fc3ac 100644
|
|
--- a/drivers/platform/surface/aggregator/ssh_packet_layer.c
|
|
+++ b/drivers/platform/surface/aggregator/ssh_packet_layer.c
|
|
@@ -2,7 +2,7 @@
|
|
/*
|
|
* SSH packet transport layer.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#include <asm/unaligned.h>
|
|
diff --git a/drivers/platform/surface/aggregator/ssh_packet_layer.h b/drivers/platform/surface/aggregator/ssh_packet_layer.h
|
|
index e8757d03f279..2eb329f0b91a 100644
|
|
--- a/drivers/platform/surface/aggregator/ssh_packet_layer.h
|
|
+++ b/drivers/platform/surface/aggregator/ssh_packet_layer.h
|
|
@@ -2,7 +2,7 @@
|
|
/*
|
|
* SSH packet transport layer.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#ifndef _SURFACE_AGGREGATOR_SSH_PACKET_LAYER_H
|
|
diff --git a/drivers/platform/surface/aggregator/ssh_parser.c b/drivers/platform/surface/aggregator/ssh_parser.c
|
|
index e2dead8de94a..b77912f8f13b 100644
|
|
--- a/drivers/platform/surface/aggregator/ssh_parser.c
|
|
+++ b/drivers/platform/surface/aggregator/ssh_parser.c
|
|
@@ -2,7 +2,7 @@
|
|
/*
|
|
* SSH message parser.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#include <asm/unaligned.h>
|
|
diff --git a/drivers/platform/surface/aggregator/ssh_parser.h b/drivers/platform/surface/aggregator/ssh_parser.h
|
|
index 63c38d350988..3bd6e180fd16 100644
|
|
--- a/drivers/platform/surface/aggregator/ssh_parser.h
|
|
+++ b/drivers/platform/surface/aggregator/ssh_parser.h
|
|
@@ -2,7 +2,7 @@
|
|
/*
|
|
* SSH message parser.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#ifndef _SURFACE_AGGREGATOR_SSH_PARSER_H
|
|
diff --git a/drivers/platform/surface/aggregator/ssh_request_layer.c b/drivers/platform/surface/aggregator/ssh_request_layer.c
|
|
index 52a83a8fcf82..bfe1aaf38065 100644
|
|
--- a/drivers/platform/surface/aggregator/ssh_request_layer.c
|
|
+++ b/drivers/platform/surface/aggregator/ssh_request_layer.c
|
|
@@ -2,7 +2,7 @@
|
|
/*
|
|
* SSH request transport layer.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#include <asm/unaligned.h>
|
|
diff --git a/drivers/platform/surface/aggregator/ssh_request_layer.h b/drivers/platform/surface/aggregator/ssh_request_layer.h
|
|
index cb35815858d1..9c3cbae2d4bd 100644
|
|
--- a/drivers/platform/surface/aggregator/ssh_request_layer.h
|
|
+++ b/drivers/platform/surface/aggregator/ssh_request_layer.h
|
|
@@ -2,7 +2,7 @@
|
|
/*
|
|
* SSH request transport layer.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#ifndef _SURFACE_AGGREGATOR_SSH_REQUEST_LAYER_H
|
|
diff --git a/drivers/platform/surface/aggregator/trace.h b/drivers/platform/surface/aggregator/trace.h
|
|
index eb332bb53ae4..de64cf169060 100644
|
|
--- a/drivers/platform/surface/aggregator/trace.h
|
|
+++ b/drivers/platform/surface/aggregator/trace.h
|
|
@@ -2,7 +2,7 @@
|
|
/*
|
|
* Trace points for SSAM/SSH.
|
|
*
|
|
- * Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#undef TRACE_SYSTEM
|
|
diff --git a/include/linux/surface_aggregator/controller.h b/include/linux/surface_aggregator/controller.h
|
|
index 7965bdc669c5..068e1982ad37 100644
|
|
--- a/include/linux/surface_aggregator/controller.h
|
|
+++ b/include/linux/surface_aggregator/controller.h
|
|
@@ -6,7 +6,7 @@
|
|
* managing access and communication to and from the SSAM EC, as well as main
|
|
* communication structures and definitions.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#ifndef _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H
|
|
diff --git a/include/linux/surface_aggregator/device.h b/include/linux/surface_aggregator/device.h
|
|
index 6ff9c58b3e17..f636c5310321 100644
|
|
--- a/include/linux/surface_aggregator/device.h
|
|
+++ b/include/linux/surface_aggregator/device.h
|
|
@@ -7,7 +7,7 @@
|
|
* Provides support for non-platform/non-ACPI SSAM clients via dedicated
|
|
* subsystem.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#ifndef _LINUX_SURFACE_AGGREGATOR_DEVICE_H
|
|
diff --git a/include/linux/surface_aggregator/serial_hub.h b/include/linux/surface_aggregator/serial_hub.h
|
|
index 64276fbfa1d5..c3de43edcffa 100644
|
|
--- a/include/linux/surface_aggregator/serial_hub.h
|
|
+++ b/include/linux/surface_aggregator/serial_hub.h
|
|
@@ -6,7 +6,7 @@
|
|
* Surface System Aggregator Module (SSAM). Provides the interface for basic
|
|
* packet- and request-based communication with the SSAM EC via SSH.
|
|
*
|
|
- * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#ifndef _LINUX_SURFACE_AGGREGATOR_SERIAL_HUB_H
|
|
--
|
|
2.33.0
|
|
|
|
From ab4937068c246b90061d16fd7fa15bf8b28ee3fe Mon Sep 17 00:00:00 2001
|
|
From: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Date: Fri, 4 Jun 2021 15:47:52 +0200
|
|
Subject: [PATCH] platform/surface: aggregator_cdev: Add support for forwarding
|
|
events to user-space
|
|
|
|
Currently, debugging unknown events requires writing a custom driver.
|
|
This is somewhat difficult, slow to adapt, and not entirely
|
|
user-friendly for quickly trying to figure out things on devices of some
|
|
third-party user. We can do better. We already have a user-space
|
|
interface intended for debugging SAM EC requests, so let's add support
|
|
for receiving events to that.
|
|
|
|
This commit provides support for receiving events by reading from the
|
|
controller file. It additionally introduces two new IOCTLs to control
|
|
which event categories will be forwarded. Specifically, a user-space
|
|
client can specify which target categories it wants to receive events
|
|
from by registering the corresponding notifier(s) via the IOCTLs and
|
|
after that, read the received events by reading from the controller
|
|
device.
|
|
|
|
Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
|
|
Link: https://lore.kernel.org/r/20210604134755.535590-5-luzmaximilian@gmail.com
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
Patchset: surface-sam
|
|
---
|
|
.../userspace-api/ioctl/ioctl-number.rst | 2 +-
|
|
.../surface/surface_aggregator_cdev.c | 460 +++++++++++++++++-
|
|
include/uapi/linux/surface_aggregator/cdev.h | 41 +-
|
|
3 files changed, 477 insertions(+), 26 deletions(-)
|
|
|
|
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
|
|
index 9bfc2b510c64..1409e40e6345 100644
|
|
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
|
|
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
|
|
@@ -325,7 +325,7 @@ Code Seq# Include File Comments
|
|
0xA3 90-9F linux/dtlk.h
|
|
0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem
|
|
0xA4 00-1F uapi/asm/sgx.h <mailto:linux-sgx@vger.kernel.org>
|
|
-0xA5 01 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator
|
|
+0xA5 01-05 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator
|
|
<mailto:luzmaximilian@gmail.com>
|
|
0xA5 20-2F linux/surface_aggregator/dtx.h Microsoft Surface DTX driver
|
|
<mailto:luzmaximilian@gmail.com>
|
|
diff --git a/drivers/platform/surface/surface_aggregator_cdev.c b/drivers/platform/surface/surface_aggregator_cdev.c
|
|
index 79e28fab7e40..dcda377896b7 100644
|
|
--- a/drivers/platform/surface/surface_aggregator_cdev.c
|
|
+++ b/drivers/platform/surface/surface_aggregator_cdev.c
|
|
@@ -3,29 +3,69 @@
|
|
* Provides user-space access to the SSAM EC via the /dev/surface/aggregator
|
|
* misc device. Intended for debugging and development.
|
|
*
|
|
- * Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#include <linux/fs.h>
|
|
+#include <linux/ioctl.h>
|
|
#include <linux/kernel.h>
|
|
+#include <linux/kfifo.h>
|
|
#include <linux/kref.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
+#include <linux/poll.h>
|
|
#include <linux/rwsem.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uaccess.h>
|
|
+#include <linux/vmalloc.h>
|
|
|
|
#include <linux/surface_aggregator/cdev.h>
|
|
#include <linux/surface_aggregator/controller.h>
|
|
+#include <linux/surface_aggregator/serial_hub.h>
|
|
|
|
#define SSAM_CDEV_DEVICE_NAME "surface_aggregator_cdev"
|
|
|
|
+
|
|
+/* -- Main structures. ------------------------------------------------------ */
|
|
+
|
|
+enum ssam_cdev_device_state {
|
|
+ SSAM_CDEV_DEVICE_SHUTDOWN_BIT = BIT(0),
|
|
+};
|
|
+
|
|
struct ssam_cdev {
|
|
struct kref kref;
|
|
struct rw_semaphore lock;
|
|
+
|
|
+ struct device *dev;
|
|
struct ssam_controller *ctrl;
|
|
struct miscdevice mdev;
|
|
+ unsigned long flags;
|
|
+
|
|
+ struct rw_semaphore client_lock; /* Guards client list. */
|
|
+ struct list_head client_list;
|
|
+};
|
|
+
|
|
+struct ssam_cdev_client;
|
|
+
|
|
+struct ssam_cdev_notifier {
|
|
+ struct ssam_cdev_client *client;
|
|
+ struct ssam_event_notifier nf;
|
|
+};
|
|
+
|
|
+struct ssam_cdev_client {
|
|
+ struct ssam_cdev *cdev;
|
|
+ struct list_head node;
|
|
+
|
|
+ struct mutex notifier_lock; /* Guards notifier access for registration */
|
|
+ struct ssam_cdev_notifier *notifier[SSH_NUM_EVENTS];
|
|
+
|
|
+ struct mutex read_lock; /* Guards FIFO buffer read access */
|
|
+ struct mutex write_lock; /* Guards FIFO buffer write access */
|
|
+ DECLARE_KFIFO(buffer, u8, 4096);
|
|
+
|
|
+ wait_queue_head_t waitq;
|
|
+ struct fasync_struct *fasync;
|
|
};
|
|
|
|
static void __ssam_cdev_release(struct kref *kref)
|
|
@@ -47,24 +87,169 @@ static void ssam_cdev_put(struct ssam_cdev *cdev)
|
|
kref_put(&cdev->kref, __ssam_cdev_release);
|
|
}
|
|
|
|
-static int ssam_cdev_device_open(struct inode *inode, struct file *filp)
|
|
+
|
|
+/* -- Notifier handling. ---------------------------------------------------- */
|
|
+
|
|
+static u32 ssam_cdev_notifier(struct ssam_event_notifier *nf, const struct ssam_event *in)
|
|
{
|
|
- struct miscdevice *mdev = filp->private_data;
|
|
- struct ssam_cdev *cdev = container_of(mdev, struct ssam_cdev, mdev);
|
|
+ struct ssam_cdev_notifier *cdev_nf = container_of(nf, struct ssam_cdev_notifier, nf);
|
|
+ struct ssam_cdev_client *client = cdev_nf->client;
|
|
+ struct ssam_cdev_event event;
|
|
+ size_t n = struct_size(&event, data, in->length);
|
|
+
|
|
+ /* Translate event. */
|
|
+ event.target_category = in->target_category;
|
|
+ event.target_id = in->target_id;
|
|
+ event.command_id = in->command_id;
|
|
+ event.instance_id = in->instance_id;
|
|
+ event.length = in->length;
|
|
+
|
|
+ mutex_lock(&client->write_lock);
|
|
+
|
|
+ /* Make sure we have enough space. */
|
|
+ if (kfifo_avail(&client->buffer) < n) {
|
|
+ dev_warn(client->cdev->dev,
|
|
+ "buffer full, dropping event (tc: %#04x, tid: %#04x, cid: %#04x, iid: %#04x)\n",
|
|
+ in->target_category, in->target_id, in->command_id, in->instance_id);
|
|
+ mutex_unlock(&client->write_lock);
|
|
+ return 0;
|
|
+ }
|
|
|
|
- filp->private_data = ssam_cdev_get(cdev);
|
|
- return stream_open(inode, filp);
|
|
+ /* Copy event header and payload. */
|
|
+ kfifo_in(&client->buffer, (const u8 *)&event, struct_size(&event, data, 0));
|
|
+ kfifo_in(&client->buffer, &in->data[0], in->length);
|
|
+
|
|
+ mutex_unlock(&client->write_lock);
|
|
+
|
|
+ /* Notify waiting readers. */
|
|
+ kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
|
+ wake_up_interruptible(&client->waitq);
|
|
+
|
|
+ /*
|
|
+ * Don't mark events as handled, this is the job of a proper driver and
|
|
+ * not the debugging interface.
|
|
+ */
|
|
+ return 0;
|
|
}
|
|
|
|
-static int ssam_cdev_device_release(struct inode *inode, struct file *filp)
|
|
+static int ssam_cdev_notifier_register(struct ssam_cdev_client *client, u8 tc, int priority)
|
|
{
|
|
- ssam_cdev_put(filp->private_data);
|
|
- return 0;
|
|
+ const u16 rqid = ssh_tc_to_rqid(tc);
|
|
+ const u16 event = ssh_rqid_to_event(rqid);
|
|
+ struct ssam_cdev_notifier *nf;
|
|
+ int status;
|
|
+
|
|
+ /* Validate notifier target category. */
|
|
+ if (!ssh_rqid_is_event(rqid))
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&client->notifier_lock);
|
|
+
|
|
+ /* Check if the notifier has already been registered. */
|
|
+ if (client->notifier[event]) {
|
|
+ mutex_unlock(&client->notifier_lock);
|
|
+ return -EEXIST;
|
|
+ }
|
|
+
|
|
+ /* Allocate new notifier. */
|
|
+ nf = kzalloc(sizeof(*nf), GFP_KERNEL);
|
|
+ if (!nf) {
|
|
+ mutex_unlock(&client->notifier_lock);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Create a dummy notifier with the minimal required fields for
|
|
+ * observer registration. Note that we can skip fully specifying event
|
|
+ * and registry here as we do not need any matching and use silent
|
|
+ * registration, which does not enable the corresponding event.
|
|
+ */
|
|
+ nf->client = client;
|
|
+ nf->nf.base.fn = ssam_cdev_notifier;
|
|
+ nf->nf.base.priority = priority;
|
|
+ nf->nf.event.id.target_category = tc;
|
|
+ nf->nf.event.mask = 0; /* Do not do any matching. */
|
|
+ nf->nf.flags = SSAM_EVENT_NOTIFIER_OBSERVER;
|
|
+
|
|
+ /* Register notifier. */
|
|
+ status = ssam_notifier_register(client->cdev->ctrl, &nf->nf);
|
|
+ if (status)
|
|
+ kfree(nf);
|
|
+ else
|
|
+ client->notifier[event] = nf;
|
|
+
|
|
+ mutex_unlock(&client->notifier_lock);
|
|
+ return status;
|
|
}
|
|
|
|
-static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg)
|
|
+static int ssam_cdev_notifier_unregister(struct ssam_cdev_client *client, u8 tc)
|
|
+{
|
|
+ const u16 rqid = ssh_tc_to_rqid(tc);
|
|
+ const u16 event = ssh_rqid_to_event(rqid);
|
|
+ int status;
|
|
+
|
|
+ /* Validate notifier target category. */
|
|
+ if (!ssh_rqid_is_event(rqid))
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&client->notifier_lock);
|
|
+
|
|
+ /* Check if the notifier is currently registered. */
|
|
+ if (!client->notifier[event]) {
|
|
+ mutex_unlock(&client->notifier_lock);
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ /* Unregister and free notifier. */
|
|
+ status = ssam_notifier_unregister(client->cdev->ctrl, &client->notifier[event]->nf);
|
|
+ kfree(client->notifier[event]);
|
|
+ client->notifier[event] = NULL;
|
|
+
|
|
+ mutex_unlock(&client->notifier_lock);
|
|
+ return status;
|
|
+}
|
|
+
|
|
+static void ssam_cdev_notifier_unregister_all(struct ssam_cdev_client *client)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ down_read(&client->cdev->lock);
|
|
+
|
|
+ /*
|
|
+ * This function may be used during shutdown, thus we need to test for
|
|
+ * cdev->ctrl instead of the SSAM_CDEV_DEVICE_SHUTDOWN_BIT bit.
|
|
+ */
|
|
+ if (client->cdev->ctrl) {
|
|
+ for (i = 0; i < SSH_NUM_EVENTS; i++)
|
|
+ ssam_cdev_notifier_unregister(client, i + 1);
|
|
+
|
|
+ } else {
|
|
+ int count = 0;
|
|
+
|
|
+ /*
|
|
+ * Device has been shut down. Any notifier remaining is a bug,
|
|
+ * so warn about that as this would otherwise hardly be
|
|
+ * noticeable. Nevertheless, free them as well.
|
|
+ */
|
|
+ mutex_lock(&client->notifier_lock);
|
|
+ for (i = 0; i < SSH_NUM_EVENTS; i++) {
|
|
+ count += !!(client->notifier[i]);
|
|
+ kfree(client->notifier[i]);
|
|
+ client->notifier[i] = NULL;
|
|
+ }
|
|
+ mutex_unlock(&client->notifier_lock);
|
|
+
|
|
+ WARN_ON(count > 0);
|
|
+ }
|
|
+
|
|
+ up_read(&client->cdev->lock);
|
|
+}
|
|
+
|
|
+
|
|
+/* -- IOCTL functions. ------------------------------------------------------ */
|
|
+
|
|
+static long ssam_cdev_request(struct ssam_cdev_client *client, struct ssam_cdev_request __user *r)
|
|
{
|
|
- struct ssam_cdev_request __user *r;
|
|
struct ssam_cdev_request rqst;
|
|
struct ssam_request spec = {};
|
|
struct ssam_response rsp = {};
|
|
@@ -72,7 +257,6 @@ static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg)
|
|
void __user *rspdata;
|
|
int status = 0, ret = 0, tmp;
|
|
|
|
- r = (struct ssam_cdev_request __user *)arg;
|
|
ret = copy_struct_from_user(&rqst, sizeof(rqst), r, sizeof(*r));
|
|
if (ret)
|
|
goto out;
|
|
@@ -152,7 +336,7 @@ static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg)
|
|
}
|
|
|
|
/* Perform request. */
|
|
- status = ssam_request_sync(cdev->ctrl, &spec, &rsp);
|
|
+ status = ssam_request_sync(client->cdev->ctrl, &spec, &rsp);
|
|
if (status)
|
|
goto out;
|
|
|
|
@@ -177,48 +361,247 @@ static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg)
|
|
return ret;
|
|
}
|
|
|
|
-static long __ssam_cdev_device_ioctl(struct ssam_cdev *cdev, unsigned int cmd,
|
|
+static long ssam_cdev_notif_register(struct ssam_cdev_client *client,
|
|
+ const struct ssam_cdev_notifier_desc __user *d)
|
|
+{
|
|
+ struct ssam_cdev_notifier_desc desc;
|
|
+ long ret;
|
|
+
|
|
+ ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return ssam_cdev_notifier_register(client, desc.target_category, desc.priority);
|
|
+}
|
|
+
|
|
+static long ssam_cdev_notif_unregister(struct ssam_cdev_client *client,
|
|
+ const struct ssam_cdev_notifier_desc __user *d)
|
|
+{
|
|
+ struct ssam_cdev_notifier_desc desc;
|
|
+ long ret;
|
|
+
|
|
+ ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return ssam_cdev_notifier_unregister(client, desc.target_category);
|
|
+}
|
|
+
|
|
+
|
|
+/* -- File operations. ------------------------------------------------------ */
|
|
+
|
|
+static int ssam_cdev_device_open(struct inode *inode, struct file *filp)
|
|
+{
|
|
+ struct miscdevice *mdev = filp->private_data;
|
|
+ struct ssam_cdev_client *client;
|
|
+ struct ssam_cdev *cdev = container_of(mdev, struct ssam_cdev, mdev);
|
|
+
|
|
+ /* Initialize client */
|
|
+ client = vzalloc(sizeof(*client));
|
|
+ if (!client)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ client->cdev = ssam_cdev_get(cdev);
|
|
+
|
|
+ INIT_LIST_HEAD(&client->node);
|
|
+
|
|
+ mutex_init(&client->notifier_lock);
|
|
+
|
|
+ mutex_init(&client->read_lock);
|
|
+ mutex_init(&client->write_lock);
|
|
+ INIT_KFIFO(client->buffer);
|
|
+ init_waitqueue_head(&client->waitq);
|
|
+
|
|
+ filp->private_data = client;
|
|
+
|
|
+ /* Attach client. */
|
|
+ down_write(&cdev->client_lock);
|
|
+
|
|
+ if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) {
|
|
+ up_write(&cdev->client_lock);
|
|
+ mutex_destroy(&client->write_lock);
|
|
+ mutex_destroy(&client->read_lock);
|
|
+ mutex_destroy(&client->notifier_lock);
|
|
+ ssam_cdev_put(client->cdev);
|
|
+ vfree(client);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ list_add_tail(&client->node, &cdev->client_list);
|
|
+
|
|
+ up_write(&cdev->client_lock);
|
|
+
|
|
+ stream_open(inode, filp);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ssam_cdev_device_release(struct inode *inode, struct file *filp)
|
|
+{
|
|
+ struct ssam_cdev_client *client = filp->private_data;
|
|
+
|
|
+ /* Force-unregister all remaining notifiers of this client. */
|
|
+ ssam_cdev_notifier_unregister_all(client);
|
|
+
|
|
+ /* Detach client. */
|
|
+ down_write(&client->cdev->client_lock);
|
|
+ list_del(&client->node);
|
|
+ up_write(&client->cdev->client_lock);
|
|
+
|
|
+ /* Free client. */
|
|
+ mutex_destroy(&client->write_lock);
|
|
+ mutex_destroy(&client->read_lock);
|
|
+
|
|
+ mutex_destroy(&client->notifier_lock);
|
|
+
|
|
+ ssam_cdev_put(client->cdev);
|
|
+ vfree(client);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static long __ssam_cdev_device_ioctl(struct ssam_cdev_client *client, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
switch (cmd) {
|
|
case SSAM_CDEV_REQUEST:
|
|
- return ssam_cdev_request(cdev, arg);
|
|
+ return ssam_cdev_request(client, (struct ssam_cdev_request __user *)arg);
|
|
+
|
|
+ case SSAM_CDEV_NOTIF_REGISTER:
|
|
+ return ssam_cdev_notif_register(client,
|
|
+ (struct ssam_cdev_notifier_desc __user *)arg);
|
|
+
|
|
+ case SSAM_CDEV_NOTIF_UNREGISTER:
|
|
+ return ssam_cdev_notif_unregister(client,
|
|
+ (struct ssam_cdev_notifier_desc __user *)arg);
|
|
|
|
default:
|
|
return -ENOTTY;
|
|
}
|
|
}
|
|
|
|
-static long ssam_cdev_device_ioctl(struct file *file, unsigned int cmd,
|
|
- unsigned long arg)
|
|
+static long ssam_cdev_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
- struct ssam_cdev *cdev = file->private_data;
|
|
+ struct ssam_cdev_client *client = file->private_data;
|
|
long status;
|
|
|
|
/* Ensure that controller is valid for as long as we need it. */
|
|
+ if (down_read_killable(&client->cdev->lock))
|
|
+ return -ERESTARTSYS;
|
|
+
|
|
+ if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &client->cdev->flags)) {
|
|
+ up_read(&client->cdev->lock);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ status = __ssam_cdev_device_ioctl(client, cmd, arg);
|
|
+
|
|
+ up_read(&client->cdev->lock);
|
|
+ return status;
|
|
+}
|
|
+
|
|
+static ssize_t ssam_cdev_read(struct file *file, char __user *buf, size_t count, loff_t *offs)
|
|
+{
|
|
+ struct ssam_cdev_client *client = file->private_data;
|
|
+ struct ssam_cdev *cdev = client->cdev;
|
|
+ unsigned int copied;
|
|
+ int status = 0;
|
|
+
|
|
if (down_read_killable(&cdev->lock))
|
|
return -ERESTARTSYS;
|
|
|
|
- if (!cdev->ctrl) {
|
|
+ /* Make sure we're not shut down. */
|
|
+ if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) {
|
|
up_read(&cdev->lock);
|
|
return -ENODEV;
|
|
}
|
|
|
|
- status = __ssam_cdev_device_ioctl(cdev, cmd, arg);
|
|
+ do {
|
|
+ /* Check availability, wait if necessary. */
|
|
+ if (kfifo_is_empty(&client->buffer)) {
|
|
+ up_read(&cdev->lock);
|
|
+
|
|
+ if (file->f_flags & O_NONBLOCK)
|
|
+ return -EAGAIN;
|
|
+
|
|
+ status = wait_event_interruptible(client->waitq,
|
|
+ !kfifo_is_empty(&client->buffer) ||
|
|
+ test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT,
|
|
+ &cdev->flags));
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ if (down_read_killable(&cdev->lock))
|
|
+ return -ERESTARTSYS;
|
|
+
|
|
+ /* Need to check that we're not shut down again. */
|
|
+ if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) {
|
|
+ up_read(&cdev->lock);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Try to read from FIFO. */
|
|
+ if (mutex_lock_interruptible(&client->read_lock)) {
|
|
+ up_read(&cdev->lock);
|
|
+ return -ERESTARTSYS;
|
|
+ }
|
|
+
|
|
+ status = kfifo_to_user(&client->buffer, buf, count, &copied);
|
|
+ mutex_unlock(&client->read_lock);
|
|
+
|
|
+ if (status < 0) {
|
|
+ up_read(&cdev->lock);
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ /* We might not have gotten anything, check this here. */
|
|
+ if (copied == 0 && (file->f_flags & O_NONBLOCK)) {
|
|
+ up_read(&cdev->lock);
|
|
+ return -EAGAIN;
|
|
+ }
|
|
+ } while (copied == 0);
|
|
|
|
up_read(&cdev->lock);
|
|
- return status;
|
|
+ return copied;
|
|
+}
|
|
+
|
|
+static __poll_t ssam_cdev_poll(struct file *file, struct poll_table_struct *pt)
|
|
+{
|
|
+ struct ssam_cdev_client *client = file->private_data;
|
|
+ __poll_t events = 0;
|
|
+
|
|
+ if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &client->cdev->flags))
|
|
+ return EPOLLHUP | EPOLLERR;
|
|
+
|
|
+ poll_wait(file, &client->waitq, pt);
|
|
+
|
|
+ if (!kfifo_is_empty(&client->buffer))
|
|
+ events |= EPOLLIN | EPOLLRDNORM;
|
|
+
|
|
+ return events;
|
|
+}
|
|
+
|
|
+static int ssam_cdev_fasync(int fd, struct file *file, int on)
|
|
+{
|
|
+ struct ssam_cdev_client *client = file->private_data;
|
|
+
|
|
+ return fasync_helper(fd, file, on, &client->fasync);
|
|
}
|
|
|
|
static const struct file_operations ssam_controller_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = ssam_cdev_device_open,
|
|
.release = ssam_cdev_device_release,
|
|
+ .read = ssam_cdev_read,
|
|
+ .poll = ssam_cdev_poll,
|
|
+ .fasync = ssam_cdev_fasync,
|
|
.unlocked_ioctl = ssam_cdev_device_ioctl,
|
|
.compat_ioctl = ssam_cdev_device_ioctl,
|
|
- .llseek = noop_llseek,
|
|
+ .llseek = no_llseek,
|
|
};
|
|
|
|
+
|
|
+/* -- Device and driver setup ----------------------------------------------- */
|
|
+
|
|
static int ssam_dbg_device_probe(struct platform_device *pdev)
|
|
{
|
|
struct ssam_controller *ctrl;
|
|
@@ -236,6 +619,7 @@ static int ssam_dbg_device_probe(struct platform_device *pdev)
|
|
kref_init(&cdev->kref);
|
|
init_rwsem(&cdev->lock);
|
|
cdev->ctrl = ctrl;
|
|
+ cdev->dev = &pdev->dev;
|
|
|
|
cdev->mdev.parent = &pdev->dev;
|
|
cdev->mdev.minor = MISC_DYNAMIC_MINOR;
|
|
@@ -243,6 +627,9 @@ static int ssam_dbg_device_probe(struct platform_device *pdev)
|
|
cdev->mdev.nodename = "surface/aggregator";
|
|
cdev->mdev.fops = &ssam_controller_fops;
|
|
|
|
+ init_rwsem(&cdev->client_lock);
|
|
+ INIT_LIST_HEAD(&cdev->client_list);
|
|
+
|
|
status = misc_register(&cdev->mdev);
|
|
if (status) {
|
|
kfree(cdev);
|
|
@@ -256,8 +643,32 @@ static int ssam_dbg_device_probe(struct platform_device *pdev)
|
|
static int ssam_dbg_device_remove(struct platform_device *pdev)
|
|
{
|
|
struct ssam_cdev *cdev = platform_get_drvdata(pdev);
|
|
+ struct ssam_cdev_client *client;
|
|
|
|
- misc_deregister(&cdev->mdev);
|
|
+ /*
|
|
+ * Mark device as shut-down. Prevent new clients from being added and
|
|
+ * new operations from being executed.
|
|
+ */
|
|
+ set_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags);
|
|
+
|
|
+ down_write(&cdev->client_lock);
|
|
+
|
|
+ /* Remove all notifiers registered by us. */
|
|
+ list_for_each_entry(client, &cdev->client_list, node) {
|
|
+ ssam_cdev_notifier_unregister_all(client);
|
|
+ }
|
|
+
|
|
+ /* Wake up async clients. */
|
|
+ list_for_each_entry(client, &cdev->client_list, node) {
|
|
+ kill_fasync(&client->fasync, SIGIO, POLL_HUP);
|
|
+ }
|
|
+
|
|
+ /* Wake up blocking clients. */
|
|
+ list_for_each_entry(client, &cdev->client_list, node) {
|
|
+ wake_up_interruptible(&client->waitq);
|
|
+ }
|
|
+
|
|
+ up_write(&cdev->client_lock);
|
|
|
|
/*
|
|
* The controller is only guaranteed to be valid for as long as the
|
|
@@ -266,8 +677,11 @@ static int ssam_dbg_device_remove(struct platform_device *pdev)
|
|
*/
|
|
down_write(&cdev->lock);
|
|
cdev->ctrl = NULL;
|
|
+ cdev->dev = NULL;
|
|
up_write(&cdev->lock);
|
|
|
|
+ misc_deregister(&cdev->mdev);
|
|
+
|
|
ssam_cdev_put(cdev);
|
|
return 0;
|
|
}
|
|
diff --git a/include/uapi/linux/surface_aggregator/cdev.h b/include/uapi/linux/surface_aggregator/cdev.h
|
|
index fbcce04abfe9..4f393fafc235 100644
|
|
--- a/include/uapi/linux/surface_aggregator/cdev.h
|
|
+++ b/include/uapi/linux/surface_aggregator/cdev.h
|
|
@@ -6,7 +6,7 @@
|
|
* device. This device provides direct user-space access to the SSAM EC.
|
|
* Intended for debugging and development.
|
|
*
|
|
- * Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com>
|
|
+ * Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
|
*/
|
|
|
|
#ifndef _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H
|
|
@@ -73,6 +73,43 @@ struct ssam_cdev_request {
|
|
} response;
|
|
} __attribute__((__packed__));
|
|
|
|
-#define SSAM_CDEV_REQUEST _IOWR(0xA5, 1, struct ssam_cdev_request)
|
|
+/**
|
|
+ * struct ssam_cdev_notifier_desc - Notifier descriptor.
|
|
+ * @priority: Priority value determining the order in which notifier
|
|
+ * callbacks will be called. A higher value means higher
|
|
+ * priority, i.e. the associated callback will be executed
|
|
+ * earlier than other (lower priority) callbacks.
|
|
+ * @target_category: The event target category for which this notifier should
|
|
+ * receive events.
|
|
+ *
|
|
+ * Specifies the notifier that should be registered or unregistered,
|
|
+ * specifically with which priority and for which target category of events.
|
|
+ */
|
|
+struct ssam_cdev_notifier_desc {
|
|
+ __s32 priority;
|
|
+ __u8 target_category;
|
|
+} __attribute__((__packed__));
|
|
+
|
|
+/**
|
|
+ * struct ssam_cdev_event - SSAM event sent by the EC.
|
|
+ * @target_category: Target category of the event source. See &enum ssam_ssh_tc.
|
|
+ * @target_id: Target ID of the event source.
|
|
+ * @command_id: Command ID of the event.
|
|
+ * @instance_id: Instance ID of the event source.
|
|
+ * @length: Length of the event payload in bytes.
|
|
+ * @data: Event payload data.
|
|
+ */
|
|
+struct ssam_cdev_event {
|
|
+ __u8 target_category;
|
|
+ __u8 target_id;
|
|
+ __u8 command_id;
|
|
+ __u8 instance_id;
|
|
+ __u16 length;
|
|
+ __u8 data[];
|
|
+} __attribute__((__packed__));
|
|
+
|
|
+#define SSAM_CDEV_REQUEST _IOWR(0xA5, 1, struct ssam_cdev_request)
|
|
+#define SSAM_CDEV_NOTIF_REGISTER _IOW(0xA5, 2, struct ssam_cdev_notifier_desc)
|
|
+#define SSAM_CDEV_NOTIF_UNREGISTER _IOW(0xA5, 3, struct ssam_cdev_notifier_desc)
|
|
|
|
#endif /* _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H */
|
|
--
|
|
2.33.0
|
|
|
|
From bba096d6352db3e754471a8b6c5d329bf442df9d Mon Sep 17 00:00:00 2001
|
|
From: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Date: Fri, 4 Jun 2021 15:47:53 +0200
|
|
Subject: [PATCH] platform/surface: aggregator_cdev: Allow enabling of events
|
|
from user-space
|
|
|
|
While events can already be enabled and disabled via the generic request
|
|
IOCTL, this bypasses the internal reference counting mechanism of the
|
|
controller. Due to that, disabling an event will turn it off regardless
|
|
of any other client having requested said event, which may break
|
|
functionality of that client.
|
|
|
|
To solve this, add IOCTLs wrapping the ssam_controller_event_enable()
|
|
and ssam_controller_event_disable() functions, which have been
|
|
previously introduced for this specific purpose.
|
|
|
|
Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
|
|
Link: https://lore.kernel.org/r/20210604134755.535590-6-luzmaximilian@gmail.com
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
Patchset: surface-sam
|
|
---
|
|
.../surface/surface_aggregator_cdev.c | 58 +++++++++++++++++++
|
|
include/uapi/linux/surface_aggregator/cdev.h | 32 ++++++++++
|
|
2 files changed, 90 insertions(+)
|
|
|
|
diff --git a/drivers/platform/surface/surface_aggregator_cdev.c b/drivers/platform/surface/surface_aggregator_cdev.c
|
|
index dcda377896b7..7b86b36eaaa0 100644
|
|
--- a/drivers/platform/surface/surface_aggregator_cdev.c
|
|
+++ b/drivers/platform/surface/surface_aggregator_cdev.c
|
|
@@ -387,6 +387,58 @@ static long ssam_cdev_notif_unregister(struct ssam_cdev_client *client,
|
|
return ssam_cdev_notifier_unregister(client, desc.target_category);
|
|
}
|
|
|
|
+static long ssam_cdev_event_enable(struct ssam_cdev_client *client,
|
|
+ const struct ssam_cdev_event_desc __user *d)
|
|
+{
|
|
+ struct ssam_cdev_event_desc desc;
|
|
+ struct ssam_event_registry reg;
|
|
+ struct ssam_event_id id;
|
|
+ long ret;
|
|
+
|
|
+ /* Read descriptor from user-space. */
|
|
+ ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Translate descriptor. */
|
|
+ reg.target_category = desc.reg.target_category;
|
|
+ reg.target_id = desc.reg.target_id;
|
|
+ reg.cid_enable = desc.reg.cid_enable;
|
|
+ reg.cid_disable = desc.reg.cid_disable;
|
|
+
|
|
+ id.target_category = desc.id.target_category;
|
|
+ id.instance = desc.id.instance;
|
|
+
|
|
+ /* Disable event. */
|
|
+ return ssam_controller_event_enable(client->cdev->ctrl, reg, id, desc.flags);
|
|
+}
|
|
+
|
|
+static long ssam_cdev_event_disable(struct ssam_cdev_client *client,
|
|
+ const struct ssam_cdev_event_desc __user *d)
|
|
+{
|
|
+ struct ssam_cdev_event_desc desc;
|
|
+ struct ssam_event_registry reg;
|
|
+ struct ssam_event_id id;
|
|
+ long ret;
|
|
+
|
|
+ /* Read descriptor from user-space. */
|
|
+ ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Translate descriptor. */
|
|
+ reg.target_category = desc.reg.target_category;
|
|
+ reg.target_id = desc.reg.target_id;
|
|
+ reg.cid_enable = desc.reg.cid_enable;
|
|
+ reg.cid_disable = desc.reg.cid_disable;
|
|
+
|
|
+ id.target_category = desc.id.target_category;
|
|
+ id.instance = desc.id.instance;
|
|
+
|
|
+ /* Disable event. */
|
|
+ return ssam_controller_event_disable(client->cdev->ctrl, reg, id, desc.flags);
|
|
+}
|
|
+
|
|
|
|
/* -- File operations. ------------------------------------------------------ */
|
|
|
|
@@ -473,6 +525,12 @@ static long __ssam_cdev_device_ioctl(struct ssam_cdev_client *client, unsigned i
|
|
return ssam_cdev_notif_unregister(client,
|
|
(struct ssam_cdev_notifier_desc __user *)arg);
|
|
|
|
+ case SSAM_CDEV_EVENT_ENABLE:
|
|
+ return ssam_cdev_event_enable(client, (struct ssam_cdev_event_desc __user *)arg);
|
|
+
|
|
+ case SSAM_CDEV_EVENT_DISABLE:
|
|
+ return ssam_cdev_event_disable(client, (struct ssam_cdev_event_desc __user *)arg);
|
|
+
|
|
default:
|
|
return -ENOTTY;
|
|
}
|
|
diff --git a/include/uapi/linux/surface_aggregator/cdev.h b/include/uapi/linux/surface_aggregator/cdev.h
|
|
index 4f393fafc235..08f46b60b151 100644
|
|
--- a/include/uapi/linux/surface_aggregator/cdev.h
|
|
+++ b/include/uapi/linux/surface_aggregator/cdev.h
|
|
@@ -90,6 +90,36 @@ struct ssam_cdev_notifier_desc {
|
|
__u8 target_category;
|
|
} __attribute__((__packed__));
|
|
|
|
+/**
|
|
+ * struct ssam_cdev_event_desc - Event descriptor.
|
|
+ * @reg: Registry via which the event will be enabled/disabled.
|
|
+ * @reg.target_category: Target category for the event registry requests.
|
|
+ * @reg.target_id: Target ID for the event registry requests.
|
|
+ * @reg.cid_enable: Command ID for the event-enable request.
|
|
+ * @reg.cid_disable: Command ID for the event-disable request.
|
|
+ * @id: ID specifying the event.
|
|
+ * @id.target_category: Target category of the event source.
|
|
+ * @id.instance: Instance ID of the event source.
|
|
+ * @flags: Flags used for enabling the event.
|
|
+ *
|
|
+ * Specifies which event should be enabled/disabled and how to do that.
|
|
+ */
|
|
+struct ssam_cdev_event_desc {
|
|
+ struct {
|
|
+ __u8 target_category;
|
|
+ __u8 target_id;
|
|
+ __u8 cid_enable;
|
|
+ __u8 cid_disable;
|
|
+ } reg;
|
|
+
|
|
+ struct {
|
|
+ __u8 target_category;
|
|
+ __u8 instance;
|
|
+ } id;
|
|
+
|
|
+ __u8 flags;
|
|
+} __attribute__((__packed__));
|
|
+
|
|
/**
|
|
* struct ssam_cdev_event - SSAM event sent by the EC.
|
|
* @target_category: Target category of the event source. See &enum ssam_ssh_tc.
|
|
@@ -111,5 +141,7 @@ struct ssam_cdev_event {
|
|
#define SSAM_CDEV_REQUEST _IOWR(0xA5, 1, struct ssam_cdev_request)
|
|
#define SSAM_CDEV_NOTIF_REGISTER _IOW(0xA5, 2, struct ssam_cdev_notifier_desc)
|
|
#define SSAM_CDEV_NOTIF_UNREGISTER _IOW(0xA5, 3, struct ssam_cdev_notifier_desc)
|
|
+#define SSAM_CDEV_EVENT_ENABLE _IOW(0xA5, 4, struct ssam_cdev_event_desc)
|
|
+#define SSAM_CDEV_EVENT_DISABLE _IOW(0xA5, 5, struct ssam_cdev_event_desc)
|
|
|
|
#endif /* _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H */
|
|
--
|
|
2.33.0
|
|
|
|
From a96012260a61b2a179e75cf113317370d5f5d7e8 Mon Sep 17 00:00:00 2001
|
|
From: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Date: Fri, 4 Jun 2021 15:47:54 +0200
|
|
Subject: [PATCH] platform/surface: aggregator_cdev: Add lockdep support
|
|
|
|
Mark functions with locking requirements via the corresponding lockdep
|
|
calls for debugging and documentary purposes.
|
|
|
|
Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
|
|
Link: https://lore.kernel.org/r/20210604134755.535590-7-luzmaximilian@gmail.com
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
Patchset: surface-sam
|
|
---
|
|
.../platform/surface/surface_aggregator_cdev.c | 16 ++++++++++++++++
|
|
1 file changed, 16 insertions(+)
|
|
|
|
diff --git a/drivers/platform/surface/surface_aggregator_cdev.c b/drivers/platform/surface/surface_aggregator_cdev.c
|
|
index 7b86b36eaaa0..30fb50fde450 100644
|
|
--- a/drivers/platform/surface/surface_aggregator_cdev.c
|
|
+++ b/drivers/platform/surface/surface_aggregator_cdev.c
|
|
@@ -139,6 +139,8 @@ static int ssam_cdev_notifier_register(struct ssam_cdev_client *client, u8 tc, i
|
|
struct ssam_cdev_notifier *nf;
|
|
int status;
|
|
|
|
+ lockdep_assert_held_read(&client->cdev->lock);
|
|
+
|
|
/* Validate notifier target category. */
|
|
if (!ssh_rqid_is_event(rqid))
|
|
return -EINVAL;
|
|
@@ -188,6 +190,8 @@ static int ssam_cdev_notifier_unregister(struct ssam_cdev_client *client, u8 tc)
|
|
const u16 event = ssh_rqid_to_event(rqid);
|
|
int status;
|
|
|
|
+ lockdep_assert_held_read(&client->cdev->lock);
|
|
+
|
|
/* Validate notifier target category. */
|
|
if (!ssh_rqid_is_event(rqid))
|
|
return -EINVAL;
|
|
@@ -257,6 +261,8 @@ static long ssam_cdev_request(struct ssam_cdev_client *client, struct ssam_cdev_
|
|
void __user *rspdata;
|
|
int status = 0, ret = 0, tmp;
|
|
|
|
+ lockdep_assert_held_read(&client->cdev->lock);
|
|
+
|
|
ret = copy_struct_from_user(&rqst, sizeof(rqst), r, sizeof(*r));
|
|
if (ret)
|
|
goto out;
|
|
@@ -367,6 +373,8 @@ static long ssam_cdev_notif_register(struct ssam_cdev_client *client,
|
|
struct ssam_cdev_notifier_desc desc;
|
|
long ret;
|
|
|
|
+ lockdep_assert_held_read(&client->cdev->lock);
|
|
+
|
|
ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
|
|
if (ret)
|
|
return ret;
|
|
@@ -380,6 +388,8 @@ static long ssam_cdev_notif_unregister(struct ssam_cdev_client *client,
|
|
struct ssam_cdev_notifier_desc desc;
|
|
long ret;
|
|
|
|
+ lockdep_assert_held_read(&client->cdev->lock);
|
|
+
|
|
ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
|
|
if (ret)
|
|
return ret;
|
|
@@ -395,6 +405,8 @@ static long ssam_cdev_event_enable(struct ssam_cdev_client *client,
|
|
struct ssam_event_id id;
|
|
long ret;
|
|
|
|
+ lockdep_assert_held_read(&client->cdev->lock);
|
|
+
|
|
/* Read descriptor from user-space. */
|
|
ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
|
|
if (ret)
|
|
@@ -421,6 +433,8 @@ static long ssam_cdev_event_disable(struct ssam_cdev_client *client,
|
|
struct ssam_event_id id;
|
|
long ret;
|
|
|
|
+ lockdep_assert_held_read(&client->cdev->lock);
|
|
+
|
|
/* Read descriptor from user-space. */
|
|
ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
|
|
if (ret)
|
|
@@ -513,6 +527,8 @@ static int ssam_cdev_device_release(struct inode *inode, struct file *filp)
|
|
static long __ssam_cdev_device_ioctl(struct ssam_cdev_client *client, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
+ lockdep_assert_held_read(&client->cdev->lock);
|
|
+
|
|
switch (cmd) {
|
|
case SSAM_CDEV_REQUEST:
|
|
return ssam_cdev_request(client, (struct ssam_cdev_request __user *)arg);
|
|
--
|
|
2.33.0
|
|
|
|
From ce84847fc64f4e994c60ca0ce06f8d214b58fc18 Mon Sep 17 00:00:00 2001
|
|
From: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Date: Wed, 2 Jun 2021 20:07:47 +0200
|
|
Subject: [PATCH] docs: driver-api: Update Surface Aggregator user-space
|
|
interface documentation
|
|
|
|
Update the controller-device user-space interface (cdev) documentation
|
|
for the newly introduced IOCTLs and event interface.
|
|
|
|
Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Patchset: surface-sam
|
|
---
|
|
.../surface_aggregator/clients/cdev.rst | 127 +++++++++++++++++-
|
|
1 file changed, 122 insertions(+), 5 deletions(-)
|
|
|
|
diff --git a/Documentation/driver-api/surface_aggregator/clients/cdev.rst b/Documentation/driver-api/surface_aggregator/clients/cdev.rst
|
|
index 248c1372d879..0134a841a079 100644
|
|
--- a/Documentation/driver-api/surface_aggregator/clients/cdev.rst
|
|
+++ b/Documentation/driver-api/surface_aggregator/clients/cdev.rst
|
|
@@ -1,9 +1,8 @@
|
|
.. SPDX-License-Identifier: GPL-2.0+
|
|
|
|
-.. |u8| replace:: :c:type:`u8 <u8>`
|
|
-.. |u16| replace:: :c:type:`u16 <u16>`
|
|
.. |ssam_cdev_request| replace:: :c:type:`struct ssam_cdev_request <ssam_cdev_request>`
|
|
.. |ssam_cdev_request_flags| replace:: :c:type:`enum ssam_cdev_request_flags <ssam_cdev_request_flags>`
|
|
+.. |ssam_cdev_event| replace:: :c:type:`struct ssam_cdev_event <ssam_cdev_event>`
|
|
|
|
==============================
|
|
User-Space EC Interface (cdev)
|
|
@@ -23,6 +22,40 @@ These IOCTLs and their respective input/output parameter structs are defined in
|
|
A small python library and scripts for accessing this interface can be found
|
|
at https://github.com/linux-surface/surface-aggregator-module/tree/master/scripts/ssam.
|
|
|
|
+.. contents::
|
|
+
|
|
+
|
|
+Receiving Events
|
|
+================
|
|
+
|
|
+Events can be received by reading from the device-file. The are represented by
|
|
+the |ssam_cdev_event| datatype.
|
|
+
|
|
+Before events are available to be read, however, the desired notifiers must be
|
|
+registered via the ``SSAM_CDEV_NOTIF_REGISTER`` IOCTL. Notifiers are, in
|
|
+essence, callbacks, called when the EC sends an event. They are, in this
|
|
+interface, associated with a specific target category and device-file-instance.
|
|
+They forward any event of this category to the buffer of the corresponding
|
|
+instance, from which it can then be read.
|
|
+
|
|
+Notifiers themselves do not enable events on the EC. Thus, it may additionally
|
|
+be necessary to enable events via the ``SSAM_CDEV_EVENT_ENABLE`` IOCTL. While
|
|
+notifiers work per-client (i.e. per-device-file-instance), events are enabled
|
|
+globally, for the EC and all of its clients (regardless of userspace or
|
|
+non-userspace). The ``SSAM_CDEV_EVENT_ENABLE`` and ``SSAM_CDEV_EVENT_DISABLE``
|
|
+IOCTLs take care of reference counting the events, such that an event is
|
|
+enabled as long as there is a client that has requested it.
|
|
+
|
|
+Note that enabled events are not automatically disabled once the client
|
|
+instance is closed. Therefore any client process (or group of processes) should
|
|
+balance their event enable calls with the corresponding event disable calls. It
|
|
+is, however, perfectly valid to enable and disable events on different client
|
|
+instances. For example, it is valid to set up notifiers and read events on
|
|
+client instance ``A``, enable those events on instance ``B`` (note that these
|
|
+will also be received by A since events are enabled/disabled globally), and
|
|
+after no more events are desired, disable the previously enabled events via
|
|
+instance ``C``.
|
|
+
|
|
|
|
Controller IOCTLs
|
|
=================
|
|
@@ -45,9 +78,33 @@ The following IOCTLs are provided:
|
|
- ``REQUEST``
|
|
- Perform synchronous SAM request.
|
|
|
|
+ * - ``0xA5``
|
|
+ - ``2``
|
|
+ - ``W``
|
|
+ - ``NOTIF_REGISTER``
|
|
+ - Register event notifier.
|
|
|
|
-``REQUEST``
|
|
------------
|
|
+ * - ``0xA5``
|
|
+ - ``3``
|
|
+ - ``W``
|
|
+ - ``NOTIF_UNREGISTER``
|
|
+ - Unregister event notifier.
|
|
+
|
|
+ * - ``0xA5``
|
|
+ - ``4``
|
|
+ - ``W``
|
|
+ - ``EVENT_ENABLE``
|
|
+ - Enable event source.
|
|
+
|
|
+ * - ``0xA5``
|
|
+ - ``5``
|
|
+ - ``W``
|
|
+ - ``EVENT_DISABLE``
|
|
+ - Disable event source.
|
|
+
|
|
+
|
|
+``SSAM_CDEV_REQUEST``
|
|
+---------------------
|
|
|
|
Defined as ``_IOWR(0xA5, 1, struct ssam_cdev_request)``.
|
|
|
|
@@ -82,6 +139,66 @@ submitted, and completed (i.e. handed back to user-space) successfully from
|
|
inside the IOCTL, but the request ``status`` member may still be negative in
|
|
case the actual execution of the request failed after it has been submitted.
|
|
|
|
-A full definition of the argument struct is provided below:
|
|
+A full definition of the argument struct is provided below.
|
|
+
|
|
+``SSAM_CDEV_NOTIF_REGISTER``
|
|
+----------------------------
|
|
+
|
|
+Defined as ``_IOW(0xA5, 2, struct ssam_cdev_notifier_desc)``.
|
|
+
|
|
+Register a notifier for the event target category specified in the given
|
|
+notifier description with the specified priority. Notifiers registration is
|
|
+required to receive events, but does not enable events themselves. After a
|
|
+notifier for a specific target category has been registered, all events of that
|
|
+category will be forwarded to the userspace client and can then be read from
|
|
+the device file instance. Note that events may have to be enabled, e.g. via the
|
|
+``SSAM_CDEV_EVENT_ENABLE`` IOCTL, before the EC will send them.
|
|
+
|
|
+Only one notifier can be registered per target category and client instance. If
|
|
+a notifier has already been registered, this IOCTL will fail with ``-EEXIST``.
|
|
+
|
|
+Notifiers will automatically be removed when the device file instance is
|
|
+closed.
|
|
+
|
|
+``SSAM_CDEV_NOTIF_UNREGISTER``
|
|
+------------------------------
|
|
+
|
|
+Defined as ``_IOW(0xA5, 3, struct ssam_cdev_notifier_desc)``.
|
|
+
|
|
+Unregisters the notifier associated with the specified target category. The
|
|
+priority field will be ignored by this IOCTL. If no notifier has been
|
|
+registered for this client instance and the given category, this IOCTL will
|
|
+fail with ``-ENOENT``.
|
|
+
|
|
+``SSAM_CDEV_EVENT_ENABLE``
|
|
+--------------------------
|
|
+
|
|
+Defined as ``_IOW(0xA5, 4, struct ssam_cdev_event_desc)``.
|
|
+
|
|
+Enable the event associated with the given event descriptor.
|
|
+
|
|
+Note that this call will not register a notifier itself, it will only enable
|
|
+events on the controller. If you want to receive events by reading from the
|
|
+device file, you will need to register the corresponding notifier(s) on that
|
|
+instance.
|
|
+
|
|
+Events are not automatically disabled when the device file is closed. This must
|
|
+be done manually, via a call to the ``SSAM_CDEV_EVENT_DISABLE`` IOCTL.
|
|
+
|
|
+``SSAM_CDEV_EVENT_DISABLE``
|
|
+---------------------------
|
|
+
|
|
+Defined as ``_IOW(0xA5, 5, struct ssam_cdev_event_desc)``.
|
|
+
|
|
+Disable the event associated with the given event descriptor.
|
|
+
|
|
+Note that this will not unregister any notifiers. Events may still be received
|
|
+and forwarded to user-space after this call. The only safe way of stopping
|
|
+events from being received is unregistering all previously registered
|
|
+notifiers.
|
|
+
|
|
+
|
|
+Structures and Enums
|
|
+====================
|
|
|
|
.. kernel-doc:: include/uapi/linux/surface_aggregator/cdev.h
|
|
--
|
|
2.33.0
|
|
|
|
From 51b3bcb37da96838183cc99e0773808abfa06cd5 Mon Sep 17 00:00:00 2001
|
|
From: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Date: Fri, 4 Jun 2021 23:09:06 +0200
|
|
Subject: [PATCH] platform/surface: aggregator: Do not return uninitialized
|
|
value
|
|
|
|
The status variable in ssam_nf_refcount_disable_free() is only set when
|
|
the reference count equals zero. Otherwise, it is returned
|
|
uninitialized. Fix this by always initializing status to zero.
|
|
|
|
Reported-by: kernel test robot <lkp@intel.com>
|
|
Fixes: 640ee17199e4 ("platform/surface: aggregator: Allow enabling of events without notifiers")
|
|
Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Link: https://lore.kernel.org/r/20210604210907.25738-2-luzmaximilian@gmail.com
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
Patchset: surface-sam
|
|
---
|
|
drivers/platform/surface/aggregator/controller.c | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c
|
|
index 6646f4d6e10d..634399387d76 100644
|
|
--- a/drivers/platform/surface/aggregator/controller.c
|
|
+++ b/drivers/platform/surface/aggregator/controller.c
|
|
@@ -2228,7 +2228,7 @@ static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl,
|
|
const struct ssam_event_registry reg = entry->key.reg;
|
|
const struct ssam_event_id id = entry->key.id;
|
|
struct ssam_nf *nf = &ctrl->cplt.event.notif;
|
|
- int status;
|
|
+ int status = 0;
|
|
|
|
lockdep_assert_held(&nf->lock);
|
|
|
|
--
|
|
2.33.0
|
|
|
|
From 3fef58d7f20633549584c2c28459f292a88a6f5a Mon Sep 17 00:00:00 2001
|
|
From: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Date: Fri, 4 Jun 2021 23:09:07 +0200
|
|
Subject: [PATCH] platform/surface: aggregator: Drop unnecessary variable
|
|
initialization
|
|
|
|
The status variable in ssam_controller_event_disable() is always set, no
|
|
need to initialize it.
|
|
|
|
Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Link: https://lore.kernel.org/r/20210604210907.25738-3-luzmaximilian@gmail.com
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
Patchset: surface-sam
|
|
---
|
|
drivers/platform/surface/aggregator/controller.c | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c
|
|
index 634399387d76..b8c377b3f932 100644
|
|
--- a/drivers/platform/surface/aggregator/controller.c
|
|
+++ b/drivers/platform/surface/aggregator/controller.c
|
|
@@ -2464,7 +2464,7 @@ int ssam_controller_event_disable(struct ssam_controller *ctrl,
|
|
u16 rqid = ssh_tc_to_rqid(id.target_category);
|
|
struct ssam_nf *nf = &ctrl->cplt.event.notif;
|
|
struct ssam_nf_refcount_entry *entry;
|
|
- int status = 0;
|
|
+ int status;
|
|
|
|
if (!ssh_rqid_is_event(rqid))
|
|
return -EINVAL;
|
|
--
|
|
2.33.0
|
|
|
|
From c55a8b13e2165c6568cee66415ae49e98ff36deb Mon Sep 17 00:00:00 2001
|
|
From: Baokun Li <libaokun1@huawei.com>
|
|
Date: Wed, 9 Jun 2021 15:26:38 +0800
|
|
Subject: [PATCH] platform/surface: aggregator: Use list_move_tail instead of
|
|
list_del/list_add_tail in ssh_request_layer.c
|
|
|
|
Using list_move_tail() instead of list_del() + list_add_tail() in ssh_request_layer.c.
|
|
|
|
Reported-by: Hulk Robot <hulkci@huawei.com>
|
|
Signed-off-by: Baokun Li <libaokun1@huawei.com>
|
|
Reviewed-by: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Link: https://lore.kernel.org/r/20210609072638.1358174-1-libaokun1@huawei.com
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
Patchset: surface-sam
|
|
---
|
|
.../platform/surface/aggregator/ssh_request_layer.c | 10 +++-------
|
|
1 file changed, 3 insertions(+), 7 deletions(-)
|
|
|
|
diff --git a/drivers/platform/surface/aggregator/ssh_request_layer.c b/drivers/platform/surface/aggregator/ssh_request_layer.c
|
|
index bfe1aaf38065..790f7f0eee98 100644
|
|
--- a/drivers/platform/surface/aggregator/ssh_request_layer.c
|
|
+++ b/drivers/platform/surface/aggregator/ssh_request_layer.c
|
|
@@ -863,9 +863,7 @@ static void ssh_rtl_timeout_reap(struct work_struct *work)
|
|
clear_bit(SSH_REQUEST_SF_PENDING_BIT, &r->state);
|
|
|
|
atomic_dec(&rtl->pending.count);
|
|
- list_del(&r->node);
|
|
-
|
|
- list_add_tail(&r->node, &claimed);
|
|
+ list_move_tail(&r->node, &claimed);
|
|
}
|
|
spin_unlock(&rtl->pending.lock);
|
|
|
|
@@ -1204,8 +1202,7 @@ void ssh_rtl_shutdown(struct ssh_rtl *rtl)
|
|
smp_mb__before_atomic();
|
|
clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &r->state);
|
|
|
|
- list_del(&r->node);
|
|
- list_add_tail(&r->node, &claimed);
|
|
+ list_move_tail(&r->node, &claimed);
|
|
}
|
|
spin_unlock(&rtl->queue.lock);
|
|
|
|
@@ -1238,8 +1235,7 @@ void ssh_rtl_shutdown(struct ssh_rtl *rtl)
|
|
smp_mb__before_atomic();
|
|
clear_bit(SSH_REQUEST_SF_PENDING_BIT, &r->state);
|
|
|
|
- list_del(&r->node);
|
|
- list_add_tail(&r->node, &claimed);
|
|
+ list_move_tail(&r->node, &claimed);
|
|
}
|
|
spin_unlock(&rtl->pending.lock);
|
|
}
|
|
--
|
|
2.33.0
|
|
|
|
From 89cedb89f0155bc4c83b4cd7adf17252106ed3ab Mon Sep 17 00:00:00 2001
|
|
From: Baokun Li <libaokun1@huawei.com>
|
|
Date: Wed, 9 Jun 2021 15:24:48 +0800
|
|
Subject: [PATCH] platform/surface: aggregator: Use list_move_tail instead of
|
|
list_del/list_add_tail in ssh_packet_layer.c
|
|
|
|
Using list_move_tail() instead of list_del() + list_add_tail() in ssh_packet_layer.c.
|
|
|
|
Reported-by: Hulk Robot <hulkci@huawei.com>
|
|
Signed-off-by: Baokun Li <libaokun1@huawei.com>
|
|
Reviewed-by: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Link: https://lore.kernel.org/r/20210609072448.1357524-1-libaokun1@huawei.com
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
Patchset: surface-sam
|
|
---
|
|
drivers/platform/surface/aggregator/ssh_packet_layer.c | 10 +++-------
|
|
1 file changed, 3 insertions(+), 7 deletions(-)
|
|
|
|
diff --git a/drivers/platform/surface/aggregator/ssh_packet_layer.c b/drivers/platform/surface/aggregator/ssh_packet_layer.c
|
|
index 5e08049fc3ac..8a4451c1ffe5 100644
|
|
--- a/drivers/platform/surface/aggregator/ssh_packet_layer.c
|
|
+++ b/drivers/platform/surface/aggregator/ssh_packet_layer.c
|
|
@@ -1567,9 +1567,7 @@ static void ssh_ptl_timeout_reap(struct work_struct *work)
|
|
clear_bit(SSH_PACKET_SF_PENDING_BIT, &p->state);
|
|
|
|
atomic_dec(&ptl->pending.count);
|
|
- list_del(&p->pending_node);
|
|
-
|
|
- list_add_tail(&p->pending_node, &claimed);
|
|
+ list_move_tail(&p->pending_node, &claimed);
|
|
}
|
|
|
|
spin_unlock(&ptl->pending.lock);
|
|
@@ -1957,8 +1955,7 @@ void ssh_ptl_shutdown(struct ssh_ptl *ptl)
|
|
smp_mb__before_atomic();
|
|
clear_bit(SSH_PACKET_SF_QUEUED_BIT, &p->state);
|
|
|
|
- list_del(&p->queue_node);
|
|
- list_add_tail(&p->queue_node, &complete_q);
|
|
+ list_move_tail(&p->queue_node, &complete_q);
|
|
}
|
|
spin_unlock(&ptl->queue.lock);
|
|
|
|
@@ -1970,8 +1967,7 @@ void ssh_ptl_shutdown(struct ssh_ptl *ptl)
|
|
smp_mb__before_atomic();
|
|
clear_bit(SSH_PACKET_SF_PENDING_BIT, &p->state);
|
|
|
|
- list_del(&p->pending_node);
|
|
- list_add_tail(&p->pending_node, &complete_q);
|
|
+ list_move_tail(&p->pending_node, &complete_q);
|
|
}
|
|
atomic_set(&ptl->pending.count, 0);
|
|
spin_unlock(&ptl->pending.lock);
|
|
--
|
|
2.33.0
|
|
|