linux-doc.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
@ 2020-12-03 21:26 Maximilian Luz
  2020-12-03 21:26 ` [PATCH v2 7/9] docs: driver-api: Add Surface Aggregator subsystem documentation Maximilian Luz
                   ` (3 more replies)
  0 siblings, 4 replies; 31+ messages in thread
From: Maximilian Luz @ 2020-12-03 21:26 UTC (permalink / raw)
  To: linux-kernel
  Cc: Maximilian Luz, Hans de Goede, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Greg Kroah-Hartman,
	Rob Herring, Jiri Slaby, Rafael J . Wysocki, Len Brown,
	Steven Rostedt, Ingo Molnar, Masahiro Yamada, Michal Marek,
	Jonathan Corbet, Blaž Hrastnik, Dorian Stoll,
	platform-driver-x86, linux-serial, linux-acpi, linux-kbuild,
	linux-doc

Hello,

Here is version two of the Surface System Aggregator Module (SAM/SSAM)
driver series, adding initial support for the embedded controller on 5th
and later generation Microsoft Surface devices. Initial support includes
the ACPI interface to the controller, via which battery and thermal
information is provided on some of these devices.

The previous version and cover letter detailing what this series is
about can be found at

  https://lore.kernel.org/platform-driver-x86/20201115192143.21571-1-luzmaximilian@gmail.com/

This patch-set can also be found at the following repository and
reference, if you prefer to look at a kernel tree instead of these
emails:

  https://github.com/linux-surface/kernel tags/s/surface-aggregator/v2

Thank you all for the feedback to v1, I hope I have addressed all
comments.

Regards,
Max


Note: This patch depends on

  [PATCH v4] platform/surface: Create a platform subdirectory for
             Microsoft Surface devices

which can be found at

  https://lore.kernel.org/platform-driver-x86/20201009141128.683254-1-luzmaximilian@gmail.com/

and is currently in platform-drivers-x86/for-next.


Changes in v1 (from RFC, overview):
 - move to platform/surface
 - add copyright lines
 - change SPDX identifier to GPL-2.0+ (was GPL-2.0-or-later)
 - change user-space interface from debugfs to misc-device
 - address issues in user-space interface
 - fix typos in commit messages and documentation
 - fix some bugs, address other issues

Changes in v2 (overview):
 - simplify some code, mostly with regards to concurrency
 - add architectural overview to documentation
 - improve comments for documentation
 - use printk specifier for hex prefix instead of hard-coding it
 - spell check comments and strings, fix typos
 - unify comment style
 - run checkpatch --strict, fix these and other style issues

Changes regarding specific patches (and more details) can be found on
the individual patch.


Maximilian Luz (9):
  platform/surface: Add Surface Aggregator subsystem
  platform/surface: aggregator: Add control packet allocation caching
  platform/surface: aggregator: Add event item allocation caching
  platform/surface: aggregator: Add trace points
  platform/surface: aggregator: Add error injection capabilities
  platform/surface: aggregator: Add dedicated bus and device type
  docs: driver-api: Add Surface Aggregator subsystem documentation
  platform/surface: Add Surface Aggregator user-space interface
  platform/surface: Add Surface ACPI Notify driver

 Documentation/driver-api/index.rst            |    1 +
 .../surface_aggregator/client-api.rst         |   38 +
 .../driver-api/surface_aggregator/client.rst  |  393 +++
 .../surface_aggregator/clients/cdev.rst       |   85 +
 .../surface_aggregator/clients/index.rst      |   21 +
 .../surface_aggregator/clients/san.rst        |   44 +
 .../driver-api/surface_aggregator/index.rst   |   21 +
 .../surface_aggregator/internal-api.rst       |   67 +
 .../surface_aggregator/internal.rst           |  577 ++++
 .../surface_aggregator/overview.rst           |   77 +
 .../driver-api/surface_aggregator/ssh.rst     |  344 +++
 .../userspace-api/ioctl/ioctl-number.rst      |    2 +
 MAINTAINERS                                   |   13 +
 drivers/platform/surface/Kconfig              |   38 +
 drivers/platform/surface/Makefile             |    3 +
 drivers/platform/surface/aggregator/Kconfig   |   69 +
 drivers/platform/surface/aggregator/Makefile  |   17 +
 drivers/platform/surface/aggregator/bus.c     |  415 +++
 drivers/platform/surface/aggregator/bus.h     |   27 +
 .../platform/surface/aggregator/controller.c  | 2543 +++++++++++++++++
 .../platform/surface/aggregator/controller.h  |  285 ++
 drivers/platform/surface/aggregator/core.c    |  833 ++++++
 .../platform/surface/aggregator/ssh_msgb.h    |  205 ++
 .../surface/aggregator/ssh_packet_layer.c     | 2046 +++++++++++++
 .../surface/aggregator/ssh_packet_layer.h     |  190 ++
 .../platform/surface/aggregator/ssh_parser.c  |  228 ++
 .../platform/surface/aggregator/ssh_parser.h  |  154 +
 .../surface/aggregator/ssh_request_layer.c    | 1256 ++++++++
 .../surface/aggregator/ssh_request_layer.h    |  143 +
 drivers/platform/surface/aggregator/trace.h   |  632 ++++
 .../platform/surface/surface_acpi_notify.c    |  886 ++++++
 .../surface/surface_aggregator_cdev.c         |  299 ++
 include/linux/mod_devicetable.h               |   18 +
 include/linux/surface_acpi_notify.h           |   39 +
 include/linux/surface_aggregator/controller.h |  824 ++++++
 include/linux/surface_aggregator/device.h     |  423 +++
 include/linux/surface_aggregator/serial_hub.h |  672 +++++
 include/uapi/linux/surface_aggregator/cdev.h  |   58 +
 scripts/mod/devicetable-offsets.c             |    8 +
 scripts/mod/file2alias.c                      |   23 +
 40 files changed, 14017 insertions(+)
 create mode 100644 Documentation/driver-api/surface_aggregator/client-api.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/client.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/clients/cdev.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/clients/index.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/clients/san.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/index.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/internal-api.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/internal.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/overview.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/ssh.rst
 create mode 100644 drivers/platform/surface/aggregator/Kconfig
 create mode 100644 drivers/platform/surface/aggregator/Makefile
 create mode 100644 drivers/platform/surface/aggregator/bus.c
 create mode 100644 drivers/platform/surface/aggregator/bus.h
 create mode 100644 drivers/platform/surface/aggregator/controller.c
 create mode 100644 drivers/platform/surface/aggregator/controller.h
 create mode 100644 drivers/platform/surface/aggregator/core.c
 create mode 100644 drivers/platform/surface/aggregator/ssh_msgb.h
 create mode 100644 drivers/platform/surface/aggregator/ssh_packet_layer.c
 create mode 100644 drivers/platform/surface/aggregator/ssh_packet_layer.h
 create mode 100644 drivers/platform/surface/aggregator/ssh_parser.c
 create mode 100644 drivers/platform/surface/aggregator/ssh_parser.h
 create mode 100644 drivers/platform/surface/aggregator/ssh_request_layer.c
 create mode 100644 drivers/platform/surface/aggregator/ssh_request_layer.h
 create mode 100644 drivers/platform/surface/aggregator/trace.h
 create mode 100644 drivers/platform/surface/surface_acpi_notify.c
 create mode 100644 drivers/platform/surface/surface_aggregator_cdev.c
 create mode 100644 include/linux/surface_acpi_notify.h
 create mode 100644 include/linux/surface_aggregator/controller.h
 create mode 100644 include/linux/surface_aggregator/device.h
 create mode 100644 include/linux/surface_aggregator/serial_hub.h
 create mode 100644 include/uapi/linux/surface_aggregator/cdev.h

--
2.29.2


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v2 7/9] docs: driver-api: Add Surface Aggregator subsystem documentation
  2020-12-03 21:26 [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module Maximilian Luz
@ 2020-12-03 21:26 ` Maximilian Luz
  2020-12-15 16:25   ` Hans de Goede
  2020-12-03 21:26 ` [PATCH v2 8/9] platform/surface: Add Surface Aggregator user-space interface Maximilian Luz
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 31+ messages in thread
From: Maximilian Luz @ 2020-12-03 21:26 UTC (permalink / raw)
  To: linux-kernel
  Cc: Maximilian Luz, Hans de Goede, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Greg Kroah-Hartman,
	Jonathan Corbet, Blaž Hrastnik, Dorian Stoll,
	platform-driver-x86, linux-doc

Add documentation for the Surface Aggregator subsystem and its client
drivers, giving an overview of the subsystem, its use-cases, its
internal structure and internal API, as well as its external API for
writing client drivers.

Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
---

Changes in v1 (from RFC):
 - change SPDX identifier to GPL-2.0+ (was GPL-2.0-or-later)
 - fix typos

Changes in v2:
 - add architectural overview for core driver internals
 - reorder chapters
 - improve wording

---
 Documentation/driver-api/index.rst            |   1 +
 .../surface_aggregator/client-api.rst         |  38 ++
 .../driver-api/surface_aggregator/client.rst  | 393 ++++++++++++
 .../surface_aggregator/clients/index.rst      |  10 +
 .../driver-api/surface_aggregator/index.rst   |  21 +
 .../surface_aggregator/internal-api.rst       |  67 ++
 .../surface_aggregator/internal.rst           | 577 ++++++++++++++++++
 .../surface_aggregator/overview.rst           |  77 +++
 .../driver-api/surface_aggregator/ssh.rst     | 344 +++++++++++
 MAINTAINERS                                   |   1 +
 10 files changed, 1529 insertions(+)
 create mode 100644 Documentation/driver-api/surface_aggregator/client-api.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/client.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/clients/index.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/index.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/internal-api.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/internal.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/overview.rst
 create mode 100644 Documentation/driver-api/surface_aggregator/ssh.rst

diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst
index f357f3eb400c..699dc7cac0fb 100644
--- a/Documentation/driver-api/index.rst
+++ b/Documentation/driver-api/index.rst
@@ -97,6 +97,7 @@ available subsections can be seen below.
    rfkill
    serial/index
    sm501
+   surface_aggregator/index
    switchtec
    sync_file
    vfio-mediated-device
diff --git a/Documentation/driver-api/surface_aggregator/client-api.rst b/Documentation/driver-api/surface_aggregator/client-api.rst
new file mode 100644
index 000000000000..8e0b000d0e64
--- /dev/null
+++ b/Documentation/driver-api/surface_aggregator/client-api.rst
@@ -0,0 +1,38 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+===============================
+Client Driver API Documentation
+===============================
+
+.. contents::
+    :depth: 2
+
+
+Serial Hub Communication
+========================
+
+.. kernel-doc:: include/linux/surface_aggregator/serial_hub.h
+
+.. kernel-doc:: drivers/platform/surface/aggregator/ssh_packet_layer.c
+    :export:
+
+
+Controller and Core Interface
+=============================
+
+.. kernel-doc:: include/linux/surface_aggregator/controller.h
+
+.. kernel-doc:: drivers/platform/surface/aggregator/controller.c
+    :export:
+
+.. kernel-doc:: drivers/platform/surface/aggregator/core.c
+    :export:
+
+
+Client Bus and Client Device API
+================================
+
+.. kernel-doc:: include/linux/surface_aggregator/device.h
+
+.. kernel-doc:: drivers/platform/surface/aggregator/bus.c
+    :export:
diff --git a/Documentation/driver-api/surface_aggregator/client.rst b/Documentation/driver-api/surface_aggregator/client.rst
new file mode 100644
index 000000000000..26d13085a117
--- /dev/null
+++ b/Documentation/driver-api/surface_aggregator/client.rst
@@ -0,0 +1,393 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+.. |ssam_controller| replace:: :c:type:`struct ssam_controller <ssam_controller>`
+.. |ssam_device| replace:: :c:type:`struct ssam_device <ssam_device>`
+.. |ssam_device_driver| replace:: :c:type:`struct ssam_device_driver <ssam_device_driver>`
+.. |ssam_client_bind| replace:: :c:func:`ssam_client_bind`
+.. |ssam_client_link| replace:: :c:func:`ssam_client_link`
+.. |ssam_get_controller| replace:: :c:func:`ssam_get_controller`
+.. |ssam_controller_get| replace:: :c:func:`ssam_controller_get`
+.. |ssam_controller_put| replace:: :c:func:`ssam_controller_put`
+.. |ssam_device_alloc| replace:: :c:func:`ssam_device_alloc`
+.. |ssam_device_add| replace:: :c:func:`ssam_device_add`
+.. |ssam_device_remove| replace:: :c:func:`ssam_device_remove`
+.. |ssam_device_driver_register| replace:: :c:func:`ssam_device_driver_register`
+.. |ssam_device_driver_unregister| replace:: :c:func:`ssam_device_driver_unregister`
+.. |module_ssam_device_driver| replace:: :c:func:`module_ssam_device_driver`
+.. |SSAM_DEVICE| replace:: :c:func:`SSAM_DEVICE`
+.. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register`
+.. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister`
+.. |ssam_request_sync| replace:: :c:func:`ssam_request_sync`
+.. |ssam_event_mask| replace:: :c:type:`enum ssam_event_mask <ssam_event_mask>`
+
+
+======================
+Writing Client Drivers
+======================
+
+For the API documentation, refer to:
+
+.. toctree::
+   :maxdepth: 2
+
+   client-api
+
+
+Overview
+========
+
+Client drivers can be set up in two main ways, depending on how the
+corresponding device is made available to the system. We specifically
+differentiate between devices that are presented to the system via one of
+the conventional ways, e.g. as platform devices via ACPI, and devices that
+are non-discoverable and instead need to be explicitly provided by some
+other mechanism, as discussed further below.
+
+
+Non-SSAM Client Drivers
+=======================
+
+All communication with the SAM EC is handled via the |ssam_controller|
+representing that EC to the kernel. Drivers targeting a non-SSAM device (and
+thus not being a |ssam_device_driver|) need to explicitly establish a
+connection/relation to that controller. This can be done via the
+|ssam_client_bind| function. Said function returns a reference to the SSAM
+controller, but, more importantly, also establishes a device link between
+client device and controller (this can also be done separate via
+|ssam_client_link|). It is important to do this, as it, first, guarantees
+that the returned controller is valid for use in the client driver for as
+long as this driver is bound to its device, i.e. that the driver gets
+unbound before the controller ever becomes invalid, and, second, as it
+ensures correct suspend/resume ordering. This setup should be done in the
+driver's probe function, and may be used to defer probing in case the SSAM
+subsystem is not ready yet, for example:
+
+.. code-block:: c
+
+   static int client_driver_probe(struct platform_device *pdev)
+   {
+           struct ssam_controller *ctrl;
+
+           ctrl = ssam_client_bind(&pdev->dev);
+           if (IS_ERR(ctrl))
+                   return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
+
+           // ...
+
+           return 0;
+   }
+
+The controller may be separately obtained via |ssam_get_controller| and its
+lifetime be guaranteed via |ssam_controller_get| and |ssam_controller_put|.
+Note that none of these functions, however, guarantee that the controller
+will not be shut down or suspended. These functions essentially only operate
+on the reference, i.e. only guarantee a bare minimum of accessibility
+without any guarantees at all on practical operability.
+
+
+Adding SSAM Devices
+===================
+
+If a device does not already exist/is not already provided via conventional
+means, it should be provided as |ssam_device| via the SSAM client device
+hub. New devices can be added to this hub by entering their UID into the
+corresponding registry. SSAM devices can also be manually allocated via
+|ssam_device_alloc|, subsequently to which they have to be added via
+|ssam_device_add| and eventually removed via |ssam_device_remove|. By
+default, the parent of the device is set to the controller device provided
+for allocation, however this may be changed before the device is added. Note
+that, when changing the parent device, care must be taken to ensure that the
+controller lifetime and suspend/resume ordering guarantees, in the default
+setup provided through the parent-child relation, are preserved. If
+necessary, by use of |ssam_client_link| as is done for non-SSAM client
+drivers and described in more detail above.
+
+A client device must always be removed by the party which added the
+respective device before the controller shuts down. Such removal can be
+guaranteed by linking the driver providing the SSAM device to the controller
+via |ssam_client_link|, causing it to unbind before the controller driver
+unbinds. Client devices registered with the controller as parent are
+automatically removed when the controller shuts down, but this should not be
+relied upon, especially as this does not extend to client devices with a
+different parent.
+
+
+SSAM Client Drivers
+===================
+
+SSAM client device drivers are, in essence, no different than other device
+driver types. They are represented via |ssam_device_driver| and bind to a
+|ssam_device| via its UID (:c:type:`struct ssam_device.uid <ssam_device>`)
+member and the match table
+(:c:type:`struct ssam_device_driver.match_table <ssam_device_driver>`),
+which should be set when declaring the driver struct instance. Refer to the
+|SSAM_DEVICE| macro documentation for more details on how to define members
+of the driver's match table.
+
+The UID for SSAM client devices consists of a ``domain``, a ``category``,
+a ``target``, an ``instance``, and a ``function``. The ``domain`` is used
+differentiate between physical SAM devices
+(:c:type:`SSAM_DOMAIN_SERIALHUB <ssam_device_domain>`), i.e. devices that can
+be accessed via the Surface Serial Hub, and virtual ones
+(:c:type:`SSAM_DOMAIN_VIRTUAL <ssam_device_domain>`), such as client-device
+hubs, that have no real representation on the SAM EC and are solely used on
+the kernel/driver-side. For physical devices, ``category`` represents the
+target category, ``target`` the target ID, and ``instance`` the instance ID
+used to access the physical SAM device. In addition, ``function`` references
+a specific device functionality, but has no meaning to the SAM EC. The
+(default) name of a client device is generated based on its UID.
+
+A driver instance can be registered via |ssam_device_driver_register| and
+unregistered via |ssam_device_driver_unregister|. For convenience, the
+|module_ssam_device_driver| macro may be used to define module init- and
+exit-functions registering the driver.
+
+The controller associated with a SSAM client device can be found in its
+:c:type:`struct ssam_device.ctrl <ssam_device>` member. This reference is
+guaranteed to be valid for at least as long as the client driver is bound,
+but should also be valid for as long as the client device exists. Note,
+however, that access outside of the bound client driver must ensure that the
+controller device is not suspended while making any requests or
+(un-)registering event notifiers (and thus should generally be avoided). This
+is guaranteed when the controller is accessed from inside the bound client
+driver.
+
+
+Making Synchronous Requests
+===========================
+
+Synchronous requests are (currently) the main form of host-initiated
+communication with the EC. There are a couple of ways to define and execute
+such requests, however, most of them boil down to something similar as shown
+in the example below. This example defines a write-read request, meaning
+that the caller provides an argument to the SAM EC and receives a response.
+The caller needs to know the (maximum) length of the response payload and
+provide a buffer for it.
+
+Care must be taken to ensure that any command payload data passed to the SAM
+EC is provided in little-endian format and, similarly, any response payload
+data received from it is converted from little-endian to host endianness.
+
+.. code-block:: c
+
+   int perform_request(struct ssam_controller *ctrl, u32 arg, u32 *ret)
+   {
+           struct ssam_request rqst;
+           struct ssam_response resp;
+           int status;
+
+           /* Convert request argument to little-endian. */
+           __le32 arg_le = cpu_to_le32(arg);
+           __le32 ret_le = cpu_to_le32(0);
+
+           /*
+            * Initialize request specification. Replace this with your values.
+            * The rqst.payload field may be NULL if rqst.length is zero,
+            * indicating that the request does not have any argument.
+            *
+            * Note: The request parameters used here are not valid, i.e.
+            *       they do not correspond to an actual SAM/EC request.
+            */
+           rqst.target_category = SSAM_SSH_TC_SAM;
+           rqst.target_id = 0x01;
+           rqst.command_id = 0x02;
+           rqst.instance_id = 0x03;
+           rqst.flags = SSAM_REQUEST_HAS_RESPONSE;
+           rqst.length = sizeof(arg_le);
+           rqst.payload = (u8 *)&arg_le;
+
+           /* Initialize request response. */
+           resp.capacity = sizeof(ret_le);
+           resp.length = 0;
+           resp.pointer = (u8 *)&ret_le;
+
+           /*
+            * Perform actual request. The response pointer may be null in case
+            * the request does not have any response. This must be consistent
+            * with the SSAM_REQUEST_HAS_RESPONSE flag set in the specification
+            * above.
+            */
+           status = ssam_request_sync(ctrl, &rqst, &resp);
+
+           /*
+            * Alternatively use
+            *
+            *   ssam_request_sync_onstack(ctrl, &rqst, &resp, sizeof(arg_le));
+            *
+            * to perform the request, allocating the message buffer directly
+            * on the stack as opposed to allocation via kzalloc().
+            */
+
+           /*
+            * Convert request response back to native format. Note that in the
+            * error case, this value is not touched by the SSAM core, i.e.
+            * 'ret_le' will be zero as specified in its initialization.
+            */
+           *ret = le32_to_cpu(ret_le);
+
+           return status;
+   }
+
+Note that |ssam_request_sync| in its essence is a wrapper over lower-level
+request primitives, which may also be used to perform requests. Refer to its
+implementation and documentation for more details.
+
+An arguably more user-friendly way of defining such functions is by using
+one of the generator macros, for example via:
+
+.. code-block:: c
+
+   SSAM_DEFINE_SYNC_REQUEST_W(__ssam_tmp_perf_mode_set, __le32, {
+           .target_category = SSAM_SSH_TC_TMP,
+           .target_id       = 0x01,
+           .command_id      = 0x03,
+           .instance_id     = 0x00,
+   });
+
+This example defines a function
+
+.. code-block:: c
+
+   int __ssam_tmp_perf_mode_set(struct ssam_controller *ctrl, const __le32 *arg);
+
+executing the specified request, with the controller passed in when calling
+said function. In this example, the argument is provided via the ``arg``
+pointer. Note that the generated function allocates the message buffer on
+the stack. Thus, if the argument provided via the request is large, these
+kinds of macros should be avoided. Also note that, in contrast to the
+previous non-macro example, this function does not do any endianness
+conversion, which has to be handled by the caller. Apart from those
+differences the function generated by the macro is similar to the one
+provided in the non-macro example above.
+
+The full list of such function-generating macros is
+
+- :c:func:`SSAM_DEFINE_SYNC_REQUEST_N` for requests without return value and
+  without argument.
+- :c:func:`SSAM_DEFINE_SYNC_REQUEST_R` for requests with return value but no
+  argument.
+- :c:func:`SSAM_DEFINE_SYNC_REQUEST_W` for requests without return value but
+  with argument.
+
+Refer to their respective documentation for more details. For each one of
+these macros, a special variant is provided, which targets request types
+applicable to multiple instances of the same device type:
+
+- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_N`
+- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_R`
+- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_W`
+
+The difference of those macros to the previously mentioned versions is, that
+the device target and instance IDs are not fixed for the generated function,
+but instead have to be provided by the caller of said function.
+
+Additionally, variants for direct use with client devices, i.e.
+|ssam_device|, are also provided. These can, for example, be used as
+follows:
+
+.. code-block:: c
+
+   SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_sta, __le32, {
+           .target_category = SSAM_SSH_TC_BAT,
+           .command_id      = 0x01,
+   });
+
+This invocation of the macro defines a function
+
+.. code-block:: c
+
+   int ssam_bat_get_sta(struct ssam_device *sdev, __le32 *ret);
+
+executing the specified request, using the device IDs and controller given
+in the client device. The full list of such macros for client devices is:
+
+- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_N`
+- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_R`
+- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_W`
+
+
+Handling Events
+===============
+
+To receive events from the SAM EC, an event notifier must be registered for
+the desired event via |ssam_notifier_register|. The notifier must be
+unregistered via |ssam_notifier_unregister| once it is not required any
+more.
+
+Event notifiers are registered by providing (at minimum) a callback to call
+in case an event has been received, the registry specifying how the event
+should be enabled, an event ID specifying for which target category and,
+optionally and depending on the registry used, for which instance ID events
+should be enabled, and finally, flags describing how the EC will send these
+events. If the specific registry does not enable events by instance ID, the
+instance ID must be set to zero. Additionally, a priority for the respective
+notifier may be specified, which determines its order in relation to any
+other notifier registered for the same target category.
+
+By default, event notifiers will receive all events for the specific target
+category, regardless of the instance ID specified when registering the
+notifier. The core may be instructed to only call a notifier if the target
+ID or instance ID (or both) of the event match the ones implied by the
+notifier IDs (in case of target ID, the target ID of the registry), by
+providing an event mask (see |ssam_event_mask|).
+
+In general, the target ID of the registry is also the target ID of the
+enabled event (with the notable exception being keyboard input events on the
+Surface Laptop 1 and 2, which are enabled via a registry with target ID 1,
+but provide events with target ID 2).
+
+A full example for registering an event notifier and handling received
+events is provided below:
+
+.. code-block:: c
+
+   u32 notifier_callback(struct ssam_event_notifier *nf,
+                         const struct ssam_event *event)
+   {
+           int status = ...
+
+           /* Handle the event here ... */
+
+           /* Convert return value and indicate that we handled the event. */
+           return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED;
+   }
+
+   int setup_notifier(struct ssam_device *sdev,
+                      struct ssam_event_notifier *nf)
+   {
+           /* Set priority wrt. other handlers of same target category. */
+           nf->base.priority = 1;
+
+           /* Set event/notifier callback. */
+           nf->base.fn = notifier_callback;
+
+           /* Specify event registry, i.e. how events get enabled/disabled. */
+           nf->event.reg = SSAM_EVENT_REGISTRY_KIP;
+
+           /* Specify which event to enable/disable */
+           nf->event.id.target_category = sdev->uid.category;
+           nf->event.id.instance = sdev->uid.instance;
+
+           /*
+            * Specify for which events the notifier callback gets executed.
+            * This essentially tells the core if it can skip notifiers that
+            * don't have target or instance IDs matching those of the event.
+            */
+           nf->event.mask = SSAM_EVENT_MASK_STRICT;
+
+           /* Specify event flags. */
+           nf->event.flags = SSAM_EVENT_SEQUENCED;
+
+           return ssam_notifier_register(sdev->ctrl, nf);
+   }
+
+Multiple event notifiers can be registered for the same event. The event
+handler core takes care of enabling and disabling events when notifiers are
+registered and unregistered, by keeping track of how many notifiers for a
+specific event (combination of registry, event target category, and event
+instance ID) are currently registered. This means that a specific event will
+be enabled when the first notifier for it is being registered and disabled
+when the last notifier for it is being unregistered. Note that the event
+flags are therefore only used on the first registered notifier, however, one
+should take care that notifiers for a specific event are always registered
+with the same flag and it is considered a bug to do otherwise.
diff --git a/Documentation/driver-api/surface_aggregator/clients/index.rst b/Documentation/driver-api/surface_aggregator/clients/index.rst
new file mode 100644
index 000000000000..31e026d96102
--- /dev/null
+++ b/Documentation/driver-api/surface_aggregator/clients/index.rst
@@ -0,0 +1,10 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+===========================
+Client Driver Documentation
+===========================
+
+This is the documentation for client drivers themselves. Refer to
+:doc:`../client` for documentation on how to write client drivers.
+
+.. Place documentation for individual client drivers here.
diff --git a/Documentation/driver-api/surface_aggregator/index.rst b/Documentation/driver-api/surface_aggregator/index.rst
new file mode 100644
index 000000000000..6f3e1094904d
--- /dev/null
+++ b/Documentation/driver-api/surface_aggregator/index.rst
@@ -0,0 +1,21 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+=======================================
+Surface System Aggregator Module (SSAM)
+=======================================
+
+.. toctree::
+   :maxdepth: 2
+
+   overview
+   client
+   clients/index
+   ssh
+   internal
+
+.. only::  subproject and html
+
+   Indices
+   =======
+
+   * :ref:`genindex`
diff --git a/Documentation/driver-api/surface_aggregator/internal-api.rst b/Documentation/driver-api/surface_aggregator/internal-api.rst
new file mode 100644
index 000000000000..639a67b5a392
--- /dev/null
+++ b/Documentation/driver-api/surface_aggregator/internal-api.rst
@@ -0,0 +1,67 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+==========================
+Internal API Documentation
+==========================
+
+.. contents::
+    :depth: 2
+
+
+Packet Transport Layer
+======================
+
+.. kernel-doc:: drivers/platform/surface/aggregator/ssh_parser.h
+    :internal:
+
+.. kernel-doc:: drivers/platform/surface/aggregator/ssh_parser.c
+    :internal:
+
+.. kernel-doc:: drivers/platform/surface/aggregator/ssh_msgb.h
+    :internal:
+
+.. kernel-doc:: drivers/platform/surface/aggregator/ssh_packet_layer.h
+    :internal:
+
+.. kernel-doc:: drivers/platform/surface/aggregator/ssh_packet_layer.c
+    :internal:
+
+
+Request Transport Layer
+=======================
+
+.. kernel-doc:: drivers/platform/surface/aggregator/ssh_request_layer.h
+    :internal:
+
+.. kernel-doc:: drivers/platform/surface/aggregator/ssh_request_layer.c
+    :internal:
+
+
+Controller
+==========
+
+.. kernel-doc:: drivers/platform/surface/aggregator/controller.h
+    :internal:
+
+.. kernel-doc:: drivers/platform/surface/aggregator/controller.c
+    :internal:
+
+
+Client Device Bus
+=================
+
+.. kernel-doc:: drivers/platform/surface/aggregator/bus.c
+    :internal:
+
+
+Core
+====
+
+.. kernel-doc:: drivers/platform/surface/aggregator/core.c
+    :internal:
+
+
+Trace Helpers
+=============
+
+.. kernel-doc:: drivers/platform/surface/aggregator/trace.h
diff --git a/Documentation/driver-api/surface_aggregator/internal.rst b/Documentation/driver-api/surface_aggregator/internal.rst
new file mode 100644
index 000000000000..72704734982a
--- /dev/null
+++ b/Documentation/driver-api/surface_aggregator/internal.rst
@@ -0,0 +1,577 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+.. |ssh_ptl| replace:: :c:type:`struct ssh_ptl <ssh_ptl>`
+.. |ssh_ptl_submit| replace:: :c:func:`ssh_ptl_submit`
+.. |ssh_ptl_cancel| replace:: :c:func:`ssh_ptl_cancel`
+.. |ssh_ptl_shutdown| replace:: :c:func:`ssh_ptl_shutdown`
+.. |ssh_ptl_rx_rcvbuf| replace:: :c:func:`ssh_ptl_rx_rcvbuf`
+.. |ssh_rtl| replace:: :c:type:`struct ssh_rtl <ssh_rtl>`
+.. |ssh_rtl_submit| replace:: :c:func:`ssh_rtl_submit`
+.. |ssh_rtl_cancel| replace:: :c:func:`ssh_rtl_cancel`
+.. |ssh_rtl_shutdown| replace:: :c:func:`ssh_rtl_shutdown`
+.. |ssh_packet| replace:: :c:type:`struct ssh_packet <ssh_packet>`
+.. |ssh_packet_get| replace:: :c:func:`ssh_packet_get`
+.. |ssh_packet_put| replace:: :c:func:`ssh_packet_put`
+.. |ssh_packet_ops| replace:: :c:type:`struct ssh_packet_ops <ssh_packet_ops>`
+.. |ssh_packet_base_priority| replace:: :c:type:`enum ssh_packet_base_priority <ssh_packet_base_priority>`
+.. |ssh_packet_flags| replace:: :c:type:`enum ssh_packet_flags <ssh_packet_flags>`
+.. |SSH_PACKET_PRIORITY| replace:: :c:func:`SSH_PACKET_PRIORITY`
+.. |ssh_frame| replace:: :c:type:`struct ssh_frame <ssh_frame>`
+.. |ssh_command| replace:: :c:type:`struct ssh_command <ssh_command>`
+.. |ssh_request| replace:: :c:type:`struct ssh_request <ssh_request>`
+.. |ssh_request_get| replace:: :c:func:`ssh_request_get`
+.. |ssh_request_put| replace:: :c:func:`ssh_request_put`
+.. |ssh_request_ops| replace:: :c:type:`struct ssh_request_ops <ssh_request_ops>`
+.. |ssh_request_init| replace:: :c:func:`ssh_request_init`
+.. |ssh_request_flags| replace:: :c:type:`enum ssh_request_flags <ssh_request_flags>`
+.. |ssam_controller| replace:: :c:type:`struct ssam_controller <ssam_controller>`
+.. |ssam_device| replace:: :c:type:`struct ssam_device <ssam_device>`
+.. |ssam_device_driver| replace:: :c:type:`struct ssam_device_driver <ssam_device_driver>`
+.. |ssam_client_bind| replace:: :c:func:`ssam_client_bind`
+.. |ssam_client_link| replace:: :c:func:`ssam_client_link`
+.. |ssam_request_sync| replace:: :c:type:`struct ssam_request_sync <ssam_request_sync>`
+.. |ssam_event_registry| replace:: :c:type:`struct ssam_event_registry <ssam_event_registry>`
+.. |ssam_event_id| replace:: :c:type:`struct ssam_event_id <ssam_event_id>`
+.. |ssam_nf| replace:: :c:type:`struct ssam_nf <ssam_nf>`
+.. |ssam_nf_refcount_inc| replace:: :c:func:`ssam_nf_refcount_inc`
+.. |ssam_nf_refcount_dec| replace:: :c:func:`ssam_nf_refcount_dec`
+.. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register`
+.. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister`
+.. |ssam_cplt| replace:: :c:type:`struct ssam_cplt <ssam_cplt>`
+.. |ssam_event_queue| replace:: :c:type:`struct ssam_event_queue <ssam_event_queue>`
+.. |ssam_request_sync_submit| replace:: :c:func:`ssam_request_sync_submit`
+
+=====================
+Core Driver Internals
+=====================
+
+Architectural overview of the Surface System Aggregator Module (SSAM) core
+and Surface Serial Hub (SSH) driver. For the API documentation, refer to:
+
+.. toctree::
+   :maxdepth: 2
+
+   internal-api
+
+
+Overview
+========
+
+The SSAM core implementation is structured in layers, somewhat following the
+SSH protocol structure:
+
+Lower-level packet transport is implemented in the *packet transport layer
+(PTL)*, directly building on top of the serial device (serdev)
+infrastructure of the kernel. As the name indicates, this layer deals with
+the packet transport logic and handles things like packet validation, packet
+acknowledgment (ACKing), packet (retransmission) timeouts, and relaying
+packet payloads to higher-level layers.
+
+Above this sits the *request transport layer (RTL)*. This layer is centered
+around command-type packet payloads, i.e. requests (sent from host to EC),
+responses of the EC to those requests, and events (sent from EC to host).
+It, specifically, distinguishes events from request responses, matches
+responses to their corresponding requests, and implements request timeouts.
+
+The *controller* layer is building on top of this and essentially decides
+how request responses and, especially, events are dealt with. It provides an
+event notifier system, handles event activation/deactivation, provides a
+workqueue for event and asynchronous request completion, and also manages
+the message counters required for building command messages (``SEQ``,
+``RQID``). This layer basically provides a fundamental interface to the SAM
+EC for use in other kernel drivers.
+
+While the controller layer already provides an interface for other kernel
+drivers, the client *bus* extends this interface to provide support for
+native SSAM devices, i.e. devices that are not defined in ACPI and not
+implemented as platform devices, via |ssam_device| and |ssam_device_driver|
+simplify management of client devices and client drivers.
+
+Refer to :doc:`client` for documentation regarding the client device/driver
+API and interface options for other kernel drivers. It is recommended to
+familiarize oneself with that chapter and the :doc:`ssh` before continuing
+with the architectural overview below.
+
+
+Packet Transport Layer
+======================
+
+The packet transport layer is represented via |ssh_ptl| and is structured
+around the following key concepts:
+
+Packets
+-------
+
+Packets are the fundamental transmission unit of the SSH protocol. They are
+managed by the packet transport layer, which is essentially the lowest layer
+of the driver and is built upon by other components of the SSAM core.
+Packets to be transmitted by the SSAM core are represented via |ssh_packet|
+(in contrast, packets received by the core do not have any specific
+structure and are managed entirely via the raw |ssh_frame|).
+
+This structure contains the required fields to manage the packet inside the
+transport layer, as well as a reference to the buffer containing the data to
+be transmitted (i.e. the message wrapped in |ssh_frame|). Most notably, it
+contains an internal reference count, which is used for managing its
+lifetime (accessible via |ssh_packet_get| and |ssh_packet_put|). When this
+counter reaches zero, the ``release()`` callback provided to the packet via
+its |ssh_packet_ops| reference is executed, which may then deallocate the
+packet or its enclosing structure (e.g. |ssh_request|).
+
+In addition to the ``release`` callback, the |ssh_packet_ops| reference also
+provides a ``complete()`` callback, which is run once the packet has been
+completed and provides the status of this completion, i.e. zero on success
+or a negative errno value in case of an error. Once the packet has been
+submitted to the packet transport layer, the ``complete()`` callback is
+always guaranteed to be executed before the ``release()`` callback, i.e. the
+packet will always be completed, either successfully, with an error, or due
+to cancellation, before it will be released.
+
+The state of a packet is managed via its ``state`` flags
+(|ssh_packet_flags|), which also contains the packet type. In particular,
+the following bits are noteworthy:
+
+* ``SSH_PACKET_SF_LOCKED_BIT``: This bit is set when completion, either
+  through error or success, is imminent. It indicates that no further
+  references of the packet should be taken and any existing references
+  should be dropped as soon as possible. The process setting this bit is
+  responsible for removing any references to this packet from the packet
+  queue and pending set.
+
+* ``SSH_PACKET_SF_COMPLETED_BIT``: This bit is set by the process running the
+  ``complete()`` callback and is used to ensure that this callback only runs
+  once.
+
+* ``SSH_PACKET_SF_QUEUED_BIT``: This bit is set when the packet is queued on
+  the packet queue and cleared when it is dequeued.
+
+* ``SSH_PACKET_SF_PENDING_BIT``: This bit is set when the packet is added to
+  the pending set and cleared when it is removed from it.
+
+Packet Queue
+------------
+
+The packet queue is the first of the two fundamental collections in the
+packet transport layer. It is a priority queue, with priority of the
+respective packets based on the packet type (major) and number of tries
+(minor). See |SSH_PACKET_PRIORITY| for more details on the priority value.
+
+All packets to be transmitted by the transport layer must be submitted to
+this queue via |ssh_ptl_submit|. Note that this includes control packets
+sent by the transport layer itself. Internally, data packets can be
+re-submitted to this queue due to timeouts or NAK packets sent by the EC.
+
+Pending Set
+-----------
+
+The pending set is the second of the two fundamental collections in the
+packet transport layer. It stores references to packets that have already
+been transmitted, but wait for acknowledgment (e.g. the corresponding ACK
+packet) by the EC.
+
+Note that a packet may both be pending and queued if it has been
+re-submitted due to a packet acknowledgment timeout or NAK. On such a
+re-submission, packets are not removed from the pending set.
+
+Transmitter Thread
+------------------
+
+The transmitter thread is responsible for most of the actual work regarding
+packet transmission. In each iteration, it (waits for and) checks if the
+next packet on the queue (if any) can be transmitted and, if so, removes it
+from the queue and increments its counter for the number of transmission
+attempts, i.e. tries. If the packet is sequenced, i.e. requires an ACK by
+the EC, the packet is added to the pending set. Next, the packet's data is
+submitted to the serdev subsystem. In case of an error or timeout during
+this submission, the packet is completed by the transmitter thread with the
+status value of the callback set accordingly. In case the packet is
+unsequenced, i.e. does not require an ACK by the EC, the packet is completed
+with success on the transmitter thread.
+
+Transmission of sequenced packets is limited by the number of concurrently
+pending packets, i.e. a limit on how many packets may be waiting for an ACK
+from the EC in parallel. This limit is currently set to one (see :doc:`ssh`
+for the reasoning behind this). Control packets (i.e. ACK and NAK) can
+always be transmitted.
+
+Receiver Thread
+---------------
+
+Any data received from the EC is put into a FIFO buffer for further
+processing. This processing happens on the receiver thread. The receiver
+thread parses and validates the received message into its |ssh_frame| and
+corresponding payload. It prepares and submits the necessary ACK (and on
+validation error or invalid data NAK) packets for the received messages.
+
+This thread also handles further processing, such as matching ACK messages
+to the corresponding pending packet (via sequence ID) and completing it, as
+well as initiating re-submission of all currently pending packets on
+receival of a NAK message (re-submission in case of a NAK is similar to
+re-submission due to timeout, see below for more details on that). Note that
+the successful completion of a sequenced packet will always run on the
+receiver thread (whereas any failure-indicating completion will run on the
+process where the failure occurred).
+
+Any payload data is forwarded via a callback to the next upper layer, i.e.
+the request transport layer.
+
+Timeout Reaper
+--------------
+
+The packet acknowledgment timeout is a per-packet timeout for sequenced
+packets, started when the respective packet begins (re-)transmission (i.e.
+this timeout is armed once per transmission attempt on the transmitter
+thread). It is used to trigger re-submission or, when the number of tries
+has been exceeded, cancellation of the packet in question.
+
+This timeout is handled via a dedicated reaper task, which is essentially a
+work item (re-)scheduled to run when the next packet is set to time out. The
+work item then checks the set of pending packets for any packets that have
+exceeded the timeout and, if there are any remaining packets, re-schedules
+itself to the next appropriate point in time.
+
+If a timeout has been detected by the reaper, the packet will either be
+re-submitted if it still has some remaining tries left, or completed with
+``-ETIMEDOUT`` as status if not. Note that re-submission, in this case and
+triggered by receival of a NAK, means that the packet is added to the queue
+with a now incremented number of tries, yielding a higher priority. The
+timeout for the packet will be disabled until the next transmission attempt
+and the packet remains on the pending set.
+
+Note that due to transmission and packet acknowledgment timeouts, the packet
+transport layer is always guaranteed to make progress, if only through
+timing out packets, and will never fully block.
+
+Concurrency and Locking
+-----------------------
+
+There are two main locks in the packet transport layer: One guarding access
+to the packet queue and one guarding access to the pending set. These
+collections may only be accessed and modified under the respective lock. If
+access to both collections is needed, the pending lock must be acquired
+before the queue lock to avoid deadlocks.
+
+In addition to guarding the collections, after initial packet submission
+certain packet fields may only be accessed under one of the locks.
+Specifically, the packet priority must only be accessed while holding the
+queue lock and the packet timestamp must only be accessed while holding the
+pending lock.
+
+Other parts of the packet transport layer are guarded independently. State
+flags are managed by atomic bit operations and, if necessary, memory
+barriers. Modifications to the timeout reaper work item and expiration date
+are guarded by their own lock.
+
+The reference of the packet to the packet transport layer (``ptl``) is
+somewhat special. It is either set when the upper layer request is submitted
+or, if there is none, when the packet is first submitted. After it is set,
+it will not change its value. Functions that may run concurrently with
+submission, i.e. cancellation, can not rely on the ``ptl`` reference to be
+set. Access to it in these functions is guarded by ``READ_ONCE()``, whereas
+setting ``ptl`` is equally guarded with ``WRITE_ONCE()`` for symmetry.
+
+Some packet fields may be read outside of the respective locks guarding
+them, specifically priority and state for tracing. In those cases, proper
+access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such
+read-only access is only allowed when stale values are not critical.
+
+With respect to the interface for higher layers, packet submission
+(|ssh_ptl_submit|), packet cancellation (|ssh_ptl_cancel|), data receival
+(|ssh_ptl_rx_rcvbuf|), and layer shutdown (|ssh_ptl_shutdown|) may always be
+executed concurrently with respect to each other. Note that packet
+submission may not run concurrently with itself for the same packet.
+Equally, shutdown and data receival may also not run concurrently with
+themselves (but may run concurrently with each other).
+
+
+Request Transport Layer
+=======================
+
+The request transport layer is represented via |ssh_rtl| and builds on top
+of the packet transport layer. It deals with requests, i.e. SSH packets sent
+by the host containing a |ssh_command| as frame payload. This layer
+separates responses to requests from events, which are also sent by the EC
+via a |ssh_command| payload. While responses are handled in this layer,
+events are relayed to the next upper layer, i.e. the controller layer, via
+the corresponding callback. The request transport layer is structured around
+the following key concepts:
+
+Request
+-------
+
+Requests are packets with a command-type payload, sent from host to EC to
+query data from or trigger an action on it (or both simultaneously). They
+are represented by |ssh_request|, wrapping the underlying |ssh_packet|
+storing its message data (i.e. SSH frame with command payload). Note that
+all top-level representations, e.g. |ssam_request_sync| are built upon this
+struct.
+
+As |ssh_request| extends |ssh_packet|, its lifetime is also managed by the
+reference counter inside the packet struct (which can be accessed via
+|ssh_request_get| and |ssh_request_put|). Once the counter reaches zero, the
+``release()`` callback of the |ssh_request_ops| reference of the request is
+called.
+
+Requests can have an optional response that is equally sent via a SSH
+message with command-type payload (from EC to host). The party constructing
+the request must know if a response is expected and mark this in the request
+flags provided to |ssh_request_init|, so that the request transport layer
+can wait for this response.
+
+Similar to |ssh_packet|, |ssh_request| also has a ``complete()`` callback
+provided via its request ops reference and is guaranteed to be completed
+before it is released once it has been submitted to the request transport
+layer via |ssh_rtl_submit|. For a request without a response, successful
+completion will occur once the underlying packet has been successfully
+transmitted by the packet transport layer (i.e. from within the packet
+completion callback). For a request with response, successful completion
+will occur once the response has been received and matched to the request
+via its request ID (which happens on the packet layer's data-received
+callback running on the receiver thread). If the request is completed with
+an error, the status value will be set to the corresponding (negative) errno
+value.
+
+The state of a request is again managed via its ``state`` flags
+(|ssh_request_flags|), which also encode the request type. In particular,
+the following bits are noteworthy:
+
+* ``SSH_REQUEST_SF_LOCKED_BIT``: This bit is set when completion, either
+  through error or success, is imminent. It indicates that no further
+  references of the request should be taken and any existing references
+  should be dropped as soon as possible. The process setting this bit is
+  responsible for removing any references to this request from the request
+  queue and pending set.
+
+* ``SSH_REQUEST_SF_COMPLETED_BIT``: This bit is set by the process running the
+  ``complete()`` callback and is used to ensure that this callback only runs
+  once.
+
+* ``SSH_REQUEST_SF_QUEUED_BIT``: This bit is set when the request is queued on
+  the request queue and cleared when it is dequeued.
+
+* ``SSH_REQUEST_SF_PENDING_BIT``: This bit is set when the request is added to
+  the pending set and cleared when it is removed from it.
+
+Request Queue
+-------------
+
+The request queue is the first of the two fundamental collections in the
+request transport layer. In contrast to the packet queue of the packet
+transport layer, it is not a priority queue and the simple first come first
+serve principle applies.
+
+All requests to be transmitted by the request transport layer must be
+submitted to this queue via |ssh_rtl_submit|. Once submitted, requests may
+not be re-submitted, and will not be re-submitted automatically on timeout.
+Instead, the request is completed with a timeout error. If desired, the
+caller can create and submit a new request for another try, but it must not
+submit the same request again.
+
+Pending Set
+-----------
+
+The pending set is the second of the two fundamental collections in the
+request transport layer. This collection stores references to all pending
+requests, i.e. requests awaiting a response from the EC (similar to what the
+pending set of the packet transport layer does for packets).
+
+Transmitter Task
+----------------
+
+The transmitter task is scheduled when a new request is available for
+transmission. It checks if the next request on the request queue can be
+transmitted and, if so, submits its underlying packet to the packet
+transport layer. This check ensures that only a limited number of
+requests can be pending, i.e. waiting for a response, at the same time. If
+the request requires a response, the request is added to the pending set
+before its packet is submitted.
+
+Packet Completion Callback
+--------------------------
+
+The packet completion callback is executed once the underlying packet of a
+request has been completed. In case of an error completion, the
+corresponding request is completed with the error value provided in this
+callback.
+
+On successful packet completion, further processing depends on the request.
+If the request expects a response, it is marked as transmitted and the
+request timeout is started. If the request does not expect a response, it is
+completed with success.
+
+Data-Received Callback
+----------------------
+
+The data received callback notifies the request transport layer of data
+being received by the underlying packet transport layer via a data-type
+frame. In general, this is expected to be a command-type payload.
+
+If the request ID of the command is one of the request IDs reserved for
+events (one to ``SSH_NUM_EVENTS``, inclusively), it is forwarded to the
+event callback registered in the request transport layer. If the request ID
+indicates a response to a request, the respective request is looked up in
+the pending set and, if found and marked as transmitted, completed with
+success.
+
+Timeout Reaper
+--------------
+
+The request-response-timeout is a per-request timeout for requests expecting
+a response. It is used to ensure that a request does not wait indefinitely
+on a response from the EC and is started after the underlying packet has
+been successfully completed.
+
+This timeout is, similar to the packet acknowledgment timeout on the packet
+transport layer, handled via a dedicated reaper task. This task is
+essentially a work-item (re-)scheduled to run when the next request is set
+to time out. The work item then scans the set of pending requests for any
+requests that have timed out and completes them with ``-ETIMEDOUT`` as
+status. Requests will not be re-submitted automatically. Instead, the issuer
+of the request must construct and submit a new request, if so desired.
+
+Note that this timeout, in combination with packet transmission and
+acknowledgment timeouts, guarantees that the request layer will always make
+progress, even if only through timing out packets, and never fully block.
+
+Concurrency and Locking
+-----------------------
+
+Similar to the packet transport layer, there are two main locks in the
+request transport layer: One guarding access to the request queue and one
+guarding access to the pending set. These collections may only be accessed
+and modified under the respective lock.
+
+Other parts of the request transport layer are guarded independently. State
+flags are (again) managed by atomic bit operations and, if necessary, memory
+barriers. Modifications to the timeout reaper work item and expiration date
+are guarded by their own lock.
+
+Some request fields may be read outside of the respective locks guarding
+them, specifically the state for tracing. In those cases, proper access is
+ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such read-only
+access is only allowed when stale values are not critical.
+
+With respect to the interface for higher layers, request submission
+(|ssh_rtl_submit|), request cancellation (|ssh_rtl_cancel|), and layer
+shutdown (|ssh_rtl_shutdown|) may always be executed concurrently with
+respect to each other. Note that request submission may not run concurrently
+with itself for the same request (and also may only be called once per
+request). Equally, shutdown may also not run concurrently with itself.
+
+
+Controller Layer
+================
+
+The controller layer extends on the request transport layer to provide an
+easy-to-use interface for client drivers. It is represented by
+|ssam_controller| and the SSH driver. While the lower level transport layers
+take care of transmitting and handling packets and requests, the controller
+layer takes on more of a management role. Specifically, it handles device
+initialization, power management, and event handling, including event
+delivery and registration via the (event) completion system (|ssam_cplt|).
+
+Event Registration
+------------------
+
+In general, an event (or rather a class of events) has to be explicitly
+requested by the host before the EC will send it (HID input events seem to
+be the exception). This is done via an event-enable request (similarly,
+events should be disabled via an event-disable request once no longer
+desired).
+
+The specific request used to enable (or disable) an event is given via an
+event registry, i.e. the governing authority of this event (so to speak),
+represented by |ssam_event_registry|. As parameters to this request, the
+target category and, depending on the event registry, instance ID of the
+event to be enabled must be provided. This (optional) instance ID must be
+zero if the registry does not use it. Together, target category and instance
+ID form the event ID, represented by |ssam_event_id|. In short, both, event
+registry and event ID, are required to uniquely identify a respective class
+of events.
+
+Note that a further *request ID* parameter must be provided for the
+enable-event request. This parameter does not influence the class of events
+being enabled, but instead is set as the request ID (RQID) on each event of
+this class sent by the EC. It is used to identify events (as a limited
+number of request IDs is reserved for use in events only, specifically one
+to ``SSH_NUM_EVENTS`` inclusively) and also map events to their specific
+class. Currently, the controller always sets this parameter to the target
+category specified in |ssam_event_id|.
+
+As multiple client drivers may rely on the same (or overlapping) classes of
+events and enable/disable calls are strictly binary (i.e. on/off), the
+controller has to manage access to these events. It does so via reference
+counting, storing the counter inside an RB-tree based mapping with event
+registry and ID as key (there is no known list of valid event registry and
+event ID combinations). See |ssam_nf|, |ssam_nf_refcount_inc|, and
+|ssam_nf_refcount_dec| for details.
+
+This management is done together with notifier registration (described in
+the next section) via the top-level |ssam_notifier_register| and
+|ssam_notifier_unregister| functions.
+
+Event Delivery
+--------------
+
+To receive events, a client driver has to register an event notifier via
+|ssam_notifier_register|. This increments the reference counter for that
+specific class of events (as detailed in the previous section), enables the
+class on the EC (if it has not been enabled already), and installs the
+provided notifier callback.
+
+Notifier callbacks are stored in lists, with one (RCU) list per target
+category (provided via the event ID; NB: there is a fixed known number of
+target categories). There is no known association from the combination of
+event registry and event ID to the command data (target ID, target category,
+command ID, and instance ID) that can be provided by an event class, apart
+from target category and instance ID given via the event ID.
+
+Note that due to the way notifiers are (or rather have to be) stored, client
+drivers may receive events that they have not requested and need to account
+for them. Specifically, they will, by default, receive all events from the
+same target category. To simplify dealing with this, filtering of events by
+target ID (provided via the event registry) and instance ID (provided via
+the event ID) can be requested when registering a notifier. This filtering
+is applied when iterating over the notifiers at the time they are executed.
+
+All notifier callbacks are executed on a dedicated workqueue, the so-called
+completion workqueue. After an event has been received via the callback
+installed in the request layer (running on the receiver thread of the packet
+transport layer), it will be put on its respective event queue
+(|ssam_event_queue|). From this event queue the completion work item of that
+queue (running on the completion workqueue) will pick up the event and
+execute the notifier callback. This is done to avoid blocking on the
+receiver thread.
+
+There is one event queue per combination of target ID and target category.
+This is done to ensure that notifier callbacks are executed in sequence for
+events of the same target ID and target category. Callbacks can be executed
+in parallel for events with a different combination of target ID and target
+category.
+
+Concurrency and Locking
+-----------------------
+
+Most of the concurrency related safety guarantees of the controller are
+provided by the lower-level request transport layer. In addition to this,
+event (un-)registration is guarded by its own lock.
+
+Access to the controller state is guarded by the state lock. This lock is a
+read/write semaphore. The reader part can be used to ensure that the state
+does not change while functions depending on the state to stay the same
+(e.g. |ssam_notifier_register|, |ssam_notifier_unregister|,
+|ssam_request_sync_submit|, and derivatives) are executed and this guarantee
+is not already provided otherwise (e.g. through |ssam_client_bind| or
+|ssam_client_link|). The writer part guards any transitions that will change
+the state, i.e. initialization, destruction, suspension, and resumption.
+
+The controller state may be accessed (read-only) outside the state lock for
+smoke-testing against invalid API usage (e.g. in |ssam_request_sync_submit|).
+Note that such checks are not supposed to (and will not) protect against all
+invalid usages, but rather aim to help catch them. In those cases, proper
+variable access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``.
+
+Assuming any preconditions on the state not changing have been satisfied,
+all non-initialization and non-shutdown functions may run concurrently with
+each other. This includes |ssam_notifier_register|, |ssam_notifier_unregister|,
+|ssam_request_sync_submit|, as well as all functions building on top of those.
diff --git a/Documentation/driver-api/surface_aggregator/overview.rst b/Documentation/driver-api/surface_aggregator/overview.rst
new file mode 100644
index 000000000000..1e9d57e50063
--- /dev/null
+++ b/Documentation/driver-api/surface_aggregator/overview.rst
@@ -0,0 +1,77 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+========
+Overview
+========
+
+The Surface/System Aggregator Module (SAM, SSAM) is an (arguably *the*)
+embedded controller (EC) on Microsoft Surface devices. It has been originally
+introduced on 4th generation devices (Surface Pro 4, Surface Book 1), but
+its responsibilities and feature-set have since been expanded significantly
+with the following generations.
+
+
+Features and Integration
+========================
+
+Not much is currently known about SAM on 4th generation devices (Surface Pro
+4, Surface Book 1), due to the use of a different communication interface
+between host and EC (as detailed below). On 5th (Surface Pro 2017, Surface
+Book 2, Surface Laptop 1) and later generation devices, SAM is responsible
+for providing battery information (both current status and static values,
+such as maximum capacity etc.), as well as an assortment of temperature
+sensors (e.g. skin temperature) and cooling/performance-mode setting to the
+host. On the Surface Book 2, specifically, it additionally provides an
+interface for properly handling clipboard detachment (i.e. separating the
+display part from the keyboard part of the device), on the Surface Laptop 1
+and 2 it is required for keyboard HID input. This HID subsystem has been
+restructured for 7th generation devices and on those, specifically Surface
+Laptop 3 and Surface Book 3, is responsible for all major HID input (i.e.
+keyboard and touchpad).
+
+While features have not changed much on a coarse level since the 5th
+generation, internal interfaces have undergone some rather large changes. On
+5th and 6th generation devices, both battery and temperature information is
+exposed to ACPI via a shim driver (referred to as Surface ACPI Notify, or
+SAN), translating ACPI generic serial bus write-/read-accesses to SAM
+requests. On 7th generation devices, this additional layer is gone and these
+devices require a driver hooking directly into the SAM interface. Equally,
+on newer generations, less devices are declared in ACPI, making them a bit
+harder to discover and requiring us to hard-code a sort of device registry.
+Due to this, a SSAM bus and subsystem with client devices
+(:c:type:`struct ssam_device <ssam_device>`) has been implemented.
+
+
+Communication
+=============
+
+The type of communication interface between host and EC depends on the
+generation of the Surface device. On 4th generation devices, host and EC
+communicate via HID, specifically using a HID-over-I2C device, whereas on
+5th and later generations, communication takes place via a USART serial
+device. In accordance to the drivers found on other operating systems, we
+refer to the serial device and its driver as Surface Serial Hub (SSH). When
+needed, we differentiate between both types of SAM by referring to them as
+SAM-over-SSH and SAM-over-HID.
+
+Currently, this subsystem only supports SAM-over-SSH. The SSH communication
+interface is described in more detail below. The HID interface has not been
+reverse engineered yet and it is, at the moment, unclear how many (and
+which) concepts of the SSH interface detailed below can be transferred to
+it.
+
+Surface Serial Hub
+------------------
+
+As already elaborated above, the Surface Serial Hub (SSH) is the
+communication interface for SAM on 5th- and all later-generation Surface
+devices. On the highest level, communication can be separated into two main
+types: Requests, messages sent from host to EC that may trigger a direct
+response from the EC (explicitly associated with the request), and events
+(sometimes also referred to as notifications), sent from EC to host without
+being a direct response to a previous request. We may also refer to requests
+without response as commands. In general, events need to be enabled via one
+of multiple dedicated requests before they are sent by the EC.
+
+See :doc:`ssh` for a more technical protocol documentation and
+:doc:`internal` for an overview of the internal driver architecture.
diff --git a/Documentation/driver-api/surface_aggregator/ssh.rst b/Documentation/driver-api/surface_aggregator/ssh.rst
new file mode 100644
index 000000000000..bf007d6c9873
--- /dev/null
+++ b/Documentation/driver-api/surface_aggregator/ssh.rst
@@ -0,0 +1,344 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+.. |u8| replace:: :c:type:`u8 <u8>`
+.. |u16| replace:: :c:type:`u16 <u16>`
+.. |TYPE| replace:: ``TYPE``
+.. |LEN| replace:: ``LEN``
+.. |SEQ| replace:: ``SEQ``
+.. |SYN| replace:: ``SYN``
+.. |NAK| replace:: ``NAK``
+.. |ACK| replace:: ``ACK``
+.. |DATA| replace:: ``DATA``
+.. |DATA_SEQ| replace:: ``DATA_SEQ``
+.. |DATA_NSQ| replace:: ``DATA_NSQ``
+.. |TC| replace:: ``TC``
+.. |TID| replace:: ``TID``
+.. |IID| replace:: ``IID``
+.. |RQID| replace:: ``RQID``
+.. |CID| replace:: ``CID``
+
+===========================
+Surface Serial Hub Protocol
+===========================
+
+The Surface Serial Hub (SSH) is the central communication interface for the
+embedded Surface Aggregator Module controller (SAM or EC), found on newer
+Surface generations. We will refer to this protocol and interface as
+SAM-over-SSH, as opposed to SAM-over-HID for the older generations.
+
+On Surface devices with SAM-over-SSH, SAM is connected to the host via UART
+and defined in ACPI as device with ID ``MSHW0084``. On these devices,
+significant functionality is provided via SAM, including access to battery
+and power information and events, thermal read-outs and events, and many
+more. For Surface Laptops, keyboard input is handled via HID directed
+through SAM, on the Surface Laptop 3 and Surface Book 3 this also includes
+touchpad input.
+
+Note that the standard disclaimer for this subsystem also applies to this
+document: All of this has been reverse-engineered and may thus be erroneous
+and/or incomplete.
+
+All CRCs used in the following are two-byte ``crc_ccitt_false(0xffff, ...)``.
+All multi-byte values are little-endian, there is no implicit padding between
+values.
+
+
+SSH Packet Protocol: Definitions
+================================
+
+The fundamental communication unit of the SSH protocol is a frame
+(:c:type:`struct ssh_frame <ssh_frame>`). A frame consists of the following
+fields, packed together and in order:
+
+.. flat-table:: SSH Frame
+   :widths: 1 1 4
+   :header-rows: 1
+
+   * - Field
+     - Type
+     - Description
+
+   * - |TYPE|
+     - |u8|
+     - Type identifier of the frame.
+
+   * - |LEN|
+     - |u16|
+     - Length of the payload associated with the frame.
+
+   * - |SEQ|
+     - |u8|
+     - Sequence ID (see explanation below).
+
+Each frame structure is followed by a CRC over this structure. The CRC over
+the frame structure (|TYPE|, |LEN|, and |SEQ| fields) is placed directly
+after the frame structure and before the payload. The payload is followed by
+its own CRC (over all payload bytes). If the payload is not present (i.e.
+the frame has ``LEN=0``), the CRC of the payload is still present and will
+evaluate to ``0xffff``. The |LEN| field does not include any of the CRCs, it
+equals the number of bytes inbetween the CRC of the frame and the CRC of the
+payload.
+
+Additionally, the following fixed two-byte sequences are used:
+
+.. flat-table:: SSH Byte Sequences
+   :widths: 1 1 4
+   :header-rows: 1
+
+   * - Name
+     - Value
+     - Description
+
+   * - |SYN|
+     - ``[0xAA, 0x55]``
+     - Synchronization bytes.
+
+A message consists of |SYN|, followed by the frame (|TYPE|, |LEN|, |SEQ| and
+CRC) and, if specified in the frame (i.e. ``LEN > 0``), payload bytes,
+followed finally, regardless if the payload is present, the payload CRC. The
+messages corresponding to an exchange are, in part, identified by having the
+same sequence ID (|SEQ|), stored inside the frame (more on this in the next
+section). The sequence ID is a wrapping counter.
+
+A frame can have the following types
+(:c:type:`enum ssh_frame_type <ssh_frame_type>`):
+
+.. flat-table:: SSH Frame Types
+   :widths: 1 1 4
+   :header-rows: 1
+
+   * - Name
+     - Value
+     - Short Description
+
+   * - |NAK|
+     - ``0x04``
+     - Sent on error in previously received message.
+
+   * - |ACK|
+     - ``0x40``
+     - Sent to acknowledge receival of |DATA| frame.
+
+   * - |DATA_SEQ|
+     - ``0x80``
+     - Sent to transfer data. Sequenced.
+
+   * - |DATA_NSQ|
+     - ``0x00``
+     - Same as |DATA_SEQ|, but does not need to be ACKed.
+
+Both |NAK|- and |ACK|-type frames are used to control flow of messages and
+thus do not carry a payload. |DATA_SEQ|- and |DATA_NSQ|-type frames on the
+other hand must carry a payload. The flow sequence and interaction of
+different frame types will be described in more depth in the next section.
+
+
+SSH Packet Protocol: Flow Sequence
+==================================
+
+Each exchange begins with |SYN|, followed by a |DATA_SEQ|- or
+|DATA_NSQ|-type frame, followed by its CRC, payload, and payload CRC. In
+case of a |DATA_NSQ|-type frame, the exchange is then finished. In case of a
+|DATA_SEQ|-type frame, the receiving party has to acknowledge receival of
+the frame by responding with a message containing an |ACK|-type frame with
+the same sequence ID of the |DATA| frame. In other words, the sequence ID of
+the |ACK| frame specifies the |DATA| frame to be acknowledged. In case of an
+error, e.g. an invalid CRC, the receiving party responds with a message
+containing an |NAK|-type frame. As the sequence ID of the previous data
+frame, for which an error is indicated via the |NAK| frame, cannot be relied
+upon, the sequence ID of the |NAK| frame should not be used and is set to
+zero. After receival of an |NAK| frame, the sending party should re-send all
+outstanding (non-ACKed) messages.
+
+Sequence IDs are not synchronized between the two parties, meaning that they
+are managed independently for each party. Identifying the messages
+corresponding to a single exchange thus relies on the sequence ID as well as
+the type of the message, and the context. Specifically, the sequence ID is
+used to associate an ``ACK`` with its ``DATA_SEQ``-type frame, but not
+``DATA_SEQ``- or ``DATA_NSQ``-type frames with other ``DATA``- type frames.
+
+An example exchange might look like this:
+
+::
+
+    tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) -----------------------------
+    rx: ------------------------------------- SYN FRAME(A) CRC(F) CRC(P) --
+
+where both frames have the same sequence ID (``SEQ``). Here, ``FRAME(D)``
+indicates a |DATA_SEQ|-type frame, ``FRAME(A)`` an ``ACK``-type frame,
+``CRC(F)`` the CRC over the previous frame, ``CRC(P)`` the CRC over the
+previous payload. In case of an error, the exchange would look like this:
+
+::
+
+    tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) -----------------------------
+    rx: ------------------------------------- SYN FRAME(N) CRC(F) CRC(P) --
+
+upon which the sender should re-send the message. ``FRAME(N)`` indicates an
+|NAK|-type frame. Note that the sequence ID of the |NAK|-type frame is fixed
+to zero. For |DATA_NSQ|-type frames, both exchanges are the same:
+
+::
+
+    tx: -- SYN FRAME(DATA_NSQ) CRC(F) PAYLOAD CRC(P) ----------------------
+    rx: -------------------------------------------------------------------
+
+Here, an error can be detected, but not corrected or indicated to the
+sending party. These exchanges are symmetric, i.e. switching ``rx`` and
+``tx`` results again in a valid exchange. Currently, no longer exchanges are
+known.
+
+
+Commands: Requests, Responses, and Events
+=========================================
+
+Commands are sent as payload inside a data frame. Currently, this is the
+only known payload type of |DATA| frames, with a payload-type value of
+``0x80`` (:c:type:`SSH_PLD_TYPE_CMD <ssh_payload_type>`).
+
+The command-type payload (:c:type:`struct ssh_command <ssh_command>`)
+consists of an eight-byte command structure, followed by optional and
+variable length command data. The length of this optional data is derived
+from the frame payload length given in the corresponding frame, i.e. it is
+``frame.len - sizeof(struct ssh_command)``. The command struct contains the
+following fields, packed together and in order:
+
+.. flat-table:: SSH Command
+   :widths: 1 1 4
+   :header-rows: 1
+
+   * - Field
+     - Type
+     - Description
+
+   * - |TYPE|
+     - |u8|
+     - Type of the payload. For commands always ``0x80``.
+
+   * - |TC|
+     - |u8|
+     - Target category.
+
+   * - |TID| (out)
+     - |u8|
+     - Target ID for outgoing (host to EC) commands.
+
+   * - |TID| (in)
+     - |u8|
+     - Target ID for incoming (EC to host) commands.
+
+   * - |IID|
+     - |u8|
+     - Instance ID.
+
+   * - |RQID|
+     - |u16|
+     - Request ID.
+
+   * - |CID|
+     - |u8|
+     - Command ID.
+
+The command struct and data, in general, does not contain any failure
+detection mechanism (e.g. CRCs), this is solely done on the frame level.
+
+Command-type payloads are used by the host to send commands and requests to
+the EC as well as by the EC to send responses and events back to the host.
+We differentiate between requests (sent by the host), responses (sent by the
+EC in response to a request), and events (sent by the EC without a preceding
+request).
+
+Commands and events are uniquely identified by their target category
+(``TC``) and command ID (``CID``). The target category specifies a general
+category for the command (e.g. system in general, vs. battery and AC, vs.
+temperature, and so on), while the command ID specifies the command inside
+that category. Only the combination of |TC| + |CID| is unique. Additionally,
+commands have an instance ID (``IID``), which is used to differentiate
+between different sub-devices. For example ``TC=3`` ``CID=1`` is a
+request to get the temperature on a thermal sensor, where |IID| specifies
+the respective sensor. If the instance ID is not used, it should be set to
+zero. If instance IDs are used, they, in general, start with a value of one,
+whereas zero may be used for instance independent queries, if applicable. A
+response to a request should have the same target category, command ID, and
+instance ID as the corresponding request.
+
+Responses are matched to their corresponding request via the request ID
+(``RQID``) field. This is a 16 bit wrapping counter similar to the sequence
+ID on the frames. Note that the sequence ID of the frames for a
+request-response pair does not match. Only the request ID has to match.
+Frame-protocol wise these are two separate exchanges, and may even be
+separated, e.g. by an event being sent after the request but before the
+response. Not all commands produce a response, and this is not detectable by
+|TC| + |CID|. It is the responsibility of the issuing party to wait for a
+response (or signal this to the communication framework, as is done in
+SAN/ACPI via the ``SNC`` flag).
+
+Events are identified by unique and reserved request IDs. These IDs should
+not be used by the host when sending a new request. They are used on the
+host to, first, detect events and, second, match them with a registered
+event handler. Request IDs for events are chosen by the host and directed to
+the EC when setting up and enabling an event source (via the
+enable-event-source request). The EC then uses the specified request ID for
+events sent from the respective source. Note that an event should still be
+identified by its target category, command ID, and, if applicable, instance
+ID, as a single event source can send multiple different event types. In
+general, however, a single target category should map to a single reserved
+event request ID.
+
+Furthermore, requests, responses, and events have an associated target ID
+(``TID``). This target ID is split into output (host to EC) and input (EC to
+host) fields, with the respecting other field (e.g. output field on incoming
+messages) set to zero. Two ``TID`` values are known: Primary (``0x01``) and
+secondary (``0x02``). In general, the response to a request should have the
+same ``TID`` value, however, the field (output vs. input) should be used in
+accordance to the direction in which the response is sent (i.e. on the input
+field, as responses are generally sent from the EC to the host).
+
+Note that, even though requests and events should be uniquely identifiable
+by target category and command ID alone, the EC may require specific
+target ID and instance ID values to accept a command. A command that is
+accepted for ``TID=1``, for example, may not be accepted for ``TID=2``
+and vice versa.
+
+
+Limitations and Observations
+============================
+
+The protocol can, in theory, handle up to ``U8_MAX`` frames in parallel,
+with up to ``U16_MAX`` pending requests (neglecting request IDs reserved for
+events). In practice, however, this is more limited. From our testing
+(although via a python and thus a user-space program), it seems that the EC
+can handle up to four requests (mostly) reliably in parallel at a certain
+time. With five or more requests in parallel, consistent discarding of
+commands (ACKed frame but no command response) has been observed. For five
+simultaneous commands, this reproducibly resulted in one command being
+dropped and four commands being handled.
+
+However, it has also been noted that, even with three requests in parallel,
+occasional frame drops happen. Apart from this, with a limit of three
+pending requests, no dropped commands (i.e. command being dropped but frame
+carrying command being ACKed) have been observed. In any case, frames (and
+possibly also commands) should be re-sent by the host if a certain timeout
+is exceeded. This is done by the EC for frames with a timeout of one second,
+up to two re-tries (i.e. three transmissions in total). The limit of
+re-tries also applies to received NAKs, and, in a worst case scenario, can
+lead to entire messages being dropped.
+
+While this also seems to work fine for pending data frames as long as no
+transmission failures occur, implementation and handling of these seems to
+depend on the assumption that there is only one non-acknowledged data frame.
+In particular, the detection of repeated frames relies on the last sequence
+number. This means that, if a frame that has been successfully received by
+the EC is sent again, e.g. due to the host not receiving an |ACK|, the EC
+will only detect this if it has the sequence ID of the last frame received
+by the EC. As an example: Sending two frames with ``SEQ=0`` and ``SEQ=1``
+followed by a repetition of ``SEQ=0`` will not detect the second ``SEQ=0``
+frame as such, and thus execute the command in this frame each time it has
+been received, i.e. twice in this example. Sending ``SEQ=0``, ``SEQ=1`` and
+then repeating ``SEQ=1`` will detect the second ``SEQ=1`` as repetition of
+the first one and ignore it, thus executing the contained command only once.
+
+In conclusion, this suggests a limit of at most one pending un-ACKed frame
+(per party, effectively leading to synchronous communication regarding
+frames) and at most three pending commands. The limit to synchronous frame
+transfers seems to be consistent with behavior observed on Windows.
diff --git a/MAINTAINERS b/MAINTAINERS
index 64730461f4df..ff5d60875cec 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11697,6 +11697,7 @@ M:	Maximilian Luz <luzmaximilian@gmail.com>
 S:	Maintained
 W:	https://github.com/linux-surface/surface-aggregator-module
 C:	irc://chat.freenode.net/##linux-surface
+F:	Documentation/driver-api/surface_aggregator/
 F:	drivers/platform/surface/aggregator/
 F:	include/linux/surface_aggregator/

--
2.29.2


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v2 8/9] platform/surface: Add Surface Aggregator user-space interface
  2020-12-03 21:26 [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module Maximilian Luz
  2020-12-03 21:26 ` [PATCH v2 7/9] docs: driver-api: Add Surface Aggregator subsystem documentation Maximilian Luz
@ 2020-12-03 21:26 ` Maximilian Luz
  2020-12-15 16:35   ` Hans de Goede
  2020-12-03 21:26 ` [PATCH v2 9/9] platform/surface: Add Surface ACPI Notify driver Maximilian Luz
  2020-12-06  7:07 ` [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module Leon Romanovsky
  3 siblings, 1 reply; 31+ messages in thread
From: Maximilian Luz @ 2020-12-03 21:26 UTC (permalink / raw)
  To: linux-kernel
  Cc: Maximilian Luz, Hans de Goede, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Greg Kroah-Hartman,
	Jonathan Corbet, Blaž Hrastnik, Dorian Stoll,
	platform-driver-x86, linux-doc

Add a misc-device providing user-space access to the Surface Aggregator
EC, mainly intended for debugging, testing, and reverse-engineering.
This interface gives user-space applications the ability to send
requests to the EC and receive the corresponding responses.

The device-file is managed by a pseudo platform-device and corresponding
driver to avoid dependence on the dedicated bus, allowing it to be
loaded in a minimal configuration.

Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
---

Changes in v1 (from RFC):
 - add copyright lines
 - change SPDX identifier to GPL-2.0+ (was GPL-2.0-or-later)
 - move from debugfs to proper misc-device
 - move interface definitions to uapi header
 - remove versioning
 - allocate platform device dynamically
 - prioritize EFAULT error in IOCTL return
 - use ENOTTY error for unknown IOCTLs
 - fix IOCTL compatibility for 32-bit user-space on 64-bit kernels
 - mark request struct as __attribute__((__packed__))
 - address issues when driver is unbound while file still open

Changes in v2:
 - add ioctl-number.rst entry
 - improve documentation
 - spell check comments and strings, fix typos
 - unify comment style
 - run checkpatch --strict, fix warnings and style issues

---
 .../surface_aggregator/clients/cdev.rst       |  85 +++++
 .../surface_aggregator/clients/index.rst      |  12 +-
 .../userspace-api/ioctl/ioctl-number.rst      |   2 +
 MAINTAINERS                                   |   2 +
 drivers/platform/surface/Kconfig              |  17 +
 drivers/platform/surface/Makefile             |   1 +
 .../surface/surface_aggregator_cdev.c         | 299 ++++++++++++++++++
 include/uapi/linux/surface_aggregator/cdev.h  |  58 ++++
 8 files changed, 475 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/driver-api/surface_aggregator/clients/cdev.rst
 create mode 100644 drivers/platform/surface/surface_aggregator_cdev.c
 create mode 100644 include/uapi/linux/surface_aggregator/cdev.h

diff --git a/Documentation/driver-api/surface_aggregator/clients/cdev.rst b/Documentation/driver-api/surface_aggregator/clients/cdev.rst
new file mode 100644
index 000000000000..f30e77def838
--- /dev/null
+++ b/Documentation/driver-api/surface_aggregator/clients/cdev.rst
@@ -0,0 +1,85 @@
+.. 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_request_flags| replace:: :c:type:`enum ssam_request_flags <ssam_request_flags>`
+
+==============================
+User-Space EC Interface (cdev)
+==============================
+
+The ``surface_aggregator_cdev`` module provides a misc-device for the SSAM
+controller to allow for a (more or less) direct connection from user-space to
+the SAM EC. It is intended to be used for development and debugging, and
+therefore should not be used or relied upon in any other way. Note that this
+module is not loaded automatically, but instead must be loaded manually.
+
+The provided interface is accessible through the ``/dev/surface/aggregator``
+device-file. All functionality of this interface is provided via IOCTLs.
+These IOCTLs and their respective input/output parameter structs are defined in
+``include/uapi/linux/surface_aggregator/cdev.h``.
+
+
+Controller IOCTLs
+=================
+
+The following IOCTLs are provided:
+
+.. flat-table:: Controller IOCTLs
+   :widths: 1 1 1 1 4
+   :header-rows: 1
+
+   * - Type
+     - Number
+     - Direction
+     - Name
+     - Description
+
+   * - ``0xA5``
+     - ``1``
+     - ``WR``
+     - ``REQUEST``
+     - Perform synchronous SAM request.
+
+
+``REQUEST``
+-----------
+
+Defined as ``_IOWR(0xA5, 1, struct ssam_cdev_request)``.
+
+Executes a synchronous SAM request. The request specification is passed in
+as argument of type |ssam_cdev_request|, which is then written to/modified
+by the IOCTL to return status and result of the request.
+
+Request payload data must be allocated separately and is passed in via the
+``payload.data`` and ``payload.length`` members. If a response is required,
+the response buffer must be allocated by the caller and passed in via the
+``response.data`` member. The ``response.length`` member must be set to the
+capacity of this buffer, or if no response is required, zero. Upon
+completion of the request, the call will write the response to the response
+buffer (if its capacity allows it) and overwrite the length field with the
+actual size of the response, in bytes.
+
+Additionally, if the request has a response, this must be indicated via the
+request flags, as is done with in-kernel requests. Request flags can be set
+via the ``flags`` member and the values correspond to the values found in
+|ssam_request_flags|.
+
+Finally, the status of the request itself is returned in the ``status``
+member (a negative errno value indicating failure). Note that failure
+indication of the IOCTL is separated from failure indication of the request:
+The IOCTL returns a negative status code if anything failed during setup of
+the request (``-EFAULT``) or if the provided argument or any of its fields
+are invalid (``-EINVAL``). In this case, the status value of the request
+argument may be set, providing more detail on what went wrong (e.g.
+``-ENOMEM`` for out-of-memory), but this value may also be zero. The IOCTL
+will return with a zero status code in case the request has been set up,
+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:
+
+.. kernel-doc:: include/uapi/linux/surface_aggregator/cdev.h
+   :functions: ssam_cdev_request
diff --git a/Documentation/driver-api/surface_aggregator/clients/index.rst b/Documentation/driver-api/surface_aggregator/clients/index.rst
index 31e026d96102..ab260ec82cfb 100644
--- a/Documentation/driver-api/surface_aggregator/clients/index.rst
+++ b/Documentation/driver-api/surface_aggregator/clients/index.rst
@@ -7,4 +7,14 @@ Client Driver Documentation
 This is the documentation for client drivers themselves. Refer to
 :doc:`../client` for documentation on how to write client drivers.

-.. Place documentation for individual client drivers here.
+.. toctree::
+   :maxdepth: 1
+
+   cdev
+
+.. only::  subproject and html
+
+   Indices
+   =======
+
+   * :ref:`genindex`
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 55a2d9b2ce33..a98895da62b3 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -323,6 +323,8 @@ Code  Seq#    Include File                                           Comments
                                                                      <mailto:tlewis@mindspring.com>
 0xA3  90-9F  linux/dtlk.h
 0xA4  00-1F  uapi/linux/tee.h                                        Generic TEE subsystem
+0xA5  01     linux/surface_aggregator/cdev.h                         Microsoft Surface Platform System Aggregator
+                                                                     <mailto:luzmaximilian@gmail.com>
 0xAA  00-3F  linux/uapi/linux/userfaultfd.h
 0xAB  00-1F  linux/nbd.h
 0xAC  00-1F  linux/raw.h
diff --git a/MAINTAINERS b/MAINTAINERS
index ff5d60875cec..f5a788f445a4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11699,7 +11699,9 @@ W:	https://github.com/linux-surface/surface-aggregator-module
 C:	irc://chat.freenode.net/##linux-surface
 F:	Documentation/driver-api/surface_aggregator/
 F:	drivers/platform/surface/aggregator/
+F:	drivers/platform/surface/surface_aggregator_cdev.c
 F:	include/linux/surface_aggregator/
+F:	include/uapi/linux/surface_aggregator/

 MICROTEK X6 SCANNER
 M:	Oliver Neukum <oliver@neukum.org>
diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig
index 5af22f5141ff..cecad7a0cb7b 100644
--- a/drivers/platform/surface/Kconfig
+++ b/drivers/platform/surface/Kconfig
@@ -40,6 +40,23 @@ config SURFACE_3_POWER_OPREGION
 	  This driver provides support for ACPI operation
 	  region of the Surface 3 battery platform driver.

+config SURFACE_AGGREGATOR_CDEV
+	tristate "Surface System Aggregator Module User-Space Interface"
+	depends on SURFACE_AGGREGATOR
+	help
+	  Provides a misc-device interface to the Surface System Aggregator
+	  Module (SSAM) controller.
+
+	  This option provides a module (called surface_aggregator_cdev), that,
+	  when loaded, will add a client device (and its respective driver) to
+	  the SSAM controller. Said client device manages a misc-device
+	  interface (/dev/surface/aggregator), which can be used by user-space
+	  tools to directly communicate with the SSAM EC by sending requests and
+	  receiving the corresponding responses.
+
+	  The provided interface is intended for debugging and development only,
+	  and should not be used otherwise.
+
 config SURFACE_GPE
 	tristate "Surface GPE/Lid Support Driver"
 	depends on ACPI
diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile
index 034134fe0264..161f0ad05795 100644
--- a/drivers/platform/surface/Makefile
+++ b/drivers/platform/surface/Makefile
@@ -8,5 +8,6 @@ obj-$(CONFIG_SURFACE3_WMI)		+= surface3-wmi.o
 obj-$(CONFIG_SURFACE_3_BUTTON)		+= surface3_button.o
 obj-$(CONFIG_SURFACE_3_POWER_OPREGION)	+= surface3_power.o
 obj-$(CONFIG_SURFACE_AGGREGATOR)	+= aggregator/
+obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV)	+= surface_aggregator_cdev.o
 obj-$(CONFIG_SURFACE_GPE)		+= surface_gpe.o
 obj-$(CONFIG_SURFACE_PRO3_BUTTON)	+= surfacepro3_button.o
diff --git a/drivers/platform/surface/surface_aggregator_cdev.c b/drivers/platform/surface/surface_aggregator_cdev.c
new file mode 100644
index 000000000000..40c30627f33a
--- /dev/null
+++ b/drivers/platform/surface/surface_aggregator_cdev.c
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 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>
+ */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/kref.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rwsem.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <linux/surface_aggregator/cdev.h>
+#include <linux/surface_aggregator/controller.h>
+
+#define SSAM_CDEV_DEVICE_NAME	"surface_aggregator_cdev"
+
+struct ssam_cdev {
+	struct kref kref;
+	struct rw_semaphore lock;
+	struct ssam_controller *ctrl;
+	struct miscdevice mdev;
+};
+
+static void __ssam_cdev_release(struct kref *kref)
+{
+	kfree(container_of(kref, struct ssam_cdev, kref));
+}
+
+static struct ssam_cdev *ssam_cdev_get(struct ssam_cdev *cdev)
+{
+	if (cdev)
+		kref_get(&cdev->kref);
+
+	return cdev;
+}
+
+static void ssam_cdev_put(struct ssam_cdev *cdev)
+{
+	if (cdev)
+		kref_put(&cdev->kref, __ssam_cdev_release);
+}
+
+static int ssam_cdev_device_open(struct inode *inode, struct file *filp)
+{
+	struct miscdevice *mdev = filp->private_data;
+	struct ssam_cdev *cdev = container_of(mdev, struct ssam_cdev, mdev);
+
+	filp->private_data = ssam_cdev_get(cdev);
+	return stream_open(inode, filp);
+}
+
+static int ssam_cdev_device_release(struct inode *inode, struct file *filp)
+{
+	ssam_cdev_put(filp->private_data);
+	return 0;
+}
+
+static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg)
+{
+	struct ssam_cdev_request __user *r;
+	struct ssam_cdev_request rqst;
+	struct ssam_request spec;
+	struct ssam_response rsp;
+	const void __user *plddata;
+	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;
+
+	plddata = u64_to_user_ptr(rqst.payload.data);
+	rspdata = u64_to_user_ptr(rqst.response.data);
+
+	/* Setup basic request fields. */
+	spec.target_category = rqst.target_category;
+	spec.target_id = rqst.target_id;
+	spec.command_id = rqst.command_id;
+	spec.instance_id = rqst.instance_id;
+	spec.flags = rqst.flags;
+	spec.length = rqst.payload.length;
+	spec.payload = NULL;
+
+	rsp.capacity = rqst.response.length;
+	rsp.length = 0;
+	rsp.pointer = NULL;
+
+	/* Get request payload from user-space. */
+	if (spec.length) {
+		if (!plddata) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		spec.payload = kzalloc(spec.length, GFP_KERNEL);
+		if (!spec.payload) {
+			status = -ENOMEM;
+			ret = -EFAULT;
+			goto out;
+		}
+
+		if (copy_from_user((void *)spec.payload, plddata, spec.length)) {
+			ret = -EFAULT;
+			goto out;
+		}
+	}
+
+	/* Allocate response buffer. */
+	if (rsp.capacity) {
+		if (!rspdata) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		rsp.pointer = kzalloc(rsp.capacity, GFP_KERNEL);
+		if (!rsp.pointer) {
+			status = -ENOMEM;
+			ret = -EFAULT;
+			goto out;
+		}
+	}
+
+	/* Perform request. */
+	status = ssam_request_sync(cdev->ctrl, &spec, &rsp);
+	if (status)
+		goto out;
+
+	/* Copy response to user-space. */
+	if (rsp.length && copy_to_user(rspdata, rsp.pointer, rsp.length))
+		ret = -EFAULT;
+
+out:
+	/* Always try to set response-length and status. */
+	tmp = put_user(rsp.length, &r->response.length);
+	if (tmp)
+		ret = tmp;
+
+	tmp = put_user(status, &r->status);
+	if (tmp)
+		ret = tmp;
+
+	/* Cleanup. */
+	kfree(spec.payload);
+	kfree(rsp.pointer);
+
+	return ret;
+}
+
+static long __ssam_cdev_device_ioctl(struct ssam_cdev *cdev, unsigned int cmd,
+				     unsigned long arg)
+{
+	switch (cmd) {
+	case SSAM_CDEV_REQUEST:
+		return ssam_cdev_request(cdev, arg);
+
+	default:
+		return -ENOTTY;
+	}
+}
+
+static long ssam_cdev_device_ioctl(struct file *file, unsigned int cmd,
+				   unsigned long arg)
+{
+	struct ssam_cdev *cdev = file->private_data;
+	long status;
+
+	/* Ensure that controller is valid for as long as we need it. */
+	if (down_read_killable(&cdev->lock))
+		return -ERESTARTSYS;
+
+	if (!cdev->ctrl) {
+		up_read(&cdev->lock);
+		return -ENODEV;
+	}
+
+	status = __ssam_cdev_device_ioctl(cdev, cmd, arg);
+
+	up_read(&cdev->lock);
+	return status;
+}
+
+static const struct file_operations ssam_controller_fops = {
+	.owner          = THIS_MODULE,
+	.open           = ssam_cdev_device_open,
+	.release        = ssam_cdev_device_release,
+	.unlocked_ioctl = ssam_cdev_device_ioctl,
+	.compat_ioctl   = ssam_cdev_device_ioctl,
+	.llseek         = noop_llseek,
+};
+
+static int ssam_dbg_device_probe(struct platform_device *pdev)
+{
+	struct ssam_controller *ctrl;
+	struct ssam_cdev *cdev;
+	int status;
+
+	ctrl = ssam_client_bind(&pdev->dev);
+	if (IS_ERR(ctrl))
+		return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
+
+	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+	if (!cdev)
+		return -ENOMEM;
+
+	kref_init(&cdev->kref);
+	init_rwsem(&cdev->lock);
+	cdev->ctrl = ctrl;
+
+	cdev->mdev.parent   = &pdev->dev;
+	cdev->mdev.minor    = MISC_DYNAMIC_MINOR;
+	cdev->mdev.name     = "surface_aggregator";
+	cdev->mdev.nodename = "surface/aggregator";
+	cdev->mdev.fops     = &ssam_controller_fops;
+
+	status = misc_register(&cdev->mdev);
+	if (status) {
+		kfree(cdev);
+		return status;
+	}
+
+	platform_set_drvdata(pdev, cdev);
+	return 0;
+}
+
+static int ssam_dbg_device_remove(struct platform_device *pdev)
+{
+	struct ssam_cdev *cdev = platform_get_drvdata(pdev);
+
+	misc_deregister(&cdev->mdev);
+
+	/*
+	 * The controller is only guaranteed to be valid for as long as the
+	 * driver is bound. Remove controller so that any lingering open files
+	 * cannot access it any more after we're gone.
+	 */
+	down_write(&cdev->lock);
+	cdev->ctrl = NULL;
+	up_write(&cdev->lock);
+
+	ssam_cdev_put(cdev);
+	return 0;
+}
+
+static struct platform_device *ssam_cdev_device;
+
+static struct platform_driver ssam_cdev_driver = {
+	.probe = ssam_dbg_device_probe,
+	.remove = ssam_dbg_device_remove,
+	.driver = {
+		.name = SSAM_CDEV_DEVICE_NAME,
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+};
+
+static int __init ssam_debug_init(void)
+{
+	int status;
+
+	ssam_cdev_device = platform_device_alloc(SSAM_CDEV_DEVICE_NAME,
+						 PLATFORM_DEVID_NONE);
+	if (!ssam_cdev_device)
+		return -ENOMEM;
+
+	status = platform_device_add(ssam_cdev_device);
+	if (status)
+		goto err_device;
+
+	status = platform_driver_register(&ssam_cdev_driver);
+	if (status)
+		goto err_driver;
+
+	return 0;
+
+err_driver:
+	platform_device_del(ssam_cdev_device);
+err_device:
+	platform_device_put(ssam_cdev_device);
+	return status;
+}
+module_init(ssam_debug_init);
+
+static void __exit ssam_debug_exit(void)
+{
+	platform_driver_unregister(&ssam_cdev_driver);
+	platform_device_unregister(ssam_cdev_device);
+}
+module_exit(ssam_debug_exit);
+
+MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
+MODULE_DESCRIPTION("User-space interface for Surface System Aggregator Module");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/surface_aggregator/cdev.h b/include/uapi/linux/surface_aggregator/cdev.h
new file mode 100644
index 000000000000..1a8bc0249f8e
--- /dev/null
+++ b/include/uapi/linux/surface_aggregator/cdev.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Surface System Aggregator Module (SSAM) user-space EC interface.
+ *
+ * Definitions, structs, and IOCTLs for the /dev/surface/aggregator misc
+ * 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>
+ */
+
+#ifndef _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H
+#define _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/**
+ * struct ssam_cdev_request - Controller request IOCTL argument.
+ * @target_category: Target category of the SAM request.
+ * @target_id:       Target ID of the SAM request.
+ * @command_id:      Command ID of the SAM request.
+ * @instance_id:     Instance ID of the SAM request.
+ * @flags:           SAM Request flags.
+ * @status:          Request status (output).
+ * @payload:         Request payload (input data).
+ * @payload.data:    Pointer to request payload data.
+ * @payload.length:  Length of request payload data (in bytes).
+ * @response:        Request response (output data).
+ * @response.data:   Pointer to response buffer.
+ * @response.length: On input: Capacity of response buffer (in bytes).
+ *                   On output: Length of request response (number of bytes
+ *                   in the buffer that are actually used).
+ */
+struct ssam_cdev_request {
+	__u8 target_category;
+	__u8 target_id;
+	__u8 command_id;
+	__u8 instance_id;
+	__u16 flags;
+	__s16 status;
+
+	struct {
+		__u64 data;
+		__u16 length;
+		__u8 __pad[6];
+	} payload;
+
+	struct {
+		__u64 data;
+		__u16 length;
+		__u8 __pad[6];
+	} response;
+} __attribute__((__packed__));
+
+#define SSAM_CDEV_REQUEST	_IOWR(0xA5, 1, struct ssam_cdev_request)
+
+#endif /* _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H */
--
2.29.2


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v2 9/9] platform/surface: Add Surface ACPI Notify driver
  2020-12-03 21:26 [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module Maximilian Luz
  2020-12-03 21:26 ` [PATCH v2 7/9] docs: driver-api: Add Surface Aggregator subsystem documentation Maximilian Luz
  2020-12-03 21:26 ` [PATCH v2 8/9] platform/surface: Add Surface Aggregator user-space interface Maximilian Luz
@ 2020-12-03 21:26 ` Maximilian Luz
  2020-12-15 17:18   ` Hans de Goede
  2020-12-06  7:07 ` [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module Leon Romanovsky
  3 siblings, 1 reply; 31+ messages in thread
From: Maximilian Luz @ 2020-12-03 21:26 UTC (permalink / raw)
  To: linux-kernel
  Cc: Maximilian Luz, Hans de Goede, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Greg Kroah-Hartman,
	Rafael J . Wysocki, Len Brown, Jonathan Corbet,
	Blaž Hrastnik, Dorian Stoll, platform-driver-x86,
	linux-acpi, linux-doc

The Surface ACPI Notify (SAN) device provides an ACPI interface to the
Surface Aggregator EC, specifically the Surface Serial Hub interface.
This interface allows EC requests to be made from ACPI code and can
convert a subset of EC events back to ACPI notifications.

Specifically, this interface provides a GenericSerialBus operation
region ACPI code can execute a request by writing the request command
data and payload to this operation region and reading back the
corresponding response via a write-then-read operation. Furthermore,
this interface provides a _DSM method to be called when certain events
from the EC have been received, essentially turning them into ACPI
notifications.

The driver provided in this commit essentially takes care of translating
the request data written to the operation region, executing the request,
waiting for it to finish, and finally writing and translating back the
response (if the request has one). Furthermore, this driver takes care
of enabling the events handled via ACPI _DSM calls. Lastly, this driver
also exposes an interface providing discrete GPU (dGPU) power-on
notifications on the Surface Book 2, which are also received via the
operation region interface (but not handled by the SAN driver directly),
making them accessible to other drivers (such as a dGPU hot-plug driver
that may be added later on).

On 5th and 6th generation Surface devices (Surface Pro 5/2017, Pro 6,
Book 2, Laptop 1 and 2), the SAN interface provides full battery and
thermal subsystem access, as well as other EC based functionality. On
those models, battery and thermal sensor devices are implemented as
standard ACPI devices of that type, however, forward ACPI calls to the
corresponding Surface Aggregator EC request via the SAN interface and
receive corresponding notifications (e.g. battery information change)
from it. This interface is therefore required to provide said
functionality on those devices.

Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
---

Changes in v1 (from RFC):
 - add copyright lines
 - change SPDX identifier to GPL-2.0+ (was GPL-2.0-or-later)
 - fix invalid pointer calculation in san_evt_tmp
 - add explicit dependency on CONFIG_ACPI
 - remove default in Kconfig

Changes in v2:
 - drop explicit dependency on ACPI in Kconfig
 - use printk specifier for hex prefix instead of hard-coding it
 - spell check comments and strings, fix typos
 - unify comment style
 - run checkpatch --strict, fix warnings and style issues

---
 .../surface_aggregator/clients/index.rst      |   1 +
 .../surface_aggregator/clients/san.rst        |  44 +
 MAINTAINERS                                   |   2 +
 drivers/platform/surface/Kconfig              |  19 +
 drivers/platform/surface/Makefile             |   1 +
 .../platform/surface/surface_acpi_notify.c    | 886 ++++++++++++++++++
 include/linux/surface_acpi_notify.h           |  39 +
 7 files changed, 992 insertions(+)
 create mode 100644 Documentation/driver-api/surface_aggregator/clients/san.rst
 create mode 100644 drivers/platform/surface/surface_acpi_notify.c
 create mode 100644 include/linux/surface_acpi_notify.h

diff --git a/Documentation/driver-api/surface_aggregator/clients/index.rst b/Documentation/driver-api/surface_aggregator/clients/index.rst
index ab260ec82cfb..3ccabce23271 100644
--- a/Documentation/driver-api/surface_aggregator/clients/index.rst
+++ b/Documentation/driver-api/surface_aggregator/clients/index.rst
@@ -11,6 +11,7 @@ This is the documentation for client drivers themselves. Refer to
    :maxdepth: 1

    cdev
+   san

 .. only::  subproject and html

diff --git a/Documentation/driver-api/surface_aggregator/clients/san.rst b/Documentation/driver-api/surface_aggregator/clients/san.rst
new file mode 100644
index 000000000000..38c2580e7758
--- /dev/null
+++ b/Documentation/driver-api/surface_aggregator/clients/san.rst
@@ -0,0 +1,44 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+.. |san_client_link| replace:: :c:func:`san_client_link`
+.. |san_dgpu_notifier_register| replace:: :c:func:`san_dgpu_notifier_register`
+.. |san_dgpu_notifier_unregister| replace:: :c:func:`san_dgpu_notifier_unregister`
+
+===================
+Surface ACPI Notify
+===================
+
+The Surface ACPI Notify (SAN) device provides the bridge between ACPI and
+SAM controller. Specifically, ACPI code can execute requests and handle
+battery and thermal events via this interface. In addition to this, events
+relating to the discrete GPU (dGPU) of the Surface Book 2 can be sent from
+ACPI code (note: the Surface Book 3 uses a different method for this). The
+only currently known event sent via this interface is a dGPU power-on
+notification. While this driver handles the former part internally, it only
+relays the dGPU events to any other driver interested via its public API and
+does not handle them.
+
+The public interface of this driver is split into two parts: Client
+registration and notifier-block registration.
+
+A client to the SAN interface can be linked as consumer to the SAN device
+via |san_client_link|. This can be used to ensure that the a client
+receiving dGPU events does not miss any events due to the SAN interface not
+being set up as this forces the client driver to unbind once the SAN driver
+is unbound.
+
+Notifier-blocks can be registered by any device for as long as the module is
+loaded, regardless of being linked as client or not. Registration is done
+with |san_dgpu_notifier_register|. If the notifier is not needed any more, it
+should be unregistered via |san_dgpu_notifier_unregister|.
+
+Consult the API documentation below for more details.
+
+
+API Documentation
+=================
+
+.. kernel-doc:: include/linux/surface_acpi_notify.h
+
+.. kernel-doc:: drivers/platform/surface/surface_acpi_notify.c
+    :export:
diff --git a/MAINTAINERS b/MAINTAINERS
index f5a788f445a4..894c9ef1b8d6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11699,7 +11699,9 @@ W:	https://github.com/linux-surface/surface-aggregator-module
 C:	irc://chat.freenode.net/##linux-surface
 F:	Documentation/driver-api/surface_aggregator/
 F:	drivers/platform/surface/aggregator/
+F:	drivers/platform/surface/surface_acpi_notify.c
 F:	drivers/platform/surface/surface_aggregator_cdev.c
+F:	include/linux/surface_acpi_notify.h
 F:	include/linux/surface_aggregator/
 F:	include/uapi/linux/surface_aggregator/

diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig
index cecad7a0cb7b..88818320105e 100644
--- a/drivers/platform/surface/Kconfig
+++ b/drivers/platform/surface/Kconfig
@@ -40,6 +40,25 @@ config SURFACE_3_POWER_OPREGION
 	  This driver provides support for ACPI operation
 	  region of the Surface 3 battery platform driver.

+config SURFACE_ACPI_NOTIFY
+	tristate "Surface ACPI Notify Driver"
+	depends on SURFACE_AGGREGATOR
+	help
+	  Surface ACPI Notify (SAN) driver for Microsoft Surface devices.
+
+	  This driver provides support for the ACPI interface (called SAN) of
+	  the Surface System Aggregator Module (SSAM) EC. This interface is used
+	  on 5th- and 6th-generation Microsoft Surface devices (including
+	  Surface Pro 5 and 6, Surface Book 2, Surface Laptops 1 and 2, and in
+	  reduced functionality on the Surface Laptop 3) to execute SSAM
+	  requests directly from ACPI code, as well as receive SSAM events and
+	  turn them into ACPI notifications. It essentially acts as a
+	  translation layer between the SSAM controller and ACPI.
+
+	  Specifically, this driver may be needed for battery status reporting,
+	  thermal sensor access, and real-time clock information, depending on
+	  the Surface device in question.
+
 config SURFACE_AGGREGATOR_CDEV
 	tristate "Surface System Aggregator Module User-Space Interface"
 	depends on SURFACE_AGGREGATOR
diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile
index 161f0ad05795..3eb971006877 100644
--- a/drivers/platform/surface/Makefile
+++ b/drivers/platform/surface/Makefile
@@ -7,6 +7,7 @@
 obj-$(CONFIG_SURFACE3_WMI)		+= surface3-wmi.o
 obj-$(CONFIG_SURFACE_3_BUTTON)		+= surface3_button.o
 obj-$(CONFIG_SURFACE_3_POWER_OPREGION)	+= surface3_power.o
+obj-$(CONFIG_SURFACE_ACPI_NOTIFY)	+= surface_acpi_notify.o
 obj-$(CONFIG_SURFACE_AGGREGATOR)	+= aggregator/
 obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV)	+= surface_aggregator_cdev.o
 obj-$(CONFIG_SURFACE_GPE)		+= surface_gpe.o
diff --git a/drivers/platform/surface/surface_acpi_notify.c b/drivers/platform/surface/surface_acpi_notify.c
new file mode 100644
index 000000000000..8cd67a669c86
--- /dev/null
+++ b/drivers/platform/surface/surface_acpi_notify.c
@@ -0,0 +1,886 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for the Surface ACPI Notify (SAN) interface/shim.
+ *
+ * Translates communication from ACPI to Surface System Aggregator Module
+ * (SSAM/SAM) requests and back, specifically SAM-over-SSH. Translates SSAM
+ * events back to ACPI notifications. Allows handling of discrete GPU
+ * notifications sent from ACPI via the SAN interface by providing them to any
+ * registered external driver.
+ *
+ * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
+ */
+
+#include <asm/unaligned.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/rwsem.h>
+
+#include <linux/surface_aggregator/controller.h>
+#include <linux/surface_acpi_notify.h>
+
+struct san_data {
+	struct device *dev;
+	struct ssam_controller *ctrl;
+
+	struct acpi_connection_info info;
+
+	struct ssam_event_notifier nf_bat;
+	struct ssam_event_notifier nf_tmp;
+};
+
+#define to_san_data(ptr, member) \
+	container_of(ptr, struct san_data, member)
+
+
+/* -- dGPU notifier interface. ---------------------------------------------- */
+
+struct san_rqsg_if {
+	struct rw_semaphore lock;
+	struct device *dev;
+	struct blocking_notifier_head nh;
+};
+
+static struct san_rqsg_if san_rqsg_if = {
+	.lock = __RWSEM_INITIALIZER(san_rqsg_if.lock),
+	.dev = NULL,
+	.nh = BLOCKING_NOTIFIER_INIT(san_rqsg_if.nh),
+};
+
+static int san_set_rqsg_interface_device(struct device *dev)
+{
+	int status = 0;
+
+	down_write(&san_rqsg_if.lock);
+	if (!san_rqsg_if.dev && dev)
+		san_rqsg_if.dev = dev;
+	else
+		status = -EBUSY;
+	up_write(&san_rqsg_if.lock);
+
+	return status;
+}
+
+/**
+ * san_client_link() - Link client as consumer to SAN device.
+ * @client: The client to link.
+ *
+ * Sets up a device link between the provided client device as consumer and
+ * the SAN device as provider. This function can be used to ensure that the
+ * SAN interface has been set up and will be set up for as long as the driver
+ * of the client device is bound. This guarantees that, during that time, all
+ * dGPU events will be received by any registered notifier.
+ *
+ * The link will be automatically removed once the client device's driver is
+ * unbound.
+ *
+ * Return: Returns zero on success, %-ENXIO if the SAN interface has not been
+ * set up yet, and %-ENOMEM if device link creation failed.
+ */
+int san_client_link(struct device *client)
+{
+	const u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER;
+	struct device_link *link;
+
+	down_read(&san_rqsg_if.lock);
+
+	if (!san_rqsg_if.dev) {
+		up_read(&san_rqsg_if.lock);
+		return -ENXIO;
+	}
+
+	link = device_link_add(client, san_rqsg_if.dev, flags);
+	if (!link) {
+		up_read(&san_rqsg_if.lock);
+		return -ENOMEM;
+	}
+
+	if (READ_ONCE(link->status) == DL_STATE_SUPPLIER_UNBIND) {
+		up_read(&san_rqsg_if.lock);
+		return -ENXIO;
+	}
+
+	up_read(&san_rqsg_if.lock);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(san_client_link);
+
+/**
+ * san_dgpu_notifier_register() - Register a SAN dGPU notifier.
+ * @nb: The notifier-block to register.
+ *
+ * Registers a SAN dGPU notifier, receiving any new SAN dGPU events sent from
+ * ACPI. The registered notifier will be called with &struct san_dgpu_event
+ * as notifier data and the command ID of that event as notifier action.
+ */
+int san_dgpu_notifier_register(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&san_rqsg_if.nh, nb);
+}
+EXPORT_SYMBOL_GPL(san_dgpu_notifier_register);
+
+/**
+ * san_dgpu_notifier_unregister() - Unregister a SAN dGPU notifier.
+ * @nb: The notifier-block to unregister.
+ */
+int san_dgpu_notifier_unregister(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&san_rqsg_if.nh, nb);
+}
+EXPORT_SYMBOL_GPL(san_dgpu_notifier_unregister);
+
+static int san_dgpu_notifier_call(struct san_dgpu_event *evt)
+{
+	int ret;
+
+	ret = blocking_notifier_call_chain(&san_rqsg_if.nh, evt->command, evt);
+	return notifier_to_errno(ret);
+}
+
+
+/* -- ACPI _DSM event relay. ------------------------------------------------ */
+
+#define SAN_DSM_REVISION	0
+
+/* 93b666c5-70c6-469f-a215-3d487c91ab3c */
+static const guid_t SAN_DSM_UUID =
+	GUID_INIT(0x93b666c5, 0x70c6, 0x469f, 0xa2, 0x15, 0x3d,
+		  0x48, 0x7c, 0x91, 0xab, 0x3c);
+
+enum san_dsm_event_fn {
+	SAN_DSM_EVENT_FN_BAT1_STAT = 0x03,
+	SAN_DSM_EVENT_FN_BAT1_INFO = 0x04,
+	SAN_DSM_EVENT_FN_ADP1_STAT = 0x05,
+	SAN_DSM_EVENT_FN_ADP1_INFO = 0x06,
+	SAN_DSM_EVENT_FN_BAT2_STAT = 0x07,
+	SAN_DSM_EVENT_FN_BAT2_INFO = 0x08,
+	SAN_DSM_EVENT_FN_THERMAL   = 0x09,
+	SAN_DSM_EVENT_FN_DPTF      = 0x0a,
+};
+
+enum sam_event_cid_bat {
+	SAM_EVENT_CID_BAT_BIX  = 0x15,
+	SAM_EVENT_CID_BAT_BST  = 0x16,
+	SAM_EVENT_CID_BAT_ADP  = 0x17,
+	SAM_EVENT_CID_BAT_PROT = 0x18,
+	SAM_EVENT_CID_BAT_DPTF = 0x4f,
+};
+
+enum sam_event_cid_tmp {
+	SAM_EVENT_CID_TMP_TRIP = 0x0b,
+};
+
+struct san_event_work {
+	struct delayed_work work;
+	struct device *dev;
+	struct ssam_event event;	/* must be last */
+};
+
+static int san_acpi_notify_event(struct device *dev, u64 func,
+				 union acpi_object *param)
+{
+	acpi_handle san = ACPI_HANDLE(dev);
+	union acpi_object *obj;
+	int status = 0;
+
+	if (!acpi_check_dsm(san, &SAN_DSM_UUID, SAN_DSM_REVISION, 1 << func))
+		return 0;
+
+	dev_dbg(dev, "notify event %#04llx\n", func);
+
+	obj = acpi_evaluate_dsm_typed(san, &SAN_DSM_UUID, SAN_DSM_REVISION,
+				      func, param, ACPI_TYPE_BUFFER);
+	if (!obj)
+		return -EFAULT;
+
+	if (obj->buffer.length != 1 || obj->buffer.pointer[0] != 0) {
+		dev_err(dev, "got unexpected result from _DSM\n");
+		status = -EPROTO;
+	}
+
+	ACPI_FREE(obj);
+	return status;
+}
+
+static int san_evt_bat_adp(struct device *dev, const struct ssam_event *event)
+{
+	int status;
+
+	status = san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_ADP1_STAT, NULL);
+	if (status)
+		return status;
+
+	/*
+	 * Ensure that the battery states get updated correctly. When the
+	 * battery is fully charged and an adapter is plugged in, it sometimes
+	 * is not updated correctly, instead showing it as charging.
+	 * Explicitly trigger battery updates to fix this.
+	 */
+
+	status = san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_BAT1_STAT, NULL);
+	if (status)
+		return status;
+
+	return san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_BAT2_STAT, NULL);
+}
+
+static int san_evt_bat_bix(struct device *dev, const struct ssam_event *event)
+{
+	enum san_dsm_event_fn fn;
+
+	if (event->instance_id == 0x02)
+		fn = SAN_DSM_EVENT_FN_BAT2_INFO;
+	else
+		fn = SAN_DSM_EVENT_FN_BAT1_INFO;
+
+	return san_acpi_notify_event(dev, fn, NULL);
+}
+
+static int san_evt_bat_bst(struct device *dev, const struct ssam_event *event)
+{
+	enum san_dsm_event_fn fn;
+
+	if (event->instance_id == 0x02)
+		fn = SAN_DSM_EVENT_FN_BAT2_STAT;
+	else
+		fn = SAN_DSM_EVENT_FN_BAT1_STAT;
+
+	return san_acpi_notify_event(dev, fn, NULL);
+}
+
+static int san_evt_bat_dptf(struct device *dev, const struct ssam_event *event)
+{
+	union acpi_object payload;
+
+	/*
+	 * The Surface ACPI expects a buffer and not a package. It specifically
+	 * checks for ObjectType (Arg3) == 0x03. This will cause a warning in
+	 * acpica/nsarguments.c, but that warning can be safely ignored.
+	 */
+	payload.type = ACPI_TYPE_BUFFER;
+	payload.buffer.length = event->length;
+	payload.buffer.pointer = (u8 *)&event->data[0];
+
+	return san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_DPTF, &payload);
+}
+
+static unsigned long san_evt_bat_delay(u8 cid)
+{
+	switch (cid) {
+	case SAM_EVENT_CID_BAT_ADP:
+		/*
+		 * Wait for battery state to update before signaling adapter
+		 * change.
+		 */
+		return msecs_to_jiffies(5000);
+
+	case SAM_EVENT_CID_BAT_BST:
+		/* Ensure we do not miss anything important due to caching. */
+		return msecs_to_jiffies(2000);
+
+	default:
+		return 0;
+	}
+}
+
+static bool san_evt_bat(const struct ssam_event *event, struct device *dev)
+{
+	int status;
+
+	switch (event->command_id) {
+	case SAM_EVENT_CID_BAT_BIX:
+		status = san_evt_bat_bix(dev, event);
+		break;
+
+	case SAM_EVENT_CID_BAT_BST:
+		status = san_evt_bat_bst(dev, event);
+		break;
+
+	case SAM_EVENT_CID_BAT_ADP:
+		status = san_evt_bat_adp(dev, event);
+		break;
+
+	case SAM_EVENT_CID_BAT_PROT:
+		/*
+		 * TODO: Implement support for battery protection status change
+		 *       event.
+		 */
+		return true;
+
+	case SAM_EVENT_CID_BAT_DPTF:
+		status = san_evt_bat_dptf(dev, event);
+		break;
+
+	default:
+		return false;
+	}
+
+	if (status) {
+		dev_err(dev, "error handling power event (cid = %#04x)\n",
+			event->command_id);
+	}
+
+	return true;
+}
+
+static void san_evt_bat_workfn(struct work_struct *work)
+{
+	struct san_event_work *ev;
+
+	ev = container_of(work, struct san_event_work, work.work);
+	san_evt_bat(&ev->event, ev->dev);
+	kfree(ev);
+}
+
+static u32 san_evt_bat_nf(struct ssam_event_notifier *nf,
+			  const struct ssam_event *event)
+{
+	struct san_data *d = to_san_data(nf, nf_bat);
+	struct san_event_work *work;
+	unsigned long delay = san_evt_bat_delay(event->command_id);
+
+	if (delay == 0)
+		return san_evt_bat(event, d->dev) ? SSAM_NOTIF_HANDLED : 0;
+
+	work = kzalloc(sizeof(*work) + event->length, GFP_KERNEL);
+	if (!work)
+		return ssam_notifier_from_errno(-ENOMEM);
+
+	INIT_DELAYED_WORK(&work->work, san_evt_bat_workfn);
+	work->dev = d->dev;
+
+	memcpy(&work->event, event, sizeof(struct ssam_event) + event->length);
+
+	schedule_delayed_work(&work->work, delay);
+	return SSAM_NOTIF_HANDLED;
+}
+
+static int san_evt_tmp_trip(struct device *dev, const struct ssam_event *event)
+{
+	union acpi_object param;
+
+	/*
+	 * The Surface ACPI expects an integer and not a package. This will
+	 * cause a warning in acpica/nsarguments.c, but that warning can be
+	 * safely ignored.
+	 */
+	param.type = ACPI_TYPE_INTEGER;
+	param.integer.value = event->instance_id;
+
+	return san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_THERMAL, &param);
+}
+
+static bool san_evt_tmp(const struct ssam_event *event, struct device *dev)
+{
+	int status;
+
+	switch (event->command_id) {
+	case SAM_EVENT_CID_TMP_TRIP:
+		status = san_evt_tmp_trip(dev, event);
+		break;
+
+	default:
+		return false;
+	}
+
+	if (status) {
+		dev_err(dev, "error handling thermal event (cid = %#04x)\n",
+			event->command_id);
+	}
+
+	return true;
+}
+
+static u32 san_evt_tmp_nf(struct ssam_event_notifier *nf,
+			  const struct ssam_event *event)
+{
+	struct san_data *d = to_san_data(nf, nf_tmp);
+
+	return san_evt_tmp(event, d->dev) ? SSAM_NOTIF_HANDLED : 0;
+}
+
+
+/* -- ACPI GSB OperationRegion handler -------------------------------------- */
+
+struct gsb_data_in {
+	u8 cv;
+} __packed;
+
+struct gsb_data_rqsx {
+	u8 cv;				/* Command value (san_gsb_request_cv). */
+	u8 tc;				/* Target category. */
+	u8 tid;				/* Target ID. */
+	u8 iid;				/* Instance ID. */
+	u8 snc;				/* Expect-response-flag. */
+	u8 cid;				/* Command ID. */
+	u16 cdl;			/* Payload length. */
+	u8 pld[];			/* Payload. */
+} __packed;
+
+struct gsb_data_etwl {
+	u8 cv;				/* Command value (should be 0x02). */
+	u8 etw3;			/* Unknown. */
+	u8 etw4;			/* Unknown. */
+	u8 msg[];			/* Error message (ASCIIZ). */
+} __packed;
+
+struct gsb_data_out {
+	u8 status;			/* _SSH communication status. */
+	u8 len;				/* _SSH payload length. */
+	u8 pld[];			/* _SSH payload. */
+} __packed;
+
+union gsb_buffer_data {
+	struct gsb_data_in   in;	/* Common input. */
+	struct gsb_data_rqsx rqsx;	/* RQSX input. */
+	struct gsb_data_etwl etwl;	/* ETWL input. */
+	struct gsb_data_out  out;	/* Output. */
+};
+
+struct gsb_buffer {
+	u8 status;			/* GSB AttribRawProcess status. */
+	u8 len;				/* GSB AttribRawProcess length. */
+	union gsb_buffer_data data;
+} __packed;
+
+#define SAN_GSB_MAX_RQSX_PAYLOAD  (U8_MAX - 2 - sizeof(struct gsb_data_rqsx))
+#define SAN_GSB_MAX_RESPONSE	  (U8_MAX - 2 - sizeof(struct gsb_data_out))
+
+#define SAN_GSB_COMMAND		0
+
+enum san_gsb_request_cv {
+	SAN_GSB_REQUEST_CV_RQST = 0x01,
+	SAN_GSB_REQUEST_CV_ETWL = 0x02,
+	SAN_GSB_REQUEST_CV_RQSG = 0x03,
+};
+
+#define SAN_REQUEST_NUM_TRIES	5
+
+static acpi_status san_etwl(struct san_data *d, struct gsb_buffer *b)
+{
+	struct gsb_data_etwl *etwl = &b->data.etwl;
+
+	if (b->len < sizeof(struct gsb_data_etwl)) {
+		dev_err(d->dev, "invalid ETWL package (len = %d)\n", b->len);
+		return AE_OK;
+	}
+
+	dev_err(d->dev, "ETWL(%#04x, %#04x): %.*s\n", etwl->etw3, etwl->etw4,
+		(unsigned int)(b->len - sizeof(struct gsb_data_etwl)),
+		(char *)etwl->msg);
+
+	/* Indicate success. */
+	b->status = 0x00;
+	b->len = 0x00;
+
+	return AE_OK;
+}
+
+static
+struct gsb_data_rqsx *san_validate_rqsx(struct device *dev, const char *type,
+					struct gsb_buffer *b)
+{
+	struct gsb_data_rqsx *rqsx = &b->data.rqsx;
+
+	if (b->len < sizeof(struct gsb_data_rqsx)) {
+		dev_err(dev, "invalid %s package (len = %d)\n", type, b->len);
+		return NULL;
+	}
+
+	if (get_unaligned(&rqsx->cdl) != b->len - sizeof(struct gsb_data_rqsx)) {
+		dev_err(dev, "bogus %s package (len = %d, cdl = %d)\n",
+			type, b->len, get_unaligned(&rqsx->cdl));
+		return NULL;
+	}
+
+	if (get_unaligned(&rqsx->cdl) > SAN_GSB_MAX_RQSX_PAYLOAD) {
+		dev_err(dev, "payload for %s package too large (cdl = %d)\n",
+			type, get_unaligned(&rqsx->cdl));
+		return NULL;
+	}
+
+	return rqsx;
+}
+
+static void gsb_rqsx_response_error(struct gsb_buffer *gsb, int status)
+{
+	gsb->status = 0x00;
+	gsb->len = 0x02;
+	gsb->data.out.status = (u8)(-status);
+	gsb->data.out.len = 0x00;
+}
+
+static void gsb_rqsx_response_success(struct gsb_buffer *gsb, u8 *ptr, size_t len)
+{
+	gsb->status = 0x00;
+	gsb->len = len + 2;
+	gsb->data.out.status = 0x00;
+	gsb->data.out.len = len;
+
+	if (len)
+		memcpy(&gsb->data.out.pld[0], ptr, len);
+}
+
+static acpi_status san_rqst_fixup_suspended(struct san_data *d,
+					    struct ssam_request *rqst,
+					    struct gsb_buffer *gsb)
+{
+	if (rqst->target_category == SSAM_SSH_TC_BAS && rqst->command_id == 0x0D) {
+		u8 base_state = 1;
+
+		/* Base state quirk:
+		 * The base state may be queried from ACPI when the EC is still
+		 * suspended. In this case it will return '-EPERM'. This query
+		 * will only be triggered from the ACPI lid GPE interrupt, thus
+		 * we are either in laptop or studio mode (base status 0x01 or
+		 * 0x02). Furthermore, we will only get here if the device (and
+		 * EC) have been suspended.
+		 *
+		 * We now assume that the device is in laptop mode (0x01). This
+		 * has the drawback that it will wake the device when unfolding
+		 * it in studio mode, but it also allows us to avoid actively
+		 * waiting for the EC to wake up, which may incur a notable
+		 * delay.
+		 */
+
+		dev_dbg(d->dev, "rqst: fixup: base-state quirk\n");
+
+		gsb_rqsx_response_success(gsb, &base_state, sizeof(base_state));
+		return AE_OK;
+	}
+
+	gsb_rqsx_response_error(gsb, -ENXIO);
+	return AE_OK;
+}
+
+static acpi_status san_rqst(struct san_data *d, struct gsb_buffer *buffer)
+{
+	u8 rspbuf[SAN_GSB_MAX_RESPONSE];
+	struct gsb_data_rqsx *gsb_rqst;
+	struct ssam_request rqst;
+	struct ssam_response rsp;
+	int status = 0;
+
+	gsb_rqst = san_validate_rqsx(d->dev, "RQST", buffer);
+	if (!gsb_rqst)
+		return AE_OK;
+
+	rqst.target_category = gsb_rqst->tc;
+	rqst.target_id = gsb_rqst->tid;
+	rqst.command_id = gsb_rqst->cid;
+	rqst.instance_id = gsb_rqst->iid;
+	rqst.flags = gsb_rqst->snc ? SSAM_REQUEST_HAS_RESPONSE : 0;
+	rqst.length = get_unaligned(&gsb_rqst->cdl);
+	rqst.payload = &gsb_rqst->pld[0];
+
+	rsp.capacity = ARRAY_SIZE(rspbuf);
+	rsp.length = 0;
+	rsp.pointer = &rspbuf[0];
+
+	/* Handle suspended device. */
+	if (d->dev->power.is_suspended) {
+		dev_warn(d->dev, "rqst: device is suspended, not executing\n");
+		return san_rqst_fixup_suspended(d, &rqst, buffer);
+	}
+
+	status = __ssam_retry(ssam_request_sync_onstack, SAN_REQUEST_NUM_TRIES,
+			      d->ctrl, &rqst, &rsp, SAN_GSB_MAX_RQSX_PAYLOAD);
+
+	if (!status) {
+		gsb_rqsx_response_success(buffer, rsp.pointer, rsp.length);
+	} else {
+		dev_err(d->dev, "rqst: failed with error %d\n", status);
+		gsb_rqsx_response_error(buffer, status);
+	}
+
+	return AE_OK;
+}
+
+static acpi_status san_rqsg(struct san_data *d, struct gsb_buffer *buffer)
+{
+	struct gsb_data_rqsx *gsb_rqsg;
+	struct san_dgpu_event evt;
+	int status;
+
+	gsb_rqsg = san_validate_rqsx(d->dev, "RQSG", buffer);
+	if (!gsb_rqsg)
+		return AE_OK;
+
+	evt.category = gsb_rqsg->tc;
+	evt.target = gsb_rqsg->tid;
+	evt.command = gsb_rqsg->cid;
+	evt.instance = gsb_rqsg->iid;
+	evt.length = get_unaligned(&gsb_rqsg->cdl);
+	evt.payload = &gsb_rqsg->pld[0];
+
+	status = san_dgpu_notifier_call(&evt);
+	if (!status) {
+		gsb_rqsx_response_success(buffer, NULL, 0);
+	} else {
+		dev_err(d->dev, "rqsg: failed with error %d\n", status);
+		gsb_rqsx_response_error(buffer, status);
+	}
+
+	return AE_OK;
+}
+
+static acpi_status san_opreg_handler(u32 function, acpi_physical_address command,
+				     u32 bits, u64 *value64, void *opreg_context,
+				     void *region_context)
+{
+	struct san_data *d = to_san_data(opreg_context, info);
+	struct gsb_buffer *buffer = (struct gsb_buffer *)value64;
+	int accessor_type = (function & 0xFFFF0000) >> 16;
+
+	if (command != SAN_GSB_COMMAND) {
+		dev_warn(d->dev, "unsupported command: %#04llx\n", command);
+		return AE_OK;
+	}
+
+	if (accessor_type != ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS) {
+		dev_err(d->dev, "invalid access type: %#04x\n", accessor_type);
+		return AE_OK;
+	}
+
+	/* Buffer must have at least contain the command-value. */
+	if (buffer->len == 0) {
+		dev_err(d->dev, "request-package too small\n");
+		return AE_OK;
+	}
+
+	switch (buffer->data.in.cv) {
+	case SAN_GSB_REQUEST_CV_RQST:
+		return san_rqst(d, buffer);
+
+	case SAN_GSB_REQUEST_CV_ETWL:
+		return san_etwl(d, buffer);
+
+	case SAN_GSB_REQUEST_CV_RQSG:
+		return san_rqsg(d, buffer);
+
+	default:
+		dev_warn(d->dev, "unsupported SAN0 request (cv: %#04x)\n",
+			 buffer->data.in.cv);
+		return AE_OK;
+	}
+}
+
+
+/* -- Driver setup. --------------------------------------------------------- */
+
+static int san_events_register(struct platform_device *pdev)
+{
+	struct san_data *d = platform_get_drvdata(pdev);
+	int status;
+
+	d->nf_bat.base.priority = 1;
+	d->nf_bat.base.fn = san_evt_bat_nf;
+	d->nf_bat.event.reg = SSAM_EVENT_REGISTRY_SAM;
+	d->nf_bat.event.id.target_category = SSAM_SSH_TC_BAT;
+	d->nf_bat.event.id.instance = 0;
+	d->nf_bat.event.mask = SSAM_EVENT_MASK_TARGET;
+	d->nf_bat.event.flags = SSAM_EVENT_SEQUENCED;
+
+	d->nf_tmp.base.priority = 1;
+	d->nf_tmp.base.fn = san_evt_tmp_nf;
+	d->nf_tmp.event.reg = SSAM_EVENT_REGISTRY_SAM;
+	d->nf_tmp.event.id.target_category = SSAM_SSH_TC_TMP;
+	d->nf_tmp.event.id.instance = 0;
+	d->nf_tmp.event.mask = SSAM_EVENT_MASK_TARGET;
+	d->nf_tmp.event.flags = SSAM_EVENT_SEQUENCED;
+
+	status = ssam_notifier_register(d->ctrl, &d->nf_bat);
+	if (status)
+		return status;
+
+	status = ssam_notifier_register(d->ctrl, &d->nf_tmp);
+	if (status)
+		ssam_notifier_unregister(d->ctrl, &d->nf_bat);
+
+	return status;
+}
+
+static void san_events_unregister(struct platform_device *pdev)
+{
+	struct san_data *d = platform_get_drvdata(pdev);
+
+	ssam_notifier_unregister(d->ctrl, &d->nf_bat);
+	ssam_notifier_unregister(d->ctrl, &d->nf_tmp);
+}
+
+#define san_consumer_printk(level, dev, handle, fmt, ...)			\
+do {										\
+	char *path = "<error getting consumer path>";				\
+	struct acpi_buffer buffer = {						\
+		.length = ACPI_ALLOCATE_BUFFER,					\
+		.pointer = NULL,						\
+	};									\
+										\
+	if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer)))	\
+		path = buffer.pointer;						\
+										\
+	dev_##level(dev, "[%s]: " fmt, path, ##__VA_ARGS__);			\
+	kfree(buffer.pointer);							\
+} while (0)
+
+#define san_consumer_dbg(dev, handle, fmt, ...) \
+	san_consumer_printk(dbg, dev, handle, fmt, ##__VA_ARGS__)
+
+#define san_consumer_warn(dev, handle, fmt, ...) \
+	san_consumer_printk(warn, dev, handle, fmt, ##__VA_ARGS__)
+
+static bool is_san_consumer(struct platform_device *pdev, acpi_handle handle)
+{
+	struct acpi_handle_list dep_devices;
+	acpi_handle supplier = ACPI_HANDLE(&pdev->dev);
+	acpi_status status;
+	int i;
+
+	if (!acpi_has_method(handle, "_DEP"))
+		return false;
+
+	status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices);
+	if (ACPI_FAILURE(status)) {
+		san_consumer_dbg(&pdev->dev, handle, "failed to evaluate _DEP\n");
+		return false;
+	}
+
+	for (i = 0; i < dep_devices.count; i++) {
+		if (dep_devices.handles[i] == supplier)
+			return true;
+	}
+
+	return false;
+}
+
+static acpi_status san_consumer_setup(acpi_handle handle, u32 lvl,
+				      void *context, void **rv)
+{
+	const u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER;
+	struct platform_device *pdev = context;
+	struct acpi_device *adev;
+	struct device_link *link;
+
+	if (!is_san_consumer(pdev, handle))
+		return AE_OK;
+
+	/* Ignore ACPI devices that are not present. */
+	if (acpi_bus_get_device(handle, &adev) != 0)
+		return AE_OK;
+
+	san_consumer_dbg(&pdev->dev, handle, "creating device link\n");
+
+	/* Try to set up device links, ignore but log errors. */
+	link = device_link_add(&adev->dev, &pdev->dev, flags);
+	if (!link) {
+		san_consumer_warn(&pdev->dev, handle, "failed to create device link\n");
+		return AE_OK;
+	}
+
+	return AE_OK;
+}
+
+static int san_consumer_links_setup(struct platform_device *pdev)
+{
+	acpi_status status;
+
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+				     ACPI_UINT32_MAX, san_consumer_setup, NULL,
+				     pdev, NULL);
+
+	return status ? -EFAULT : 0;
+}
+
+static int san_probe(struct platform_device *pdev)
+{
+	acpi_handle san = ACPI_HANDLE(&pdev->dev);
+	struct ssam_controller *ctrl;
+	struct san_data *data;
+	acpi_status astatus;
+	int status;
+
+	ctrl = ssam_client_bind(&pdev->dev);
+	if (IS_ERR(ctrl))
+		return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
+
+	status = san_consumer_links_setup(pdev);
+	if (status)
+		return status;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->dev = &pdev->dev;
+	data->ctrl = ctrl;
+
+	platform_set_drvdata(pdev, data);
+
+	astatus = acpi_install_address_space_handler(san, ACPI_ADR_SPACE_GSBUS,
+						     &san_opreg_handler, NULL,
+						     &data->info);
+	if (ACPI_FAILURE(astatus))
+		return -ENXIO;
+
+	status = san_events_register(pdev);
+	if (status)
+		goto err_enable_events;
+
+	status = san_set_rqsg_interface_device(&pdev->dev);
+	if (status)
+		goto err_install_dev;
+
+	acpi_walk_dep_device_list(san);
+	return 0;
+
+err_install_dev:
+	san_events_unregister(pdev);
+err_enable_events:
+	acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS,
+					  &san_opreg_handler);
+	return status;
+}
+
+static int san_remove(struct platform_device *pdev)
+{
+	acpi_handle san = ACPI_HANDLE(&pdev->dev);
+
+	san_set_rqsg_interface_device(NULL);
+	acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS,
+					  &san_opreg_handler);
+	san_events_unregister(pdev);
+
+	/*
+	 * We have unregistered our event sources. Now we need to ensure that
+	 * all delayed works they may have spawned are run to completion.
+	 */
+	flush_scheduled_work();
+
+	return 0;
+}
+
+static const struct acpi_device_id san_match[] = {
+	{ "MSHW0091" },
+	{ },
+};
+MODULE_DEVICE_TABLE(acpi, san_match);
+
+static struct platform_driver surface_acpi_notify = {
+	.probe = san_probe,
+	.remove = san_remove,
+	.driver = {
+		.name = "surface_acpi_notify",
+		.acpi_match_table = san_match,
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+};
+module_platform_driver(surface_acpi_notify);
+
+MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
+MODULE_DESCRIPTION("Surface ACPI Notify driver for Surface System Aggregator Module");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/surface_acpi_notify.h b/include/linux/surface_acpi_notify.h
new file mode 100644
index 000000000000..8e3e86c7d78c
--- /dev/null
+++ b/include/linux/surface_acpi_notify.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Interface for Surface ACPI Notify (SAN) driver.
+ *
+ * Provides access to discrete GPU notifications sent from ACPI via the SAN
+ * driver, which are not handled by this driver directly.
+ *
+ * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
+ */
+
+#ifndef _LINUX_SURFACE_ACPI_NOTIFY_H
+#define _LINUX_SURFACE_ACPI_NOTIFY_H
+
+#include <linux/notifier.h>
+#include <linux/types.h>
+
+/**
+ * struct san_dgpu_event - Discrete GPU ACPI event.
+ * @category: Category of the event.
+ * @target:   Target ID of the event source.
+ * @command:  Command ID of the event.
+ * @instance: Instance ID of the event source.
+ * @length:   Length of the event's payload data (in bytes).
+ * @payload:  Pointer to the event's payload data.
+ */
+struct san_dgpu_event {
+	u8 category;
+	u8 target;
+	u8 command;
+	u8 instance;
+	u16 length;
+	u8 *payload;
+};
+
+int san_client_link(struct device *client);
+int san_dgpu_notifier_register(struct notifier_block *nb);
+int san_dgpu_notifier_unregister(struct notifier_block *nb);
+
+#endif /* _LINUX_SURFACE_ACPI_NOTIFY_H */
--
2.29.2


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-03 21:26 [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module Maximilian Luz
                   ` (2 preceding siblings ...)
  2020-12-03 21:26 ` [PATCH v2 9/9] platform/surface: Add Surface ACPI Notify driver Maximilian Luz
@ 2020-12-06  7:07 ` Leon Romanovsky
  2020-12-06  8:32   ` Greg Kroah-Hartman
                     ` (2 more replies)
  3 siblings, 3 replies; 31+ messages in thread
From: Leon Romanovsky @ 2020-12-06  7:07 UTC (permalink / raw)
  To: Maximilian Luz, Greg Kroah-Hartman
  Cc: linux-kernel, Hans de Goede, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Rob Herring, Jiri Slaby,
	Rafael J . Wysocki, Len Brown, Steven Rostedt, Ingo Molnar,
	Masahiro Yamada, Michal Marek, Jonathan Corbet,
	Blaž Hrastnik, Dorian Stoll, platform-driver-x86,
	linux-serial, linux-acpi, linux-kbuild, linux-doc

On Thu, Dec 03, 2020 at 10:26:31PM +0100, Maximilian Luz wrote:
> Hello,
>
> Here is version two of the Surface System Aggregator Module (SAM/SSAM)
> driver series, adding initial support for the embedded controller on 5th
> and later generation Microsoft Surface devices. Initial support includes
> the ACPI interface to the controller, via which battery and thermal
> information is provided on some of these devices.
>
> The previous version and cover letter detailing what this series is
> about can be found at
>
>   https://lore.kernel.org/platform-driver-x86/20201115192143.21571-1-luzmaximilian@gmail.com/
>
> This patch-set can also be found at the following repository and
> reference, if you prefer to look at a kernel tree instead of these
> emails:
>
>   https://github.com/linux-surface/kernel tags/s/surface-aggregator/v2
>
> Thank you all for the feedback to v1, I hope I have addressed all
> comments.


I think that it is too far fetched to attempt and expose UAPI headers
for some obscure char device that we are all know won't be around in
a couple of years from now due to the nature of how this embedded world
works.

More on that, the whole purpose of proposed interface is to debug and
not intended to be used by any user space code.

Also the idea that you are creating new bus just for this device doesn't
really sound right. I recommend you to take a look on auxiliary bus and
use it or come with very strong justifications why it is not fit yet.

I'm sorry to say, but this series is not ready to be merged yet.

NAK: Leon Romanovsky <leon@kernel.org>

Thanks

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06  7:07 ` [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module Leon Romanovsky
@ 2020-12-06  8:32   ` Greg Kroah-Hartman
  2020-12-06  8:35     ` Leon Romanovsky
  2020-12-06 11:13     ` Maximilian Luz
  2020-12-06  8:41   ` Hans de Goede
  2020-12-06 15:58   ` Maximilian Luz
  2 siblings, 2 replies; 31+ messages in thread
From: Greg Kroah-Hartman @ 2020-12-06  8:32 UTC (permalink / raw)
  To: Leon Romanovsky
  Cc: Maximilian Luz, linux-kernel, Hans de Goede, Mark Gross,
	Andy Shevchenko, Barnabás Pőcze, Arnd Bergmann,
	Rob Herring, Jiri Slaby, Rafael J . Wysocki, Len Brown,
	Steven Rostedt, Ingo Molnar, Masahiro Yamada, Michal Marek,
	Jonathan Corbet, Blaž Hrastnik, Dorian Stoll,
	platform-driver-x86, linux-serial, linux-acpi, linux-kbuild,
	linux-doc

On Sun, Dec 06, 2020 at 09:07:05AM +0200, Leon Romanovsky wrote:
> On Thu, Dec 03, 2020 at 10:26:31PM +0100, Maximilian Luz wrote:
> > Hello,
> >
> > Here is version two of the Surface System Aggregator Module (SAM/SSAM)
> > driver series, adding initial support for the embedded controller on 5th
> > and later generation Microsoft Surface devices. Initial support includes
> > the ACPI interface to the controller, via which battery and thermal
> > information is provided on some of these devices.
> >
> > The previous version and cover letter detailing what this series is
> > about can be found at
> >
> >   https://lore.kernel.org/platform-driver-x86/20201115192143.21571-1-luzmaximilian@gmail.com/
> >
> > This patch-set can also be found at the following repository and
> > reference, if you prefer to look at a kernel tree instead of these
> > emails:
> >
> >   https://github.com/linux-surface/kernel tags/s/surface-aggregator/v2
> >
> > Thank you all for the feedback to v1, I hope I have addressed all
> > comments.
> 
> 
> I think that it is too far fetched to attempt and expose UAPI headers
> for some obscure char device that we are all know won't be around in
> a couple of years from now due to the nature of how this embedded world
> works.

No, that's not ok, we do this for loads of devices out there.  If there
is a device that wants to be supported for Linux, and a developer that
wants to support it, we will take it.

> More on that, the whole purpose of proposed interface is to debug and
> not intended to be used by any user space code.

I thought that debugfs was going to be used for most of the debugging
code, or has that changed in newer versions of this patchset?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06  8:32   ` Greg Kroah-Hartman
@ 2020-12-06  8:35     ` Leon Romanovsky
  2020-12-06 11:13     ` Maximilian Luz
  1 sibling, 0 replies; 31+ messages in thread
From: Leon Romanovsky @ 2020-12-06  8:35 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Maximilian Luz, linux-kernel, Hans de Goede, Mark Gross,
	Andy Shevchenko, Barnabás Pőcze, Arnd Bergmann,
	Rob Herring, Jiri Slaby, Rafael J . Wysocki, Len Brown,
	Steven Rostedt, Ingo Molnar, Masahiro Yamada, Michal Marek,
	Jonathan Corbet, Blaž Hrastnik, Dorian Stoll,
	platform-driver-x86, linux-serial, linux-acpi, linux-kbuild,
	linux-doc

On Sun, Dec 06, 2020 at 09:32:30AM +0100, Greg Kroah-Hartman wrote:
> On Sun, Dec 06, 2020 at 09:07:05AM +0200, Leon Romanovsky wrote:
> > On Thu, Dec 03, 2020 at 10:26:31PM +0100, Maximilian Luz wrote:
> > > Hello,
> > >
> > > Here is version two of the Surface System Aggregator Module (SAM/SSAM)
> > > driver series, adding initial support for the embedded controller on 5th
> > > and later generation Microsoft Surface devices. Initial support includes
> > > the ACPI interface to the controller, via which battery and thermal
> > > information is provided on some of these devices.
> > >
> > > The previous version and cover letter detailing what this series is
> > > about can be found at
> > >
> > >   https://lore.kernel.org/platform-driver-x86/20201115192143.21571-1-luzmaximilian@gmail.com/
> > >
> > > This patch-set can also be found at the following repository and
> > > reference, if you prefer to look at a kernel tree instead of these
> > > emails:
> > >
> > >   https://github.com/linux-surface/kernel tags/s/surface-aggregator/v2
> > >
> > > Thank you all for the feedback to v1, I hope I have addressed all
> > > comments.
> >
> >
> > I think that it is too far fetched to attempt and expose UAPI headers
> > for some obscure char device that we are all know won't be around in
> > a couple of years from now due to the nature of how this embedded world
> > works.
>
> No, that's not ok, we do this for loads of devices out there.  If there
> is a device that wants to be supported for Linux, and a developer that
> wants to support it, we will take it.
>
> > More on that, the whole purpose of proposed interface is to debug and
> > not intended to be used by any user space code.
>
> I thought that debugfs was going to be used for most of the debugging
> code, or has that changed in newer versions of this patchset?

https://lore.kernel.org/lkml/20201203212640.663931-9-luzmaximilian@gmail.com/#Z30include:uapi:linux:surface_aggregator:cdev.h

+ * Definitions, structs, and IOCTLs for the /dev/surface/aggregator misc
+ * device. This device provides direct user-space access to the SSAM EC.
+ * Intended for debugging and development.

>
> thanks,
>
> greg k-h

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06  7:07 ` [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module Leon Romanovsky
  2020-12-06  8:32   ` Greg Kroah-Hartman
@ 2020-12-06  8:41   ` Hans de Goede
  2020-12-06  8:56     ` Leon Romanovsky
  2020-12-06  8:58     ` Blaž Hrastnik
  2020-12-06 15:58   ` Maximilian Luz
  2 siblings, 2 replies; 31+ messages in thread
From: Hans de Goede @ 2020-12-06  8:41 UTC (permalink / raw)
  To: Leon Romanovsky, Maximilian Luz, Greg Kroah-Hartman
  Cc: linux-kernel, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Rob Herring, Jiri Slaby,
	Rafael J . Wysocki, Len Brown, Steven Rostedt, Ingo Molnar,
	Masahiro Yamada, Michal Marek, Jonathan Corbet,
	Blaž Hrastnik, Dorian Stoll, platform-driver-x86,
	linux-serial, linux-acpi, linux-kbuild, linux-doc

Hi Leon,

On 12/6/20 8:07 AM, Leon Romanovsky wrote:
> On Thu, Dec 03, 2020 at 10:26:31PM +0100, Maximilian Luz wrote:
>> Hello,
>>
>> Here is version two of the Surface System Aggregator Module (SAM/SSAM)
>> driver series, adding initial support for the embedded controller on 5th
>> and later generation Microsoft Surface devices. Initial support includes
>> the ACPI interface to the controller, via which battery and thermal
>> information is provided on some of these devices.
>>
>> The previous version and cover letter detailing what this series is
>> about can be found at
>>
>>   https://lore.kernel.org/platform-driver-x86/20201115192143.21571-1-luzmaximilian@gmail.com/
>>
>> This patch-set can also be found at the following repository and
>> reference, if you prefer to look at a kernel tree instead of these
>> emails:
>>
>>   https://github.com/linux-surface/kernel tags/s/surface-aggregator/v2
>>
>> Thank you all for the feedback to v1, I hope I have addressed all
>> comments.
> 
> 
> I think that it is too far fetched to attempt and expose UAPI headers
> for some obscure char device that we are all know won't be around in
> a couple of years from now due to the nature of how this embedded world
> works.

This is not for an embedded device, but for the popular line of
Microsoft Surface laptops / 2-in-1s...

> More on that, the whole purpose of proposed interface is to debug and
> not intended to be used by any user space code.

The purpose is to provide raw access to the Surface Serial Hub protocol,
just like we provide raw access to USB devices and have hidraw devices.

So this goes a litle beyond just debugging; and eventually the choice
may be made to implement some functionality with userspace drivers,
just like we do for some HID and USB devices.

Still I agree with you that adding new userspace API is something which
needs to be considered carefully. So I will look at this closely when
reviewing this set.

> Also the idea that you are creating new bus just for this device doesn't
> really sound right. I recommend you to take a look on auxiliary bus and
> use it or come with very strong justifications why it is not fit yet.

AFAIK the auxiliary bus is for sharing a single device between multiple
drivers, while the main device-driver also still offers functionality
(beyond the providing of access) itself.

This is more akin to how the WMI driver also models different WMI
functions as a bus + devices on the bus.

Or how the SDIO driver multiplex a single SDIO device into its
functions by again using a bus + devices on the bus model.

Also this has been in the works for quite a while now, the Linux on
Microsoft Surface devices community has been working on this out of
tree for a long time, see:
https://github.com/linux-surface/

And an RFC and a v1 have been posted a while ago, while auxiliary
bus support is not even in the mainline kernel yet. I would agree
with you that this should switch to auxiliary bus, despite the timing,
if that would lead to much better code. But ATM I don't really see
switching to auxiliary bus offering much benefits here.

> I'm sorry to say, but this series is not ready to be merged yet.
> 
> NAK: Leon Romanovsky <leon@kernel.org>

See above, I believe that this all is a bit harsh and I have not
really heard convincing arguments for not merging this.

Moreover such a quick nack does not really promote working upstream,
where as we actually want people to work upstream as much as possible.
I know this is not a reason for taking bad code, but I'm not
convinced that this is bad code.

I have not reviewed this myself yet, but once I have reviewed
this and any review remarks have been addressed I do expect to
merge this series through the platform-drivers-x86 tree.

Regards,

Hans de Goede
(drivers/platform/x86 and drivers/platform/surface subsys maintainer)


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06  8:41   ` Hans de Goede
@ 2020-12-06  8:56     ` Leon Romanovsky
  2020-12-06 10:04       ` Hans de Goede
  2020-12-06  8:58     ` Blaž Hrastnik
  1 sibling, 1 reply; 31+ messages in thread
From: Leon Romanovsky @ 2020-12-06  8:56 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Maximilian Luz, Greg Kroah-Hartman, linux-kernel, Mark Gross,
	Andy Shevchenko, Barnabás Pőcze, Arnd Bergmann,
	Rob Herring, Jiri Slaby, Rafael J . Wysocki, Len Brown,
	Steven Rostedt, Ingo Molnar, Masahiro Yamada, Michal Marek,
	Jonathan Corbet, Blaž Hrastnik, Dorian Stoll,
	platform-driver-x86, linux-serial, linux-acpi, linux-kbuild,
	linux-doc

On Sun, Dec 06, 2020 at 09:41:21AM +0100, Hans de Goede wrote:
> Hi Leon,
>
> On 12/6/20 8:07 AM, Leon Romanovsky wrote:
> > On Thu, Dec 03, 2020 at 10:26:31PM +0100, Maximilian Luz wrote:
> >> Hello,
> >>
> >> Here is version two of the Surface System Aggregator Module (SAM/SSAM)
> >> driver series, adding initial support for the embedded controller on 5th
> >> and later generation Microsoft Surface devices. Initial support includes
> >> the ACPI interface to the controller, via which battery and thermal
> >> information is provided on some of these devices.
> >>
> >> The previous version and cover letter detailing what this series is
> >> about can be found at
> >>
> >>   https://lore.kernel.org/platform-driver-x86/20201115192143.21571-1-luzmaximilian@gmail.com/
> >>
> >> This patch-set can also be found at the following repository and
> >> reference, if you prefer to look at a kernel tree instead of these
> >> emails:
> >>
> >>   https://github.com/linux-surface/kernel tags/s/surface-aggregator/v2
> >>
> >> Thank you all for the feedback to v1, I hope I have addressed all
> >> comments.
> >
> >
> > I think that it is too far fetched to attempt and expose UAPI headers
> > for some obscure char device that we are all know won't be around in
> > a couple of years from now due to the nature of how this embedded world
> > works.
>
> This is not for an embedded device, but for the popular line of
> Microsoft Surface laptops / 2-in-1s...

It is the naming, we don't have char device for every "laptop" vendor.
Why is Microsoft different here?

>
> > More on that, the whole purpose of proposed interface is to debug and
> > not intended to be used by any user space code.
>
> The purpose is to provide raw access to the Surface Serial Hub protocol,
> just like we provide raw access to USB devices and have hidraw devices.

USB devices implement standard protocol, this surface hub is nothing
even close to that.

>
> So this goes a litle beyond just debugging; and eventually the choice
> may be made to implement some functionality with userspace drivers,
> just like we do for some HID and USB devices.

I don't know how it goes in device/platform area, but in other large
subsystems, UAPI should be presented with working user-space part.

>
> Still I agree with you that adding new userspace API is something which
> needs to be considered carefully. So I will look at this closely when
> reviewing this set.
>
> > Also the idea that you are creating new bus just for this device doesn't
> > really sound right. I recommend you to take a look on auxiliary bus and
> > use it or come with very strong justifications why it is not fit yet.
>
> AFAIK the auxiliary bus is for sharing a single device between multiple
> drivers, while the main device-driver also still offers functionality
> (beyond the providing of access) itself.

The idea behind auxiliary bus is to slice various functionalities into
different sub-drivers, see it as a way to create subsystem inside one
driver.

>
> This is more akin to how the WMI driver also models different WMI
> functions as a bus + devices on the bus.
>
> Or how the SDIO driver multiplex a single SDIO device into its
> functions by again using a bus + devices on the bus model.
>
> Also this has been in the works for quite a while now, the Linux on
> Microsoft Surface devices community has been working on this out of
> tree for a long time, see:
> https://github.com/linux-surface/

It is not relevant, the code is merged than it is ready.

>
> And an RFC and a v1 have been posted a while ago, while auxiliary
> bus support is not even in the mainline kernel yet. I would agree
> with you that this should switch to auxiliary bus, despite the timing,
> if that would lead to much better code. But ATM I don't really see
> switching to auxiliary bus offering much benefits here.

The auxiliary bus is merged and part of linux-next.
https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git/tag/?h=auxbus-5.11-rc1

>
> > I'm sorry to say, but this series is not ready to be merged yet.
> >
> > NAK: Leon Romanovsky <leon@kernel.org>
>
> See above, I believe that this all is a bit harsh and I have not
> really heard convincing arguments for not merging this.
>
> Moreover such a quick nack does not really promote working upstream,
> where as we actually want people to work upstream as much as possible.
> I know this is not a reason for taking bad code, but I'm not
> convinced that this is bad code.

I naked explicitly for two reasons: UAPI(chardev) and lack of rationale
for the custom bus, never said "bad code".

>
> I have not reviewed this myself yet, but once I have reviewed
> this and any review remarks have been addressed I do expect to
> merge this series through the platform-drivers-x86 tree.
>
> Regards,
>
> Hans de Goede
> (drivers/platform/x86 and drivers/platform/surface subsys maintainer)
>

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06  8:41   ` Hans de Goede
  2020-12-06  8:56     ` Leon Romanovsky
@ 2020-12-06  8:58     ` Blaž Hrastnik
  2020-12-06  9:06       ` Leon Romanovsky
  1 sibling, 1 reply; 31+ messages in thread
From: Blaž Hrastnik @ 2020-12-06  8:58 UTC (permalink / raw)
  To: Hans de Goede, Leon Romanovsky, Maximilian Luz, Greg Kroah-Hartman
  Cc: lkml, Mark Gross, Andy Shevchenko, Barnabás Pőcze,
	Arnd Bergmann, Rob Herring, Jiri Slaby, Rafael J . Wysocki,
	Len Brown, Steven Rostedt, Ingo Molnar, Masahiro Yamada,
	Michal Marek, Jonathan Corbet, Dorian Stoll, platform-driver-x86,
	linux-serial, linux-acpi, linux-kbuild, linux-doc

> 
> > More on that, the whole purpose of proposed interface is to debug and
> > not intended to be used by any user space code.
> 
> The purpose is to provide raw access to the Surface Serial Hub protocol,
> just like we provide raw access to USB devices and have hidraw devices.
> 
> So this goes a litle beyond just debugging; and eventually the choice
> may be made to implement some functionality with userspace drivers,
> just like we do for some HID and USB devices.
> 
> Still I agree with you that adding new userspace API is something which
> needs to be considered carefully. So I will look at this closely when
> reviewing this set.

To add to that: this was previously a debugfs interface but was moved to misc after review on the initial RFC:
https://lkml.org/lkml/2020/9/24/96

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06  8:58     ` Blaž Hrastnik
@ 2020-12-06  9:06       ` Leon Romanovsky
  2020-12-06 10:33         ` Maximilian Luz
  0 siblings, 1 reply; 31+ messages in thread
From: Leon Romanovsky @ 2020-12-06  9:06 UTC (permalink / raw)
  To: Blaž Hrastnik
  Cc: Hans de Goede, Maximilian Luz, Greg Kroah-Hartman, lkml,
	Mark Gross, Andy Shevchenko, Barnabás Pőcze,
	Arnd Bergmann, Rob Herring, Jiri Slaby, Rafael J . Wysocki,
	Len Brown, Steven Rostedt, Ingo Molnar, Masahiro Yamada,
	Michal Marek, Jonathan Corbet, Dorian Stoll, platform-driver-x86,
	linux-serial, linux-acpi, linux-kbuild, linux-doc

On Sun, Dec 06, 2020 at 05:58:32PM +0900, Blaž Hrastnik wrote:
> >
> > > More on that, the whole purpose of proposed interface is to debug and
> > > not intended to be used by any user space code.
> >
> > The purpose is to provide raw access to the Surface Serial Hub protocol,
> > just like we provide raw access to USB devices and have hidraw devices.
> >
> > So this goes a litle beyond just debugging; and eventually the choice
> > may be made to implement some functionality with userspace drivers,
> > just like we do for some HID and USB devices.
> >
> > Still I agree with you that adding new userspace API is something which
> > needs to be considered carefully. So I will look at this closely when
> > reviewing this set.
>
> To add to that: this was previously a debugfs interface but was moved to misc after review on the initial RFC:
> https://lkml.org/lkml/2020/9/24/96

There is a huge difference between the suggestion and final implementation.

Greg suggested to add new debug module to the drivers/misc that will
open char device explicitly after user loaded that module to debug this
hub. However, the author added full blown char device as a first citizen
that has all not-break-user constrains.

Thanks


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06  8:56     ` Leon Romanovsky
@ 2020-12-06 10:04       ` Hans de Goede
  2020-12-06 10:33         ` Leon Romanovsky
  2020-12-06 10:51         ` Maximilian Luz
  0 siblings, 2 replies; 31+ messages in thread
From: Hans de Goede @ 2020-12-06 10:04 UTC (permalink / raw)
  To: Leon Romanovsky
  Cc: Maximilian Luz, Greg Kroah-Hartman, linux-kernel, Mark Gross,
	Andy Shevchenko, Barnabás Pőcze, Arnd Bergmann,
	Rob Herring, Jiri Slaby, Rafael J . Wysocki, Len Brown,
	Steven Rostedt, Ingo Molnar, Masahiro Yamada, Michal Marek,
	Jonathan Corbet, Blaž Hrastnik, Dorian Stoll,
	platform-driver-x86, linux-serial, linux-acpi, linux-kbuild,
	linux-doc

Hi,

On 12/6/20 9:56 AM, Leon Romanovsky wrote:
> On Sun, Dec 06, 2020 at 09:41:21AM +0100, Hans de Goede wrote:
>> Hi Leon,
>>
>> On 12/6/20 8:07 AM, Leon Romanovsky wrote:
>>> On Thu, Dec 03, 2020 at 10:26:31PM +0100, Maximilian Luz wrote:
>>>> Hello,
>>>>
>>>> Here is version two of the Surface System Aggregator Module (SAM/SSAM)
>>>> driver series, adding initial support for the embedded controller on 5th
>>>> and later generation Microsoft Surface devices. Initial support includes
>>>> the ACPI interface to the controller, via which battery and thermal
>>>> information is provided on some of these devices.
>>>>
>>>> The previous version and cover letter detailing what this series is
>>>> about can be found at
>>>>
>>>>   https://lore.kernel.org/platform-driver-x86/20201115192143.21571-1-luzmaximilian@gmail.com/
>>>>
>>>> This patch-set can also be found at the following repository and
>>>> reference, if you prefer to look at a kernel tree instead of these
>>>> emails:
>>>>
>>>>   https://github.com/linux-surface/kernel tags/s/surface-aggregator/v2
>>>>
>>>> Thank you all for the feedback to v1, I hope I have addressed all
>>>> comments.
>>>
>>>
>>> I think that it is too far fetched to attempt and expose UAPI headers
>>> for some obscure char device that we are all know won't be around in
>>> a couple of years from now due to the nature of how this embedded world
>>> works.
>>
>> This is not for an embedded device, but for the popular line of
>> Microsoft Surface laptops / 2-in-1s...
> 
> It is the naming, we don't have char device for every "laptop" vendor.
> Why is Microsoft different here?

Because their hardware department has invented a whole new way of dealing
with a bunch of things at the hardware level (for some reason).

Also almost all laptop vendors have a whole bunch of laptop vendor
specific userspace API in the form of sysfs files exported by
drivers/platform/x86/laptop-vendor.c drivers. E.g. do:

ls /sys/bus/platform/devices/thinkpad_acpi/

An any IBM/Lenovo Thinkpad (and only on a Thinkpad) to see a bunch
of laptop vendor specific UAPI.

Since I've become the pdx86 subsys maintainer I've actually been
pushing back against adding more of this, instead trying to
either use existing UAPIs, or defining new common UAPIs which can
be shared between vendors.

So I agree very much with you that we need to be careful about
needlessly introducing new UAPI.

But there is a difference between being careful and just nacking
it because no new UAPI may be added at all (also see GKH's response).

>>> More on that, the whole purpose of proposed interface is to debug and
>>> not intended to be used by any user space code.
>>
>> The purpose is to provide raw access to the Surface Serial Hub protocol,
>> just like we provide raw access to USB devices and have hidraw devices.
> 
> USB devices implement standard protocol, this surface hub is nothing
> even close to that.

The USB protocol just defines a transport layer, outside of the USB classes
there are plenty of proprietary protocols on top of that transport.

And this chardev just offers access to the Surface Serial Hub transport
protocol. And if you want something even closer the i2cdev module offers
raw I2C transfer access and I2C defines no protocol other then
how to read or write a number of bytes.

I do a lot of hw enablement work and being able to poke HID / USB / I2C
devices directly from userspace is very useful for this.

>> So this goes a litle beyond just debugging; and eventually the choice
>> may be made to implement some functionality with userspace drivers,
>> just like we do for some HID and USB devices.
> 
> I don't know how it goes in device/platform area, but in other large
> subsystems, UAPI should be presented with working user-space part.
> 
>>
>> Still I agree with you that adding new userspace API is something which
>> needs to be considered carefully. So I will look at this closely when
>> reviewing this set.

So this ^^^ still stands, I agree with you that adding new UAPI needs
to be considered carefully and when I get around to reviewing this
that is exactly what I will do.

Maximilian, can you perhaps explain a bit more of what you want / expect
to use the chardev for, and maybe provide pointers to the matching
userspace utilities (which I presume you have) ?

>>> Also the idea that you are creating new bus just for this device doesn't
>>> really sound right. I recommend you to take a look on auxiliary bus and
>>> use it or come with very strong justifications why it is not fit yet.
>>
>> AFAIK the auxiliary bus is for sharing a single device between multiple
>> drivers, while the main device-driver also still offers functionality
>> (beyond the providing of access) itself.
> 
> The idea behind auxiliary bus is to slice various functionalities into
> different sub-drivers, see it as a way to create subsystem inside one
> driver.

AFAIK the idea is to be able to combine multiple physical devices, e.g.
a PCI device + an ACPI enumerated platform device and then slice the
combination of those 2 up in new devices which may use parts of both
parent devices, quoting from:

https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git/tree/Documentation/driver-api/auxiliary_bus.rst?h=driver-core-next&id=7de3697e9cbd4bd3d62bafa249d57990e1b8f294

"multiple devices might implement a common intersection of functionality"

IOW this is for cases where the simpler bus + devices model does not
work well. AFAICT in this case the simpler bus + devices does work
well, so there is no need to use the auxiliary bus.

>> This is more akin to how the WMI driver also models different WMI
>> functions as a bus + devices on the bus.
>>
>> Or how the SDIO driver multiplex a single SDIO device into its
>> functions by again using a bus + devices on the bus model.
>>
>> Also this has been in the works for quite a while now, the Linux on
>> Microsoft Surface devices community has been working on this out of
>> tree for a long time, see:
>> https://github.com/linux-surface/
> 
> It is not relevant, the code is merged than it is ready.

It is relevant, you cannot expect drivers which were written during
the last 6 months to use functionality which is not even in the
mainline kernel yet (yes it is in -next, but not in mainline).

Now if that new functionality where to provide major benefits to
the code making it much cleaner / better then yes asking to rewrite
it to use that new functionality would make sense.

I need to take a closer look at the code but ATM I'm not convinced
that rewriting it to use the new auxiliary bus stuff will make it
better at all, let alone enough to warrant the effort of the rewrite.

<snip>

Regards,

Hans


p.s.

For the record I do not own any Microsoft Surface devices myself, so
I have no interests here other then to make sure that the Linux
kernel is welcoming to new new contributors and does not needlessly
scare them away.


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06 10:04       ` Hans de Goede
@ 2020-12-06 10:33         ` Leon Romanovsky
  2020-12-06 10:41           ` Hans de Goede
  2020-12-06 10:51         ` Maximilian Luz
  1 sibling, 1 reply; 31+ messages in thread
From: Leon Romanovsky @ 2020-12-06 10:33 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Maximilian Luz, Greg Kroah-Hartman, linux-kernel, Mark Gross,
	Andy Shevchenko, Barnabás Pőcze, Arnd Bergmann,
	Rob Herring, Jiri Slaby, Rafael J . Wysocki, Len Brown,
	Steven Rostedt, Ingo Molnar, Masahiro Yamada, Michal Marek,
	Jonathan Corbet, Blaž Hrastnik, Dorian Stoll,
	platform-driver-x86, linux-serial, linux-acpi, linux-kbuild,
	linux-doc

On Sun, Dec 06, 2020 at 11:04:06AM +0100, Hans de Goede wrote:
> Hi,
>
> On 12/6/20 9:56 AM, Leon Romanovsky wrote:
> > On Sun, Dec 06, 2020 at 09:41:21AM +0100, Hans de Goede wrote:
> >> Hi Leon,
> >>
> >> On 12/6/20 8:07 AM, Leon Romanovsky wrote:
> >>> On Thu, Dec 03, 2020 at 10:26:31PM +0100, Maximilian Luz wrote:
> >>>> Hello,
> >>>>
> >>>> Here is version two of the Surface System Aggregator Module (SAM/SSAM)
> >>>> driver series, adding initial support for the embedded controller on 5th
> >>>> and later generation Microsoft Surface devices. Initial support includes
> >>>> the ACPI interface to the controller, via which battery and thermal
> >>>> information is provided on some of these devices.
> >>>>
> >>>> The previous version and cover letter detailing what this series is
> >>>> about can be found at
> >>>>
> >>>>   https://lore.kernel.org/platform-driver-x86/20201115192143.21571-1-luzmaximilian@gmail.com/
> >>>>
> >>>> This patch-set can also be found at the following repository and
> >>>> reference, if you prefer to look at a kernel tree instead of these
> >>>> emails:
> >>>>
> >>>>   https://github.com/linux-surface/kernel tags/s/surface-aggregator/v2
> >>>>
> >>>> Thank you all for the feedback to v1, I hope I have addressed all
> >>>> comments.
> >>>
> >>>
> >>> I think that it is too far fetched to attempt and expose UAPI headers
> >>> for some obscure char device that we are all know won't be around in
> >>> a couple of years from now due to the nature of how this embedded world
> >>> works.
> >>
> >> This is not for an embedded device, but for the popular line of
> >> Microsoft Surface laptops / 2-in-1s...
> >
> > It is the naming, we don't have char device for every "laptop" vendor.
> > Why is Microsoft different here?
>
> Because their hardware department has invented a whole new way of dealing
> with a bunch of things at the hardware level (for some reason).

They are not different from any other vendor, it is much cheaper and easier
to do not follow standard implementations.

>
> Also almost all laptop vendors have a whole bunch of laptop vendor
> specific userspace API in the form of sysfs files exported by
> drivers/platform/x86/laptop-vendor.c drivers. E.g. do:
>
> ls /sys/bus/platform/devices/thinkpad_acpi/

It is different from the proposed /dev/surface... char device.

>
> An any IBM/Lenovo Thinkpad (and only on a Thinkpad) to see a bunch
> of laptop vendor specific UAPI.

Yes, it is gross, IBM did it in early days of Linux. Other vendors don't
have anything like that.

>
> Since I've become the pdx86 subsys maintainer I've actually been
> pushing back against adding more of this, instead trying to
> either use existing UAPIs, or defining new common UAPIs which can
> be shared between vendors.
>
> So I agree very much with you that we need to be careful about
> needlessly introducing new UAPI.
>
> But there is a difference between being careful and just nacking
> it because no new UAPI may be added at all (also see GKH's response).

I saw, the author misunderstood the Greg's comments.

>
> >>> More on that, the whole purpose of proposed interface is to debug and
> >>> not intended to be used by any user space code.
> >>
> >> The purpose is to provide raw access to the Surface Serial Hub protocol,
> >> just like we provide raw access to USB devices and have hidraw devices.
> >
> > USB devices implement standard protocol, this surface hub is nothing
> > even close to that.
>
> The USB protocol just defines a transport layer, outside of the USB classes
> there are plenty of proprietary protocols on top of that transport.
>
> And this chardev just offers access to the Surface Serial Hub transport
> protocol. And if you want something even closer the i2cdev module offers
> raw I2C transfer access and I2C defines no protocol other then
> how to read or write a number of bytes.
>
> I do a lot of hw enablement work and being able to poke HID / USB / I2C
> devices directly from userspace is very useful for this.

Greg wrote how to do it.

>
> >> So this goes a litle beyond just debugging; and eventually the choice
> >> may be made to implement some functionality with userspace drivers,
> >> just like we do for some HID and USB devices.
> >
> > I don't know how it goes in device/platform area, but in other large
> > subsystems, UAPI should be presented with working user-space part.
> >
> >>
> >> Still I agree with you that adding new userspace API is something which
> >> needs to be considered carefully. So I will look at this closely when
> >> reviewing this set.
>
> So this ^^^ still stands, I agree with you that adding new UAPI needs
> to be considered carefully and when I get around to reviewing this
> that is exactly what I will do.
>
> Maximilian, can you perhaps explain a bit more of what you want / expect
> to use the chardev for, and maybe provide pointers to the matching
> userspace utilities (which I presume you have) ?
>
> >>> Also the idea that you are creating new bus just for this device doesn't
> >>> really sound right. I recommend you to take a look on auxiliary bus and
> >>> use it or come with very strong justifications why it is not fit yet.
> >>
> >> AFAIK the auxiliary bus is for sharing a single device between multiple
> >> drivers, while the main device-driver also still offers functionality
> >> (beyond the providing of access) itself.
> >
> > The idea behind auxiliary bus is to slice various functionalities into
> > different sub-drivers, see it as a way to create subsystem inside one
> > driver.
>
> AFAIK the idea is to be able to combine multiple physical devices, e.g.
> a PCI device + an ACPI enumerated platform device and then slice the
> combination of those 2 up in new devices which may use parts of both
> parent devices, quoting from:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git/tree/Documentation/driver-api/auxiliary_bus.rst?h=driver-core-next&id=7de3697e9cbd4bd3d62bafa249d57990e1b8f294
>
> "multiple devices might implement a common intersection of functionality"

It is one way, another is to take one device and create many small
devices out of it.
https://lore.kernel.org/alsa-devel/20201026111849.1035786-1-leon@kernel.org/

>
> IOW this is for cases where the simpler bus + devices model does not
> work well. AFAICT in this case the simpler bus + devices does work
> well, so there is no need to use the auxiliary bus.

It is designed to replace invention of custom buses.

>
> >> This is more akin to how the WMI driver also models different WMI
> >> functions as a bus + devices on the bus.
> >>
> >> Or how the SDIO driver multiplex a single SDIO device into its
> >> functions by again using a bus + devices on the bus model.
> >>
> >> Also this has been in the works for quite a while now, the Linux on
> >> Microsoft Surface devices community has been working on this out of
> >> tree for a long time, see:
> >> https://github.com/linux-surface/
> >
> > It is not relevant, the code is merged than it is ready.
>
> It is relevant, you cannot expect drivers which were written during
> the last 6 months to use functionality which is not even in the
> mainline kernel yet (yes it is in -next, but not in mainline).
>
> Now if that new functionality where to provide major benefits to
> the code making it much cleaner / better then yes asking to rewrite
> it to use that new functionality would make sense.

And who will rewrite it? My experience shows that right code should be
written from the beginning otherwise the code has too many chances to be
abandoned.

Thanks

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06  9:06       ` Leon Romanovsky
@ 2020-12-06 10:33         ` Maximilian Luz
  2020-12-06 10:43           ` Hans de Goede
  2020-12-06 11:30           ` Leon Romanovsky
  0 siblings, 2 replies; 31+ messages in thread
From: Maximilian Luz @ 2020-12-06 10:33 UTC (permalink / raw)
  To: Leon Romanovsky, Blaž Hrastnik
  Cc: Hans de Goede, Greg Kroah-Hartman, lkml, Mark Gross,
	Andy Shevchenko, Barnabás Pőcze, Arnd Bergmann,
	Rob Herring, Jiri Slaby, Rafael J . Wysocki, Len Brown,
	Steven Rostedt, Ingo Molnar, Masahiro Yamada, Michal Marek,
	Jonathan Corbet, Dorian Stoll, platform-driver-x86, linux-serial,
	linux-acpi, linux-kbuild, linux-doc

On 12/6/20 10:06 AM, Leon Romanovsky wrote:> On Sun, Dec 06, 2020 at 05:58:32PM +0900, Blaž Hrastnik wrote:
>>>
>>>> More on that, the whole purpose of proposed interface is to debug and
>>>> not intended to be used by any user space code.
>>>
>>> The purpose is to provide raw access to the Surface Serial Hub protocol,
>>> just like we provide raw access to USB devices and have hidraw devices.
>>>
>>> So this goes a litle beyond just debugging; and eventually the choice
>>> may be made to implement some functionality with userspace drivers,
>>> just like we do for some HID and USB devices.
>>>
>>> Still I agree with you that adding new userspace API is something which
>>> needs to be considered carefully. So I will look at this closely when
>>> reviewing this set.
>>
>> To add to that: this was previously a debugfs interface but was moved to misc after review on the initial RFC:
>> https://lkml.org/lkml/2020/9/24/96
> 
> There is a huge difference between the suggestion and final implementation.
> 
> Greg suggested to add new debug module to the drivers/misc that will
> open char device explicitly after user loaded that module to debug this
> hub. However, the author added full blown char device as a first citizen
> that has all not-break-user constrains.

This module still needs to be loaded explicitly. And (I might be wrong
about this) the "not-break-user constraints" hold as soon as I register
a misc device at all, no? So I don't see how this is a) any different
than previously discussed with Greg and b) how the uapi header now
introduces any not-break-user constraints that would not be there
without it.

This interface is intended as a stable interface. That's something that
I committed to as soon as I decided to implement this via a misc-device.

Sure, I can move the definitions in the uapi header to the module
itself, but I don't see any benefit in that. If someone really wants to
use this interface, they can just as well copy the definitions from the
module source itself. So why not be upfront about it and make life
easier for everyone?

Regards,
Max


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06 10:33         ` Leon Romanovsky
@ 2020-12-06 10:41           ` Hans de Goede
  2020-12-06 11:41             ` Leon Romanovsky
  0 siblings, 1 reply; 31+ messages in thread
From: Hans de Goede @ 2020-12-06 10:41 UTC (permalink / raw)
  To: Leon Romanovsky
  Cc: Maximilian Luz, Greg Kroah-Hartman, linux-kernel, Mark Gross,
	Andy Shevchenko, Barnabás Pőcze, Arnd Bergmann,
	Rob Herring, Jiri Slaby, Rafael J . Wysocki, Len Brown,
	Steven Rostedt, Ingo Molnar, Masahiro Yamada, Michal Marek,
	Jonathan Corbet, Blaž Hrastnik, Dorian Stoll,
	platform-driver-x86, linux-serial, linux-acpi, linux-kbuild,
	linux-doc

Hi,

On 12/6/20 11:33 AM, Leon Romanovsky wrote:
> On Sun, Dec 06, 2020 at 11:04:06AM +0100, Hans de Goede wrote:

<snip>

>> But there is a difference between being careful and just nacking
>> it because no new UAPI may be added at all (also see GKH's response).
> 
> I saw, the author misunderstood the Greg's comments.

Quoting from patch 8/9:

"
+==============================
+User-Space EC Interface (cdev)
+==============================
+
+The ``surface_aggregator_cdev`` module provides a misc-device for the SSAM
+controller to allow for a (more or less) direct connection from user-space to
+the SAM EC. It is intended to be used for development and debugging, and
+therefore should not be used or relied upon in any other way. Note that this
+module is not loaded automatically, but instead must be loaded manually.
"

If I'm not mistaken that seems to be pretty much what Greg asked for.

Regards,

Hans


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06 10:33         ` Maximilian Luz
@ 2020-12-06 10:43           ` Hans de Goede
  2020-12-06 10:56             ` Maximilian Luz
  2020-12-06 11:30           ` Leon Romanovsky
  1 sibling, 1 reply; 31+ messages in thread
From: Hans de Goede @ 2020-12-06 10:43 UTC (permalink / raw)
  To: Maximilian Luz, Leon Romanovsky, Blaž Hrastnik
  Cc: Greg Kroah-Hartman, lkml, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Rob Herring, Jiri Slaby,
	Rafael J . Wysocki, Len Brown, Steven Rostedt, Ingo Molnar,
	Masahiro Yamada, Michal Marek, Jonathan Corbet, Dorian Stoll,
	platform-driver-x86, linux-serial, linux-acpi, linux-kbuild,
	linux-doc

Hi,

On 12/6/20 11:33 AM, Maximilian Luz wrote:
> On 12/6/20 10:06 AM, Leon Romanovsky wrote:> On Sun, Dec 06, 2020 at 05:58:32PM +0900, Blaž Hrastnik wrote:
>>>>
>>>>> More on that, the whole purpose of proposed interface is to debug and
>>>>> not intended to be used by any user space code.
>>>>
>>>> The purpose is to provide raw access to the Surface Serial Hub protocol,
>>>> just like we provide raw access to USB devices and have hidraw devices.
>>>>
>>>> So this goes a litle beyond just debugging; and eventually the choice
>>>> may be made to implement some functionality with userspace drivers,
>>>> just like we do for some HID and USB devices.
>>>>
>>>> Still I agree with you that adding new userspace API is something which
>>>> needs to be considered carefully. So I will look at this closely when
>>>> reviewing this set.
>>>
>>> To add to that: this was previously a debugfs interface but was moved to misc after review on the initial RFC:
>>> https://lkml.org/lkml/2020/9/24/96
>>
>> There is a huge difference between the suggestion and final implementation.
>>
>> Greg suggested to add new debug module to the drivers/misc that will
>> open char device explicitly after user loaded that module to debug this
>> hub. However, the author added full blown char device as a first citizen
>> that has all not-break-user constrains.
> 
> This module still needs to be loaded explicitly.

Good then I really do not see a problem with this.

> And (I might be wrong
> about this) the "not-break-user constraints" hold as soon as I register
> a misc device at all, no?

Correct.

> So I don't see how this is a) any different
> than previously discussed with Greg and b) how the uapi header now
> introduces any not-break-user constraints that would not be there
> without it.
> 
> This interface is intended as a stable interface. That's something that
> I committed to as soon as I decided to implement this via a misc-device.
> 
> Sure, I can move the definitions in the uapi header to the module
> itself, but I don't see any benefit in that.

Right, if we are going to use a misc chardev for this, then the
correct thing to do is to put the API bits for that chardev under
include/uapi.

It would still be good if you can provide a pointer to some userspace
tools using this new API; and for the next version maybe add that
pointer to the commit message

Regards,

Hans


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06 10:04       ` Hans de Goede
  2020-12-06 10:33         ` Leon Romanovsky
@ 2020-12-06 10:51         ` Maximilian Luz
  1 sibling, 0 replies; 31+ messages in thread
From: Maximilian Luz @ 2020-12-06 10:51 UTC (permalink / raw)
  To: Hans de Goede, Leon Romanovsky
  Cc: Greg Kroah-Hartman, linux-kernel, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Rob Herring, Jiri Slaby,
	Rafael J . Wysocki, Len Brown, Steven Rostedt, Ingo Molnar,
	Masahiro Yamada, Michal Marek, Jonathan Corbet,
	Blaž Hrastnik, Dorian Stoll, platform-driver-x86,
	linux-serial, linux-acpi, linux-kbuild, linux-doc

On 12/6/20 11:04 AM, Hans de Goede wrote:
> Hi,
> 
> On 12/6/20 9:56 AM, Leon Romanovsky wrote:
>> On Sun, Dec 06, 2020 at 09:41:21AM +0100, Hans de Goede wrote:
>>> Hi Leon,
>>>
>>> On 12/6/20 8:07 AM, Leon Romanovsky wrote:
>>>> On Thu, Dec 03, 2020 at 10:26:31PM +0100, Maximilian Luz wrote:
>>>>> Hello,
>>>>>
>>>>> Here is version two of the Surface System Aggregator Module (SAM/SSAM)
>>>>> driver series, adding initial support for the embedded controller on 5th
>>>>> and later generation Microsoft Surface devices. Initial support includes
>>>>> the ACPI interface to the controller, via which battery and thermal
>>>>> information is provided on some of these devices.
>>>>>
>>>>> The previous version and cover letter detailing what this series is
>>>>> about can be found at
>>>>>
>>>>>    https://lore.kernel.org/platform-driver-x86/20201115192143.21571-1-luzmaximilian@gmail.com/
>>>>>
>>>>> This patch-set can also be found at the following repository and
>>>>> reference, if you prefer to look at a kernel tree instead of these
>>>>> emails:
>>>>>
>>>>>    https://github.com/linux-surface/kernel tags/s/surface-aggregator/v2
>>>>>
>>>>> Thank you all for the feedback to v1, I hope I have addressed all
>>>>> comments.
>>>>
>>>>
>>>> I think that it is too far fetched to attempt and expose UAPI headers
>>>> for some obscure char device that we are all know won't be around in
>>>> a couple of years from now due to the nature of how this embedded world
>>>> works.
>>>
>>> This is not for an embedded device, but for the popular line of
>>> Microsoft Surface laptops / 2-in-1s...
>>
>> It is the naming, we don't have char device for every "laptop" vendor.
>> Why is Microsoft different here?
> 
> Because their hardware department has invented a whole new way of dealing
> with a bunch of things at the hardware level (for some reason).
> 
> Also almost all laptop vendors have a whole bunch of laptop vendor
> specific userspace API in the form of sysfs files exported by
> drivers/platform/x86/laptop-vendor.c drivers. E.g. do:
> 
> ls /sys/bus/platform/devices/thinkpad_acpi/
> 
> An any IBM/Lenovo Thinkpad (and only on a Thinkpad) to see a bunch
> of laptop vendor specific UAPI.
> 
> Since I've become the pdx86 subsys maintainer I've actually been
> pushing back against adding more of this, instead trying to
> either use existing UAPIs, or defining new common UAPIs which can
> be shared between vendors.
> 
> So I agree very much with you that we need to be careful about
> needlessly introducing new UAPI.
> 
> But there is a difference between being careful and just nacking
> it because no new UAPI may be added at all (also see GKH's response).
> 
>>>> More on that, the whole purpose of proposed interface is to debug and
>>>> not intended to be used by any user space code.
>>>
>>> The purpose is to provide raw access to the Surface Serial Hub protocol,
>>> just like we provide raw access to USB devices and have hidraw devices.
>>
>> USB devices implement standard protocol, this surface hub is nothing
>> even close to that.
> 
> The USB protocol just defines a transport layer, outside of the USB classes
> there are plenty of proprietary protocols on top of that transport.
> 
> And this chardev just offers access to the Surface Serial Hub transport
> protocol. And if you want something even closer the i2cdev module offers
> raw I2C transfer access and I2C defines no protocol other then
> how to read or write a number of bytes.
> 
> I do a lot of hw enablement work and being able to poke HID / USB / I2C
> devices directly from userspace is very useful for this.

This is pretty much the whole reason for this module. Surface devices
vary from generation to generation, and I can't expect some random user
to write some kernel module (or repeatedly pull 10 different versions
of it)to test if some EC command does x or y. I can tell them to run a
script with some arguments though. The main reason for this is
development, debugging is secondary and IMHO part of development.

So, IOW this interface provides direct access to the EC that would,
without it, require you to write a kernel driver.

The whole "this is intended for development and debugging" shtick is to
deter people from using it to implement user-space based drivers. While
this may be possible at some point, at the moment there is no good way
to (reliably) detect which client devices are present from user-space.
So any attempts at that will likely be unstable and should be
implemented in the kernel anyway. It is a way of prototyping drivers
though.

>>> So this goes a litle beyond just debugging; and eventually the choice
>>> may be made to implement some functionality with userspace drivers,
>>> just like we do for some HID and USB devices.
>>
>> I don't know how it goes in device/platform area, but in other large
>> subsystems, UAPI should be presented with working user-space part.
>>
>>>
>>> Still I agree with you that adding new userspace API is something which
>>> needs to be considered carefully. So I will look at this closely when
>>> reviewing this set.
> 
> So this ^^^ still stands, I agree with you that adding new UAPI needs
> to be considered carefully and when I get around to reviewing this
> that is exactly what I will do.
> 
> Maximilian, can you perhaps explain a bit more of what you want / expect
> to use the chardev for, and maybe provide pointers to the matching
> userspace utilities (which I presume you have) ?

Sure. There's a bunch of scripts at

   https://github.com/linux-surface/surface-aggregator-module/tree/master/scripts/ssam

As described above, the main idea behind this simplifying development
for devices that I can't test myself. See e.g. the "ctrl.py" script
which can be used to send a basic command to the EC. The "hid.py" script
is one that was successfully used to test commands for the Keyboard
driver on the Surface Laptop 1 and 2.

At some point my plan is to maybe split that out into its own repo and
improve usability for all that, but I haven't gotten to that yet.

[...]

Regards,
Max

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06 10:43           ` Hans de Goede
@ 2020-12-06 10:56             ` Maximilian Luz
  0 siblings, 0 replies; 31+ messages in thread
From: Maximilian Luz @ 2020-12-06 10:56 UTC (permalink / raw)
  To: Hans de Goede, Leon Romanovsky, Blaž Hrastnik
  Cc: Greg Kroah-Hartman, lkml, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Rob Herring, Jiri Slaby,
	Rafael J . Wysocki, Len Brown, Steven Rostedt, Ingo Molnar,
	Masahiro Yamada, Michal Marek, Jonathan Corbet, Dorian Stoll,
	platform-driver-x86, linux-serial, linux-acpi, linux-kbuild,
	linux-doc

On 12/6/20 11:43 AM, Hans de Goede wrote:
> Hi,
> 
> On 12/6/20 11:33 AM, Maximilian Luz wrote:
>> On 12/6/20 10:06 AM, Leon Romanovsky wrote:> On Sun, Dec 06, 2020 at 05:58:32PM +0900, Blaž Hrastnik wrote:
>>>>>
>>>>>> More on that, the whole purpose of proposed interface is to debug and
>>>>>> not intended to be used by any user space code.
>>>>>
>>>>> The purpose is to provide raw access to the Surface Serial Hub protocol,
>>>>> just like we provide raw access to USB devices and have hidraw devices.
>>>>>
>>>>> So this goes a litle beyond just debugging; and eventually the choice
>>>>> may be made to implement some functionality with userspace drivers,
>>>>> just like we do for some HID and USB devices.
>>>>>
>>>>> Still I agree with you that adding new userspace API is something which
>>>>> needs to be considered carefully. So I will look at this closely when
>>>>> reviewing this set.
>>>>
>>>> To add to that: this was previously a debugfs interface but was moved to misc after review on the initial RFC:
>>>> https://lkml.org/lkml/2020/9/24/96
>>>
>>> There is a huge difference between the suggestion and final implementation.
>>>
>>> Greg suggested to add new debug module to the drivers/misc that will
>>> open char device explicitly after user loaded that module to debug this
>>> hub. However, the author added full blown char device as a first citizen
>>> that has all not-break-user constrains.
>>
>> This module still needs to be loaded explicitly.
> 
> Good then I really do not see a problem with this.
> 
>> And (I might be wrong
>> about this) the "not-break-user constraints" hold as soon as I register
>> a misc device at all, no?
> 
> Correct.
> 
>> So I don't see how this is a) any different
>> than previously discussed with Greg and b) how the uapi header now
>> introduces any not-break-user constraints that would not be there
>> without it.
>>
>> This interface is intended as a stable interface. That's something that
>> I committed to as soon as I decided to implement this via a misc-device.
>>
>> Sure, I can move the definitions in the uapi header to the module
>> itself, but I don't see any benefit in that.
> 
> Right, if we are going to use a misc chardev for this, then the
> correct thing to do is to put the API bits for that chardev under
> include/uapi.
> 
> It would still be good if you can provide a pointer to some userspace
> tools using this new API; and for the next version maybe add that
> pointer to the commit message

Right, I will add that to the commit message. I just linked you the
scripts in my other response, but here again for completeness:

   https://github.com/linux-surface/surface-aggregator-module/tree/master/scripts/ssam

While I'm not using the header directly (the scripts are written in
python) I still think uapi is the right place to put this (please
correct me if I'm wrong). Not putting them there seems to be needless
obfuscating to me.

Regards,
Max

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06  8:32   ` Greg Kroah-Hartman
  2020-12-06  8:35     ` Leon Romanovsky
@ 2020-12-06 11:13     ` Maximilian Luz
  1 sibling, 0 replies; 31+ messages in thread
From: Maximilian Luz @ 2020-12-06 11:13 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Leon Romanovsky
  Cc: linux-kernel, Hans de Goede, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Rob Herring, Jiri Slaby,
	Rafael J . Wysocki, Len Brown, Steven Rostedt, Ingo Molnar,
	Masahiro Yamada, Michal Marek, Jonathan Corbet,
	Blaž Hrastnik, Dorian Stoll, platform-driver-x86,
	linux-serial, linux-acpi, linux-kbuild, linux-doc

On 12/6/20 9:32 AM, Greg Kroah-Hartman wrote:
> On Sun, Dec 06, 2020 at 09:07:05AM +0200, Leon Romanovsky wrote:
>> On Thu, Dec 03, 2020 at 10:26:31PM +0100, Maximilian Luz wrote:
>>> Hello,
>>>
>>> Here is version two of the Surface System Aggregator Module (SAM/SSAM)
>>> driver series, adding initial support for the embedded controller on 5th
>>> and later generation Microsoft Surface devices. Initial support includes
>>> the ACPI interface to the controller, via which battery and thermal
>>> information is provided on some of these devices.
>>>
>>> The previous version and cover letter detailing what this series is
>>> about can be found at
>>>
>>>    https://lore.kernel.org/platform-driver-x86/20201115192143.21571-1-luzmaximilian@gmail.com/
>>>
>>> This patch-set can also be found at the following repository and
>>> reference, if you prefer to look at a kernel tree instead of these
>>> emails:
>>>
>>>    https://github.com/linux-surface/kernel tags/s/surface-aggregator/v2
>>>
>>> Thank you all for the feedback to v1, I hope I have addressed all
>>> comments.
>>
>>
>> I think that it is too far fetched to attempt and expose UAPI headers
>> for some obscure char device that we are all know won't be around in
>> a couple of years from now due to the nature of how this embedded world
>> works.
> 
> No, that's not ok, we do this for loads of devices out there.  If there
> is a device that wants to be supported for Linux, and a developer that
> wants to support it, we will take it.
> 
>> More on that, the whole purpose of proposed interface is to debug and
>> not intended to be used by any user space code.
> 
> I thought that debugfs was going to be used for most of the debugging
> code, or has that changed in newer versions of this patchset?
As per previous discussion (https://lkml.org/lkml/2020/9/24/96) I have
replaced the debugfs device by a misc-device with stable interface.

I also believe that this is probably the better option long-term. The
general idea is to have a device that has direct access to the
EC/transport protocol and can be used for development and prototyping.
Debugging is a part of that. So it's more akin to something raw access
via i2cdev, hidraw, or raw access to USB devices as Hans de Goede
mentioned in one of his mails. Note that the module must still be loaded
manually

Regards,
Max

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06 10:33         ` Maximilian Luz
  2020-12-06 10:43           ` Hans de Goede
@ 2020-12-06 11:30           ` Leon Romanovsky
  2020-12-06 13:27             ` Maximilian Luz
  1 sibling, 1 reply; 31+ messages in thread
From: Leon Romanovsky @ 2020-12-06 11:30 UTC (permalink / raw)
  To: Maximilian Luz
  Cc: Blaž Hrastnik, Hans de Goede, Greg Kroah-Hartman, lkml,
	Mark Gross, Andy Shevchenko, Barnabás Pőcze,
	Arnd Bergmann, Rob Herring, Jiri Slaby, Rafael J . Wysocki,
	Len Brown, Steven Rostedt, Ingo Molnar, Masahiro Yamada,
	Michal Marek, Jonathan Corbet, Dorian Stoll, platform-driver-x86,
	linux-serial, linux-acpi, linux-kbuild, linux-doc

On Sun, Dec 06, 2020 at 11:33:40AM +0100, Maximilian Luz wrote:
> On 12/6/20 10:06 AM, Leon Romanovsky wrote:> On Sun, Dec 06, 2020 at 05:58:32PM +0900, Blaž Hrastnik wrote:
> > > >
> > > > > More on that, the whole purpose of proposed interface is to debug and
> > > > > not intended to be used by any user space code.
> > > >
> > > > The purpose is to provide raw access to the Surface Serial Hub protocol,
> > > > just like we provide raw access to USB devices and have hidraw devices.
> > > >
> > > > So this goes a litle beyond just debugging; and eventually the choice
> > > > may be made to implement some functionality with userspace drivers,
> > > > just like we do for some HID and USB devices.
> > > >
> > > > Still I agree with you that adding new userspace API is something which
> > > > needs to be considered carefully. So I will look at this closely when
> > > > reviewing this set.
> > >
> > > To add to that: this was previously a debugfs interface but was moved to misc after review on the initial RFC:
> > > https://lkml.org/lkml/2020/9/24/96
> >
> > There is a huge difference between the suggestion and final implementation.
> >
> > Greg suggested to add new debug module to the drivers/misc that will
> > open char device explicitly after user loaded that module to debug this
> > hub. However, the author added full blown char device as a first citizen
> > that has all not-break-user constrains.
>
> This module still needs to be loaded explicitly. And (I might be wrong
> about this) the "not-break-user constraints" hold as soon as I register
> a misc device at all, no?

I don't think so, files in drivers/misc/* don't have such strict policy.

> than previously discussed with Greg and b) how the uapi header now
> introduces any not-break-user constraints that would not be there
> without it.

There is a huge difference between char device for the debug and
exposed UAPI header. The first requires from the user to build and
explicitly run it, while header allows to reliably build on top of
it various applications that we don't control. The not-break-rule
talks about the second.

>
> This interface is intended as a stable interface. That's something that
> I committed to as soon as I decided to implement this via a misc-device.
>
> Sure, I can move the definitions in the uapi header to the module
> itself, but I don't see any benefit in that. If someone really wants to
> use this interface, they can just as well copy the definitions from the
> module source itself. So why not be upfront about it and make life
> easier for everyone?

Because you are actually making life harder for everyone who cares about
UAPIs exposed by the Linux and they definitely different in numbers from
those who needs debug interface for the Microsoft Surface board.

Thanks

>
> Regards,
> Max
>

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06 10:41           ` Hans de Goede
@ 2020-12-06 11:41             ` Leon Romanovsky
  2020-12-06 13:43               ` Maximilian Luz
  0 siblings, 1 reply; 31+ messages in thread
From: Leon Romanovsky @ 2020-12-06 11:41 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Maximilian Luz, Greg Kroah-Hartman, linux-kernel, Mark Gross,
	Andy Shevchenko, Barnabás Pőcze, Arnd Bergmann,
	Rob Herring, Jiri Slaby, Rafael J . Wysocki, Len Brown,
	Steven Rostedt, Ingo Molnar, Masahiro Yamada, Michal Marek,
	Jonathan Corbet, Blaž Hrastnik, Dorian Stoll,
	platform-driver-x86, linux-serial, linux-acpi, linux-kbuild,
	linux-doc

On Sun, Dec 06, 2020 at 11:41:46AM +0100, Hans de Goede wrote:
> Hi,
>
> On 12/6/20 11:33 AM, Leon Romanovsky wrote:
> > On Sun, Dec 06, 2020 at 11:04:06AM +0100, Hans de Goede wrote:
>
> <snip>
>
> >> But there is a difference between being careful and just nacking
> >> it because no new UAPI may be added at all (also see GKH's response).
> >
> > I saw, the author misunderstood the Greg's comments.
>
> Quoting from patch 8/9:
>
> "
> +==============================
> +User-Space EC Interface (cdev)
> +==============================
> +
> +The ``surface_aggregator_cdev`` module provides a misc-device for the SSAM
> +controller to allow for a (more or less) direct connection from user-space to
> +the SAM EC. It is intended to be used for development and debugging, and
> +therefore should not be used or relied upon in any other way. Note that this
> +module is not loaded automatically, but instead must be loaded manually.
> "
>
> If I'm not mistaken that seems to be pretty much what Greg asked for.

Right, unless you forget the end of his request.
 "
  The "joy" of creating a user api is that no matter how much you tell
  people "do not depend on this", they will, so no matter the file being
  in debugfs, or a misc device, you might be stuck with it for forever,
  sorry.
 "

So I still think that exposing user api for a development and debug of device
that has no future is wrong thing to do.

Thanks

>
> Regards,
>
> Hans
>

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06 11:30           ` Leon Romanovsky
@ 2020-12-06 13:27             ` Maximilian Luz
  0 siblings, 0 replies; 31+ messages in thread
From: Maximilian Luz @ 2020-12-06 13:27 UTC (permalink / raw)
  To: Leon Romanovsky, Greg Kroah-Hartman, Hans de Goede
  Cc: Blaž Hrastnik, lkml, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Rob Herring, Jiri Slaby,
	Rafael J . Wysocki, Len Brown, Steven Rostedt, Ingo Molnar,
	Masahiro Yamada, Michal Marek, Jonathan Corbet, Dorian Stoll,
	platform-driver-x86, linux-serial, linux-acpi, linux-kbuild,
	linux-doc



On 12/6/20 12:30 PM, Leon Romanovsky wrote:
> On Sun, Dec 06, 2020 at 11:33:40AM +0100, Maximilian Luz wrote:
>> On 12/6/20 10:06 AM, Leon Romanovsky wrote:> On Sun, Dec 06, 2020 at 05:58:32PM +0900, Blaž Hrastnik wrote:
>>>>>
>>>>>> More on that, the whole purpose of proposed interface is to debug and
>>>>>> not intended to be used by any user space code.
>>>>>
>>>>> The purpose is to provide raw access to the Surface Serial Hub protocol,
>>>>> just like we provide raw access to USB devices and have hidraw devices.
>>>>>
>>>>> So this goes a litle beyond just debugging; and eventually the choice
>>>>> may be made to implement some functionality with userspace drivers,
>>>>> just like we do for some HID and USB devices.
>>>>>
>>>>> Still I agree with you that adding new userspace API is something which
>>>>> needs to be considered carefully. So I will look at this closely when
>>>>> reviewing this set.
>>>>
>>>> To add to that: this was previously a debugfs interface but was moved to misc after review on the initial RFC:
>>>> https://lkml.org/lkml/2020/9/24/96
>>>
>>> There is a huge difference between the suggestion and final implementation.
>>>
>>> Greg suggested to add new debug module to the drivers/misc that will
>>> open char device explicitly after user loaded that module to debug this
>>> hub. However, the author added full blown char device as a first citizen
>>> that has all not-break-user constrains.
>>
>> This module still needs to be loaded explicitly. And (I might be wrong
>> about this) the "not-break-user constraints" hold as soon as I register
>> a misc device at all, no?
> 
> I don't think so, files in drivers/misc/* don't have such strict policy.

Can I get a link to the documentation stating that or someone else
confirming that?

Also I don't think it makes sense to have a platform/surface device in
drivers/misc, after we've explicitly decided to move this code out of
there. IIRC drivers/misc is not a place for misc-devices, but the
directory for devices that don't have any good place elsewhere.

>> than previously discussed with Greg and b) how the uapi header now
>> introduces any not-break-user constraints that would not be there
>> without it.
> 
> There is a huge difference between char device for the debug and
> exposed UAPI header. The first requires from the user to build and
> explicitly run it, while header allows to reliably build on top of
> it various applications that we don't control. The not-break-rule
> talks about the second.

So it's okay to break stuff that's not explicitly in include/uapi/?
Again, can I get someone to confirm that for me?

As already said, I'm okay with moving the definitions from the header to
the module itself (if there is a consensus on that, CC Greg, Hans),
however both allow you to build user-space tools against the API. Case
in point my python scripts, which don't use the header. Or any other
non-C-based tool. So unless there's a rule that anything without a
header in uapi is fair game, I fail to see your point.

>> This interface is intended as a stable interface. That's something that
>> I committed to as soon as I decided to implement this via a misc-device.
>>
>> Sure, I can move the definitions in the uapi header to the module
>> itself, but I don't see any benefit in that. If someone really wants to
>> use this interface, they can just as well copy the definitions from the
>> module source itself. So why not be upfront about it and make life
>> easier for everyone?
> 
> Because you are actually making life harder for everyone who cares about
> UAPIs exposed by the Linux and they definitely different in numbers from
> those who needs debug interface for the Microsoft Surface board.

This point again depends on the interface not being stable. Unless, of
course, you want me to remove the interface completely and just maintain
it out of tree...

I'm happy to be told otherwise by the authorities here, but from past
conversations it seems that basically everything providing some sort of
user-space access falls under the "don't break user-space" rule as soon
as somebody uses it.

Regards,
Max

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06 11:41             ` Leon Romanovsky
@ 2020-12-06 13:43               ` Maximilian Luz
  0 siblings, 0 replies; 31+ messages in thread
From: Maximilian Luz @ 2020-12-06 13:43 UTC (permalink / raw)
  To: Leon Romanovsky, Hans de Goede, Greg Kroah-Hartman
  Cc: linux-kernel, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Rob Herring, Jiri Slaby,
	Rafael J . Wysocki, Len Brown, Steven Rostedt, Ingo Molnar,
	Masahiro Yamada, Michal Marek, Jonathan Corbet,
	Blaž Hrastnik, Dorian Stoll, platform-driver-x86,
	linux-serial, linux-acpi, linux-kbuild, linux-doc

On 12/6/20 12:41 PM, Leon Romanovsky wrote:
> On Sun, Dec 06, 2020 at 11:41:46AM +0100, Hans de Goede wrote:
>> Hi,
>>
>> On 12/6/20 11:33 AM, Leon Romanovsky wrote:
>>> On Sun, Dec 06, 2020 at 11:04:06AM +0100, Hans de Goede wrote:
>>
>> <snip>
>>
>>>> But there is a difference between being careful and just nacking
>>>> it because no new UAPI may be added at all (also see GKH's response).
>>>
>>> I saw, the author misunderstood the Greg's comments.
>>
>> Quoting from patch 8/9:
>>
>> "
>> +==============================
>> +User-Space EC Interface (cdev)
>> +==============================
>> +
>> +The ``surface_aggregator_cdev`` module provides a misc-device for the SSAM
>> +controller to allow for a (more or less) direct connection from user-space to
>> +the SAM EC. It is intended to be used for development and debugging, and
>> +therefore should not be used or relied upon in any other way. Note that this
>> +module is not loaded automatically, but instead must be loaded manually.
>> "
>>
>> If I'm not mistaken that seems to be pretty much what Greg asked for.
> 
> Right, unless you forget the end of his request.
>   "
>    The "joy" of creating a user api is that no matter how much you tell
>    people "do not depend on this", they will, so no matter the file being
>    in debugfs, or a misc device, you might be stuck with it for forever,
>    sorry.
>   "

Which to me reads as "if you want a user-space interface for development and
debugging, you'll have to make it a stable interface, regardless where it is
implemented". Rather making a point for a well though-out stable interface.
Specifically with regards to

    "
     >  - So you suggest I go with a misc device instead of putting this into
     >    debugfs?

     Yes.
    "

Unless of course I'm misunderstanding things entirely. Greg, please feel free
to yell at me if I've got this wrong.

> So I still think that exposing user api for a development and debug of device
> that has no future is wrong thing to do.

Unless you know something that I don't, MS is rumored to come out with a new
Surface Pro 8 and Surface Laptop 4 early next year, which I fully expect to
also have this EC built in. And if they once again decided to move some
functionality normally provided via ACPI or other means to the EC for some
reason, we'll likely need that interface again.

Yes, it has no future outside of Surface devices, but so has every other
platform driver with respect to their specific platform. What are your
alternatives to exposing a user API? If we want to be able to easily test
and attempt to provide support for Surface devices, there has to be some
interaction with user-space.

With respect to stability of the interface and future changes, I believe
that IOCTLs are the way to go. If that's in debugfs or, as was the
result from the previous discussion about this, via a misc-device...
I'll be happy to implement whatever a consensus yields, as long as it
can be used for its intended purpose: aid development with regards to
the EC found on Surface devices.

Regards,
Max

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06  7:07 ` [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module Leon Romanovsky
  2020-12-06  8:32   ` Greg Kroah-Hartman
  2020-12-06  8:41   ` Hans de Goede
@ 2020-12-06 15:58   ` Maximilian Luz
  2020-12-07  6:15     ` Leon Romanovsky
  2020-12-07  8:49     ` Hans de Goede
  2 siblings, 2 replies; 31+ messages in thread
From: Maximilian Luz @ 2020-12-06 15:58 UTC (permalink / raw)
  To: Leon Romanovsky, Greg Kroah-Hartman, Hans de Goede
  Cc: linux-kernel, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Rob Herring, Jiri Slaby,
	Rafael J . Wysocki, Len Brown, Steven Rostedt, Ingo Molnar,
	Masahiro Yamada, Michal Marek, Jonathan Corbet,
	Blaž Hrastnik, Dorian Stoll, platform-driver-x86,
	linux-serial, linux-acpi, linux-kbuild, linux-doc

On 12/6/20 8:07 AM, Leon Romanovsky wrote:
> On Thu, Dec 03, 2020 at 10:26:31PM +0100, Maximilian Luz wrote:
>> Hello,
>>
>> Here is version two of the Surface System Aggregator Module (SAM/SSAM)
>> driver series, adding initial support for the embedded controller on 5th
>> and later generation Microsoft Surface devices. Initial support includes
>> the ACPI interface to the controller, via which battery and thermal
>> information is provided on some of these devices.
>>
>> The previous version and cover letter detailing what this series is
>> about can be found at
>>
>>    https://lore.kernel.org/platform-driver-x86/20201115192143.21571-1-luzmaximilian@gmail.com/
>>
>> This patch-set can also be found at the following repository and
>> reference, if you prefer to look at a kernel tree instead of these
>> emails:
>>
>>    https://github.com/linux-surface/kernel tags/s/surface-aggregator/v2
>>
>> Thank you all for the feedback to v1, I hope I have addressed all
>> comments.
> 
> 
> I think that it is too far fetched to attempt and expose UAPI headers
> for some obscure char device that we are all know won't be around in
> a couple of years from now due to the nature of how this embedded world
> works.
> 
> More on that, the whole purpose of proposed interface is to debug and
> not intended to be used by any user space code.

I believe this has already been extensively discussed. I want to focus
more on the part below in this response:

> Also the idea that you are creating new bus just for this device doesn't
> really sound right. I recommend you to take a look on auxiliary bus and
> use it or come with very strong justifications why it is not fit yet.

I tend to agree that this is a valid concern to bring up, and adding a
new bus is not something that should be done lightly.

Let's ignore that this has been merged into -next after I've submitted
this (and that I only recently became aware of this) for the time being.
If I would see a clear benefit, I would not hesitate to switch the
driver and subsystem over to this.

What does concern me most, is the device/driver matching by string.
Right now, this subsystem matches those via a device UID. This UID is
directly tied to the EC functionality provided by the device. A bit of
background to this:

Requests sent to the EC contain an address, so to say. This consists of

  - Target category (TC): Broad group of functionality, e.g. battery/AC,
    thermal, HID input, ..., i.e. a subsystem of sorts.

  - Target ID (TID): Some major device, e.g. the dual batteries on the
    Surface Book 3 are addressed by target ID 1 and 2, some functionality
    is only available at 2 and some only at 1. May be related to physical
    parts of/locations on the device.

  - Instance ID (IID): A device instance, e.g. for thermal sensors each
    sensor is at TC=0x03 (thermal) and has a different instance ID.

Those can be used to pretty much uniquely identify a sub-device on the
EC.

Note the "pretty much". To truly make them unique we can add a function
ID (FN). With that, we can for example match for TC=0x03, TID=*, IID=*,
FN=0x00 to load a driver against all thermal sensors. And this is
basically the device UID that the subsystem uses for matching (modulo
domain for virtual devices, i.e. device hubs). Sure, we can use some
string, but that then leads to having to come up with creative names
once we need some driver specific data, e.g. in the battery driver [1]:

     const struct auxiliary_device_id my_auxiliary_id_table[] = {
         { .name = "surface_aggregator_registry.battery", .driver_data = x },
         { .name = "surface_aggregator_registry.battery_sb3", .driver_data = y },
         { },
     }

Arguably, not _that_ big of a deal.

What worries me more is that this will block any path of auto-detecting
devices on a more general/global level. Right now, we hard-code devices
because we haven't found any way to detect them via some EC query yet
[2] (FYI the node groups contain all devices that will eventually be
added to the bus, which are already 11 devices on the Surface Book 3
without taking missing thermal sensors into account; also they are
spread across a bunch of subsystems, so not just platform). That's of
course not an ideal solution and one that I hope we can eventually fix.
If we can auto-detect devices, it's very likely that we know or can
easily get to the device UID. A meaningful string is somewhat more
difficult.

This registry, which is loaded against a platform device that, from what
we can tell differentiates the models for some driver bindings by
Windows (that's speculation), is also the reason why we don't register
client devices directly under the main module, so instead of a nice
"surface_aggregator.<devicename>", you'll get
"surface_aggregator_registry.<devicename>". And it may not end there.

Something that's currently not implemented is support for thermal
sensors on 7th generation devices. With thermal sensors, we can already
detect which sensors, i.e. which IIDs, are present. Naturally, that's
part of the EC-API for thermal devices (TC=0x03), so would warrant a
master driver that registers the individual sensor drivers (that's a
place where I'd argue that in a normal situation, the auxiliary bus
makes sense). So with the auxiliary bus we'd now end up with devices
with "surface_thermal.sensor" for the sensors as well as
"surface_aggregator_registry.<devicename>", both of type ssam_device
(which then would be a wrapper around auxiliary_device with UID stored
in that wrapper). Note that they need to be of type ssam_device (or
another wrapper around that) as they again need the reference to the
controller device, their UID for access, etc. With a proper bus, device,
and the UID for matching, we can just add the sensor devices to the bus
again, as they will have a meaningful and guaranteed unique UID.

 From some reports I've seen it looks like thermal sensors may also be
available separately on TID=0x01 as well as TID=0x02 on some devices,
at which point I believe you'd need to introduce some IDA for ID
allocation to not cause a clash with IDs. At least if you separate the
base drivers for each TC, which I guess should be preferred due to
code-reuse. Then again they might use different event registries so you
may end up needing "surface_thermal.sensor_tc1" and
"surface_thermal.sensor_tc2" as device names to differentiate those
for driver loading. Or store the registry in software node properties
when registering the device.

I'm repeating myself here, but to me it looks cleaner to have a single
bus type as opposed to spreading the same base auxiliary device type
over several namespaces.

Which then leads me to the question of how a function like
"is_ssam_device()", i.e. a function testing if the device is of a given
type, would be implemented without enforcing and testing against some
part of the device name. Something that, again, doesn't look clean to
me. Although the use of such a function could probably avoided, but that
then feels like working around the auxiliary bus.

Unfortunately, there are a couple more hypotheticals at play than I'd
like to have (making this not an easy decision), but it's a reverse
engineered driver so I guess that comes with the territory. All in all,
I believe it's possible to do this (i.e. use the auxiliary bus), but, to
me at least, the implementation using a discrete bus feels tidier and
more true to the hardware (or virtual hardware anyway) behind this. I'm
happy to hear any arguments against this though.

Regards,
Max

[1]: https://github.com/linux-surface/surface-aggregator-module/blob/61b9bb859c30a8e17654c3a06696feb2691438f7/module/src/clients/surface_battery.c#L1075-L1079
[2]: https://github.com/linux-surface/surface-aggregator-module/blob/61b9bb859c30a8e17654c3a06696feb2691438f7/module/src/clients/surface_aggregator_registry.c

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06 15:58   ` Maximilian Luz
@ 2020-12-07  6:15     ` Leon Romanovsky
  2020-12-07  8:49     ` Hans de Goede
  1 sibling, 0 replies; 31+ messages in thread
From: Leon Romanovsky @ 2020-12-07  6:15 UTC (permalink / raw)
  To: Maximilian Luz
  Cc: Greg Kroah-Hartman, Hans de Goede, linux-kernel, Mark Gross,
	Andy Shevchenko, Barnabás Pőcze, Arnd Bergmann,
	Rob Herring, Jiri Slaby, Rafael J . Wysocki, Len Brown,
	Steven Rostedt, Ingo Molnar, Masahiro Yamada, Michal Marek,
	Jonathan Corbet, Blaž Hrastnik, Dorian Stoll,
	platform-driver-x86, linux-serial, linux-acpi, linux-kbuild,
	linux-doc

On Sun, Dec 06, 2020 at 04:58:52PM +0100, Maximilian Luz wrote:
> On 12/6/20 8:07 AM, Leon Romanovsky wrote:
> > On Thu, Dec 03, 2020 at 10:26:31PM +0100, Maximilian Luz wrote:
> > > Hello,
> > >
> > > Here is version two of the Surface System Aggregator Module (SAM/SSAM)
> > > driver series, adding initial support for the embedded controller on 5th
> > > and later generation Microsoft Surface devices. Initial support includes
> > > the ACPI interface to the controller, via which battery and thermal
> > > information is provided on some of these devices.
> > >
> > > The previous version and cover letter detailing what this series is
> > > about can be found at
> > >
> > >    https://lore.kernel.org/platform-driver-x86/20201115192143.21571-1-luzmaximilian@gmail.com/
> > >
> > > This patch-set can also be found at the following repository and
> > > reference, if you prefer to look at a kernel tree instead of these
> > > emails:
> > >
> > >    https://github.com/linux-surface/kernel tags/s/surface-aggregator/v2
> > >
> > > Thank you all for the feedback to v1, I hope I have addressed all
> > > comments.
> >
> >
> > I think that it is too far fetched to attempt and expose UAPI headers
> > for some obscure char device that we are all know won't be around in
> > a couple of years from now due to the nature of how this embedded world
> > works.
> >
> > More on that, the whole purpose of proposed interface is to debug and
> > not intended to be used by any user space code.
>
> I believe this has already been extensively discussed. I want to focus
> more on the part below in this response:
>
> > Also the idea that you are creating new bus just for this device doesn't
> > really sound right. I recommend you to take a look on auxiliary bus and
> > use it or come with very strong justifications why it is not fit yet.
>
> I tend to agree that this is a valid concern to bring up, and adding a
> new bus is not something that should be done lightly.
>
> Let's ignore that this has been merged into -next after I've submitted
> this (and that I only recently became aware of this) for the time being.
> If I would see a clear benefit, I would not hesitate to switch the
> driver and subsystem over to this.
>
> What does concern me most, is the device/driver matching by string.
> Right now, this subsystem matches those via a device UID. This UID is
> directly tied to the EC functionality provided by the device. A bit of
> background to this:
>
> Requests sent to the EC contain an address, so to say. This consists of
>
>  - Target category (TC): Broad group of functionality, e.g. battery/AC,
>    thermal, HID input, ..., i.e. a subsystem of sorts.
>
>  - Target ID (TID): Some major device, e.g. the dual batteries on the
>    Surface Book 3 are addressed by target ID 1 and 2, some functionality
>    is only available at 2 and some only at 1. May be related to physical
>    parts of/locations on the device.
>
>  - Instance ID (IID): A device instance, e.g. for thermal sensors each
>    sensor is at TC=0x03 (thermal) and has a different instance ID.
>
> Those can be used to pretty much uniquely identify a sub-device on the
> EC.
>
> Note the "pretty much". To truly make them unique we can add a function
> ID (FN). With that, we can for example match for TC=0x03, TID=*, IID=*,
> FN=0x00 to load a driver against all thermal sensors. And this is
> basically the device UID that the subsystem uses for matching (modulo
> domain for virtual devices, i.e. device hubs). Sure, we can use some
> string, but that then leads to having to come up with creative names
> once we need some driver specific data, e.g. in the battery driver [1]:
>
>     const struct auxiliary_device_id my_auxiliary_id_table[] = {
>         { .name = "surface_aggregator_registry.battery", .driver_data = x },
>         { .name = "surface_aggregator_registry.battery_sb3", .driver_data = y },
>         { },
>     }
>
> Arguably, not _that_ big of a deal.
>
> What worries me more is that this will block any path of auto-detecting
> devices on a more general/global level. Right now, we hard-code devices
> because we haven't found any way to detect them via some EC query yet
> [2] (FYI the node groups contain all devices that will eventually be
> added to the bus, which are already 11 devices on the Surface Book 3
> without taking missing thermal sensors into account; also they are
> spread across a bunch of subsystems, so not just platform). That's of
> course not an ideal solution and one that I hope we can eventually fix.
> If we can auto-detect devices, it's very likely that we know or can
> easily get to the device UID. A meaningful string is somewhat more
> difficult.
>
> This registry, which is loaded against a platform device that, from what
> we can tell differentiates the models for some driver bindings by
> Windows (that's speculation), is also the reason why we don't register
> client devices directly under the main module, so instead of a nice
> "surface_aggregator.<devicename>", you'll get
> "surface_aggregator_registry.<devicename>". And it may not end there.
>
> Something that's currently not implemented is support for thermal
> sensors on 7th generation devices. With thermal sensors, we can already
> detect which sensors, i.e. which IIDs, are present. Naturally, that's
> part of the EC-API for thermal devices (TC=0x03), so would warrant a
> master driver that registers the individual sensor drivers (that's a
> place where I'd argue that in a normal situation, the auxiliary bus
> makes sense). So with the auxiliary bus we'd now end up with devices
> with "surface_thermal.sensor" for the sensors as well as
> "surface_aggregator_registry.<devicename>", both of type ssam_device
> (which then would be a wrapper around auxiliary_device with UID stored
> in that wrapper). Note that they need to be of type ssam_device (or
> another wrapper around that) as they again need the reference to the
> controller device, their UID for access, etc. With a proper bus, device,
> and the UID for matching, we can just add the sensor devices to the bus
> again, as they will have a meaningful and guaranteed unique UID.
>
> From some reports I've seen it looks like thermal sensors may also be
> available separately on TID=0x01 as well as TID=0x02 on some devices,
> at which point I believe you'd need to introduce some IDA for ID
> allocation to not cause a clash with IDs. At least if you separate the
> base drivers for each TC, which I guess should be preferred due to
> code-reuse. Then again they might use different event registries so you
> may end up needing "surface_thermal.sensor_tc1" and
> "surface_thermal.sensor_tc2" as device names to differentiate those
> for driver loading. Or store the registry in software node properties
> when registering the device.
>
> I'm repeating myself here, but to me it looks cleaner to have a single
> bus type as opposed to spreading the same base auxiliary device type
> over several namespaces.
>
> Which then leads me to the question of how a function like
> "is_ssam_device()", i.e. a function testing if the device is of a given
> type, would be implemented without enforcing and testing against some
> part of the device name. Something that, again, doesn't look clean to
> me. Although the use of such a function could probably avoided, but that
> then feels like working around the auxiliary bus.
>
> Unfortunately, there are a couple more hypotheticals at play than I'd
> like to have (making this not an easy decision), but it's a reverse
> engineered driver so I guess that comes with the territory. All in all,
> I believe it's possible to do this (i.e. use the auxiliary bus), but, to
> me at least, the implementation using a discrete bus feels tidier and
> more true to the hardware (or virtual hardware anyway) behind this. I'm
> happy to hear any arguments against this though.

I'm not so certain why you are so focused on the names and UIDs.
The names are simply the aliases and needed to match between major
device driver that is responsible for the discovery and triggering
sub-driver with implementation.

Nothing prohibits from the logic: "if uid = 123; trigger "sensor""

Different devices and separation between them (*_registry module)
are expected to be in that main driver.

As an outcome of it, you will get: proper module autoload, device
discover and power management.

Thanks

>
> Regards,
> Max
>
> [1]: https://github.com/linux-surface/surface-aggregator-module/blob/61b9bb859c30a8e17654c3a06696feb2691438f7/module/src/clients/surface_battery.c#L1075-L1079
> [2]: https://github.com/linux-surface/surface-aggregator-module/blob/61b9bb859c30a8e17654c3a06696feb2691438f7/module/src/clients/surface_aggregator_registry.c

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-06 15:58   ` Maximilian Luz
  2020-12-07  6:15     ` Leon Romanovsky
@ 2020-12-07  8:49     ` Hans de Goede
  2020-12-07  9:12       ` Greg Kroah-Hartman
  1 sibling, 1 reply; 31+ messages in thread
From: Hans de Goede @ 2020-12-07  8:49 UTC (permalink / raw)
  To: Maximilian Luz, Leon Romanovsky, Greg Kroah-Hartman
  Cc: linux-kernel, Mark Gross, Andy Shevchenko,
	Barnabás Pőcze, Arnd Bergmann, Rob Herring, Jiri Slaby,
	Rafael J . Wysocki, Len Brown, Steven Rostedt, Ingo Molnar,
	Masahiro Yamada, Michal Marek, Jonathan Corbet,
	Blaž Hrastnik, Dorian Stoll, platform-driver-x86,
	linux-serial, linux-acpi, linux-kbuild, linux-doc

Hi,

On 12/6/20 4:58 PM, Maximilian Luz wrote:
> On 12/6/20 8:07 AM, Leon Romanovsky wrote:
>> On Thu, Dec 03, 2020 at 10:26:31PM +0100, Maximilian Luz wrote:
>>> Hello,
>>>
>>> Here is version two of the Surface System Aggregator Module (SAM/SSAM)
>>> driver series, adding initial support for the embedded controller on 5th
>>> and later generation Microsoft Surface devices. Initial support includes
>>> the ACPI interface to the controller, via which battery and thermal
>>> information is provided on some of these devices.
>>>
>>> The previous version and cover letter detailing what this series is
>>> about can be found at
>>>
>>>    https://lore.kernel.org/platform-driver-x86/20201115192143.21571-1-luzmaximilian@gmail.com/
>>>
>>> This patch-set can also be found at the following repository and
>>> reference, if you prefer to look at a kernel tree instead of these
>>> emails:
>>>
>>>    https://github.com/linux-surface/kernel tags/s/surface-aggregator/v2
>>>
>>> Thank you all for the feedback to v1, I hope I have addressed all
>>> comments.
>>
>>
>> I think that it is too far fetched to attempt and expose UAPI headers
>> for some obscure char device that we are all know won't be around in
>> a couple of years from now due to the nature of how this embedded world
>> works.
>>
>> More on that, the whole purpose of proposed interface is to debug and
>> not intended to be used by any user space code.
> 
> I believe this has already been extensively discussed. I want to focus
> more on the part below in this response:
> 
>> Also the idea that you are creating new bus just for this device doesn't
>> really sound right. I recommend you to take a look on auxiliary bus and
>> use it or come with very strong justifications why it is not fit yet.
> 
> I tend to agree that this is a valid concern to bring up, and adding a
> new bus is not something that should be done lightly.
> 
> Let's ignore that this has been merged into -next after I've submitted
> this (and that I only recently became aware of this) for the time being.
> If I would see a clear benefit, I would not hesitate to switch the
> driver and subsystem over to this.
> 
> What does concern me most, is the device/driver matching by string.
> Right now, this subsystem matches those via a device UID. This UID is
> directly tied to the EC functionality provided by the device. A bit of
> background to this:
> 
> Requests sent to the EC contain an address, so to say. This consists of
> 
>  - Target category (TC): Broad group of functionality, e.g. battery/AC,
>    thermal, HID input, ..., i.e. a subsystem of sorts.
> 
>  - Target ID (TID): Some major device, e.g. the dual batteries on the
>    Surface Book 3 are addressed by target ID 1 and 2, some functionality
>    is only available at 2 and some only at 1. May be related to physical
>    parts of/locations on the device.
> 
>  - Instance ID (IID): A device instance, e.g. for thermal sensors each
>    sensor is at TC=0x03 (thermal) and has a different instance ID.
> 
> Those can be used to pretty much uniquely identify a sub-device on the
> EC.

Thank you for this explanation, that is going to be useful to know
when I get around to reviewing this set (although I guess that you
probably also have written this down in one of the commit msgs /
docs I did not check).

> 
> Note the "pretty much". To truly make them unique we can add a function
> ID (FN). With that, we can for example match for TC=0x03, TID=*, IID=*,
> FN=0x00 to load a driver against all thermal sensors. And this is
> basically the device UID that the subsystem uses for matching (modulo
> domain for virtual devices, i.e. device hubs). Sure, we can use some
> string, but that then leads to having to come up with creative names
> once we need some driver specific data, e.g. in the battery driver [1]:
> 
>     const struct auxiliary_device_id my_auxiliary_id_table[] = {
>         { .name = "surface_aggregator_registry.battery", .driver_data = x },
>         { .name = "surface_aggregator_registry.battery_sb3", .driver_data = y },
>         { },
>     }
> 
> Arguably, not _that_ big of a deal.
> 
> What worries me more is that this will block any path of auto-detecting
> devices on a more general/global level. Right now, we hard-code devices
> because we haven't found any way to detect them via some EC query yet
> [2] (FYI the node groups contain all devices that will eventually be
> added to the bus, which are already 11 devices on the Surface Book 3
> without taking missing thermal sensors into account; also they are
> spread across a bunch of subsystems, so not just platform). That's of
> course not an ideal solution and one that I hope we can eventually fix.
> If we can auto-detect devices, it's very likely that we know or can
> easily get to the device UID. A meaningful string is somewhat more
> difficult.
> 
> This registry, which is loaded against a platform device that, from what
> we can tell differentiates the models for some driver bindings by
> Windows (that's speculation), is also the reason why we don't register
> client devices directly under the main module, so instead of a nice
> "surface_aggregator.<devicename>", you'll get
> "surface_aggregator_registry.<devicename>". And it may not end there.
> 
> Something that's currently not implemented is support for thermal
> sensors on 7th generation devices. With thermal sensors, we can already
> detect which sensors, i.e. which IIDs, are present. Naturally, that's
> part of the EC-API for thermal devices (TC=0x03), so would warrant a
> master driver that registers the individual sensor drivers (that's a
> place where I'd argue that in a normal situation, the auxiliary bus
> makes sense). So with the auxiliary bus we'd now end up with devices
> with "surface_thermal.sensor" for the sensors as well as
> "surface_aggregator_registry.<devicename>", both of type ssam_device
> (which then would be a wrapper around auxiliary_device with UID stored
> in that wrapper). Note that they need to be of type ssam_device (or
> another wrapper around that) as they again need the reference to the
> controller device, their UID for access, etc. With a proper bus, device,
> and the UID for matching, we can just add the sensor devices to the bus
> again, as they will have a meaningful and guaranteed unique UID.
> 
> From some reports I've seen it looks like thermal sensors may also be
> available separately on TID=0x01 as well as TID=0x02 on some devices,
> at which point I believe you'd need to introduce some IDA for ID
> allocation to not cause a clash with IDs. At least if you separate the
> base drivers for each TC, which I guess should be preferred due to
> code-reuse. Then again they might use different event registries so you
> may end up needing "surface_thermal.sensor_tc1" and
> "surface_thermal.sensor_tc2" as device names to differentiate those
> for driver loading. Or store the registry in software node properties
> when registering the device.
> 
> I'm repeating myself here, but to me it looks cleaner to have a single
> bus type as opposed to spreading the same base auxiliary device type
> over several namespaces.
> 
> Which then leads me to the question of how a function like
> "is_ssam_device()", i.e. a function testing if the device is of a given
> type, would be implemented without enforcing and testing against some
> part of the device name. Something that, again, doesn't look clean to
> me. Although the use of such a function could probably avoided, but that
> then feels like working around the auxiliary bus.
> 
> Unfortunately, there are a couple more hypotheticals at play than I'd
> like to have (making this not an easy decision), but it's a reverse
> engineered driver so I guess that comes with the territory. All in all,


> I believe it's possible to do this (i.e. use the auxiliary bus), but, to
> me at least, the implementation using a discrete bus feels tidier and
> more true to the hardware (or virtual hardware anyway) behind this. I'm
> happy to hear any arguments against this though.

I agree, the whole setup with the TC + TID + IID feels like the functionality
is nicely (and cleanly) split into separate functions and as with other
busses using a bus + 1 device per function for this is a perfectly clean
way to handle this.

Note if in the future you do see benefit in switching the auxiliary bus
I have no problems with that. But atm I don't really see any benefits of
doing so, so then we would just be switching over for the sake of switching
over which does not seem productive.

Regards,

Hans


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module
  2020-12-07  8:49     ` Hans de Goede
@ 2020-12-07  9:12       ` Greg Kroah-Hartman
  0 siblings, 0 replies; 31+ messages in thread
From: Greg Kroah-Hartman @ 2020-12-07  9:12 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Maximilian Luz, Leon Romanovsky, linux-kernel, Mark Gross,
	Andy Shevchenko, Barnabás Pőcze, Arnd Bergmann,
	Rob Herring, Jiri Slaby, Rafael J . Wysocki, Len Brown,
	Steven Rostedt, Ingo Molnar, Masahiro Yamada, Michal Marek,
	Jonathan Corbet, Blaž Hrastnik, Dorian Stoll,
	platform-driver-x86, linux-serial, linux-acpi, linux-kbuild,
	linux-doc

On Mon, Dec 07, 2020 at 09:49:03AM +0100, Hans de Goede wrote:
> Note if in the future you do see benefit in switching the auxiliary bus
> I have no problems with that. But atm I don't really see any benefits of
> doing so, so then we would just be switching over for the sake of switching
> over which does not seem productive.

I too do not see the benefit at this time to switch either.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 7/9] docs: driver-api: Add Surface Aggregator subsystem documentation
  2020-12-03 21:26 ` [PATCH v2 7/9] docs: driver-api: Add Surface Aggregator subsystem documentation Maximilian Luz
@ 2020-12-15 16:25   ` Hans de Goede
  0 siblings, 0 replies; 31+ messages in thread
From: Hans de Goede @ 2020-12-15 16:25 UTC (permalink / raw)
  To: Maximilian Luz, linux-kernel
  Cc: Mark Gross, Andy Shevchenko, Barnabás Pőcze,
	Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet,
	Blaž Hrastnik, Dorian Stoll, platform-driver-x86, linux-doc

Hi,

On 12/3/20 10:26 PM, Maximilian Luz wrote:
> Add documentation for the Surface Aggregator subsystem and its client
> drivers, giving an overview of the subsystem, its use-cases, its
> internal structure and internal API, as well as its external API for
> writing client drivers.
> 
> Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>

Thanks, patch looks good to me:

Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Regards,

Hans



> ---
> 
> Changes in v1 (from RFC):
>  - change SPDX identifier to GPL-2.0+ (was GPL-2.0-or-later)
>  - fix typos
> 
> Changes in v2:
>  - add architectural overview for core driver internals
>  - reorder chapters
>  - improve wording
> 
> ---
>  Documentation/driver-api/index.rst            |   1 +
>  .../surface_aggregator/client-api.rst         |  38 ++
>  .../driver-api/surface_aggregator/client.rst  | 393 ++++++++++++
>  .../surface_aggregator/clients/index.rst      |  10 +
>  .../driver-api/surface_aggregator/index.rst   |  21 +
>  .../surface_aggregator/internal-api.rst       |  67 ++
>  .../surface_aggregator/internal.rst           | 577 ++++++++++++++++++
>  .../surface_aggregator/overview.rst           |  77 +++
>  .../driver-api/surface_aggregator/ssh.rst     | 344 +++++++++++
>  MAINTAINERS                                   |   1 +
>  10 files changed, 1529 insertions(+)
>  create mode 100644 Documentation/driver-api/surface_aggregator/client-api.rst
>  create mode 100644 Documentation/driver-api/surface_aggregator/client.rst
>  create mode 100644 Documentation/driver-api/surface_aggregator/clients/index.rst
>  create mode 100644 Documentation/driver-api/surface_aggregator/index.rst
>  create mode 100644 Documentation/driver-api/surface_aggregator/internal-api.rst
>  create mode 100644 Documentation/driver-api/surface_aggregator/internal.rst
>  create mode 100644 Documentation/driver-api/surface_aggregator/overview.rst
>  create mode 100644 Documentation/driver-api/surface_aggregator/ssh.rst
> 
> diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst
> index f357f3eb400c..699dc7cac0fb 100644
> --- a/Documentation/driver-api/index.rst
> +++ b/Documentation/driver-api/index.rst
> @@ -97,6 +97,7 @@ available subsections can be seen below.
>     rfkill
>     serial/index
>     sm501
> +   surface_aggregator/index
>     switchtec
>     sync_file
>     vfio-mediated-device
> diff --git a/Documentation/driver-api/surface_aggregator/client-api.rst b/Documentation/driver-api/surface_aggregator/client-api.rst
> new file mode 100644
> index 000000000000..8e0b000d0e64
> --- /dev/null
> +++ b/Documentation/driver-api/surface_aggregator/client-api.rst
> @@ -0,0 +1,38 @@
> +.. SPDX-License-Identifier: GPL-2.0+
> +
> +===============================
> +Client Driver API Documentation
> +===============================
> +
> +.. contents::
> +    :depth: 2
> +
> +
> +Serial Hub Communication
> +========================
> +
> +.. kernel-doc:: include/linux/surface_aggregator/serial_hub.h
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/ssh_packet_layer.c
> +    :export:
> +
> +
> +Controller and Core Interface
> +=============================
> +
> +.. kernel-doc:: include/linux/surface_aggregator/controller.h
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/controller.c
> +    :export:
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/core.c
> +    :export:
> +
> +
> +Client Bus and Client Device API
> +================================
> +
> +.. kernel-doc:: include/linux/surface_aggregator/device.h
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/bus.c
> +    :export:
> diff --git a/Documentation/driver-api/surface_aggregator/client.rst b/Documentation/driver-api/surface_aggregator/client.rst
> new file mode 100644
> index 000000000000..26d13085a117
> --- /dev/null
> +++ b/Documentation/driver-api/surface_aggregator/client.rst
> @@ -0,0 +1,393 @@
> +.. SPDX-License-Identifier: GPL-2.0+
> +
> +.. |ssam_controller| replace:: :c:type:`struct ssam_controller <ssam_controller>`
> +.. |ssam_device| replace:: :c:type:`struct ssam_device <ssam_device>`
> +.. |ssam_device_driver| replace:: :c:type:`struct ssam_device_driver <ssam_device_driver>`
> +.. |ssam_client_bind| replace:: :c:func:`ssam_client_bind`
> +.. |ssam_client_link| replace:: :c:func:`ssam_client_link`
> +.. |ssam_get_controller| replace:: :c:func:`ssam_get_controller`
> +.. |ssam_controller_get| replace:: :c:func:`ssam_controller_get`
> +.. |ssam_controller_put| replace:: :c:func:`ssam_controller_put`
> +.. |ssam_device_alloc| replace:: :c:func:`ssam_device_alloc`
> +.. |ssam_device_add| replace:: :c:func:`ssam_device_add`
> +.. |ssam_device_remove| replace:: :c:func:`ssam_device_remove`
> +.. |ssam_device_driver_register| replace:: :c:func:`ssam_device_driver_register`
> +.. |ssam_device_driver_unregister| replace:: :c:func:`ssam_device_driver_unregister`
> +.. |module_ssam_device_driver| replace:: :c:func:`module_ssam_device_driver`
> +.. |SSAM_DEVICE| replace:: :c:func:`SSAM_DEVICE`
> +.. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register`
> +.. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister`
> +.. |ssam_request_sync| replace:: :c:func:`ssam_request_sync`
> +.. |ssam_event_mask| replace:: :c:type:`enum ssam_event_mask <ssam_event_mask>`
> +
> +
> +======================
> +Writing Client Drivers
> +======================
> +
> +For the API documentation, refer to:
> +
> +.. toctree::
> +   :maxdepth: 2
> +
> +   client-api
> +
> +
> +Overview
> +========
> +
> +Client drivers can be set up in two main ways, depending on how the
> +corresponding device is made available to the system. We specifically
> +differentiate between devices that are presented to the system via one of
> +the conventional ways, e.g. as platform devices via ACPI, and devices that
> +are non-discoverable and instead need to be explicitly provided by some
> +other mechanism, as discussed further below.
> +
> +
> +Non-SSAM Client Drivers
> +=======================
> +
> +All communication with the SAM EC is handled via the |ssam_controller|
> +representing that EC to the kernel. Drivers targeting a non-SSAM device (and
> +thus not being a |ssam_device_driver|) need to explicitly establish a
> +connection/relation to that controller. This can be done via the
> +|ssam_client_bind| function. Said function returns a reference to the SSAM
> +controller, but, more importantly, also establishes a device link between
> +client device and controller (this can also be done separate via
> +|ssam_client_link|). It is important to do this, as it, first, guarantees
> +that the returned controller is valid for use in the client driver for as
> +long as this driver is bound to its device, i.e. that the driver gets
> +unbound before the controller ever becomes invalid, and, second, as it
> +ensures correct suspend/resume ordering. This setup should be done in the
> +driver's probe function, and may be used to defer probing in case the SSAM
> +subsystem is not ready yet, for example:
> +
> +.. code-block:: c
> +
> +   static int client_driver_probe(struct platform_device *pdev)
> +   {
> +           struct ssam_controller *ctrl;
> +
> +           ctrl = ssam_client_bind(&pdev->dev);
> +           if (IS_ERR(ctrl))
> +                   return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
> +
> +           // ...
> +
> +           return 0;
> +   }
> +
> +The controller may be separately obtained via |ssam_get_controller| and its
> +lifetime be guaranteed via |ssam_controller_get| and |ssam_controller_put|.
> +Note that none of these functions, however, guarantee that the controller
> +will not be shut down or suspended. These functions essentially only operate
> +on the reference, i.e. only guarantee a bare minimum of accessibility
> +without any guarantees at all on practical operability.
> +
> +
> +Adding SSAM Devices
> +===================
> +
> +If a device does not already exist/is not already provided via conventional
> +means, it should be provided as |ssam_device| via the SSAM client device
> +hub. New devices can be added to this hub by entering their UID into the
> +corresponding registry. SSAM devices can also be manually allocated via
> +|ssam_device_alloc|, subsequently to which they have to be added via
> +|ssam_device_add| and eventually removed via |ssam_device_remove|. By
> +default, the parent of the device is set to the controller device provided
> +for allocation, however this may be changed before the device is added. Note
> +that, when changing the parent device, care must be taken to ensure that the
> +controller lifetime and suspend/resume ordering guarantees, in the default
> +setup provided through the parent-child relation, are preserved. If
> +necessary, by use of |ssam_client_link| as is done for non-SSAM client
> +drivers and described in more detail above.
> +
> +A client device must always be removed by the party which added the
> +respective device before the controller shuts down. Such removal can be
> +guaranteed by linking the driver providing the SSAM device to the controller
> +via |ssam_client_link|, causing it to unbind before the controller driver
> +unbinds. Client devices registered with the controller as parent are
> +automatically removed when the controller shuts down, but this should not be
> +relied upon, especially as this does not extend to client devices with a
> +different parent.
> +
> +
> +SSAM Client Drivers
> +===================
> +
> +SSAM client device drivers are, in essence, no different than other device
> +driver types. They are represented via |ssam_device_driver| and bind to a
> +|ssam_device| via its UID (:c:type:`struct ssam_device.uid <ssam_device>`)
> +member and the match table
> +(:c:type:`struct ssam_device_driver.match_table <ssam_device_driver>`),
> +which should be set when declaring the driver struct instance. Refer to the
> +|SSAM_DEVICE| macro documentation for more details on how to define members
> +of the driver's match table.
> +
> +The UID for SSAM client devices consists of a ``domain``, a ``category``,
> +a ``target``, an ``instance``, and a ``function``. The ``domain`` is used
> +differentiate between physical SAM devices
> +(:c:type:`SSAM_DOMAIN_SERIALHUB <ssam_device_domain>`), i.e. devices that can
> +be accessed via the Surface Serial Hub, and virtual ones
> +(:c:type:`SSAM_DOMAIN_VIRTUAL <ssam_device_domain>`), such as client-device
> +hubs, that have no real representation on the SAM EC and are solely used on
> +the kernel/driver-side. For physical devices, ``category`` represents the
> +target category, ``target`` the target ID, and ``instance`` the instance ID
> +used to access the physical SAM device. In addition, ``function`` references
> +a specific device functionality, but has no meaning to the SAM EC. The
> +(default) name of a client device is generated based on its UID.
> +
> +A driver instance can be registered via |ssam_device_driver_register| and
> +unregistered via |ssam_device_driver_unregister|. For convenience, the
> +|module_ssam_device_driver| macro may be used to define module init- and
> +exit-functions registering the driver.
> +
> +The controller associated with a SSAM client device can be found in its
> +:c:type:`struct ssam_device.ctrl <ssam_device>` member. This reference is
> +guaranteed to be valid for at least as long as the client driver is bound,
> +but should also be valid for as long as the client device exists. Note,
> +however, that access outside of the bound client driver must ensure that the
> +controller device is not suspended while making any requests or
> +(un-)registering event notifiers (and thus should generally be avoided). This
> +is guaranteed when the controller is accessed from inside the bound client
> +driver.
> +
> +
> +Making Synchronous Requests
> +===========================
> +
> +Synchronous requests are (currently) the main form of host-initiated
> +communication with the EC. There are a couple of ways to define and execute
> +such requests, however, most of them boil down to something similar as shown
> +in the example below. This example defines a write-read request, meaning
> +that the caller provides an argument to the SAM EC and receives a response.
> +The caller needs to know the (maximum) length of the response payload and
> +provide a buffer for it.
> +
> +Care must be taken to ensure that any command payload data passed to the SAM
> +EC is provided in little-endian format and, similarly, any response payload
> +data received from it is converted from little-endian to host endianness.
> +
> +.. code-block:: c
> +
> +   int perform_request(struct ssam_controller *ctrl, u32 arg, u32 *ret)
> +   {
> +           struct ssam_request rqst;
> +           struct ssam_response resp;
> +           int status;
> +
> +           /* Convert request argument to little-endian. */
> +           __le32 arg_le = cpu_to_le32(arg);
> +           __le32 ret_le = cpu_to_le32(0);
> +
> +           /*
> +            * Initialize request specification. Replace this with your values.
> +            * The rqst.payload field may be NULL if rqst.length is zero,
> +            * indicating that the request does not have any argument.
> +            *
> +            * Note: The request parameters used here are not valid, i.e.
> +            *       they do not correspond to an actual SAM/EC request.
> +            */
> +           rqst.target_category = SSAM_SSH_TC_SAM;
> +           rqst.target_id = 0x01;
> +           rqst.command_id = 0x02;
> +           rqst.instance_id = 0x03;
> +           rqst.flags = SSAM_REQUEST_HAS_RESPONSE;
> +           rqst.length = sizeof(arg_le);
> +           rqst.payload = (u8 *)&arg_le;
> +
> +           /* Initialize request response. */
> +           resp.capacity = sizeof(ret_le);
> +           resp.length = 0;
> +           resp.pointer = (u8 *)&ret_le;
> +
> +           /*
> +            * Perform actual request. The response pointer may be null in case
> +            * the request does not have any response. This must be consistent
> +            * with the SSAM_REQUEST_HAS_RESPONSE flag set in the specification
> +            * above.
> +            */
> +           status = ssam_request_sync(ctrl, &rqst, &resp);
> +
> +           /*
> +            * Alternatively use
> +            *
> +            *   ssam_request_sync_onstack(ctrl, &rqst, &resp, sizeof(arg_le));
> +            *
> +            * to perform the request, allocating the message buffer directly
> +            * on the stack as opposed to allocation via kzalloc().
> +            */
> +
> +           /*
> +            * Convert request response back to native format. Note that in the
> +            * error case, this value is not touched by the SSAM core, i.e.
> +            * 'ret_le' will be zero as specified in its initialization.
> +            */
> +           *ret = le32_to_cpu(ret_le);
> +
> +           return status;
> +   }
> +
> +Note that |ssam_request_sync| in its essence is a wrapper over lower-level
> +request primitives, which may also be used to perform requests. Refer to its
> +implementation and documentation for more details.
> +
> +An arguably more user-friendly way of defining such functions is by using
> +one of the generator macros, for example via:
> +
> +.. code-block:: c
> +
> +   SSAM_DEFINE_SYNC_REQUEST_W(__ssam_tmp_perf_mode_set, __le32, {
> +           .target_category = SSAM_SSH_TC_TMP,
> +           .target_id       = 0x01,
> +           .command_id      = 0x03,
> +           .instance_id     = 0x00,
> +   });
> +
> +This example defines a function
> +
> +.. code-block:: c
> +
> +   int __ssam_tmp_perf_mode_set(struct ssam_controller *ctrl, const __le32 *arg);
> +
> +executing the specified request, with the controller passed in when calling
> +said function. In this example, the argument is provided via the ``arg``
> +pointer. Note that the generated function allocates the message buffer on
> +the stack. Thus, if the argument provided via the request is large, these
> +kinds of macros should be avoided. Also note that, in contrast to the
> +previous non-macro example, this function does not do any endianness
> +conversion, which has to be handled by the caller. Apart from those
> +differences the function generated by the macro is similar to the one
> +provided in the non-macro example above.
> +
> +The full list of such function-generating macros is
> +
> +- :c:func:`SSAM_DEFINE_SYNC_REQUEST_N` for requests without return value and
> +  without argument.
> +- :c:func:`SSAM_DEFINE_SYNC_REQUEST_R` for requests with return value but no
> +  argument.
> +- :c:func:`SSAM_DEFINE_SYNC_REQUEST_W` for requests without return value but
> +  with argument.
> +
> +Refer to their respective documentation for more details. For each one of
> +these macros, a special variant is provided, which targets request types
> +applicable to multiple instances of the same device type:
> +
> +- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_N`
> +- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_R`
> +- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_W`
> +
> +The difference of those macros to the previously mentioned versions is, that
> +the device target and instance IDs are not fixed for the generated function,
> +but instead have to be provided by the caller of said function.
> +
> +Additionally, variants for direct use with client devices, i.e.
> +|ssam_device|, are also provided. These can, for example, be used as
> +follows:
> +
> +.. code-block:: c
> +
> +   SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_sta, __le32, {
> +           .target_category = SSAM_SSH_TC_BAT,
> +           .command_id      = 0x01,
> +   });
> +
> +This invocation of the macro defines a function
> +
> +.. code-block:: c
> +
> +   int ssam_bat_get_sta(struct ssam_device *sdev, __le32 *ret);
> +
> +executing the specified request, using the device IDs and controller given
> +in the client device. The full list of such macros for client devices is:
> +
> +- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_N`
> +- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_R`
> +- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_W`
> +
> +
> +Handling Events
> +===============
> +
> +To receive events from the SAM EC, an event notifier must be registered for
> +the desired event via |ssam_notifier_register|. The notifier must be
> +unregistered via |ssam_notifier_unregister| once it is not required any
> +more.
> +
> +Event notifiers are registered by providing (at minimum) a callback to call
> +in case an event has been received, the registry specifying how the event
> +should be enabled, an event ID specifying for which target category and,
> +optionally and depending on the registry used, for which instance ID events
> +should be enabled, and finally, flags describing how the EC will send these
> +events. If the specific registry does not enable events by instance ID, the
> +instance ID must be set to zero. Additionally, a priority for the respective
> +notifier may be specified, which determines its order in relation to any
> +other notifier registered for the same target category.
> +
> +By default, event notifiers will receive all events for the specific target
> +category, regardless of the instance ID specified when registering the
> +notifier. The core may be instructed to only call a notifier if the target
> +ID or instance ID (or both) of the event match the ones implied by the
> +notifier IDs (in case of target ID, the target ID of the registry), by
> +providing an event mask (see |ssam_event_mask|).
> +
> +In general, the target ID of the registry is also the target ID of the
> +enabled event (with the notable exception being keyboard input events on the
> +Surface Laptop 1 and 2, which are enabled via a registry with target ID 1,
> +but provide events with target ID 2).
> +
> +A full example for registering an event notifier and handling received
> +events is provided below:
> +
> +.. code-block:: c
> +
> +   u32 notifier_callback(struct ssam_event_notifier *nf,
> +                         const struct ssam_event *event)
> +   {
> +           int status = ...
> +
> +           /* Handle the event here ... */
> +
> +           /* Convert return value and indicate that we handled the event. */
> +           return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED;
> +   }
> +
> +   int setup_notifier(struct ssam_device *sdev,
> +                      struct ssam_event_notifier *nf)
> +   {
> +           /* Set priority wrt. other handlers of same target category. */
> +           nf->base.priority = 1;
> +
> +           /* Set event/notifier callback. */
> +           nf->base.fn = notifier_callback;
> +
> +           /* Specify event registry, i.e. how events get enabled/disabled. */
> +           nf->event.reg = SSAM_EVENT_REGISTRY_KIP;
> +
> +           /* Specify which event to enable/disable */
> +           nf->event.id.target_category = sdev->uid.category;
> +           nf->event.id.instance = sdev->uid.instance;
> +
> +           /*
> +            * Specify for which events the notifier callback gets executed.
> +            * This essentially tells the core if it can skip notifiers that
> +            * don't have target or instance IDs matching those of the event.
> +            */
> +           nf->event.mask = SSAM_EVENT_MASK_STRICT;
> +
> +           /* Specify event flags. */
> +           nf->event.flags = SSAM_EVENT_SEQUENCED;
> +
> +           return ssam_notifier_register(sdev->ctrl, nf);
> +   }
> +
> +Multiple event notifiers can be registered for the same event. The event
> +handler core takes care of enabling and disabling events when notifiers are
> +registered and unregistered, by keeping track of how many notifiers for a
> +specific event (combination of registry, event target category, and event
> +instance ID) are currently registered. This means that a specific event will
> +be enabled when the first notifier for it is being registered and disabled
> +when the last notifier for it is being unregistered. Note that the event
> +flags are therefore only used on the first registered notifier, however, one
> +should take care that notifiers for a specific event are always registered
> +with the same flag and it is considered a bug to do otherwise.
> diff --git a/Documentation/driver-api/surface_aggregator/clients/index.rst b/Documentation/driver-api/surface_aggregator/clients/index.rst
> new file mode 100644
> index 000000000000..31e026d96102
> --- /dev/null
> +++ b/Documentation/driver-api/surface_aggregator/clients/index.rst
> @@ -0,0 +1,10 @@
> +.. SPDX-License-Identifier: GPL-2.0+
> +
> +===========================
> +Client Driver Documentation
> +===========================
> +
> +This is the documentation for client drivers themselves. Refer to
> +:doc:`../client` for documentation on how to write client drivers.
> +
> +.. Place documentation for individual client drivers here.
> diff --git a/Documentation/driver-api/surface_aggregator/index.rst b/Documentation/driver-api/surface_aggregator/index.rst
> new file mode 100644
> index 000000000000..6f3e1094904d
> --- /dev/null
> +++ b/Documentation/driver-api/surface_aggregator/index.rst
> @@ -0,0 +1,21 @@
> +.. SPDX-License-Identifier: GPL-2.0+
> +
> +=======================================
> +Surface System Aggregator Module (SSAM)
> +=======================================
> +
> +.. toctree::
> +   :maxdepth: 2
> +
> +   overview
> +   client
> +   clients/index
> +   ssh
> +   internal
> +
> +.. only::  subproject and html
> +
> +   Indices
> +   =======
> +
> +   * :ref:`genindex`
> diff --git a/Documentation/driver-api/surface_aggregator/internal-api.rst b/Documentation/driver-api/surface_aggregator/internal-api.rst
> new file mode 100644
> index 000000000000..639a67b5a392
> --- /dev/null
> +++ b/Documentation/driver-api/surface_aggregator/internal-api.rst
> @@ -0,0 +1,67 @@
> +.. SPDX-License-Identifier: GPL-2.0+
> +
> +==========================
> +Internal API Documentation
> +==========================
> +
> +.. contents::
> +    :depth: 2
> +
> +
> +Packet Transport Layer
> +======================
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/ssh_parser.h
> +    :internal:
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/ssh_parser.c
> +    :internal:
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/ssh_msgb.h
> +    :internal:
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/ssh_packet_layer.h
> +    :internal:
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/ssh_packet_layer.c
> +    :internal:
> +
> +
> +Request Transport Layer
> +=======================
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/ssh_request_layer.h
> +    :internal:
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/ssh_request_layer.c
> +    :internal:
> +
> +
> +Controller
> +==========
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/controller.h
> +    :internal:
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/controller.c
> +    :internal:
> +
> +
> +Client Device Bus
> +=================
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/bus.c
> +    :internal:
> +
> +
> +Core
> +====
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/core.c
> +    :internal:
> +
> +
> +Trace Helpers
> +=============
> +
> +.. kernel-doc:: drivers/platform/surface/aggregator/trace.h
> diff --git a/Documentation/driver-api/surface_aggregator/internal.rst b/Documentation/driver-api/surface_aggregator/internal.rst
> new file mode 100644
> index 000000000000..72704734982a
> --- /dev/null
> +++ b/Documentation/driver-api/surface_aggregator/internal.rst
> @@ -0,0 +1,577 @@
> +.. SPDX-License-Identifier: GPL-2.0+
> +
> +.. |ssh_ptl| replace:: :c:type:`struct ssh_ptl <ssh_ptl>`
> +.. |ssh_ptl_submit| replace:: :c:func:`ssh_ptl_submit`
> +.. |ssh_ptl_cancel| replace:: :c:func:`ssh_ptl_cancel`
> +.. |ssh_ptl_shutdown| replace:: :c:func:`ssh_ptl_shutdown`
> +.. |ssh_ptl_rx_rcvbuf| replace:: :c:func:`ssh_ptl_rx_rcvbuf`
> +.. |ssh_rtl| replace:: :c:type:`struct ssh_rtl <ssh_rtl>`
> +.. |ssh_rtl_submit| replace:: :c:func:`ssh_rtl_submit`
> +.. |ssh_rtl_cancel| replace:: :c:func:`ssh_rtl_cancel`
> +.. |ssh_rtl_shutdown| replace:: :c:func:`ssh_rtl_shutdown`
> +.. |ssh_packet| replace:: :c:type:`struct ssh_packet <ssh_packet>`
> +.. |ssh_packet_get| replace:: :c:func:`ssh_packet_get`
> +.. |ssh_packet_put| replace:: :c:func:`ssh_packet_put`
> +.. |ssh_packet_ops| replace:: :c:type:`struct ssh_packet_ops <ssh_packet_ops>`
> +.. |ssh_packet_base_priority| replace:: :c:type:`enum ssh_packet_base_priority <ssh_packet_base_priority>`
> +.. |ssh_packet_flags| replace:: :c:type:`enum ssh_packet_flags <ssh_packet_flags>`
> +.. |SSH_PACKET_PRIORITY| replace:: :c:func:`SSH_PACKET_PRIORITY`
> +.. |ssh_frame| replace:: :c:type:`struct ssh_frame <ssh_frame>`
> +.. |ssh_command| replace:: :c:type:`struct ssh_command <ssh_command>`
> +.. |ssh_request| replace:: :c:type:`struct ssh_request <ssh_request>`
> +.. |ssh_request_get| replace:: :c:func:`ssh_request_get`
> +.. |ssh_request_put| replace:: :c:func:`ssh_request_put`
> +.. |ssh_request_ops| replace:: :c:type:`struct ssh_request_ops <ssh_request_ops>`
> +.. |ssh_request_init| replace:: :c:func:`ssh_request_init`
> +.. |ssh_request_flags| replace:: :c:type:`enum ssh_request_flags <ssh_request_flags>`
> +.. |ssam_controller| replace:: :c:type:`struct ssam_controller <ssam_controller>`
> +.. |ssam_device| replace:: :c:type:`struct ssam_device <ssam_device>`
> +.. |ssam_device_driver| replace:: :c:type:`struct ssam_device_driver <ssam_device_driver>`
> +.. |ssam_client_bind| replace:: :c:func:`ssam_client_bind`
> +.. |ssam_client_link| replace:: :c:func:`ssam_client_link`
> +.. |ssam_request_sync| replace:: :c:type:`struct ssam_request_sync <ssam_request_sync>`
> +.. |ssam_event_registry| replace:: :c:type:`struct ssam_event_registry <ssam_event_registry>`
> +.. |ssam_event_id| replace:: :c:type:`struct ssam_event_id <ssam_event_id>`
> +.. |ssam_nf| replace:: :c:type:`struct ssam_nf <ssam_nf>`
> +.. |ssam_nf_refcount_inc| replace:: :c:func:`ssam_nf_refcount_inc`
> +.. |ssam_nf_refcount_dec| replace:: :c:func:`ssam_nf_refcount_dec`
> +.. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register`
> +.. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister`
> +.. |ssam_cplt| replace:: :c:type:`struct ssam_cplt <ssam_cplt>`
> +.. |ssam_event_queue| replace:: :c:type:`struct ssam_event_queue <ssam_event_queue>`
> +.. |ssam_request_sync_submit| replace:: :c:func:`ssam_request_sync_submit`
> +
> +=====================
> +Core Driver Internals
> +=====================
> +
> +Architectural overview of the Surface System Aggregator Module (SSAM) core
> +and Surface Serial Hub (SSH) driver. For the API documentation, refer to:
> +
> +.. toctree::
> +   :maxdepth: 2
> +
> +   internal-api
> +
> +
> +Overview
> +========
> +
> +The SSAM core implementation is structured in layers, somewhat following the
> +SSH protocol structure:
> +
> +Lower-level packet transport is implemented in the *packet transport layer
> +(PTL)*, directly building on top of the serial device (serdev)
> +infrastructure of the kernel. As the name indicates, this layer deals with
> +the packet transport logic and handles things like packet validation, packet
> +acknowledgment (ACKing), packet (retransmission) timeouts, and relaying
> +packet payloads to higher-level layers.
> +
> +Above this sits the *request transport layer (RTL)*. This layer is centered
> +around command-type packet payloads, i.e. requests (sent from host to EC),
> +responses of the EC to those requests, and events (sent from EC to host).
> +It, specifically, distinguishes events from request responses, matches
> +responses to their corresponding requests, and implements request timeouts.
> +
> +The *controller* layer is building on top of this and essentially decides
> +how request responses and, especially, events are dealt with. It provides an
> +event notifier system, handles event activation/deactivation, provides a
> +workqueue for event and asynchronous request completion, and also manages
> +the message counters required for building command messages (``SEQ``,
> +``RQID``). This layer basically provides a fundamental interface to the SAM
> +EC for use in other kernel drivers.
> +
> +While the controller layer already provides an interface for other kernel
> +drivers, the client *bus* extends this interface to provide support for
> +native SSAM devices, i.e. devices that are not defined in ACPI and not
> +implemented as platform devices, via |ssam_device| and |ssam_device_driver|
> +simplify management of client devices and client drivers.
> +
> +Refer to :doc:`client` for documentation regarding the client device/driver
> +API and interface options for other kernel drivers. It is recommended to
> +familiarize oneself with that chapter and the :doc:`ssh` before continuing
> +with the architectural overview below.
> +
> +
> +Packet Transport Layer
> +======================
> +
> +The packet transport layer is represented via |ssh_ptl| and is structured
> +around the following key concepts:
> +
> +Packets
> +-------
> +
> +Packets are the fundamental transmission unit of the SSH protocol. They are
> +managed by the packet transport layer, which is essentially the lowest layer
> +of the driver and is built upon by other components of the SSAM core.
> +Packets to be transmitted by the SSAM core are represented via |ssh_packet|
> +(in contrast, packets received by the core do not have any specific
> +structure and are managed entirely via the raw |ssh_frame|).
> +
> +This structure contains the required fields to manage the packet inside the
> +transport layer, as well as a reference to the buffer containing the data to
> +be transmitted (i.e. the message wrapped in |ssh_frame|). Most notably, it
> +contains an internal reference count, which is used for managing its
> +lifetime (accessible via |ssh_packet_get| and |ssh_packet_put|). When this
> +counter reaches zero, the ``release()`` callback provided to the packet via
> +its |ssh_packet_ops| reference is executed, which may then deallocate the
> +packet or its enclosing structure (e.g. |ssh_request|).
> +
> +In addition to the ``release`` callback, the |ssh_packet_ops| reference also
> +provides a ``complete()`` callback, which is run once the packet has been
> +completed and provides the status of this completion, i.e. zero on success
> +or a negative errno value in case of an error. Once the packet has been
> +submitted to the packet transport layer, the ``complete()`` callback is
> +always guaranteed to be executed before the ``release()`` callback, i.e. the
> +packet will always be completed, either successfully, with an error, or due
> +to cancellation, before it will be released.
> +
> +The state of a packet is managed via its ``state`` flags
> +(|ssh_packet_flags|), which also contains the packet type. In particular,
> +the following bits are noteworthy:
> +
> +* ``SSH_PACKET_SF_LOCKED_BIT``: This bit is set when completion, either
> +  through error or success, is imminent. It indicates that no further
> +  references of the packet should be taken and any existing references
> +  should be dropped as soon as possible. The process setting this bit is
> +  responsible for removing any references to this packet from the packet
> +  queue and pending set.
> +
> +* ``SSH_PACKET_SF_COMPLETED_BIT``: This bit is set by the process running the
> +  ``complete()`` callback and is used to ensure that this callback only runs
> +  once.
> +
> +* ``SSH_PACKET_SF_QUEUED_BIT``: This bit is set when the packet is queued on
> +  the packet queue and cleared when it is dequeued.
> +
> +* ``SSH_PACKET_SF_PENDING_BIT``: This bit is set when the packet is added to
> +  the pending set and cleared when it is removed from it.
> +
> +Packet Queue
> +------------
> +
> +The packet queue is the first of the two fundamental collections in the
> +packet transport layer. It is a priority queue, with priority of the
> +respective packets based on the packet type (major) and number of tries
> +(minor). See |SSH_PACKET_PRIORITY| for more details on the priority value.
> +
> +All packets to be transmitted by the transport layer must be submitted to
> +this queue via |ssh_ptl_submit|. Note that this includes control packets
> +sent by the transport layer itself. Internally, data packets can be
> +re-submitted to this queue due to timeouts or NAK packets sent by the EC.
> +
> +Pending Set
> +-----------
> +
> +The pending set is the second of the two fundamental collections in the
> +packet transport layer. It stores references to packets that have already
> +been transmitted, but wait for acknowledgment (e.g. the corresponding ACK
> +packet) by the EC.
> +
> +Note that a packet may both be pending and queued if it has been
> +re-submitted due to a packet acknowledgment timeout or NAK. On such a
> +re-submission, packets are not removed from the pending set.
> +
> +Transmitter Thread
> +------------------
> +
> +The transmitter thread is responsible for most of the actual work regarding
> +packet transmission. In each iteration, it (waits for and) checks if the
> +next packet on the queue (if any) can be transmitted and, if so, removes it
> +from the queue and increments its counter for the number of transmission
> +attempts, i.e. tries. If the packet is sequenced, i.e. requires an ACK by
> +the EC, the packet is added to the pending set. Next, the packet's data is
> +submitted to the serdev subsystem. In case of an error or timeout during
> +this submission, the packet is completed by the transmitter thread with the
> +status value of the callback set accordingly. In case the packet is
> +unsequenced, i.e. does not require an ACK by the EC, the packet is completed
> +with success on the transmitter thread.
> +
> +Transmission of sequenced packets is limited by the number of concurrently
> +pending packets, i.e. a limit on how many packets may be waiting for an ACK
> +from the EC in parallel. This limit is currently set to one (see :doc:`ssh`
> +for the reasoning behind this). Control packets (i.e. ACK and NAK) can
> +always be transmitted.
> +
> +Receiver Thread
> +---------------
> +
> +Any data received from the EC is put into a FIFO buffer for further
> +processing. This processing happens on the receiver thread. The receiver
> +thread parses and validates the received message into its |ssh_frame| and
> +corresponding payload. It prepares and submits the necessary ACK (and on
> +validation error or invalid data NAK) packets for the received messages.
> +
> +This thread also handles further processing, such as matching ACK messages
> +to the corresponding pending packet (via sequence ID) and completing it, as
> +well as initiating re-submission of all currently pending packets on
> +receival of a NAK message (re-submission in case of a NAK is similar to
> +re-submission due to timeout, see below for more details on that). Note that
> +the successful completion of a sequenced packet will always run on the
> +receiver thread (whereas any failure-indicating completion will run on the
> +process where the failure occurred).
> +
> +Any payload data is forwarded via a callback to the next upper layer, i.e.
> +the request transport layer.
> +
> +Timeout Reaper
> +--------------
> +
> +The packet acknowledgment timeout is a per-packet timeout for sequenced
> +packets, started when the respective packet begins (re-)transmission (i.e.
> +this timeout is armed once per transmission attempt on the transmitter
> +thread). It is used to trigger re-submission or, when the number of tries
> +has been exceeded, cancellation of the packet in question.
> +
> +This timeout is handled via a dedicated reaper task, which is essentially a
> +work item (re-)scheduled to run when the next packet is set to time out. The
> +work item then checks the set of pending packets for any packets that have
> +exceeded the timeout and, if there are any remaining packets, re-schedules
> +itself to the next appropriate point in time.
> +
> +If a timeout has been detected by the reaper, the packet will either be
> +re-submitted if it still has some remaining tries left, or completed with
> +``-ETIMEDOUT`` as status if not. Note that re-submission, in this case and
> +triggered by receival of a NAK, means that the packet is added to the queue
> +with a now incremented number of tries, yielding a higher priority. The
> +timeout for the packet will be disabled until the next transmission attempt
> +and the packet remains on the pending set.
> +
> +Note that due to transmission and packet acknowledgment timeouts, the packet
> +transport layer is always guaranteed to make progress, if only through
> +timing out packets, and will never fully block.
> +
> +Concurrency and Locking
> +-----------------------
> +
> +There are two main locks in the packet transport layer: One guarding access
> +to the packet queue and one guarding access to the pending set. These
> +collections may only be accessed and modified under the respective lock. If
> +access to both collections is needed, the pending lock must be acquired
> +before the queue lock to avoid deadlocks.
> +
> +In addition to guarding the collections, after initial packet submission
> +certain packet fields may only be accessed under one of the locks.
> +Specifically, the packet priority must only be accessed while holding the
> +queue lock and the packet timestamp must only be accessed while holding the
> +pending lock.
> +
> +Other parts of the packet transport layer are guarded independently. State
> +flags are managed by atomic bit operations and, if necessary, memory
> +barriers. Modifications to the timeout reaper work item and expiration date
> +are guarded by their own lock.
> +
> +The reference of the packet to the packet transport layer (``ptl``) is
> +somewhat special. It is either set when the upper layer request is submitted
> +or, if there is none, when the packet is first submitted. After it is set,
> +it will not change its value. Functions that may run concurrently with
> +submission, i.e. cancellation, can not rely on the ``ptl`` reference to be
> +set. Access to it in these functions is guarded by ``READ_ONCE()``, whereas
> +setting ``ptl`` is equally guarded with ``WRITE_ONCE()`` for symmetry.
> +
> +Some packet fields may be read outside of the respective locks guarding
> +them, specifically priority and state for tracing. In those cases, proper
> +access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such
> +read-only access is only allowed when stale values are not critical.
> +
> +With respect to the interface for higher layers, packet submission
> +(|ssh_ptl_submit|), packet cancellation (|ssh_ptl_cancel|), data receival
> +(|ssh_ptl_rx_rcvbuf|), and layer shutdown (|ssh_ptl_shutdown|) may always be
> +executed concurrently with respect to each other. Note that packet
> +submission may not run concurrently with itself for the same packet.
> +Equally, shutdown and data receival may also not run concurrently with
> +themselves (but may run concurrently with each other).
> +
> +
> +Request Transport Layer
> +=======================
> +
> +The request transport layer is represented via |ssh_rtl| and builds on top
> +of the packet transport layer. It deals with requests, i.e. SSH packets sent
> +by the host containing a |ssh_command| as frame payload. This layer
> +separates responses to requests from events, which are also sent by the EC
> +via a |ssh_command| payload. While responses are handled in this layer,
> +events are relayed to the next upper layer, i.e. the controller layer, via
> +the corresponding callback. The request transport layer is structured around
> +the following key concepts:
> +
> +Request
> +-------
> +
> +Requests are packets with a command-type payload, sent from host to EC to
> +query data from or trigger an action on it (or both simultaneously). They
> +are represented by |ssh_request|, wrapping the underlying |ssh_packet|
> +storing its message data (i.e. SSH frame with command payload). Note that
> +all top-level representations, e.g. |ssam_request_sync| are built upon this
> +struct.
> +
> +As |ssh_request| extends |ssh_packet|, its lifetime is also managed by the
> +reference counter inside the packet struct (which can be accessed via
> +|ssh_request_get| and |ssh_request_put|). Once the counter reaches zero, the
> +``release()`` callback of the |ssh_request_ops| reference of the request is
> +called.
> +
> +Requests can have an optional response that is equally sent via a SSH
> +message with command-type payload (from EC to host). The party constructing
> +the request must know if a response is expected and mark this in the request
> +flags provided to |ssh_request_init|, so that the request transport layer
> +can wait for this response.
> +
> +Similar to |ssh_packet|, |ssh_request| also has a ``complete()`` callback
> +provided via its request ops reference and is guaranteed to be completed
> +before it is released once it has been submitted to the request transport
> +layer via |ssh_rtl_submit|. For a request without a response, successful
> +completion will occur once the underlying packet has been successfully
> +transmitted by the packet transport layer (i.e. from within the packet
> +completion callback). For a request with response, successful completion
> +will occur once the response has been received and matched to the request
> +via its request ID (which happens on the packet layer's data-received
> +callback running on the receiver thread). If the request is completed with
> +an error, the status value will be set to the corresponding (negative) errno
> +value.
> +
> +The state of a request is again managed via its ``state`` flags
> +(|ssh_request_flags|), which also encode the request type. In particular,
> +the following bits are noteworthy:
> +
> +* ``SSH_REQUEST_SF_LOCKED_BIT``: This bit is set when completion, either
> +  through error or success, is imminent. It indicates that no further
> +  references of the request should be taken and any existing references
> +  should be dropped as soon as possible. The process setting this bit is
> +  responsible for removing any references to this request from the request
> +  queue and pending set.
> +
> +* ``SSH_REQUEST_SF_COMPLETED_BIT``: This bit is set by the process running the
> +  ``complete()`` callback and is used to ensure that this callback only runs
> +  once.
> +
> +* ``SSH_REQUEST_SF_QUEUED_BIT``: This bit is set when the request is queued on
> +  the request queue and cleared when it is dequeued.
> +
> +* ``SSH_REQUEST_SF_PENDING_BIT``: This bit is set when the request is added to
> +  the pending set and cleared when it is removed from it.
> +
> +Request Queue
> +-------------
> +
> +The request queue is the first of the two fundamental collections in the
> +request transport layer. In contrast to the packet queue of the packet
> +transport layer, it is not a priority queue and the simple first come first
> +serve principle applies.
> +
> +All requests to be transmitted by the request transport layer must be
> +submitted to this queue via |ssh_rtl_submit|. Once submitted, requests may
> +not be re-submitted, and will not be re-submitted automatically on timeout.
> +Instead, the request is completed with a timeout error. If desired, the
> +caller can create and submit a new request for another try, but it must not
> +submit the same request again.
> +
> +Pending Set
> +-----------
> +
> +The pending set is the second of the two fundamental collections in the
> +request transport layer. This collection stores references to all pending
> +requests, i.e. requests awaiting a response from the EC (similar to what the
> +pending set of the packet transport layer does for packets).
> +
> +Transmitter Task
> +----------------
> +
> +The transmitter task is scheduled when a new request is available for
> +transmission. It checks if the next request on the request queue can be
> +transmitted and, if so, submits its underlying packet to the packet
> +transport layer. This check ensures that only a limited number of
> +requests can be pending, i.e. waiting for a response, at the same time. If
> +the request requires a response, the request is added to the pending set
> +before its packet is submitted.
> +
> +Packet Completion Callback
> +--------------------------
> +
> +The packet completion callback is executed once the underlying packet of a
> +request has been completed. In case of an error completion, the
> +corresponding request is completed with the error value provided in this
> +callback.
> +
> +On successful packet completion, further processing depends on the request.
> +If the request expects a response, it is marked as transmitted and the
> +request timeout is started. If the request does not expect a response, it is
> +completed with success.
> +
> +Data-Received Callback
> +----------------------
> +
> +The data received callback notifies the request transport layer of data
> +being received by the underlying packet transport layer via a data-type
> +frame. In general, this is expected to be a command-type payload.
> +
> +If the request ID of the command is one of the request IDs reserved for
> +events (one to ``SSH_NUM_EVENTS``, inclusively), it is forwarded to the
> +event callback registered in the request transport layer. If the request ID
> +indicates a response to a request, the respective request is looked up in
> +the pending set and, if found and marked as transmitted, completed with
> +success.
> +
> +Timeout Reaper
> +--------------
> +
> +The request-response-timeout is a per-request timeout for requests expecting
> +a response. It is used to ensure that a request does not wait indefinitely
> +on a response from the EC and is started after the underlying packet has
> +been successfully completed.
> +
> +This timeout is, similar to the packet acknowledgment timeout on the packet
> +transport layer, handled via a dedicated reaper task. This task is
> +essentially a work-item (re-)scheduled to run when the next request is set
> +to time out. The work item then scans the set of pending requests for any
> +requests that have timed out and completes them with ``-ETIMEDOUT`` as
> +status. Requests will not be re-submitted automatically. Instead, the issuer
> +of the request must construct and submit a new request, if so desired.
> +
> +Note that this timeout, in combination with packet transmission and
> +acknowledgment timeouts, guarantees that the request layer will always make
> +progress, even if only through timing out packets, and never fully block.
> +
> +Concurrency and Locking
> +-----------------------
> +
> +Similar to the packet transport layer, there are two main locks in the
> +request transport layer: One guarding access to the request queue and one
> +guarding access to the pending set. These collections may only be accessed
> +and modified under the respective lock.
> +
> +Other parts of the request transport layer are guarded independently. State
> +flags are (again) managed by atomic bit operations and, if necessary, memory
> +barriers. Modifications to the timeout reaper work item and expiration date
> +are guarded by their own lock.
> +
> +Some request fields may be read outside of the respective locks guarding
> +them, specifically the state for tracing. In those cases, proper access is
> +ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such read-only
> +access is only allowed when stale values are not critical.
> +
> +With respect to the interface for higher layers, request submission
> +(|ssh_rtl_submit|), request cancellation (|ssh_rtl_cancel|), and layer
> +shutdown (|ssh_rtl_shutdown|) may always be executed concurrently with
> +respect to each other. Note that request submission may not run concurrently
> +with itself for the same request (and also may only be called once per
> +request). Equally, shutdown may also not run concurrently with itself.
> +
> +
> +Controller Layer
> +================
> +
> +The controller layer extends on the request transport layer to provide an
> +easy-to-use interface for client drivers. It is represented by
> +|ssam_controller| and the SSH driver. While the lower level transport layers
> +take care of transmitting and handling packets and requests, the controller
> +layer takes on more of a management role. Specifically, it handles device
> +initialization, power management, and event handling, including event
> +delivery and registration via the (event) completion system (|ssam_cplt|).
> +
> +Event Registration
> +------------------
> +
> +In general, an event (or rather a class of events) has to be explicitly
> +requested by the host before the EC will send it (HID input events seem to
> +be the exception). This is done via an event-enable request (similarly,
> +events should be disabled via an event-disable request once no longer
> +desired).
> +
> +The specific request used to enable (or disable) an event is given via an
> +event registry, i.e. the governing authority of this event (so to speak),
> +represented by |ssam_event_registry|. As parameters to this request, the
> +target category and, depending on the event registry, instance ID of the
> +event to be enabled must be provided. This (optional) instance ID must be
> +zero if the registry does not use it. Together, target category and instance
> +ID form the event ID, represented by |ssam_event_id|. In short, both, event
> +registry and event ID, are required to uniquely identify a respective class
> +of events.
> +
> +Note that a further *request ID* parameter must be provided for the
> +enable-event request. This parameter does not influence the class of events
> +being enabled, but instead is set as the request ID (RQID) on each event of
> +this class sent by the EC. It is used to identify events (as a limited
> +number of request IDs is reserved for use in events only, specifically one
> +to ``SSH_NUM_EVENTS`` inclusively) and also map events to their specific
> +class. Currently, the controller always sets this parameter to the target
> +category specified in |ssam_event_id|.
> +
> +As multiple client drivers may rely on the same (or overlapping) classes of
> +events and enable/disable calls are strictly binary (i.e. on/off), the
> +controller has to manage access to these events. It does so via reference
> +counting, storing the counter inside an RB-tree based mapping with event
> +registry and ID as key (there is no known list of valid event registry and
> +event ID combinations). See |ssam_nf|, |ssam_nf_refcount_inc|, and
> +|ssam_nf_refcount_dec| for details.
> +
> +This management is done together with notifier registration (described in
> +the next section) via the top-level |ssam_notifier_register| and
> +|ssam_notifier_unregister| functions.
> +
> +Event Delivery
> +--------------
> +
> +To receive events, a client driver has to register an event notifier via
> +|ssam_notifier_register|. This increments the reference counter for that
> +specific class of events (as detailed in the previous section), enables the
> +class on the EC (if it has not been enabled already), and installs the
> +provided notifier callback.
> +
> +Notifier callbacks are stored in lists, with one (RCU) list per target
> +category (provided via the event ID; NB: there is a fixed known number of
> +target categories). There is no known association from the combination of
> +event registry and event ID to the command data (target ID, target category,
> +command ID, and instance ID) that can be provided by an event class, apart
> +from target category and instance ID given via the event ID.
> +
> +Note that due to the way notifiers are (or rather have to be) stored, client
> +drivers may receive events that they have not requested and need to account
> +for them. Specifically, they will, by default, receive all events from the
> +same target category. To simplify dealing with this, filtering of events by
> +target ID (provided via the event registry) and instance ID (provided via
> +the event ID) can be requested when registering a notifier. This filtering
> +is applied when iterating over the notifiers at the time they are executed.
> +
> +All notifier callbacks are executed on a dedicated workqueue, the so-called
> +completion workqueue. After an event has been received via the callback
> +installed in the request layer (running on the receiver thread of the packet
> +transport layer), it will be put on its respective event queue
> +(|ssam_event_queue|). From this event queue the completion work item of that
> +queue (running on the completion workqueue) will pick up the event and
> +execute the notifier callback. This is done to avoid blocking on the
> +receiver thread.
> +
> +There is one event queue per combination of target ID and target category.
> +This is done to ensure that notifier callbacks are executed in sequence for
> +events of the same target ID and target category. Callbacks can be executed
> +in parallel for events with a different combination of target ID and target
> +category.
> +
> +Concurrency and Locking
> +-----------------------
> +
> +Most of the concurrency related safety guarantees of the controller are
> +provided by the lower-level request transport layer. In addition to this,
> +event (un-)registration is guarded by its own lock.
> +
> +Access to the controller state is guarded by the state lock. This lock is a
> +read/write semaphore. The reader part can be used to ensure that the state
> +does not change while functions depending on the state to stay the same
> +(e.g. |ssam_notifier_register|, |ssam_notifier_unregister|,
> +|ssam_request_sync_submit|, and derivatives) are executed and this guarantee
> +is not already provided otherwise (e.g. through |ssam_client_bind| or
> +|ssam_client_link|). The writer part guards any transitions that will change
> +the state, i.e. initialization, destruction, suspension, and resumption.
> +
> +The controller state may be accessed (read-only) outside the state lock for
> +smoke-testing against invalid API usage (e.g. in |ssam_request_sync_submit|).
> +Note that such checks are not supposed to (and will not) protect against all
> +invalid usages, but rather aim to help catch them. In those cases, proper
> +variable access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``.
> +
> +Assuming any preconditions on the state not changing have been satisfied,
> +all non-initialization and non-shutdown functions may run concurrently with
> +each other. This includes |ssam_notifier_register|, |ssam_notifier_unregister|,
> +|ssam_request_sync_submit|, as well as all functions building on top of those.
> diff --git a/Documentation/driver-api/surface_aggregator/overview.rst b/Documentation/driver-api/surface_aggregator/overview.rst
> new file mode 100644
> index 000000000000..1e9d57e50063
> --- /dev/null
> +++ b/Documentation/driver-api/surface_aggregator/overview.rst
> @@ -0,0 +1,77 @@
> +.. SPDX-License-Identifier: GPL-2.0+
> +
> +========
> +Overview
> +========
> +
> +The Surface/System Aggregator Module (SAM, SSAM) is an (arguably *the*)
> +embedded controller (EC) on Microsoft Surface devices. It has been originally
> +introduced on 4th generation devices (Surface Pro 4, Surface Book 1), but
> +its responsibilities and feature-set have since been expanded significantly
> +with the following generations.
> +
> +
> +Features and Integration
> +========================
> +
> +Not much is currently known about SAM on 4th generation devices (Surface Pro
> +4, Surface Book 1), due to the use of a different communication interface
> +between host and EC (as detailed below). On 5th (Surface Pro 2017, Surface
> +Book 2, Surface Laptop 1) and later generation devices, SAM is responsible
> +for providing battery information (both current status and static values,
> +such as maximum capacity etc.), as well as an assortment of temperature
> +sensors (e.g. skin temperature) and cooling/performance-mode setting to the
> +host. On the Surface Book 2, specifically, it additionally provides an
> +interface for properly handling clipboard detachment (i.e. separating the
> +display part from the keyboard part of the device), on the Surface Laptop 1
> +and 2 it is required for keyboard HID input. This HID subsystem has been
> +restructured for 7th generation devices and on those, specifically Surface
> +Laptop 3 and Surface Book 3, is responsible for all major HID input (i.e.
> +keyboard and touchpad).
> +
> +While features have not changed much on a coarse level since the 5th
> +generation, internal interfaces have undergone some rather large changes. On
> +5th and 6th generation devices, both battery and temperature information is
> +exposed to ACPI via a shim driver (referred to as Surface ACPI Notify, or
> +SAN), translating ACPI generic serial bus write-/read-accesses to SAM
> +requests. On 7th generation devices, this additional layer is gone and these
> +devices require a driver hooking directly into the SAM interface. Equally,
> +on newer generations, less devices are declared in ACPI, making them a bit
> +harder to discover and requiring us to hard-code a sort of device registry.
> +Due to this, a SSAM bus and subsystem with client devices
> +(:c:type:`struct ssam_device <ssam_device>`) has been implemented.
> +
> +
> +Communication
> +=============
> +
> +The type of communication interface between host and EC depends on the
> +generation of the Surface device. On 4th generation devices, host and EC
> +communicate via HID, specifically using a HID-over-I2C device, whereas on
> +5th and later generations, communication takes place via a USART serial
> +device. In accordance to the drivers found on other operating systems, we
> +refer to the serial device and its driver as Surface Serial Hub (SSH). When
> +needed, we differentiate between both types of SAM by referring to them as
> +SAM-over-SSH and SAM-over-HID.
> +
> +Currently, this subsystem only supports SAM-over-SSH. The SSH communication
> +interface is described in more detail below. The HID interface has not been
> +reverse engineered yet and it is, at the moment, unclear how many (and
> +which) concepts of the SSH interface detailed below can be transferred to
> +it.
> +
> +Surface Serial Hub
> +------------------
> +
> +As already elaborated above, the Surface Serial Hub (SSH) is the
> +communication interface for SAM on 5th- and all later-generation Surface
> +devices. On the highest level, communication can be separated into two main
> +types: Requests, messages sent from host to EC that may trigger a direct
> +response from the EC (explicitly associated with the request), and events
> +(sometimes also referred to as notifications), sent from EC to host without
> +being a direct response to a previous request. We may also refer to requests
> +without response as commands. In general, events need to be enabled via one
> +of multiple dedicated requests before they are sent by the EC.
> +
> +See :doc:`ssh` for a more technical protocol documentation and
> +:doc:`internal` for an overview of the internal driver architecture.
> diff --git a/Documentation/driver-api/surface_aggregator/ssh.rst b/Documentation/driver-api/surface_aggregator/ssh.rst
> new file mode 100644
> index 000000000000..bf007d6c9873
> --- /dev/null
> +++ b/Documentation/driver-api/surface_aggregator/ssh.rst
> @@ -0,0 +1,344 @@
> +.. SPDX-License-Identifier: GPL-2.0+
> +
> +.. |u8| replace:: :c:type:`u8 <u8>`
> +.. |u16| replace:: :c:type:`u16 <u16>`
> +.. |TYPE| replace:: ``TYPE``
> +.. |LEN| replace:: ``LEN``
> +.. |SEQ| replace:: ``SEQ``
> +.. |SYN| replace:: ``SYN``
> +.. |NAK| replace:: ``NAK``
> +.. |ACK| replace:: ``ACK``
> +.. |DATA| replace:: ``DATA``
> +.. |DATA_SEQ| replace:: ``DATA_SEQ``
> +.. |DATA_NSQ| replace:: ``DATA_NSQ``
> +.. |TC| replace:: ``TC``
> +.. |TID| replace:: ``TID``
> +.. |IID| replace:: ``IID``
> +.. |RQID| replace:: ``RQID``
> +.. |CID| replace:: ``CID``
> +
> +===========================
> +Surface Serial Hub Protocol
> +===========================
> +
> +The Surface Serial Hub (SSH) is the central communication interface for the
> +embedded Surface Aggregator Module controller (SAM or EC), found on newer
> +Surface generations. We will refer to this protocol and interface as
> +SAM-over-SSH, as opposed to SAM-over-HID for the older generations.
> +
> +On Surface devices with SAM-over-SSH, SAM is connected to the host via UART
> +and defined in ACPI as device with ID ``MSHW0084``. On these devices,
> +significant functionality is provided via SAM, including access to battery
> +and power information and events, thermal read-outs and events, and many
> +more. For Surface Laptops, keyboard input is handled via HID directed
> +through SAM, on the Surface Laptop 3 and Surface Book 3 this also includes
> +touchpad input.
> +
> +Note that the standard disclaimer for this subsystem also applies to this
> +document: All of this has been reverse-engineered and may thus be erroneous
> +and/or incomplete.
> +
> +All CRCs used in the following are two-byte ``crc_ccitt_false(0xffff, ...)``.
> +All multi-byte values are little-endian, there is no implicit padding between
> +values.
> +
> +
> +SSH Packet Protocol: Definitions
> +================================
> +
> +The fundamental communication unit of the SSH protocol is a frame
> +(:c:type:`struct ssh_frame <ssh_frame>`). A frame consists of the following
> +fields, packed together and in order:
> +
> +.. flat-table:: SSH Frame
> +   :widths: 1 1 4
> +   :header-rows: 1
> +
> +   * - Field
> +     - Type
> +     - Description
> +
> +   * - |TYPE|
> +     - |u8|
> +     - Type identifier of the frame.
> +
> +   * - |LEN|
> +     - |u16|
> +     - Length of the payload associated with the frame.
> +
> +   * - |SEQ|
> +     - |u8|
> +     - Sequence ID (see explanation below).
> +
> +Each frame structure is followed by a CRC over this structure. The CRC over
> +the frame structure (|TYPE|, |LEN|, and |SEQ| fields) is placed directly
> +after the frame structure and before the payload. The payload is followed by
> +its own CRC (over all payload bytes). If the payload is not present (i.e.
> +the frame has ``LEN=0``), the CRC of the payload is still present and will
> +evaluate to ``0xffff``. The |LEN| field does not include any of the CRCs, it
> +equals the number of bytes inbetween the CRC of the frame and the CRC of the
> +payload.
> +
> +Additionally, the following fixed two-byte sequences are used:
> +
> +.. flat-table:: SSH Byte Sequences
> +   :widths: 1 1 4
> +   :header-rows: 1
> +
> +   * - Name
> +     - Value
> +     - Description
> +
> +   * - |SYN|
> +     - ``[0xAA, 0x55]``
> +     - Synchronization bytes.
> +
> +A message consists of |SYN|, followed by the frame (|TYPE|, |LEN|, |SEQ| and
> +CRC) and, if specified in the frame (i.e. ``LEN > 0``), payload bytes,
> +followed finally, regardless if the payload is present, the payload CRC. The
> +messages corresponding to an exchange are, in part, identified by having the
> +same sequence ID (|SEQ|), stored inside the frame (more on this in the next
> +section). The sequence ID is a wrapping counter.
> +
> +A frame can have the following types
> +(:c:type:`enum ssh_frame_type <ssh_frame_type>`):
> +
> +.. flat-table:: SSH Frame Types
> +   :widths: 1 1 4
> +   :header-rows: 1
> +
> +   * - Name
> +     - Value
> +     - Short Description
> +
> +   * - |NAK|
> +     - ``0x04``
> +     - Sent on error in previously received message.
> +
> +   * - |ACK|
> +     - ``0x40``
> +     - Sent to acknowledge receival of |DATA| frame.
> +
> +   * - |DATA_SEQ|
> +     - ``0x80``
> +     - Sent to transfer data. Sequenced.
> +
> +   * - |DATA_NSQ|
> +     - ``0x00``
> +     - Same as |DATA_SEQ|, but does not need to be ACKed.
> +
> +Both |NAK|- and |ACK|-type frames are used to control flow of messages and
> +thus do not carry a payload. |DATA_SEQ|- and |DATA_NSQ|-type frames on the
> +other hand must carry a payload. The flow sequence and interaction of
> +different frame types will be described in more depth in the next section.
> +
> +
> +SSH Packet Protocol: Flow Sequence
> +==================================
> +
> +Each exchange begins with |SYN|, followed by a |DATA_SEQ|- or
> +|DATA_NSQ|-type frame, followed by its CRC, payload, and payload CRC. In
> +case of a |DATA_NSQ|-type frame, the exchange is then finished. In case of a
> +|DATA_SEQ|-type frame, the receiving party has to acknowledge receival of
> +the frame by responding with a message containing an |ACK|-type frame with
> +the same sequence ID of the |DATA| frame. In other words, the sequence ID of
> +the |ACK| frame specifies the |DATA| frame to be acknowledged. In case of an
> +error, e.g. an invalid CRC, the receiving party responds with a message
> +containing an |NAK|-type frame. As the sequence ID of the previous data
> +frame, for which an error is indicated via the |NAK| frame, cannot be relied
> +upon, the sequence ID of the |NAK| frame should not be used and is set to
> +zero. After receival of an |NAK| frame, the sending party should re-send all
> +outstanding (non-ACKed) messages.
> +
> +Sequence IDs are not synchronized between the two parties, meaning that they
> +are managed independently for each party. Identifying the messages
> +corresponding to a single exchange thus relies on the sequence ID as well as
> +the type of the message, and the context. Specifically, the sequence ID is
> +used to associate an ``ACK`` with its ``DATA_SEQ``-type frame, but not
> +``DATA_SEQ``- or ``DATA_NSQ``-type frames with other ``DATA``- type frames.
> +
> +An example exchange might look like this:
> +
> +::
> +
> +    tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) -----------------------------
> +    rx: ------------------------------------- SYN FRAME(A) CRC(F) CRC(P) --
> +
> +where both frames have the same sequence ID (``SEQ``). Here, ``FRAME(D)``
> +indicates a |DATA_SEQ|-type frame, ``FRAME(A)`` an ``ACK``-type frame,
> +``CRC(F)`` the CRC over the previous frame, ``CRC(P)`` the CRC over the
> +previous payload. In case of an error, the exchange would look like this:
> +
> +::
> +
> +    tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) -----------------------------
> +    rx: ------------------------------------- SYN FRAME(N) CRC(F) CRC(P) --
> +
> +upon which the sender should re-send the message. ``FRAME(N)`` indicates an
> +|NAK|-type frame. Note that the sequence ID of the |NAK|-type frame is fixed
> +to zero. For |DATA_NSQ|-type frames, both exchanges are the same:
> +
> +::
> +
> +    tx: -- SYN FRAME(DATA_NSQ) CRC(F) PAYLOAD CRC(P) ----------------------
> +    rx: -------------------------------------------------------------------
> +
> +Here, an error can be detected, but not corrected or indicated to the
> +sending party. These exchanges are symmetric, i.e. switching ``rx`` and
> +``tx`` results again in a valid exchange. Currently, no longer exchanges are
> +known.
> +
> +
> +Commands: Requests, Responses, and Events
> +=========================================
> +
> +Commands are sent as payload inside a data frame. Currently, this is the
> +only known payload type of |DATA| frames, with a payload-type value of
> +``0x80`` (:c:type:`SSH_PLD_TYPE_CMD <ssh_payload_type>`).
> +
> +The command-type payload (:c:type:`struct ssh_command <ssh_command>`)
> +consists of an eight-byte command structure, followed by optional and
> +variable length command data. The length of this optional data is derived
> +from the frame payload length given in the corresponding frame, i.e. it is
> +``frame.len - sizeof(struct ssh_command)``. The command struct contains the
> +following fields, packed together and in order:
> +
> +.. flat-table:: SSH Command
> +   :widths: 1 1 4
> +   :header-rows: 1
> +
> +   * - Field
> +     - Type
> +     - Description
> +
> +   * - |TYPE|
> +     - |u8|
> +     - Type of the payload. For commands always ``0x80``.
> +
> +   * - |TC|
> +     - |u8|
> +     - Target category.
> +
> +   * - |TID| (out)
> +     - |u8|
> +     - Target ID for outgoing (host to EC) commands.
> +
> +   * - |TID| (in)
> +     - |u8|
> +     - Target ID for incoming (EC to host) commands.
> +
> +   * - |IID|
> +     - |u8|
> +     - Instance ID.
> +
> +   * - |RQID|
> +     - |u16|
> +     - Request ID.
> +
> +   * - |CID|
> +     - |u8|
> +     - Command ID.
> +
> +The command struct and data, in general, does not contain any failure
> +detection mechanism (e.g. CRCs), this is solely done on the frame level.
> +
> +Command-type payloads are used by the host to send commands and requests to
> +the EC as well as by the EC to send responses and events back to the host.
> +We differentiate between requests (sent by the host), responses (sent by the
> +EC in response to a request), and events (sent by the EC without a preceding
> +request).
> +
> +Commands and events are uniquely identified by their target category
> +(``TC``) and command ID (``CID``). The target category specifies a general
> +category for the command (e.g. system in general, vs. battery and AC, vs.
> +temperature, and so on), while the command ID specifies the command inside
> +that category. Only the combination of |TC| + |CID| is unique. Additionally,
> +commands have an instance ID (``IID``), which is used to differentiate
> +between different sub-devices. For example ``TC=3`` ``CID=1`` is a
> +request to get the temperature on a thermal sensor, where |IID| specifies
> +the respective sensor. If the instance ID is not used, it should be set to
> +zero. If instance IDs are used, they, in general, start with a value of one,
> +whereas zero may be used for instance independent queries, if applicable. A
> +response to a request should have the same target category, command ID, and
> +instance ID as the corresponding request.
> +
> +Responses are matched to their corresponding request via the request ID
> +(``RQID``) field. This is a 16 bit wrapping counter similar to the sequence
> +ID on the frames. Note that the sequence ID of the frames for a
> +request-response pair does not match. Only the request ID has to match.
> +Frame-protocol wise these are two separate exchanges, and may even be
> +separated, e.g. by an event being sent after the request but before the
> +response. Not all commands produce a response, and this is not detectable by
> +|TC| + |CID|. It is the responsibility of the issuing party to wait for a
> +response (or signal this to the communication framework, as is done in
> +SAN/ACPI via the ``SNC`` flag).
> +
> +Events are identified by unique and reserved request IDs. These IDs should
> +not be used by the host when sending a new request. They are used on the
> +host to, first, detect events and, second, match them with a registered
> +event handler. Request IDs for events are chosen by the host and directed to
> +the EC when setting up and enabling an event source (via the
> +enable-event-source request). The EC then uses the specified request ID for
> +events sent from the respective source. Note that an event should still be
> +identified by its target category, command ID, and, if applicable, instance
> +ID, as a single event source can send multiple different event types. In
> +general, however, a single target category should map to a single reserved
> +event request ID.
> +
> +Furthermore, requests, responses, and events have an associated target ID
> +(``TID``). This target ID is split into output (host to EC) and input (EC to
> +host) fields, with the respecting other field (e.g. output field on incoming
> +messages) set to zero. Two ``TID`` values are known: Primary (``0x01``) and
> +secondary (``0x02``). In general, the response to a request should have the
> +same ``TID`` value, however, the field (output vs. input) should be used in
> +accordance to the direction in which the response is sent (i.e. on the input
> +field, as responses are generally sent from the EC to the host).
> +
> +Note that, even though requests and events should be uniquely identifiable
> +by target category and command ID alone, the EC may require specific
> +target ID and instance ID values to accept a command. A command that is
> +accepted for ``TID=1``, for example, may not be accepted for ``TID=2``
> +and vice versa.
> +
> +
> +Limitations and Observations
> +============================
> +
> +The protocol can, in theory, handle up to ``U8_MAX`` frames in parallel,
> +with up to ``U16_MAX`` pending requests (neglecting request IDs reserved for
> +events). In practice, however, this is more limited. From our testing
> +(although via a python and thus a user-space program), it seems that the EC
> +can handle up to four requests (mostly) reliably in parallel at a certain
> +time. With five or more requests in parallel, consistent discarding of
> +commands (ACKed frame but no command response) has been observed. For five
> +simultaneous commands, this reproducibly resulted in one command being
> +dropped and four commands being handled.
> +
> +However, it has also been noted that, even with three requests in parallel,
> +occasional frame drops happen. Apart from this, with a limit of three
> +pending requests, no dropped commands (i.e. command being dropped but frame
> +carrying command being ACKed) have been observed. In any case, frames (and
> +possibly also commands) should be re-sent by the host if a certain timeout
> +is exceeded. This is done by the EC for frames with a timeout of one second,
> +up to two re-tries (i.e. three transmissions in total). The limit of
> +re-tries also applies to received NAKs, and, in a worst case scenario, can
> +lead to entire messages being dropped.
> +
> +While this also seems to work fine for pending data frames as long as no
> +transmission failures occur, implementation and handling of these seems to
> +depend on the assumption that there is only one non-acknowledged data frame.
> +In particular, the detection of repeated frames relies on the last sequence
> +number. This means that, if a frame that has been successfully received by
> +the EC is sent again, e.g. due to the host not receiving an |ACK|, the EC
> +will only detect this if it has the sequence ID of the last frame received
> +by the EC. As an example: Sending two frames with ``SEQ=0`` and ``SEQ=1``
> +followed by a repetition of ``SEQ=0`` will not detect the second ``SEQ=0``
> +frame as such, and thus execute the command in this frame each time it has
> +been received, i.e. twice in this example. Sending ``SEQ=0``, ``SEQ=1`` and
> +then repeating ``SEQ=1`` will detect the second ``SEQ=1`` as repetition of
> +the first one and ignore it, thus executing the contained command only once.
> +
> +In conclusion, this suggests a limit of at most one pending un-ACKed frame
> +(per party, effectively leading to synchronous communication regarding
> +frames) and at most three pending commands. The limit to synchronous frame
> +transfers seems to be consistent with behavior observed on Windows.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 64730461f4df..ff5d60875cec 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11697,6 +11697,7 @@ M:	Maximilian Luz <luzmaximilian@gmail.com>
>  S:	Maintained
>  W:	https://github.com/linux-surface/surface-aggregator-module
>  C:	irc://chat.freenode.net/##linux-surface
> +F:	Documentation/driver-api/surface_aggregator/
>  F:	drivers/platform/surface/aggregator/
>  F:	include/linux/surface_aggregator/
> 
> --
> 2.29.2
> 


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 8/9] platform/surface: Add Surface Aggregator user-space interface
  2020-12-03 21:26 ` [PATCH v2 8/9] platform/surface: Add Surface Aggregator user-space interface Maximilian Luz
@ 2020-12-15 16:35   ` Hans de Goede
  2020-12-15 20:00     ` Maximilian Luz
  0 siblings, 1 reply; 31+ messages in thread
From: Hans de Goede @ 2020-12-15 16:35 UTC (permalink / raw)
  To: Maximilian Luz, linux-kernel
  Cc: Mark Gross, Andy Shevchenko, Barnabás Pőcze,
	Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet,
	Blaž Hrastnik, Dorian Stoll, platform-driver-x86, linux-doc

Hi,

On 12/3/20 10:26 PM, Maximilian Luz wrote:
> Add a misc-device providing user-space access to the Surface Aggregator
> EC, mainly intended for debugging, testing, and reverse-engineering.
> This interface gives user-space applications the ability to send
> requests to the EC and receive the corresponding responses.
> 
> The device-file is managed by a pseudo platform-device and corresponding
> driver to avoid dependence on the dedicated bus, allowing it to be
> loaded in a minimal configuration.
> 
> Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>

1 review comment inline:

> ---
> 
> Changes in v1 (from RFC):
>  - add copyright lines
>  - change SPDX identifier to GPL-2.0+ (was GPL-2.0-or-later)
>  - move from debugfs to proper misc-device
>  - move interface definitions to uapi header
>  - remove versioning
>  - allocate platform device dynamically
>  - prioritize EFAULT error in IOCTL return
>  - use ENOTTY error for unknown IOCTLs
>  - fix IOCTL compatibility for 32-bit user-space on 64-bit kernels
>  - mark request struct as __attribute__((__packed__))
>  - address issues when driver is unbound while file still open
> 
> Changes in v2:
>  - add ioctl-number.rst entry
>  - improve documentation
>  - spell check comments and strings, fix typos
>  - unify comment style
>  - run checkpatch --strict, fix warnings and style issues
> 
> ---
>  .../surface_aggregator/clients/cdev.rst       |  85 +++++
>  .../surface_aggregator/clients/index.rst      |  12 +-
>  .../userspace-api/ioctl/ioctl-number.rst      |   2 +
>  MAINTAINERS                                   |   2 +
>  drivers/platform/surface/Kconfig              |  17 +
>  drivers/platform/surface/Makefile             |   1 +
>  .../surface/surface_aggregator_cdev.c         | 299 ++++++++++++++++++
>  include/uapi/linux/surface_aggregator/cdev.h  |  58 ++++
>  8 files changed, 475 insertions(+), 1 deletion(-)
>  create mode 100644 Documentation/driver-api/surface_aggregator/clients/cdev.rst
>  create mode 100644 drivers/platform/surface/surface_aggregator_cdev.c
>  create mode 100644 include/uapi/linux/surface_aggregator/cdev.h
> 
> diff --git a/Documentation/driver-api/surface_aggregator/clients/cdev.rst b/Documentation/driver-api/surface_aggregator/clients/cdev.rst
> new file mode 100644
> index 000000000000..f30e77def838
> --- /dev/null
> +++ b/Documentation/driver-api/surface_aggregator/clients/cdev.rst
> @@ -0,0 +1,85 @@
> +.. 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_request_flags| replace:: :c:type:`enum ssam_request_flags <ssam_request_flags>`
> +
> +==============================
> +User-Space EC Interface (cdev)
> +==============================
> +
> +The ``surface_aggregator_cdev`` module provides a misc-device for the SSAM
> +controller to allow for a (more or less) direct connection from user-space to
> +the SAM EC. It is intended to be used for development and debugging, and
> +therefore should not be used or relied upon in any other way. Note that this
> +module is not loaded automatically, but instead must be loaded manually.
> +
> +The provided interface is accessible through the ``/dev/surface/aggregator``
> +device-file. All functionality of this interface is provided via IOCTLs.
> +These IOCTLs and their respective input/output parameter structs are defined in
> +``include/uapi/linux/surface_aggregator/cdev.h``.
> +
> +
> +Controller IOCTLs
> +=================
> +
> +The following IOCTLs are provided:
> +
> +.. flat-table:: Controller IOCTLs
> +   :widths: 1 1 1 1 4
> +   :header-rows: 1
> +
> +   * - Type
> +     - Number
> +     - Direction
> +     - Name
> +     - Description
> +
> +   * - ``0xA5``
> +     - ``1``
> +     - ``WR``
> +     - ``REQUEST``
> +     - Perform synchronous SAM request.
> +
> +
> +``REQUEST``
> +-----------
> +
> +Defined as ``_IOWR(0xA5, 1, struct ssam_cdev_request)``.
> +
> +Executes a synchronous SAM request. The request specification is passed in
> +as argument of type |ssam_cdev_request|, which is then written to/modified
> +by the IOCTL to return status and result of the request.
> +
> +Request payload data must be allocated separately and is passed in via the
> +``payload.data`` and ``payload.length`` members. If a response is required,
> +the response buffer must be allocated by the caller and passed in via the
> +``response.data`` member. The ``response.length`` member must be set to the
> +capacity of this buffer, or if no response is required, zero. Upon
> +completion of the request, the call will write the response to the response
> +buffer (if its capacity allows it) and overwrite the length field with the
> +actual size of the response, in bytes.
> +
> +Additionally, if the request has a response, this must be indicated via the
> +request flags, as is done with in-kernel requests. Request flags can be set
> +via the ``flags`` member and the values correspond to the values found in
> +|ssam_request_flags|.
> +
> +Finally, the status of the request itself is returned in the ``status``
> +member (a negative errno value indicating failure). Note that failure
> +indication of the IOCTL is separated from failure indication of the request:
> +The IOCTL returns a negative status code if anything failed during setup of
> +the request (``-EFAULT``) or if the provided argument or any of its fields
> +are invalid (``-EINVAL``). In this case, the status value of the request
> +argument may be set, providing more detail on what went wrong (e.g.
> +``-ENOMEM`` for out-of-memory), but this value may also be zero. The IOCTL
> +will return with a zero status code in case the request has been set up,
> +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:
> +
> +.. kernel-doc:: include/uapi/linux/surface_aggregator/cdev.h
> +   :functions: ssam_cdev_request
> diff --git a/Documentation/driver-api/surface_aggregator/clients/index.rst b/Documentation/driver-api/surface_aggregator/clients/index.rst
> index 31e026d96102..ab260ec82cfb 100644
> --- a/Documentation/driver-api/surface_aggregator/clients/index.rst
> +++ b/Documentation/driver-api/surface_aggregator/clients/index.rst
> @@ -7,4 +7,14 @@ Client Driver Documentation
>  This is the documentation for client drivers themselves. Refer to
>  :doc:`../client` for documentation on how to write client drivers.
> 
> -.. Place documentation for individual client drivers here.
> +.. toctree::
> +   :maxdepth: 1
> +
> +   cdev
> +
> +.. only::  subproject and html
> +
> +   Indices
> +   =======
> +
> +   * :ref:`genindex`
> diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
> index 55a2d9b2ce33..a98895da62b3 100644
> --- a/Documentation/userspace-api/ioctl/ioctl-number.rst
> +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
> @@ -323,6 +323,8 @@ Code  Seq#    Include File                                           Comments
>                                                                       <mailto:tlewis@mindspring.com>
>  0xA3  90-9F  linux/dtlk.h
>  0xA4  00-1F  uapi/linux/tee.h                                        Generic TEE subsystem
> +0xA5  01     linux/surface_aggregator/cdev.h                         Microsoft Surface Platform System Aggregator
> +                                                                     <mailto:luzmaximilian@gmail.com>
>  0xAA  00-3F  linux/uapi/linux/userfaultfd.h
>  0xAB  00-1F  linux/nbd.h
>  0xAC  00-1F  linux/raw.h
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ff5d60875cec..f5a788f445a4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11699,7 +11699,9 @@ W:	https://github.com/linux-surface/surface-aggregator-module
>  C:	irc://chat.freenode.net/##linux-surface
>  F:	Documentation/driver-api/surface_aggregator/
>  F:	drivers/platform/surface/aggregator/
> +F:	drivers/platform/surface/surface_aggregator_cdev.c
>  F:	include/linux/surface_aggregator/
> +F:	include/uapi/linux/surface_aggregator/
> 
>  MICROTEK X6 SCANNER
>  M:	Oliver Neukum <oliver@neukum.org>
> diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig
> index 5af22f5141ff..cecad7a0cb7b 100644
> --- a/drivers/platform/surface/Kconfig
> +++ b/drivers/platform/surface/Kconfig
> @@ -40,6 +40,23 @@ config SURFACE_3_POWER_OPREGION
>  	  This driver provides support for ACPI operation
>  	  region of the Surface 3 battery platform driver.
> 
> +config SURFACE_AGGREGATOR_CDEV
> +	tristate "Surface System Aggregator Module User-Space Interface"
> +	depends on SURFACE_AGGREGATOR
> +	help
> +	  Provides a misc-device interface to the Surface System Aggregator
> +	  Module (SSAM) controller.
> +
> +	  This option provides a module (called surface_aggregator_cdev), that,
> +	  when loaded, will add a client device (and its respective driver) to
> +	  the SSAM controller. Said client device manages a misc-device
> +	  interface (/dev/surface/aggregator), which can be used by user-space
> +	  tools to directly communicate with the SSAM EC by sending requests and
> +	  receiving the corresponding responses.
> +
> +	  The provided interface is intended for debugging and development only,
> +	  and should not be used otherwise.
> +
>  config SURFACE_GPE
>  	tristate "Surface GPE/Lid Support Driver"
>  	depends on ACPI
> diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile
> index 034134fe0264..161f0ad05795 100644
> --- a/drivers/platform/surface/Makefile
> +++ b/drivers/platform/surface/Makefile
> @@ -8,5 +8,6 @@ obj-$(CONFIG_SURFACE3_WMI)		+= surface3-wmi.o
>  obj-$(CONFIG_SURFACE_3_BUTTON)		+= surface3_button.o
>  obj-$(CONFIG_SURFACE_3_POWER_OPREGION)	+= surface3_power.o
>  obj-$(CONFIG_SURFACE_AGGREGATOR)	+= aggregator/
> +obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV)	+= surface_aggregator_cdev.o
>  obj-$(CONFIG_SURFACE_GPE)		+= surface_gpe.o
>  obj-$(CONFIG_SURFACE_PRO3_BUTTON)	+= surfacepro3_button.o
> diff --git a/drivers/platform/surface/surface_aggregator_cdev.c b/drivers/platform/surface/surface_aggregator_cdev.c
> new file mode 100644
> index 000000000000..40c30627f33a
> --- /dev/null
> +++ b/drivers/platform/surface/surface_aggregator_cdev.c
> @@ -0,0 +1,299 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * 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>
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/kernel.h>
> +#include <linux/kref.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/rwsem.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +
> +#include <linux/surface_aggregator/cdev.h>
> +#include <linux/surface_aggregator/controller.h>
> +
> +#define SSAM_CDEV_DEVICE_NAME	"surface_aggregator_cdev"
> +
> +struct ssam_cdev {
> +	struct kref kref;
> +	struct rw_semaphore lock;
> +	struct ssam_controller *ctrl;
> +	struct miscdevice mdev;
> +};
> +
> +static void __ssam_cdev_release(struct kref *kref)
> +{
> +	kfree(container_of(kref, struct ssam_cdev, kref));
> +}
> +
> +static struct ssam_cdev *ssam_cdev_get(struct ssam_cdev *cdev)
> +{
> +	if (cdev)
> +		kref_get(&cdev->kref);
> +
> +	return cdev;
> +}
> +
> +static void ssam_cdev_put(struct ssam_cdev *cdev)
> +{
> +	if (cdev)
> +		kref_put(&cdev->kref, __ssam_cdev_release);
> +}
> +
> +static int ssam_cdev_device_open(struct inode *inode, struct file *filp)
> +{
> +	struct miscdevice *mdev = filp->private_data;
> +	struct ssam_cdev *cdev = container_of(mdev, struct ssam_cdev, mdev);
> +
> +	filp->private_data = ssam_cdev_get(cdev);
> +	return stream_open(inode, filp);
> +}
> +
> +static int ssam_cdev_device_release(struct inode *inode, struct file *filp)
> +{
> +	ssam_cdev_put(filp->private_data);
> +	return 0;
> +}
> +
> +static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg)
> +{
> +	struct ssam_cdev_request __user *r;
> +	struct ssam_cdev_request rqst;
> +	struct ssam_request spec;
> +	struct ssam_response rsp;
> +	const void __user *plddata;
> +	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;
> +
> +	plddata = u64_to_user_ptr(rqst.payload.data);
> +	rspdata = u64_to_user_ptr(rqst.response.data);
> +
> +	/* Setup basic request fields. */
> +	spec.target_category = rqst.target_category;
> +	spec.target_id = rqst.target_id;
> +	spec.command_id = rqst.command_id;
> +	spec.instance_id = rqst.instance_id;
> +	spec.flags = rqst.flags;
> +	spec.length = rqst.payload.length;
> +	spec.payload = NULL;
> +
> +	rsp.capacity = rqst.response.length;
> +	rsp.length = 0;
> +	rsp.pointer = NULL;
> +
> +	/* Get request payload from user-space. */
> +	if (spec.length) {
> +		if (!plddata) {
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +
> +		spec.payload = kzalloc(spec.length, GFP_KERNEL);
> +		if (!spec.payload) {
> +			status = -ENOMEM;
> +			ret = -EFAULT;
> +			goto out;
> +		}
> +
> +		if (copy_from_user((void *)spec.payload, plddata, spec.length)) {
> +			ret = -EFAULT;
> +			goto out;
> +		}
> +	}
> +
> +	/* Allocate response buffer. */
> +	if (rsp.capacity) {
> +		if (!rspdata) {
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +
> +		rsp.pointer = kzalloc(rsp.capacity, GFP_KERNEL);
> +		if (!rsp.pointer) {
> +			status = -ENOMEM;
> +			ret = -EFAULT;

This is weird, -EFAULT should only be used if a SEGFAULT
would have been raised if the code was running in
userspace rather then in kernelspace, IOW if userspace
has provided an invalid pointer (or a too small buffer,
causing the pointer to become invalid at some point in
the buffer).

IMHO you should simply do ret = -ENOMEM here.

Otherwise this looks good to me.

Regards,

Hans



> +			goto out;
> +		}
> +	}
> +
> +	/* Perform request. */
> +	status = ssam_request_sync(cdev->ctrl, &spec, &rsp);
> +	if (status)
> +		goto out;
> +
> +	/* Copy response to user-space. */
> +	if (rsp.length && copy_to_user(rspdata, rsp.pointer, rsp.length))
> +		ret = -EFAULT;
> +
> +out:
> +	/* Always try to set response-length and status. */
> +	tmp = put_user(rsp.length, &r->response.length);
> +	if (tmp)
> +		ret = tmp;
> +
> +	tmp = put_user(status, &r->status);
> +	if (tmp)
> +		ret = tmp;
> +
> +	/* Cleanup. */
> +	kfree(spec.payload);
> +	kfree(rsp.pointer);
> +
> +	return ret;
> +}
> +
> +static long __ssam_cdev_device_ioctl(struct ssam_cdev *cdev, unsigned int cmd,
> +				     unsigned long arg)
> +{
> +	switch (cmd) {
> +	case SSAM_CDEV_REQUEST:
> +		return ssam_cdev_request(cdev, arg);
> +
> +	default:
> +		return -ENOTTY;
> +	}
> +}
> +
> +static long ssam_cdev_device_ioctl(struct file *file, unsigned int cmd,
> +				   unsigned long arg)
> +{
> +	struct ssam_cdev *cdev = file->private_data;
> +	long status;
> +
> +	/* Ensure that controller is valid for as long as we need it. */
> +	if (down_read_killable(&cdev->lock))
> +		return -ERESTARTSYS;
> +
> +	if (!cdev->ctrl) {
> +		up_read(&cdev->lock);
> +		return -ENODEV;
> +	}
> +
> +	status = __ssam_cdev_device_ioctl(cdev, cmd, arg);
> +
> +	up_read(&cdev->lock);
> +	return status;
> +}
> +
> +static const struct file_operations ssam_controller_fops = {
> +	.owner          = THIS_MODULE,
> +	.open           = ssam_cdev_device_open,
> +	.release        = ssam_cdev_device_release,
> +	.unlocked_ioctl = ssam_cdev_device_ioctl,
> +	.compat_ioctl   = ssam_cdev_device_ioctl,
> +	.llseek         = noop_llseek,
> +};
> +
> +static int ssam_dbg_device_probe(struct platform_device *pdev)
> +{
> +	struct ssam_controller *ctrl;
> +	struct ssam_cdev *cdev;
> +	int status;
> +
> +	ctrl = ssam_client_bind(&pdev->dev);
> +	if (IS_ERR(ctrl))
> +		return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
> +
> +	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
> +	if (!cdev)
> +		return -ENOMEM;
> +
> +	kref_init(&cdev->kref);
> +	init_rwsem(&cdev->lock);
> +	cdev->ctrl = ctrl;
> +
> +	cdev->mdev.parent   = &pdev->dev;
> +	cdev->mdev.minor    = MISC_DYNAMIC_MINOR;
> +	cdev->mdev.name     = "surface_aggregator";
> +	cdev->mdev.nodename = "surface/aggregator";
> +	cdev->mdev.fops     = &ssam_controller_fops;
> +
> +	status = misc_register(&cdev->mdev);
> +	if (status) {
> +		kfree(cdev);
> +		return status;
> +	}
> +
> +	platform_set_drvdata(pdev, cdev);
> +	return 0;
> +}
> +
> +static int ssam_dbg_device_remove(struct platform_device *pdev)
> +{
> +	struct ssam_cdev *cdev = platform_get_drvdata(pdev);
> +
> +	misc_deregister(&cdev->mdev);
> +
> +	/*
> +	 * The controller is only guaranteed to be valid for as long as the
> +	 * driver is bound. Remove controller so that any lingering open files
> +	 * cannot access it any more after we're gone.
> +	 */
> +	down_write(&cdev->lock);
> +	cdev->ctrl = NULL;
> +	up_write(&cdev->lock);
> +
> +	ssam_cdev_put(cdev);
> +	return 0;
> +}
> +
> +static struct platform_device *ssam_cdev_device;
> +
> +static struct platform_driver ssam_cdev_driver = {
> +	.probe = ssam_dbg_device_probe,
> +	.remove = ssam_dbg_device_remove,
> +	.driver = {
> +		.name = SSAM_CDEV_DEVICE_NAME,
> +		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
> +	},
> +};
> +
> +static int __init ssam_debug_init(void)
> +{
> +	int status;
> +
> +	ssam_cdev_device = platform_device_alloc(SSAM_CDEV_DEVICE_NAME,
> +						 PLATFORM_DEVID_NONE);
> +	if (!ssam_cdev_device)
> +		return -ENOMEM;
> +
> +	status = platform_device_add(ssam_cdev_device);
> +	if (status)
> +		goto err_device;
> +
> +	status = platform_driver_register(&ssam_cdev_driver);
> +	if (status)
> +		goto err_driver;
> +
> +	return 0;
> +
> +err_driver:
> +	platform_device_del(ssam_cdev_device);
> +err_device:
> +	platform_device_put(ssam_cdev_device);
> +	return status;
> +}
> +module_init(ssam_debug_init);
> +
> +static void __exit ssam_debug_exit(void)
> +{
> +	platform_driver_unregister(&ssam_cdev_driver);
> +	platform_device_unregister(ssam_cdev_device);
> +}
> +module_exit(ssam_debug_exit);
> +
> +MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
> +MODULE_DESCRIPTION("User-space interface for Surface System Aggregator Module");
> +MODULE_LICENSE("GPL");
> diff --git a/include/uapi/linux/surface_aggregator/cdev.h b/include/uapi/linux/surface_aggregator/cdev.h
> new file mode 100644
> index 000000000000..1a8bc0249f8e
> --- /dev/null
> +++ b/include/uapi/linux/surface_aggregator/cdev.h
> @@ -0,0 +1,58 @@
> +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> +/*
> + * Surface System Aggregator Module (SSAM) user-space EC interface.
> + *
> + * Definitions, structs, and IOCTLs for the /dev/surface/aggregator misc
> + * 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>
> + */
> +
> +#ifndef _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H
> +#define _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H
> +
> +#include <linux/ioctl.h>
> +#include <linux/types.h>
> +
> +/**
> + * struct ssam_cdev_request - Controller request IOCTL argument.
> + * @target_category: Target category of the SAM request.
> + * @target_id:       Target ID of the SAM request.
> + * @command_id:      Command ID of the SAM request.
> + * @instance_id:     Instance ID of the SAM request.
> + * @flags:           SAM Request flags.
> + * @status:          Request status (output).
> + * @payload:         Request payload (input data).
> + * @payload.data:    Pointer to request payload data.
> + * @payload.length:  Length of request payload data (in bytes).
> + * @response:        Request response (output data).
> + * @response.data:   Pointer to response buffer.
> + * @response.length: On input: Capacity of response buffer (in bytes).
> + *                   On output: Length of request response (number of bytes
> + *                   in the buffer that are actually used).
> + */
> +struct ssam_cdev_request {
> +	__u8 target_category;
> +	__u8 target_id;
> +	__u8 command_id;
> +	__u8 instance_id;
> +	__u16 flags;
> +	__s16 status;
> +
> +	struct {
> +		__u64 data;
> +		__u16 length;
> +		__u8 __pad[6];
> +	} payload;
> +
> +	struct {
> +		__u64 data;
> +		__u16 length;
> +		__u8 __pad[6];
> +	} response;
> +} __attribute__((__packed__));
> +
> +#define SSAM_CDEV_REQUEST	_IOWR(0xA5, 1, struct ssam_cdev_request)
> +
> +#endif /* _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H */
> --
> 2.29.2
> 


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 9/9] platform/surface: Add Surface ACPI Notify driver
  2020-12-03 21:26 ` [PATCH v2 9/9] platform/surface: Add Surface ACPI Notify driver Maximilian Luz
@ 2020-12-15 17:18   ` Hans de Goede
  0 siblings, 0 replies; 31+ messages in thread
From: Hans de Goede @ 2020-12-15 17:18 UTC (permalink / raw)
  To: Maximilian Luz, linux-kernel
  Cc: Mark Gross, Andy Shevchenko, Barnabás Pőcze,
	Arnd Bergmann, Greg Kroah-Hartman, Rafael J . Wysocki, Len Brown,
	Jonathan Corbet, Blaž Hrastnik, Dorian Stoll,
	platform-driver-x86, linux-acpi, linux-doc

Hi,

On 12/3/20 10:26 PM, Maximilian Luz wrote:
> The Surface ACPI Notify (SAN) device provides an ACPI interface to the
> Surface Aggregator EC, specifically the Surface Serial Hub interface.
> This interface allows EC requests to be made from ACPI code and can
> convert a subset of EC events back to ACPI notifications.
> 
> Specifically, this interface provides a GenericSerialBus operation
> region ACPI code can execute a request by writing the request command
> data and payload to this operation region and reading back the
> corresponding response via a write-then-read operation. Furthermore,
> this interface provides a _DSM method to be called when certain events
> from the EC have been received, essentially turning them into ACPI
> notifications.
> 
> The driver provided in this commit essentially takes care of translating
> the request data written to the operation region, executing the request,
> waiting for it to finish, and finally writing and translating back the
> response (if the request has one). Furthermore, this driver takes care
> of enabling the events handled via ACPI _DSM calls. Lastly, this driver
> also exposes an interface providing discrete GPU (dGPU) power-on
> notifications on the Surface Book 2, which are also received via the
> operation region interface (but not handled by the SAN driver directly),
> making them accessible to other drivers (such as a dGPU hot-plug driver
> that may be added later on).
> 
> On 5th and 6th generation Surface devices (Surface Pro 5/2017, Pro 6,
> Book 2, Laptop 1 and 2), the SAN interface provides full battery and
> thermal subsystem access, as well as other EC based functionality. On
> those models, battery and thermal sensor devices are implemented as
> standard ACPI devices of that type, however, forward ACPI calls to the
> corresponding Surface Aggregator EC request via the SAN interface and
> receive corresponding notifications (e.g. battery information change)
> from it. This interface is therefore required to provide said
> functionality on those devices.
> 
> Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>

Thanks, patch looks good to me:

Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Regards,

Hans


> ---
> 
> Changes in v1 (from RFC):
>  - add copyright lines
>  - change SPDX identifier to GPL-2.0+ (was GPL-2.0-or-later)
>  - fix invalid pointer calculation in san_evt_tmp
>  - add explicit dependency on CONFIG_ACPI
>  - remove default in Kconfig
> 
> Changes in v2:
>  - drop explicit dependency on ACPI in Kconfig
>  - use printk specifier for hex prefix instead of hard-coding it
>  - spell check comments and strings, fix typos
>  - unify comment style
>  - run checkpatch --strict, fix warnings and style issues
> 
> ---
>  .../surface_aggregator/clients/index.rst      |   1 +
>  .../surface_aggregator/clients/san.rst        |  44 +
>  MAINTAINERS                                   |   2 +
>  drivers/platform/surface/Kconfig              |  19 +
>  drivers/platform/surface/Makefile             |   1 +
>  .../platform/surface/surface_acpi_notify.c    | 886 ++++++++++++++++++
>  include/linux/surface_acpi_notify.h           |  39 +
>  7 files changed, 992 insertions(+)
>  create mode 100644 Documentation/driver-api/surface_aggregator/clients/san.rst
>  create mode 100644 drivers/platform/surface/surface_acpi_notify.c
>  create mode 100644 include/linux/surface_acpi_notify.h
> 
> diff --git a/Documentation/driver-api/surface_aggregator/clients/index.rst b/Documentation/driver-api/surface_aggregator/clients/index.rst
> index ab260ec82cfb..3ccabce23271 100644
> --- a/Documentation/driver-api/surface_aggregator/clients/index.rst
> +++ b/Documentation/driver-api/surface_aggregator/clients/index.rst
> @@ -11,6 +11,7 @@ This is the documentation for client drivers themselves. Refer to
>     :maxdepth: 1
> 
>     cdev
> +   san
> 
>  .. only::  subproject and html
> 
> diff --git a/Documentation/driver-api/surface_aggregator/clients/san.rst b/Documentation/driver-api/surface_aggregator/clients/san.rst
> new file mode 100644
> index 000000000000..38c2580e7758
> --- /dev/null
> +++ b/Documentation/driver-api/surface_aggregator/clients/san.rst
> @@ -0,0 +1,44 @@
> +.. SPDX-License-Identifier: GPL-2.0+
> +
> +.. |san_client_link| replace:: :c:func:`san_client_link`
> +.. |san_dgpu_notifier_register| replace:: :c:func:`san_dgpu_notifier_register`
> +.. |san_dgpu_notifier_unregister| replace:: :c:func:`san_dgpu_notifier_unregister`
> +
> +===================
> +Surface ACPI Notify
> +===================
> +
> +The Surface ACPI Notify (SAN) device provides the bridge between ACPI and
> +SAM controller. Specifically, ACPI code can execute requests and handle
> +battery and thermal events via this interface. In addition to this, events
> +relating to the discrete GPU (dGPU) of the Surface Book 2 can be sent from
> +ACPI code (note: the Surface Book 3 uses a different method for this). The
> +only currently known event sent via this interface is a dGPU power-on
> +notification. While this driver handles the former part internally, it only
> +relays the dGPU events to any other driver interested via its public API and
> +does not handle them.
> +
> +The public interface of this driver is split into two parts: Client
> +registration and notifier-block registration.
> +
> +A client to the SAN interface can be linked as consumer to the SAN device
> +via |san_client_link|. This can be used to ensure that the a client
> +receiving dGPU events does not miss any events due to the SAN interface not
> +being set up as this forces the client driver to unbind once the SAN driver
> +is unbound.
> +
> +Notifier-blocks can be registered by any device for as long as the module is
> +loaded, regardless of being linked as client or not. Registration is done
> +with |san_dgpu_notifier_register|. If the notifier is not needed any more, it
> +should be unregistered via |san_dgpu_notifier_unregister|.
> +
> +Consult the API documentation below for more details.
> +
> +
> +API Documentation
> +=================
> +
> +.. kernel-doc:: include/linux/surface_acpi_notify.h
> +
> +.. kernel-doc:: drivers/platform/surface/surface_acpi_notify.c
> +    :export:
> diff --git a/MAINTAINERS b/MAINTAINERS
> index f5a788f445a4..894c9ef1b8d6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11699,7 +11699,9 @@ W:	https://github.com/linux-surface/surface-aggregator-module
>  C:	irc://chat.freenode.net/##linux-surface
>  F:	Documentation/driver-api/surface_aggregator/
>  F:	drivers/platform/surface/aggregator/
> +F:	drivers/platform/surface/surface_acpi_notify.c
>  F:	drivers/platform/surface/surface_aggregator_cdev.c
> +F:	include/linux/surface_acpi_notify.h
>  F:	include/linux/surface_aggregator/
>  F:	include/uapi/linux/surface_aggregator/
> 
> diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig
> index cecad7a0cb7b..88818320105e 100644
> --- a/drivers/platform/surface/Kconfig
> +++ b/drivers/platform/surface/Kconfig
> @@ -40,6 +40,25 @@ config SURFACE_3_POWER_OPREGION
>  	  This driver provides support for ACPI operation
>  	  region of the Surface 3 battery platform driver.
> 
> +config SURFACE_ACPI_NOTIFY
> +	tristate "Surface ACPI Notify Driver"
> +	depends on SURFACE_AGGREGATOR
> +	help
> +	  Surface ACPI Notify (SAN) driver for Microsoft Surface devices.
> +
> +	  This driver provides support for the ACPI interface (called SAN) of
> +	  the Surface System Aggregator Module (SSAM) EC. This interface is used
> +	  on 5th- and 6th-generation Microsoft Surface devices (including
> +	  Surface Pro 5 and 6, Surface Book 2, Surface Laptops 1 and 2, and in
> +	  reduced functionality on the Surface Laptop 3) to execute SSAM
> +	  requests directly from ACPI code, as well as receive SSAM events and
> +	  turn them into ACPI notifications. It essentially acts as a
> +	  translation layer between the SSAM controller and ACPI.
> +
> +	  Specifically, this driver may be needed for battery status reporting,
> +	  thermal sensor access, and real-time clock information, depending on
> +	  the Surface device in question.
> +
>  config SURFACE_AGGREGATOR_CDEV
>  	tristate "Surface System Aggregator Module User-Space Interface"
>  	depends on SURFACE_AGGREGATOR
> diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile
> index 161f0ad05795..3eb971006877 100644
> --- a/drivers/platform/surface/Makefile
> +++ b/drivers/platform/surface/Makefile
> @@ -7,6 +7,7 @@
>  obj-$(CONFIG_SURFACE3_WMI)		+= surface3-wmi.o
>  obj-$(CONFIG_SURFACE_3_BUTTON)		+= surface3_button.o
>  obj-$(CONFIG_SURFACE_3_POWER_OPREGION)	+= surface3_power.o
> +obj-$(CONFIG_SURFACE_ACPI_NOTIFY)	+= surface_acpi_notify.o
>  obj-$(CONFIG_SURFACE_AGGREGATOR)	+= aggregator/
>  obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV)	+= surface_aggregator_cdev.o
>  obj-$(CONFIG_SURFACE_GPE)		+= surface_gpe.o
> diff --git a/drivers/platform/surface/surface_acpi_notify.c b/drivers/platform/surface/surface_acpi_notify.c
> new file mode 100644
> index 000000000000..8cd67a669c86
> --- /dev/null
> +++ b/drivers/platform/surface/surface_acpi_notify.c
> @@ -0,0 +1,886 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Driver for the Surface ACPI Notify (SAN) interface/shim.
> + *
> + * Translates communication from ACPI to Surface System Aggregator Module
> + * (SSAM/SAM) requests and back, specifically SAM-over-SSH. Translates SSAM
> + * events back to ACPI notifications. Allows handling of discrete GPU
> + * notifications sent from ACPI via the SAN interface by providing them to any
> + * registered external driver.
> + *
> + * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
> + */
> +
> +#include <asm/unaligned.h>
> +#include <linux/acpi.h>
> +#include <linux/delay.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/notifier.h>
> +#include <linux/platform_device.h>
> +#include <linux/rwsem.h>
> +
> +#include <linux/surface_aggregator/controller.h>
> +#include <linux/surface_acpi_notify.h>
> +
> +struct san_data {
> +	struct device *dev;
> +	struct ssam_controller *ctrl;
> +
> +	struct acpi_connection_info info;
> +
> +	struct ssam_event_notifier nf_bat;
> +	struct ssam_event_notifier nf_tmp;
> +};
> +
> +#define to_san_data(ptr, member) \
> +	container_of(ptr, struct san_data, member)
> +
> +
> +/* -- dGPU notifier interface. ---------------------------------------------- */
> +
> +struct san_rqsg_if {
> +	struct rw_semaphore lock;
> +	struct device *dev;
> +	struct blocking_notifier_head nh;
> +};
> +
> +static struct san_rqsg_if san_rqsg_if = {
> +	.lock = __RWSEM_INITIALIZER(san_rqsg_if.lock),
> +	.dev = NULL,
> +	.nh = BLOCKING_NOTIFIER_INIT(san_rqsg_if.nh),
> +};
> +
> +static int san_set_rqsg_interface_device(struct device *dev)
> +{
> +	int status = 0;
> +
> +	down_write(&san_rqsg_if.lock);
> +	if (!san_rqsg_if.dev && dev)
> +		san_rqsg_if.dev = dev;
> +	else
> +		status = -EBUSY;
> +	up_write(&san_rqsg_if.lock);
> +
> +	return status;
> +}
> +
> +/**
> + * san_client_link() - Link client as consumer to SAN device.
> + * @client: The client to link.
> + *
> + * Sets up a device link between the provided client device as consumer and
> + * the SAN device as provider. This function can be used to ensure that the
> + * SAN interface has been set up and will be set up for as long as the driver
> + * of the client device is bound. This guarantees that, during that time, all
> + * dGPU events will be received by any registered notifier.
> + *
> + * The link will be automatically removed once the client device's driver is
> + * unbound.
> + *
> + * Return: Returns zero on success, %-ENXIO if the SAN interface has not been
> + * set up yet, and %-ENOMEM if device link creation failed.
> + */
> +int san_client_link(struct device *client)
> +{
> +	const u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER;
> +	struct device_link *link;
> +
> +	down_read(&san_rqsg_if.lock);
> +
> +	if (!san_rqsg_if.dev) {
> +		up_read(&san_rqsg_if.lock);
> +		return -ENXIO;
> +	}
> +
> +	link = device_link_add(client, san_rqsg_if.dev, flags);
> +	if (!link) {
> +		up_read(&san_rqsg_if.lock);
> +		return -ENOMEM;
> +	}
> +
> +	if (READ_ONCE(link->status) == DL_STATE_SUPPLIER_UNBIND) {
> +		up_read(&san_rqsg_if.lock);
> +		return -ENXIO;
> +	}
> +
> +	up_read(&san_rqsg_if.lock);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(san_client_link);
> +
> +/**
> + * san_dgpu_notifier_register() - Register a SAN dGPU notifier.
> + * @nb: The notifier-block to register.
> + *
> + * Registers a SAN dGPU notifier, receiving any new SAN dGPU events sent from
> + * ACPI. The registered notifier will be called with &struct san_dgpu_event
> + * as notifier data and the command ID of that event as notifier action.
> + */
> +int san_dgpu_notifier_register(struct notifier_block *nb)
> +{
> +	return blocking_notifier_chain_register(&san_rqsg_if.nh, nb);
> +}
> +EXPORT_SYMBOL_GPL(san_dgpu_notifier_register);
> +
> +/**
> + * san_dgpu_notifier_unregister() - Unregister a SAN dGPU notifier.
> + * @nb: The notifier-block to unregister.
> + */
> +int san_dgpu_notifier_unregister(struct notifier_block *nb)
> +{
> +	return blocking_notifier_chain_unregister(&san_rqsg_if.nh, nb);
> +}
> +EXPORT_SYMBOL_GPL(san_dgpu_notifier_unregister);
> +
> +static int san_dgpu_notifier_call(struct san_dgpu_event *evt)
> +{
> +	int ret;
> +
> +	ret = blocking_notifier_call_chain(&san_rqsg_if.nh, evt->command, evt);
> +	return notifier_to_errno(ret);
> +}
> +
> +
> +/* -- ACPI _DSM event relay. ------------------------------------------------ */
> +
> +#define SAN_DSM_REVISION	0
> +
> +/* 93b666c5-70c6-469f-a215-3d487c91ab3c */
> +static const guid_t SAN_DSM_UUID =
> +	GUID_INIT(0x93b666c5, 0x70c6, 0x469f, 0xa2, 0x15, 0x3d,
> +		  0x48, 0x7c, 0x91, 0xab, 0x3c);
> +
> +enum san_dsm_event_fn {
> +	SAN_DSM_EVENT_FN_BAT1_STAT = 0x03,
> +	SAN_DSM_EVENT_FN_BAT1_INFO = 0x04,
> +	SAN_DSM_EVENT_FN_ADP1_STAT = 0x05,
> +	SAN_DSM_EVENT_FN_ADP1_INFO = 0x06,
> +	SAN_DSM_EVENT_FN_BAT2_STAT = 0x07,
> +	SAN_DSM_EVENT_FN_BAT2_INFO = 0x08,
> +	SAN_DSM_EVENT_FN_THERMAL   = 0x09,
> +	SAN_DSM_EVENT_FN_DPTF      = 0x0a,
> +};
> +
> +enum sam_event_cid_bat {
> +	SAM_EVENT_CID_BAT_BIX  = 0x15,
> +	SAM_EVENT_CID_BAT_BST  = 0x16,
> +	SAM_EVENT_CID_BAT_ADP  = 0x17,
> +	SAM_EVENT_CID_BAT_PROT = 0x18,
> +	SAM_EVENT_CID_BAT_DPTF = 0x4f,
> +};
> +
> +enum sam_event_cid_tmp {
> +	SAM_EVENT_CID_TMP_TRIP = 0x0b,
> +};
> +
> +struct san_event_work {
> +	struct delayed_work work;
> +	struct device *dev;
> +	struct ssam_event event;	/* must be last */
> +};
> +
> +static int san_acpi_notify_event(struct device *dev, u64 func,
> +				 union acpi_object *param)
> +{
> +	acpi_handle san = ACPI_HANDLE(dev);
> +	union acpi_object *obj;
> +	int status = 0;
> +
> +	if (!acpi_check_dsm(san, &SAN_DSM_UUID, SAN_DSM_REVISION, 1 << func))
> +		return 0;
> +
> +	dev_dbg(dev, "notify event %#04llx\n", func);
> +
> +	obj = acpi_evaluate_dsm_typed(san, &SAN_DSM_UUID, SAN_DSM_REVISION,
> +				      func, param, ACPI_TYPE_BUFFER);
> +	if (!obj)
> +		return -EFAULT;
> +
> +	if (obj->buffer.length != 1 || obj->buffer.pointer[0] != 0) {
> +		dev_err(dev, "got unexpected result from _DSM\n");
> +		status = -EPROTO;
> +	}
> +
> +	ACPI_FREE(obj);
> +	return status;
> +}
> +
> +static int san_evt_bat_adp(struct device *dev, const struct ssam_event *event)
> +{
> +	int status;
> +
> +	status = san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_ADP1_STAT, NULL);
> +	if (status)
> +		return status;
> +
> +	/*
> +	 * Ensure that the battery states get updated correctly. When the
> +	 * battery is fully charged and an adapter is plugged in, it sometimes
> +	 * is not updated correctly, instead showing it as charging.
> +	 * Explicitly trigger battery updates to fix this.
> +	 */
> +
> +	status = san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_BAT1_STAT, NULL);
> +	if (status)
> +		return status;
> +
> +	return san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_BAT2_STAT, NULL);
> +}
> +
> +static int san_evt_bat_bix(struct device *dev, const struct ssam_event *event)
> +{
> +	enum san_dsm_event_fn fn;
> +
> +	if (event->instance_id == 0x02)
> +		fn = SAN_DSM_EVENT_FN_BAT2_INFO;
> +	else
> +		fn = SAN_DSM_EVENT_FN_BAT1_INFO;
> +
> +	return san_acpi_notify_event(dev, fn, NULL);
> +}
> +
> +static int san_evt_bat_bst(struct device *dev, const struct ssam_event *event)
> +{
> +	enum san_dsm_event_fn fn;
> +
> +	if (event->instance_id == 0x02)
> +		fn = SAN_DSM_EVENT_FN_BAT2_STAT;
> +	else
> +		fn = SAN_DSM_EVENT_FN_BAT1_STAT;
> +
> +	return san_acpi_notify_event(dev, fn, NULL);
> +}
> +
> +static int san_evt_bat_dptf(struct device *dev, const struct ssam_event *event)
> +{
> +	union acpi_object payload;
> +
> +	/*
> +	 * The Surface ACPI expects a buffer and not a package. It specifically
> +	 * checks for ObjectType (Arg3) == 0x03. This will cause a warning in
> +	 * acpica/nsarguments.c, but that warning can be safely ignored.
> +	 */
> +	payload.type = ACPI_TYPE_BUFFER;
> +	payload.buffer.length = event->length;
> +	payload.buffer.pointer = (u8 *)&event->data[0];
> +
> +	return san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_DPTF, &payload);
> +}
> +
> +static unsigned long san_evt_bat_delay(u8 cid)
> +{
> +	switch (cid) {
> +	case SAM_EVENT_CID_BAT_ADP:
> +		/*
> +		 * Wait for battery state to update before signaling adapter
> +		 * change.
> +		 */
> +		return msecs_to_jiffies(5000);
> +
> +	case SAM_EVENT_CID_BAT_BST:
> +		/* Ensure we do not miss anything important due to caching. */
> +		return msecs_to_jiffies(2000);
> +
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static bool san_evt_bat(const struct ssam_event *event, struct device *dev)
> +{
> +	int status;
> +
> +	switch (event->command_id) {
> +	case SAM_EVENT_CID_BAT_BIX:
> +		status = san_evt_bat_bix(dev, event);
> +		break;
> +
> +	case SAM_EVENT_CID_BAT_BST:
> +		status = san_evt_bat_bst(dev, event);
> +		break;
> +
> +	case SAM_EVENT_CID_BAT_ADP:
> +		status = san_evt_bat_adp(dev, event);
> +		break;
> +
> +	case SAM_EVENT_CID_BAT_PROT:
> +		/*
> +		 * TODO: Implement support for battery protection status change
> +		 *       event.
> +		 */
> +		return true;
> +
> +	case SAM_EVENT_CID_BAT_DPTF:
> +		status = san_evt_bat_dptf(dev, event);
> +		break;
> +
> +	default:
> +		return false;
> +	}
> +
> +	if (status) {
> +		dev_err(dev, "error handling power event (cid = %#04x)\n",
> +			event->command_id);
> +	}
> +
> +	return true;
> +}
> +
> +static void san_evt_bat_workfn(struct work_struct *work)
> +{
> +	struct san_event_work *ev;
> +
> +	ev = container_of(work, struct san_event_work, work.work);
> +	san_evt_bat(&ev->event, ev->dev);
> +	kfree(ev);
> +}
> +
> +static u32 san_evt_bat_nf(struct ssam_event_notifier *nf,
> +			  const struct ssam_event *event)
> +{
> +	struct san_data *d = to_san_data(nf, nf_bat);
> +	struct san_event_work *work;
> +	unsigned long delay = san_evt_bat_delay(event->command_id);
> +
> +	if (delay == 0)
> +		return san_evt_bat(event, d->dev) ? SSAM_NOTIF_HANDLED : 0;
> +
> +	work = kzalloc(sizeof(*work) + event->length, GFP_KERNEL);
> +	if (!work)
> +		return ssam_notifier_from_errno(-ENOMEM);
> +
> +	INIT_DELAYED_WORK(&work->work, san_evt_bat_workfn);
> +	work->dev = d->dev;
> +
> +	memcpy(&work->event, event, sizeof(struct ssam_event) + event->length);
> +
> +	schedule_delayed_work(&work->work, delay);
> +	return SSAM_NOTIF_HANDLED;
> +}
> +
> +static int san_evt_tmp_trip(struct device *dev, const struct ssam_event *event)
> +{
> +	union acpi_object param;
> +
> +	/*
> +	 * The Surface ACPI expects an integer and not a package. This will
> +	 * cause a warning in acpica/nsarguments.c, but that warning can be
> +	 * safely ignored.
> +	 */
> +	param.type = ACPI_TYPE_INTEGER;
> +	param.integer.value = event->instance_id;
> +
> +	return san_acpi_notify_event(dev, SAN_DSM_EVENT_FN_THERMAL, &param);
> +}
> +
> +static bool san_evt_tmp(const struct ssam_event *event, struct device *dev)
> +{
> +	int status;
> +
> +	switch (event->command_id) {
> +	case SAM_EVENT_CID_TMP_TRIP:
> +		status = san_evt_tmp_trip(dev, event);
> +		break;
> +
> +	default:
> +		return false;
> +	}
> +
> +	if (status) {
> +		dev_err(dev, "error handling thermal event (cid = %#04x)\n",
> +			event->command_id);
> +	}
> +
> +	return true;
> +}
> +
> +static u32 san_evt_tmp_nf(struct ssam_event_notifier *nf,
> +			  const struct ssam_event *event)
> +{
> +	struct san_data *d = to_san_data(nf, nf_tmp);
> +
> +	return san_evt_tmp(event, d->dev) ? SSAM_NOTIF_HANDLED : 0;
> +}
> +
> +
> +/* -- ACPI GSB OperationRegion handler -------------------------------------- */
> +
> +struct gsb_data_in {
> +	u8 cv;
> +} __packed;
> +
> +struct gsb_data_rqsx {
> +	u8 cv;				/* Command value (san_gsb_request_cv). */
> +	u8 tc;				/* Target category. */
> +	u8 tid;				/* Target ID. */
> +	u8 iid;				/* Instance ID. */
> +	u8 snc;				/* Expect-response-flag. */
> +	u8 cid;				/* Command ID. */
> +	u16 cdl;			/* Payload length. */
> +	u8 pld[];			/* Payload. */
> +} __packed;
> +
> +struct gsb_data_etwl {
> +	u8 cv;				/* Command value (should be 0x02). */
> +	u8 etw3;			/* Unknown. */
> +	u8 etw4;			/* Unknown. */
> +	u8 msg[];			/* Error message (ASCIIZ). */
> +} __packed;
> +
> +struct gsb_data_out {
> +	u8 status;			/* _SSH communication status. */
> +	u8 len;				/* _SSH payload length. */
> +	u8 pld[];			/* _SSH payload. */
> +} __packed;
> +
> +union gsb_buffer_data {
> +	struct gsb_data_in   in;	/* Common input. */
> +	struct gsb_data_rqsx rqsx;	/* RQSX input. */
> +	struct gsb_data_etwl etwl;	/* ETWL input. */
> +	struct gsb_data_out  out;	/* Output. */
> +};
> +
> +struct gsb_buffer {
> +	u8 status;			/* GSB AttribRawProcess status. */
> +	u8 len;				/* GSB AttribRawProcess length. */
> +	union gsb_buffer_data data;
> +} __packed;
> +
> +#define SAN_GSB_MAX_RQSX_PAYLOAD  (U8_MAX - 2 - sizeof(struct gsb_data_rqsx))
> +#define SAN_GSB_MAX_RESPONSE	  (U8_MAX - 2 - sizeof(struct gsb_data_out))
> +
> +#define SAN_GSB_COMMAND		0
> +
> +enum san_gsb_request_cv {
> +	SAN_GSB_REQUEST_CV_RQST = 0x01,
> +	SAN_GSB_REQUEST_CV_ETWL = 0x02,
> +	SAN_GSB_REQUEST_CV_RQSG = 0x03,
> +};
> +
> +#define SAN_REQUEST_NUM_TRIES	5
> +
> +static acpi_status san_etwl(struct san_data *d, struct gsb_buffer *b)
> +{
> +	struct gsb_data_etwl *etwl = &b->data.etwl;
> +
> +	if (b->len < sizeof(struct gsb_data_etwl)) {
> +		dev_err(d->dev, "invalid ETWL package (len = %d)\n", b->len);
> +		return AE_OK;
> +	}
> +
> +	dev_err(d->dev, "ETWL(%#04x, %#04x): %.*s\n", etwl->etw3, etwl->etw4,
> +		(unsigned int)(b->len - sizeof(struct gsb_data_etwl)),
> +		(char *)etwl->msg);
> +
> +	/* Indicate success. */
> +	b->status = 0x00;
> +	b->len = 0x00;
> +
> +	return AE_OK;
> +}
> +
> +static
> +struct gsb_data_rqsx *san_validate_rqsx(struct device *dev, const char *type,
> +					struct gsb_buffer *b)
> +{
> +	struct gsb_data_rqsx *rqsx = &b->data.rqsx;
> +
> +	if (b->len < sizeof(struct gsb_data_rqsx)) {
> +		dev_err(dev, "invalid %s package (len = %d)\n", type, b->len);
> +		return NULL;
> +	}
> +
> +	if (get_unaligned(&rqsx->cdl) != b->len - sizeof(struct gsb_data_rqsx)) {
> +		dev_err(dev, "bogus %s package (len = %d, cdl = %d)\n",
> +			type, b->len, get_unaligned(&rqsx->cdl));
> +		return NULL;
> +	}
> +
> +	if (get_unaligned(&rqsx->cdl) > SAN_GSB_MAX_RQSX_PAYLOAD) {
> +		dev_err(dev, "payload for %s package too large (cdl = %d)\n",
> +			type, get_unaligned(&rqsx->cdl));
> +		return NULL;
> +	}
> +
> +	return rqsx;
> +}
> +
> +static void gsb_rqsx_response_error(struct gsb_buffer *gsb, int status)
> +{
> +	gsb->status = 0x00;
> +	gsb->len = 0x02;
> +	gsb->data.out.status = (u8)(-status);
> +	gsb->data.out.len = 0x00;
> +}
> +
> +static void gsb_rqsx_response_success(struct gsb_buffer *gsb, u8 *ptr, size_t len)
> +{
> +	gsb->status = 0x00;
> +	gsb->len = len + 2;
> +	gsb->data.out.status = 0x00;
> +	gsb->data.out.len = len;
> +
> +	if (len)
> +		memcpy(&gsb->data.out.pld[0], ptr, len);
> +}
> +
> +static acpi_status san_rqst_fixup_suspended(struct san_data *d,
> +					    struct ssam_request *rqst,
> +					    struct gsb_buffer *gsb)
> +{
> +	if (rqst->target_category == SSAM_SSH_TC_BAS && rqst->command_id == 0x0D) {
> +		u8 base_state = 1;
> +
> +		/* Base state quirk:
> +		 * The base state may be queried from ACPI when the EC is still
> +		 * suspended. In this case it will return '-EPERM'. This query
> +		 * will only be triggered from the ACPI lid GPE interrupt, thus
> +		 * we are either in laptop or studio mode (base status 0x01 or
> +		 * 0x02). Furthermore, we will only get here if the device (and
> +		 * EC) have been suspended.
> +		 *
> +		 * We now assume that the device is in laptop mode (0x01). This
> +		 * has the drawback that it will wake the device when unfolding
> +		 * it in studio mode, but it also allows us to avoid actively
> +		 * waiting for the EC to wake up, which may incur a notable
> +		 * delay.
> +		 */
> +
> +		dev_dbg(d->dev, "rqst: fixup: base-state quirk\n");
> +
> +		gsb_rqsx_response_success(gsb, &base_state, sizeof(base_state));
> +		return AE_OK;
> +	}
> +
> +	gsb_rqsx_response_error(gsb, -ENXIO);
> +	return AE_OK;
> +}
> +
> +static acpi_status san_rqst(struct san_data *d, struct gsb_buffer *buffer)
> +{
> +	u8 rspbuf[SAN_GSB_MAX_RESPONSE];
> +	struct gsb_data_rqsx *gsb_rqst;
> +	struct ssam_request rqst;
> +	struct ssam_response rsp;
> +	int status = 0;
> +
> +	gsb_rqst = san_validate_rqsx(d->dev, "RQST", buffer);
> +	if (!gsb_rqst)
> +		return AE_OK;
> +
> +	rqst.target_category = gsb_rqst->tc;
> +	rqst.target_id = gsb_rqst->tid;
> +	rqst.command_id = gsb_rqst->cid;
> +	rqst.instance_id = gsb_rqst->iid;
> +	rqst.flags = gsb_rqst->snc ? SSAM_REQUEST_HAS_RESPONSE : 0;
> +	rqst.length = get_unaligned(&gsb_rqst->cdl);
> +	rqst.payload = &gsb_rqst->pld[0];
> +
> +	rsp.capacity = ARRAY_SIZE(rspbuf);
> +	rsp.length = 0;
> +	rsp.pointer = &rspbuf[0];
> +
> +	/* Handle suspended device. */
> +	if (d->dev->power.is_suspended) {
> +		dev_warn(d->dev, "rqst: device is suspended, not executing\n");
> +		return san_rqst_fixup_suspended(d, &rqst, buffer);
> +	}
> +
> +	status = __ssam_retry(ssam_request_sync_onstack, SAN_REQUEST_NUM_TRIES,
> +			      d->ctrl, &rqst, &rsp, SAN_GSB_MAX_RQSX_PAYLOAD);
> +
> +	if (!status) {
> +		gsb_rqsx_response_success(buffer, rsp.pointer, rsp.length);
> +	} else {
> +		dev_err(d->dev, "rqst: failed with error %d\n", status);
> +		gsb_rqsx_response_error(buffer, status);
> +	}
> +
> +	return AE_OK;
> +}
> +
> +static acpi_status san_rqsg(struct san_data *d, struct gsb_buffer *buffer)
> +{
> +	struct gsb_data_rqsx *gsb_rqsg;
> +	struct san_dgpu_event evt;
> +	int status;
> +
> +	gsb_rqsg = san_validate_rqsx(d->dev, "RQSG", buffer);
> +	if (!gsb_rqsg)
> +		return AE_OK;
> +
> +	evt.category = gsb_rqsg->tc;
> +	evt.target = gsb_rqsg->tid;
> +	evt.command = gsb_rqsg->cid;
> +	evt.instance = gsb_rqsg->iid;
> +	evt.length = get_unaligned(&gsb_rqsg->cdl);
> +	evt.payload = &gsb_rqsg->pld[0];
> +
> +	status = san_dgpu_notifier_call(&evt);
> +	if (!status) {
> +		gsb_rqsx_response_success(buffer, NULL, 0);
> +	} else {
> +		dev_err(d->dev, "rqsg: failed with error %d\n", status);
> +		gsb_rqsx_response_error(buffer, status);
> +	}
> +
> +	return AE_OK;
> +}
> +
> +static acpi_status san_opreg_handler(u32 function, acpi_physical_address command,
> +				     u32 bits, u64 *value64, void *opreg_context,
> +				     void *region_context)
> +{
> +	struct san_data *d = to_san_data(opreg_context, info);
> +	struct gsb_buffer *buffer = (struct gsb_buffer *)value64;
> +	int accessor_type = (function & 0xFFFF0000) >> 16;
> +
> +	if (command != SAN_GSB_COMMAND) {
> +		dev_warn(d->dev, "unsupported command: %#04llx\n", command);
> +		return AE_OK;
> +	}
> +
> +	if (accessor_type != ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS) {
> +		dev_err(d->dev, "invalid access type: %#04x\n", accessor_type);
> +		return AE_OK;
> +	}
> +
> +	/* Buffer must have at least contain the command-value. */
> +	if (buffer->len == 0) {
> +		dev_err(d->dev, "request-package too small\n");
> +		return AE_OK;
> +	}
> +
> +	switch (buffer->data.in.cv) {
> +	case SAN_GSB_REQUEST_CV_RQST:
> +		return san_rqst(d, buffer);
> +
> +	case SAN_GSB_REQUEST_CV_ETWL:
> +		return san_etwl(d, buffer);
> +
> +	case SAN_GSB_REQUEST_CV_RQSG:
> +		return san_rqsg(d, buffer);
> +
> +	default:
> +		dev_warn(d->dev, "unsupported SAN0 request (cv: %#04x)\n",
> +			 buffer->data.in.cv);
> +		return AE_OK;
> +	}
> +}
> +
> +
> +/* -- Driver setup. --------------------------------------------------------- */
> +
> +static int san_events_register(struct platform_device *pdev)
> +{
> +	struct san_data *d = platform_get_drvdata(pdev);
> +	int status;
> +
> +	d->nf_bat.base.priority = 1;
> +	d->nf_bat.base.fn = san_evt_bat_nf;
> +	d->nf_bat.event.reg = SSAM_EVENT_REGISTRY_SAM;
> +	d->nf_bat.event.id.target_category = SSAM_SSH_TC_BAT;
> +	d->nf_bat.event.id.instance = 0;
> +	d->nf_bat.event.mask = SSAM_EVENT_MASK_TARGET;
> +	d->nf_bat.event.flags = SSAM_EVENT_SEQUENCED;
> +
> +	d->nf_tmp.base.priority = 1;
> +	d->nf_tmp.base.fn = san_evt_tmp_nf;
> +	d->nf_tmp.event.reg = SSAM_EVENT_REGISTRY_SAM;
> +	d->nf_tmp.event.id.target_category = SSAM_SSH_TC_TMP;
> +	d->nf_tmp.event.id.instance = 0;
> +	d->nf_tmp.event.mask = SSAM_EVENT_MASK_TARGET;
> +	d->nf_tmp.event.flags = SSAM_EVENT_SEQUENCED;
> +
> +	status = ssam_notifier_register(d->ctrl, &d->nf_bat);
> +	if (status)
> +		return status;
> +
> +	status = ssam_notifier_register(d->ctrl, &d->nf_tmp);
> +	if (status)
> +		ssam_notifier_unregister(d->ctrl, &d->nf_bat);
> +
> +	return status;
> +}
> +
> +static void san_events_unregister(struct platform_device *pdev)
> +{
> +	struct san_data *d = platform_get_drvdata(pdev);
> +
> +	ssam_notifier_unregister(d->ctrl, &d->nf_bat);
> +	ssam_notifier_unregister(d->ctrl, &d->nf_tmp);
> +}
> +
> +#define san_consumer_printk(level, dev, handle, fmt, ...)			\
> +do {										\
> +	char *path = "<error getting consumer path>";				\
> +	struct acpi_buffer buffer = {						\
> +		.length = ACPI_ALLOCATE_BUFFER,					\
> +		.pointer = NULL,						\
> +	};									\
> +										\
> +	if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer)))	\
> +		path = buffer.pointer;						\
> +										\
> +	dev_##level(dev, "[%s]: " fmt, path, ##__VA_ARGS__);			\
> +	kfree(buffer.pointer);							\
> +} while (0)
> +
> +#define san_consumer_dbg(dev, handle, fmt, ...) \
> +	san_consumer_printk(dbg, dev, handle, fmt, ##__VA_ARGS__)
> +
> +#define san_consumer_warn(dev, handle, fmt, ...) \
> +	san_consumer_printk(warn, dev, handle, fmt, ##__VA_ARGS__)
> +
> +static bool is_san_consumer(struct platform_device *pdev, acpi_handle handle)
> +{
> +	struct acpi_handle_list dep_devices;
> +	acpi_handle supplier = ACPI_HANDLE(&pdev->dev);
> +	acpi_status status;
> +	int i;
> +
> +	if (!acpi_has_method(handle, "_DEP"))
> +		return false;
> +
> +	status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices);
> +	if (ACPI_FAILURE(status)) {
> +		san_consumer_dbg(&pdev->dev, handle, "failed to evaluate _DEP\n");
> +		return false;
> +	}
> +
> +	for (i = 0; i < dep_devices.count; i++) {
> +		if (dep_devices.handles[i] == supplier)
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +static acpi_status san_consumer_setup(acpi_handle handle, u32 lvl,
> +				      void *context, void **rv)
> +{
> +	const u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER;
> +	struct platform_device *pdev = context;
> +	struct acpi_device *adev;
> +	struct device_link *link;
> +
> +	if (!is_san_consumer(pdev, handle))
> +		return AE_OK;
> +
> +	/* Ignore ACPI devices that are not present. */
> +	if (acpi_bus_get_device(handle, &adev) != 0)
> +		return AE_OK;
> +
> +	san_consumer_dbg(&pdev->dev, handle, "creating device link\n");
> +
> +	/* Try to set up device links, ignore but log errors. */
> +	link = device_link_add(&adev->dev, &pdev->dev, flags);
> +	if (!link) {
> +		san_consumer_warn(&pdev->dev, handle, "failed to create device link\n");
> +		return AE_OK;
> +	}
> +
> +	return AE_OK;
> +}
> +
> +static int san_consumer_links_setup(struct platform_device *pdev)
> +{
> +	acpi_status status;
> +
> +	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
> +				     ACPI_UINT32_MAX, san_consumer_setup, NULL,
> +				     pdev, NULL);
> +
> +	return status ? -EFAULT : 0;
> +}
> +
> +static int san_probe(struct platform_device *pdev)
> +{
> +	acpi_handle san = ACPI_HANDLE(&pdev->dev);
> +	struct ssam_controller *ctrl;
> +	struct san_data *data;
> +	acpi_status astatus;
> +	int status;
> +
> +	ctrl = ssam_client_bind(&pdev->dev);
> +	if (IS_ERR(ctrl))
> +		return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
> +
> +	status = san_consumer_links_setup(pdev);
> +	if (status)
> +		return status;
> +
> +	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	data->dev = &pdev->dev;
> +	data->ctrl = ctrl;
> +
> +	platform_set_drvdata(pdev, data);
> +
> +	astatus = acpi_install_address_space_handler(san, ACPI_ADR_SPACE_GSBUS,
> +						     &san_opreg_handler, NULL,
> +						     &data->info);
> +	if (ACPI_FAILURE(astatus))
> +		return -ENXIO;
> +
> +	status = san_events_register(pdev);
> +	if (status)
> +		goto err_enable_events;
> +
> +	status = san_set_rqsg_interface_device(&pdev->dev);
> +	if (status)
> +		goto err_install_dev;
> +
> +	acpi_walk_dep_device_list(san);
> +	return 0;
> +
> +err_install_dev:
> +	san_events_unregister(pdev);
> +err_enable_events:
> +	acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS,
> +					  &san_opreg_handler);
> +	return status;
> +}
> +
> +static int san_remove(struct platform_device *pdev)
> +{
> +	acpi_handle san = ACPI_HANDLE(&pdev->dev);
> +
> +	san_set_rqsg_interface_device(NULL);
> +	acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS,
> +					  &san_opreg_handler);
> +	san_events_unregister(pdev);
> +
> +	/*
> +	 * We have unregistered our event sources. Now we need to ensure that
> +	 * all delayed works they may have spawned are run to completion.
> +	 */
> +	flush_scheduled_work();
> +
> +	return 0;
> +}
> +
> +static const struct acpi_device_id san_match[] = {
> +	{ "MSHW0091" },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(acpi, san_match);
> +
> +static struct platform_driver surface_acpi_notify = {
> +	.probe = san_probe,
> +	.remove = san_remove,
> +	.driver = {
> +		.name = "surface_acpi_notify",
> +		.acpi_match_table = san_match,
> +		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
> +	},
> +};
> +module_platform_driver(surface_acpi_notify);
> +
> +MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
> +MODULE_DESCRIPTION("Surface ACPI Notify driver for Surface System Aggregator Module");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/surface_acpi_notify.h b/include/linux/surface_acpi_notify.h
> new file mode 100644
> index 000000000000..8e3e86c7d78c
> --- /dev/null
> +++ b/include/linux/surface_acpi_notify.h
> @@ -0,0 +1,39 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Interface for Surface ACPI Notify (SAN) driver.
> + *
> + * Provides access to discrete GPU notifications sent from ACPI via the SAN
> + * driver, which are not handled by this driver directly.
> + *
> + * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
> + */
> +
> +#ifndef _LINUX_SURFACE_ACPI_NOTIFY_H
> +#define _LINUX_SURFACE_ACPI_NOTIFY_H
> +
> +#include <linux/notifier.h>
> +#include <linux/types.h>
> +
> +/**
> + * struct san_dgpu_event - Discrete GPU ACPI event.
> + * @category: Category of the event.
> + * @target:   Target ID of the event source.
> + * @command:  Command ID of the event.
> + * @instance: Instance ID of the event source.
> + * @length:   Length of the event's payload data (in bytes).
> + * @payload:  Pointer to the event's payload data.
> + */
> +struct san_dgpu_event {
> +	u8 category;
> +	u8 target;
> +	u8 command;
> +	u8 instance;
> +	u16 length;
> +	u8 *payload;
> +};
> +
> +int san_client_link(struct device *client);
> +int san_dgpu_notifier_register(struct notifier_block *nb);
> +int san_dgpu_notifier_unregister(struct notifier_block *nb);
> +
> +#endif /* _LINUX_SURFACE_ACPI_NOTIFY_H */
> --
> 2.29.2
> 


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 8/9] platform/surface: Add Surface Aggregator user-space interface
  2020-12-15 16:35   ` Hans de Goede
@ 2020-12-15 20:00     ` Maximilian Luz
  0 siblings, 0 replies; 31+ messages in thread
From: Maximilian Luz @ 2020-12-15 20:00 UTC (permalink / raw)
  To: Hans de Goede, linux-kernel
  Cc: Mark Gross, Andy Shevchenko, Barnabás Pőcze,
	Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet,
	Blaž Hrastnik, Dorian Stoll, platform-driver-x86, linux-doc

On 12/15/20 5:35 PM, Hans de Goede wrote:
> Hi,
> 
> On 12/3/20 10:26 PM, Maximilian Luz wrote:
>> Add a misc-device providing user-space access to the Surface Aggregator
>> EC, mainly intended for debugging, testing, and reverse-engineering.
>> This interface gives user-space applications the ability to send
>> requests to the EC and receive the corresponding responses.
>>
>> The device-file is managed by a pseudo platform-device and corresponding
>> driver to avoid dependence on the dedicated bus, allowing it to be
>> loaded in a minimal configuration.
>>
>> Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
> 
> 1 review comment inline:
>

[...]

>> +static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg)
>> +{
>> +	struct ssam_cdev_request __user *r;
>> +	struct ssam_cdev_request rqst;
>> +	struct ssam_request spec;
>> +	struct ssam_response rsp;
>> +	const void __user *plddata;
>> +	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;
>> +
>> +	plddata = u64_to_user_ptr(rqst.payload.data);
>> +	rspdata = u64_to_user_ptr(rqst.response.data);
>> +
>> +	/* Setup basic request fields. */
>> +	spec.target_category = rqst.target_category;
>> +	spec.target_id = rqst.target_id;
>> +	spec.command_id = rqst.command_id;
>> +	spec.instance_id = rqst.instance_id;
>> +	spec.flags = rqst.flags;
>> +	spec.length = rqst.payload.length;
>> +	spec.payload = NULL;
>> +
>> +	rsp.capacity = rqst.response.length;
>> +	rsp.length = 0;
>> +	rsp.pointer = NULL;
>> +
>> +	/* Get request payload from user-space. */
>> +	if (spec.length) {
>> +		if (!plddata) {
>> +			ret = -EINVAL;
>> +			goto out;
>> +		}
>> +
>> +		spec.payload = kzalloc(spec.length, GFP_KERNEL);
>> +		if (!spec.payload) {
>> +			status = -ENOMEM;
>> +			ret = -EFAULT;
>> +			goto out;
>> +		}
>> +
>> +		if (copy_from_user((void *)spec.payload, plddata, spec.length)) {
>> +			ret = -EFAULT;
>> +			goto out;
>> +		}
>> +	}
>> +
>> +	/* Allocate response buffer. */
>> +	if (rsp.capacity) {
>> +		if (!rspdata) {
>> +			ret = -EINVAL;
>> +			goto out;
>> +		}
>> +
>> +		rsp.pointer = kzalloc(rsp.capacity, GFP_KERNEL);
>> +		if (!rsp.pointer) {
>> +			status = -ENOMEM;
>> +			ret = -EFAULT;
> 
> This is weird, -EFAULT should only be used if a SEGFAULT
> would have been raised if the code was running in
> userspace rather then in kernelspace, IOW if userspace
> has provided an invalid pointer (or a too small buffer,
> causing the pointer to become invalid at some point in
> the buffer).

Oh, right.

> IMHO you should simply do ret = -ENOMEM here.

Yes. that looks better. I will change that as suggested.
> Otherwise this looks good to me.

Thanks,
Max

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2020-12-15 20:01 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-03 21:26 [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module Maximilian Luz
2020-12-03 21:26 ` [PATCH v2 7/9] docs: driver-api: Add Surface Aggregator subsystem documentation Maximilian Luz
2020-12-15 16:25   ` Hans de Goede
2020-12-03 21:26 ` [PATCH v2 8/9] platform/surface: Add Surface Aggregator user-space interface Maximilian Luz
2020-12-15 16:35   ` Hans de Goede
2020-12-15 20:00     ` Maximilian Luz
2020-12-03 21:26 ` [PATCH v2 9/9] platform/surface: Add Surface ACPI Notify driver Maximilian Luz
2020-12-15 17:18   ` Hans de Goede
2020-12-06  7:07 ` [PATCH v2 0/9] Add support for Microsoft Surface System Aggregator Module Leon Romanovsky
2020-12-06  8:32   ` Greg Kroah-Hartman
2020-12-06  8:35     ` Leon Romanovsky
2020-12-06 11:13     ` Maximilian Luz
2020-12-06  8:41   ` Hans de Goede
2020-12-06  8:56     ` Leon Romanovsky
2020-12-06 10:04       ` Hans de Goede
2020-12-06 10:33         ` Leon Romanovsky
2020-12-06 10:41           ` Hans de Goede
2020-12-06 11:41             ` Leon Romanovsky
2020-12-06 13:43               ` Maximilian Luz
2020-12-06 10:51         ` Maximilian Luz
2020-12-06  8:58     ` Blaž Hrastnik
2020-12-06  9:06       ` Leon Romanovsky
2020-12-06 10:33         ` Maximilian Luz
2020-12-06 10:43           ` Hans de Goede
2020-12-06 10:56             ` Maximilian Luz
2020-12-06 11:30           ` Leon Romanovsky
2020-12-06 13:27             ` Maximilian Luz
2020-12-06 15:58   ` Maximilian Luz
2020-12-07  6:15     ` Leon Romanovsky
2020-12-07  8:49     ` Hans de Goede
2020-12-07  9:12       ` Greg Kroah-Hartman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).