Re: [PATCH 1/9] platform/surface: Add Surface Aggregator subsystem

From: Andy Shevchenko
Date: Mon Nov 16 2020 - 08:33:04 EST


On Sun, Nov 15, 2020 at 9:25 PM Maximilian Luz <luzmaximilian@xxxxxxxxx> wrote:
>
> Add Surface System Aggregator Module core and Surface Serial Hub driver,
> required for the embedded controller found on Microsoft Surface devices.
>
> The Surface System Aggregator Module (SSAM, SAM or Surface Aggregator)
> is an embedded controller (EC) found on 4th and later generation
> Microsoft Surface devices, with the exception of the Surface Go series.
> This EC provides various functionality, depending on the device in
> question. This can include battery status and thermal reporting (5th and
> later generations), but also HID keyboard (6th+) and touchpad input
> (7th+) on Surface Laptop and Surface Book 3 series devices.
>
> This patch provides the basic necessities for communication with the SAM
> EC on 5th and later generation devices. On these devices, the EC
> provides an interface that acts as serial device, called the Surface
> Serial Hub (SSH). 4th generation devices, on which the EC interface is
> provided via an HID-over-I2C device, are not supported by this patch.
>
> Specifically, this patch adds a driver for the SSH device (device HID
> MSHW0084 in ACPI), as well as a controller structure and associated API.
> This represents the functional core of the Surface Aggregator kernel
> subsystem, introduced with this patch, and will be expanded upon in
> subsequent commits.
>
> The SSH driver acts as the main attachment point for this subsystem and
> sets-up and manages the controller structure. The controller in turn
> provides a basic communication interface, allowing to send requests from
> host to EC and receiving the corresponding responses, as well as
> managing and receiving events, sent from EC to host. It is structured
> into multiple layers, with the top layer presenting the API used by
> other kernel drivers and the lower layers modeled after the serial
> protocol used for communication.
>
> Said other drivers are then responsible for providing the (Surface model
> specific) functionality accessible through the EC (e.g. battery status
> reporting, thermal information, ...) via said controller structure and
> API, and will be added in future commits.

...

> +menuconfig SURFACE_AGGREGATOR
> + tristate "Microsoft Surface System Aggregator Module Subsystem and Drivers"
> + depends on SERIAL_DEV_BUS
> + depends on ACPI
> + select CRC_CCITT
> + help
> + The Surface System Aggregator Module (Surface SAM or SSAM) is an
> + embedded controller (EC) found on 5th- and later-generation Microsoft
> + Surface devices (i.e. Surface Pro 5, Surface Book 2, Surface Laptop,
> + and newer, with exception of Surface Go series devices).
> +
> + Depending on the device in question, this EC provides varying
> + functionality, including:
> + - EC access from ACPI via Surface ACPI Notify (5th- and 6th-generation)
> + - battery status information (all devices)
> + - thermal sensor access (all devices)
> + - performance mode / cooling mode control (all devices)
> + - clipboard detachment system control (Surface Book 2 and 3)
> + - HID / keyboard input (Surface Laptops, Surface Book 3)
> +
> + This option controls whether the Surface SAM subsystem core will be
> + built. This includes a driver for the Surface Serial Hub (SSH), which
> + is the device responsible for the communication with the EC, and a
> + basic kernel interface exposing the EC functionality to other client
> + drivers, i.e. allowing them to make requests to the EC and receive
> + events from it. Selecting this option alone will not provide any
> + client drivers and therefore no functionality beyond the in-kernel
> + interface. Said functionality is the responsibility of the respective
> + client drivers.
> +
> + Note: While 4th-generation Surface devices also make use of a SAM EC,
> + due to a difference in the communication interface of the controller,
> + only 5th and later generations are currently supported. Specifically,
> + devices using SAM-over-SSH are supported, whereas devices using
> + SAM-over-HID, which is used on the 4th generation, are currently not
> + supported.

>From this help text I didn't get if it will be a module or what if I
chose the above?

...

> +/* -- Safe counters. -------------------------------------------------------- */

Why can't XArray be used here?

...

> +
> +

One blank line is enough

...

> +static bool ssam_event_matches_notifier(
> + const struct ssam_event_notifier *notif,
> + const struct ssam_event *event)

Perhaps

static bool
ssam_event_matches_notifier(const struct ssam_event_notifier *n,
const struct ssam_event *event)

(or even switch to 100 limit, also note notif ->n — no need to repeat same word)

...

> + nb = rcu_dereference_raw(nh->head);
> + while (nb) {
> + nf = container_of(nb, struct ssam_event_notifier, base);
> + next_nb = rcu_dereference_raw(nb->next);
> +
> + if (ssam_event_matches_notifier(nf, event)) {
> + ret = (ret & SSAM_NOTIF_STATE_MASK) | nb->fn(nf, event);
> + if (ret & SSAM_NOTIF_STOP)
> + break;

The returned value is a bitmask?!
What are you returning at the end?

> + }
> +
> + nb = next_nb;
> + }

...

> +static int __ssam_nfblk_insert(struct ssam_nf_head *nh, struct ssam_notifier_block *nb)
> +{
> + struct ssam_notifier_block **link = &nh->head;
> +
> + while ((*link) != NULL) {
> + if (unlikely((*link) == nb)) {
> + WARN(1, "double register detected");
> + return -EINVAL;
> + }
> +
> + if (nb->priority > (*link)->priority)
> + break;
> +
> + link = &((*link)->next);
> + }
> +
> + nb->next = *link;
> + rcu_assign_pointer(*link, nb);
> +
> + return 0;
> +}

If you need RCU (which is also the Q per se), why not use RCU list?

