brintos

brintos / linux-shallow public Read only

0
0
Text · 17.2 KiB · e100ab0 Raw
398 lines · plain
1.. SPDX-License-Identifier: GPL-2.0+2 3.. |ssam_controller| replace:: :c:type:`struct ssam_controller <ssam_controller>`4.. |ssam_device| replace:: :c:type:`struct ssam_device <ssam_device>`5.. |ssam_device_driver| replace:: :c:type:`struct ssam_device_driver <ssam_device_driver>`6.. |ssam_client_bind| replace:: :c:func:`ssam_client_bind`7.. |ssam_client_link| replace:: :c:func:`ssam_client_link`8.. |ssam_get_controller| replace:: :c:func:`ssam_get_controller`9.. |ssam_controller_get| replace:: :c:func:`ssam_controller_get`10.. |ssam_controller_put| replace:: :c:func:`ssam_controller_put`11.. |ssam_device_alloc| replace:: :c:func:`ssam_device_alloc`12.. |ssam_device_add| replace:: :c:func:`ssam_device_add`13.. |ssam_device_remove| replace:: :c:func:`ssam_device_remove`14.. |ssam_device_driver_register| replace:: :c:func:`ssam_device_driver_register`15.. |ssam_device_driver_unregister| replace:: :c:func:`ssam_device_driver_unregister`16.. |module_ssam_device_driver| replace:: :c:func:`module_ssam_device_driver`17.. |SSAM_DEVICE| replace:: :c:func:`SSAM_DEVICE`18.. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register`19.. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister`20.. |ssam_device_notifier_register| replace:: :c:func:`ssam_device_notifier_register`21.. |ssam_device_notifier_unregister| replace:: :c:func:`ssam_device_notifier_unregister`22.. |ssam_request_do_sync| replace:: :c:func:`ssam_request_do_sync`23.. |ssam_event_mask| replace:: :c:type:`enum ssam_event_mask <ssam_event_mask>`24 25 26======================27Writing Client Drivers28======================29 30For the API documentation, refer to:31 32.. toctree::33   :maxdepth: 234 35   client-api36 37 38Overview39========40 41Client drivers can be set up in two main ways, depending on how the42corresponding device is made available to the system. We specifically43differentiate between devices that are presented to the system via one of44the conventional ways, e.g. as platform devices via ACPI, and devices that45are non-discoverable and instead need to be explicitly provided by some46other mechanism, as discussed further below.47 48 49Non-SSAM Client Drivers50=======================51 52All communication with the SAM EC is handled via the |ssam_controller|53representing that EC to the kernel. Drivers targeting a non-SSAM device (and54thus not being a |ssam_device_driver|) need to explicitly establish a55connection/relation to that controller. This can be done via the56|ssam_client_bind| function. Said function returns a reference to the SSAM57controller, but, more importantly, also establishes a device link between58client device and controller (this can also be done separate via59|ssam_client_link|). It is important to do this, as it, first, guarantees60that the returned controller is valid for use in the client driver for as61long as this driver is bound to its device, i.e. that the driver gets62unbound before the controller ever becomes invalid, and, second, as it63ensures correct suspend/resume ordering. This setup should be done in the64driver's probe function, and may be used to defer probing in case the SSAM65subsystem is not ready yet, for example:66 67.. code-block:: c68 69   static int client_driver_probe(struct platform_device *pdev)70   {71           struct ssam_controller *ctrl;72 73           ctrl = ssam_client_bind(&pdev->dev);74           if (IS_ERR(ctrl))75                   return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);76 77           // ...78 79           return 0;80   }81 82The controller may be separately obtained via |ssam_get_controller| and its83lifetime be guaranteed via |ssam_controller_get| and |ssam_controller_put|.84Note that none of these functions, however, guarantee that the controller85will not be shut down or suspended. These functions essentially only operate86on the reference, i.e. only guarantee a bare minimum of accessibility87without any guarantees at all on practical operability.88 89 90Adding SSAM Devices91===================92 93If a device does not already exist/is not already provided via conventional94means, it should be provided as |ssam_device| via the SSAM client device95hub. New devices can be added to this hub by entering their UID into the96corresponding registry. SSAM devices can also be manually allocated via97|ssam_device_alloc|, subsequently to which they have to be added via98|ssam_device_add| and eventually removed via |ssam_device_remove|. By99default, the parent of the device is set to the controller device provided100for allocation, however this may be changed before the device is added. Note101that, when changing the parent device, care must be taken to ensure that the102controller lifetime and suspend/resume ordering guarantees, in the default103setup provided through the parent-child relation, are preserved. If104necessary, by use of |ssam_client_link| as is done for non-SSAM client105drivers and described in more detail above.106 107A client device must always be removed by the party which added the108respective device before the controller shuts down. Such removal can be109guaranteed by linking the driver providing the SSAM device to the controller110via |ssam_client_link|, causing it to unbind before the controller driver111unbinds. Client devices registered with the controller as parent are112automatically removed when the controller shuts down, but this should not be113relied upon, especially as this does not extend to client devices with a114different parent.115 116 117SSAM Client Drivers118===================119 120SSAM client device drivers are, in essence, no different than other device121driver types. They are represented via |ssam_device_driver| and bind to a122|ssam_device| via its UID (:c:type:`struct ssam_device.uid <ssam_device>`)123member and the match table124(:c:type:`struct ssam_device_driver.match_table <ssam_device_driver>`),125which should be set when declaring the driver struct instance. Refer to the126|SSAM_DEVICE| macro documentation for more details on how to define members127of the driver's match table.128 129The UID for SSAM client devices consists of a ``domain``, a ``category``,130a ``target``, an ``instance``, and a ``function``. The ``domain`` is used131differentiate between physical SAM devices132(:c:type:`SSAM_DOMAIN_SERIALHUB <ssam_device_domain>`), i.e. devices that can133be accessed via the Surface Serial Hub, and virtual ones134(:c:type:`SSAM_DOMAIN_VIRTUAL <ssam_device_domain>`), such as client-device135hubs, that have no real representation on the SAM EC and are solely used on136the kernel/driver-side. For physical devices, ``category`` represents the137target category, ``target`` the target ID, and ``instance`` the instance ID138used to access the physical SAM device. In addition, ``function`` references139a specific device functionality, but has no meaning to the SAM EC. The140(default) name of a client device is generated based on its UID.141 142A driver instance can be registered via |ssam_device_driver_register| and143unregistered via |ssam_device_driver_unregister|. For convenience, the144|module_ssam_device_driver| macro may be used to define module init- and145exit-functions registering the driver.146 147The controller associated with a SSAM client device can be found in its148:c:type:`struct ssam_device.ctrl <ssam_device>` member. This reference is149guaranteed to be valid for at least as long as the client driver is bound,150but should also be valid for as long as the client device exists. Note,151however, that access outside of the bound client driver must ensure that the152controller device is not suspended while making any requests or153(un-)registering event notifiers (and thus should generally be avoided). This154is guaranteed when the controller is accessed from inside the bound client155driver.156 157 158Making Synchronous Requests159===========================160 161Synchronous requests are (currently) the main form of host-initiated162communication with the EC. There are a couple of ways to define and execute163such requests, however, most of them boil down to something similar as shown164in the example below. This example defines a write-read request, meaning165that the caller provides an argument to the SAM EC and receives a response.166The caller needs to know the (maximum) length of the response payload and167provide a buffer for it.168 169Care must be taken to ensure that any command payload data passed to the SAM170EC is provided in little-endian format and, similarly, any response payload171data received from it is converted from little-endian to host endianness.172 173.. code-block:: c174 175   int perform_request(struct ssam_controller *ctrl, u32 arg, u32 *ret)176   {177           struct ssam_request rqst;178           struct ssam_response resp;179           int status;180 181           /* Convert request argument to little-endian. */182           __le32 arg_le = cpu_to_le32(arg);183           __le32 ret_le = cpu_to_le32(0);184 185           /*186            * Initialize request specification. Replace this with your values.187            * The rqst.payload field may be NULL if rqst.length is zero,188            * indicating that the request does not have any argument.189            *190            * Note: The request parameters used here are not valid, i.e.191            *       they do not correspond to an actual SAM/EC request.192            */193           rqst.target_category = SSAM_SSH_TC_SAM;194           rqst.target_id = SSAM_SSH_TID_SAM;195           rqst.command_id = 0x02;196           rqst.instance_id = 0x03;197           rqst.flags = SSAM_REQUEST_HAS_RESPONSE;198           rqst.length = sizeof(arg_le);199           rqst.payload = (u8 *)&arg_le;200 201           /* Initialize request response. */202           resp.capacity = sizeof(ret_le);203           resp.length = 0;204           resp.pointer = (u8 *)&ret_le;205 206           /*207            * Perform actual request. The response pointer may be null in case208            * the request does not have any response. This must be consistent209            * with the SSAM_REQUEST_HAS_RESPONSE flag set in the specification210            * above.211            */212           status = ssam_request_do_sync(ctrl, &rqst, &resp);213 214           /*215            * Alternatively use216            *217            *   ssam_request_do_sync_onstack(ctrl, &rqst, &resp, sizeof(arg_le));218            *219            * to perform the request, allocating the message buffer directly220            * on the stack as opposed to allocation via kzalloc().221            */222 223           /*224            * Convert request response back to native format. Note that in the225            * error case, this value is not touched by the SSAM core, i.e.226            * 'ret_le' will be zero as specified in its initialization.227            */228           *ret = le32_to_cpu(ret_le);229 230           return status;231   }232 233Note that |ssam_request_do_sync| in its essence is a wrapper over lower-level234request primitives, which may also be used to perform requests. Refer to its235implementation and documentation for more details.236 237An arguably more user-friendly way of defining such functions is by using238one of the generator macros, for example via:239 240.. code-block:: c241 242   SSAM_DEFINE_SYNC_REQUEST_W(__ssam_tmp_perf_mode_set, __le32, {243           .target_category = SSAM_SSH_TC_TMP,244           .target_id       = SSAM_SSH_TID_SAM,245           .command_id      = 0x03,246           .instance_id     = 0x00,247   });248 249This example defines a function250 251.. code-block:: c252 253   static int __ssam_tmp_perf_mode_set(struct ssam_controller *ctrl, const __le32 *arg);254 255executing the specified request, with the controller passed in when calling256said function. In this example, the argument is provided via the ``arg``257pointer. Note that the generated function allocates the message buffer on258the stack. Thus, if the argument provided via the request is large, these259kinds of macros should be avoided. Also note that, in contrast to the260previous non-macro example, this function does not do any endianness261conversion, which has to be handled by the caller. Apart from those262differences the function generated by the macro is similar to the one263provided in the non-macro example above.264 265The full list of such function-generating macros is266 267- :c:func:`SSAM_DEFINE_SYNC_REQUEST_N` for requests without return value and268  without argument.269- :c:func:`SSAM_DEFINE_SYNC_REQUEST_R` for requests with return value but no270  argument.271- :c:func:`SSAM_DEFINE_SYNC_REQUEST_W` for requests without return value but272  with argument.273 274Refer to their respective documentation for more details. For each one of275these macros, a special variant is provided, which targets request types276applicable to multiple instances of the same device type:277 278- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_N`279- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_R`280- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_W`281 282The difference of those macros to the previously mentioned versions is, that283the device target and instance IDs are not fixed for the generated function,284but instead have to be provided by the caller of said function.285 286Additionally, variants for direct use with client devices, i.e.287|ssam_device|, are also provided. These can, for example, be used as288follows:289 290.. code-block:: c291 292   SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_sta, __le32, {293           .target_category = SSAM_SSH_TC_BAT,294           .command_id      = 0x01,295   });296 297This invocation of the macro defines a function298 299.. code-block:: c300 301   static int ssam_bat_get_sta(struct ssam_device *sdev, __le32 *ret);302 303executing the specified request, using the device IDs and controller given304in the client device. The full list of such macros for client devices is:305 306- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_N`307- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_R`308- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_W`309 310 311Handling Events312===============313 314To receive events from the SAM EC, an event notifier must be registered for315the desired event via |ssam_notifier_register|. The notifier must be316unregistered via |ssam_notifier_unregister| once it is not required any317more. For |ssam_device| type clients, the |ssam_device_notifier_register| and318|ssam_device_notifier_unregister| wrappers should be preferred as they properly319handle hot-removal of client devices.320 321Event notifiers are registered by providing (at minimum) a callback to call322in case an event has been received, the registry specifying how the event323should be enabled, an event ID specifying for which target category and,324optionally and depending on the registry used, for which instance ID events325should be enabled, and finally, flags describing how the EC will send these326events. If the specific registry does not enable events by instance ID, the327instance ID must be set to zero. Additionally, a priority for the respective328notifier may be specified, which determines its order in relation to any329other notifier registered for the same target category.330 331By default, event notifiers will receive all events for the specific target332category, regardless of the instance ID specified when registering the333notifier. The core may be instructed to only call a notifier if the target334ID or instance ID (or both) of the event match the ones implied by the335notifier IDs (in case of target ID, the target ID of the registry), by336providing an event mask (see |ssam_event_mask|).337 338In general, the target ID of the registry is also the target ID of the339enabled event (with the notable exception being keyboard input events on the340Surface Laptop 1 and 2, which are enabled via a registry with target ID 1,341but provide events with target ID 2).342 343A full example for registering an event notifier and handling received344events is provided below:345 346.. code-block:: c347 348   u32 notifier_callback(struct ssam_event_notifier *nf,349                         const struct ssam_event *event)350   {351           int status = ...352 353           /* Handle the event here ... */354 355           /* Convert return value and indicate that we handled the event. */356           return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED;357   }358 359   int setup_notifier(struct ssam_device *sdev,360                      struct ssam_event_notifier *nf)361   {362           /* Set priority wrt. other handlers of same target category. */363           nf->base.priority = 1;364 365           /* Set event/notifier callback. */366           nf->base.fn = notifier_callback;367 368           /* Specify event registry, i.e. how events get enabled/disabled. */369           nf->event.reg = SSAM_EVENT_REGISTRY_KIP;370 371           /* Specify which event to enable/disable */372           nf->event.id.target_category = sdev->uid.category;373           nf->event.id.instance = sdev->uid.instance;374 375           /*376            * Specify for which events the notifier callback gets executed.377            * This essentially tells the core if it can skip notifiers that378            * don't have target or instance IDs matching those of the event.379            */380           nf->event.mask = SSAM_EVENT_MASK_STRICT;381 382           /* Specify event flags. */383           nf->event.flags = SSAM_EVENT_SEQUENCED;384 385           return ssam_notifier_register(sdev->ctrl, nf);386   }387 388Multiple event notifiers can be registered for the same event. The event389handler core takes care of enabling and disabling events when notifiers are390registered and unregistered, by keeping track of how many notifiers for a391specific event (combination of registry, event target category, and event392instance ID) are currently registered. This means that a specific event will393be enabled when the first notifier for it is being registered and disabled394when the last notifier for it is being unregistered. Note that the event395flags are therefore only used on the first registered notifier, however, one396should take care that notifiers for a specific event are always registered397with the same flag and it is considered a bug to do otherwise.398