...

> + while ((*link) != NULL) {

Redundant parentheses, redundant ' != NULL' part.

> + if ((*link) == nb)
> + return link;
> +
> + link = &((*link)->next);
> + }

...

> + * struct ssam_nf_refcount_entry - RB-tree entry for referecnce counting event

In above you mistyped 'which' as 'whic' or so, and here reference.
Perhaps go thru spell checker?

...

> + * registered, or ``ERR_PTR(-ENOMEM)`` if the entry could not be allocated.

Better to spell out "error pointer" instead of cryptic ERR_PTR().

...

> + * Executa registered callbacks in order of their priority until either no
> + * callback is left or a callback returned a value with the %SSAM_NOTIF_STOP

returns

> + * bit set. Note that this bit is set automatically when converting non.zero
> + * error values via ssam_notifier_from_errno() to notifier values.

...

> + for (i = i - 1; i >= 0; i--)

while (i--)

> + ssam_nf_head_destroy(&nf->head[i]);

...

> + // limit number of processed events to avoid livelocking
> + for (i = 0; i < 10; i++) {

Magic number! Also, this will be better to read in a form of

unsigned int iterations = 10;

do {
...
} while (--iterations);

> + item = ssam_event_queue_pop(queue);
> + if (item == NULL)
> + return;
> +
> + ssam_nf_call(nf, dev, item->rqid, &item->event);
> + kfree(item);
> + }

...

> +static const guid_t SSAM_SSH_DSM_GUID = GUID_INIT(0xd5e383e1, 0xd892, 0x4a76,
> + 0x89, 0xfc, 0xf6, 0xaa, 0xae, 0x7e, 0xd5, 0xb5);

Can you use usual pattern for these UIDs, like
static const guid_t SSAM_SSH_DSM_GUID =
GUID_INIT(0xd5e383e1, 0xd892, 0x4a76,
0x89, 0xfc, 0xf6, 0xaa, 0xae, 0x7e, 0xd5, 0xb5);
?

Also put a comment how this UID will look like in a string representation.

...

> + if (!acpi_has_method(handle, "_DSM"))
> + return 0;

Hmm... What's the expectation?

> + obj = acpi_evaluate_dsm_typed(handle, &SSAM_SSH_DSM_GUID,
> + SSAM_SSH_DSM_REVISION, 0, NULL,
> + ACPI_TYPE_BUFFER);
> + if (!obj)
> + return -EFAULT;

EFAULT?! Perhaps you can simply return 0 here, no?

> + for (i = 0; i < obj->buffer.length && i < 8; i++)
> + mask |= (((u64)obj->buffer.pointer[i]) << (i * 8));

Don't we have some helpers for this? At least I remember similar code
went to one of PDx86 drivers like intel-vbtn or so.

> + if (mask & 0x01)

BIT(0) ?

> + *funcs = mask;

...

> + caps->ssh_power_profile = (u32)-1;
> + caps->screen_on_sleep_idle_timeout = (u32)-1;
> + caps->screen_off_sleep_idle_timeout = (u32)-1;
> + caps->d3_closes_handle = false;
> + caps->ssh_buffer_size = (u32)-1;

Use proper types and their limits (limits.h missed?).

...

> + // initialize request and packet transport layers

Inconsistent style of comments.

...

> + * In the course of this shutdown procedure, all currently registered
> + * notifiers will be unregistered. It is, however, strongly recommended to not
> + * rely on this behavior, and instead the party registring the notifier should

registering

> + * unregister it before the controller gets shut down, e.g. via the SSAM bus
> + * which guarantees client devices to be removed before a shutdown.

> + * Note that events may still be pending after this call, but due to the
> + * notifiers being unregistered, the will be dropped when the controller is

the?!

> + * subsequently being destroyed via ssam_controller_destroy().

...

> + * Ensures that all resources associated with the controller get freed. This
> + * function should only be called after the controller has been stopped via
> + * ssam_controller_shutdown(). In general, this function should not be called
> + * directly. The only valid place to call this function direclty is during

directly

> + * initialization, before the controller has been fully initialized and passed
> + * to other processes. This function is called automatically when the
> + * reference count of the controller reaches zero.

...

> + * ssam_request_sync_free() - Free a synchronous request.
> + * @rqst: The request to free.

to be freed?

...

> + * Allocates a synchronous request struct on the stack, fully initializes it
> + * using the provided buffer as message data buffer, submits it, and then
> + * waits for its completion before returning its staus. The

status

> + * SSH_COMMAND_MESSAGE_LENGTH() macro can be used to compute the required
> + * message buffer size.

...

> + * This is a wrapper for the raw SAM request to enable an event, thus it does
> + * not handle referecnce counting for enable/disable of events. If an event
> + * has already been enabled, the EC will ignore this request.

Grammar and English language style somehow feels not okay.

...

> + u8 buf[1] = { 0x00 };

Can't be simply buf ?

...

> + * This function will only send the display-off notification command if
> + * display noticications are supported by the EC. Currently all known devices
> + * support these notification.

Spell check!

...

> + * This function will only send the display-on notification command if display
> + * noticications are supported by the EC. Currently all known devices support
> + * these notification.

Ditto.

...

> + ssam_err(ctrl, "unexpected response from D0-exit notification:"
> + " 0x%02x\n", response);

Don't split string literals. Had you run a checkpatch?
Please do and use strict mode.


...

Overall impression of this submission:
- it's quite huge as for a single patch
- it feels like quite an overengineered solution: READ_ONCE, RCU,
list + spin_lock, RB tree of notifiers, my head!
- where is the architectural document of all these?

--
With Best Regards,
Andy Shevchenko