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_request_sync| replace:: :c:func:`ssam_request_sync` 21.. |ssam_event_mask| replace:: :c:type:`enum ssam_event_mask <ssam_event_mask>` 22 23 24====================== 25Writing Client Drivers 26====================== 27 28For the API documentation, refer to: 29 30.. toctree:: 31 :maxdepth: 2 32 33 client-api 34 35 36Overview 37======== 38 39Client drivers can be set up in two main ways, depending on how the 40corresponding device is made available to the system. We specifically 41differentiate between devices that are presented to the system via one of 42the conventional ways, e.g. as platform devices via ACPI, and devices that 43are non-discoverable and instead need to be explicitly provided by some 44other mechanism, as discussed further below. 45 46 47Non-SSAM Client Drivers 48======================= 49 50All communication with the SAM EC is handled via the |ssam_controller| 51representing that EC to the kernel. Drivers targeting a non-SSAM device (and 52thus not being a |ssam_device_driver|) need to explicitly establish a 53connection/relation to that controller. This can be done via the 54|ssam_client_bind| function. Said function returns a reference to the SSAM 55controller, but, more importantly, also establishes a device link between 56client device and controller (this can also be done separate via 57|ssam_client_link|). It is important to do this, as it, first, guarantees 58that the returned controller is valid for use in the client driver for as 59long as this driver is bound to its device, i.e. that the driver gets 60unbound before the controller ever becomes invalid, and, second, as it 61ensures correct suspend/resume ordering. This setup should be done in the 62driver's probe function, and may be used to defer probing in case the SSAM 63subsystem is not ready yet, for example: 64 65.. code-block:: c 66 67 static int client_driver_probe(struct platform_device *pdev) 68 { 69 struct ssam_controller *ctrl; 70 71 ctrl = ssam_client_bind(&pdev->dev); 72 if (IS_ERR(ctrl)) 73 return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl); 74 75 // ... 76 77 return 0; 78 } 79 80The controller may be separately obtained via |ssam_get_controller| and its 81lifetime be guaranteed via |ssam_controller_get| and |ssam_controller_put|. 82Note that none of these functions, however, guarantee that the controller 83will not be shut down or suspended. These functions essentially only operate 84on the reference, i.e. only guarantee a bare minimum of accessibility 85without any guarantees at all on practical operability. 86 87 88Adding SSAM Devices 89=================== 90 91If a device does not already exist/is not already provided via conventional 92means, it should be provided as |ssam_device| via the SSAM client device 93hub. New devices can be added to this hub by entering their UID into the 94corresponding registry. SSAM devices can also be manually allocated via 95|ssam_device_alloc|, subsequently to which they have to be added via 96|ssam_device_add| and eventually removed via |ssam_device_remove|. By 97default, the parent of the device is set to the controller device provided 98for allocation, however this may be changed before the device is added. Note 99that, when changing the parent device, care must be taken to ensure that the 100controller lifetime and suspend/resume ordering guarantees, in the default 101setup provided through the parent-child relation, are preserved. If 102necessary, by use of |ssam_client_link| as is done for non-SSAM client 103drivers and described in more detail above. 104 105A client device must always be removed by the party which added the 106respective device before the controller shuts down. Such removal can be 107guaranteed by linking the driver providing the SSAM device to the controller 108via |ssam_client_link|, causing it to unbind before the controller driver 109unbinds. Client devices registered with the controller as parent are 110automatically removed when the controller shuts down, but this should not be 111relied upon, especially as this does not extend to client devices with a 112different parent. 113 114 115SSAM Client Drivers 116=================== 117 118SSAM client device drivers are, in essence, no different than other device 119driver types. They are represented via |ssam_device_driver| and bind to a 120|ssam_device| via its UID (:c:type:`struct ssam_device.uid <ssam_device>`) 121member and the match table 122(:c:type:`struct ssam_device_driver.match_table <ssam_device_driver>`), 123which should be set when declaring the driver struct instance. Refer to the 124|SSAM_DEVICE| macro documentation for more details on how to define members 125of the driver's match table. 126 127The UID for SSAM client devices consists of a ``domain``, a ``category``, 128a ``target``, an ``instance``, and a ``function``. The ``domain`` is used 129differentiate between physical SAM devices 130(:c:type:`SSAM_DOMAIN_SERIALHUB <ssam_device_domain>`), i.e. devices that can 131be accessed via the Surface Serial Hub, and virtual ones 132(:c:type:`SSAM_DOMAIN_VIRTUAL <ssam_device_domain>`), such as client-device 133hubs, that have no real representation on the SAM EC and are solely used on 134the kernel/driver-side. For physical devices, ``category`` represents the 135target category, ``target`` the target ID, and ``instance`` the instance ID 136used to access the physical SAM device. In addition, ``function`` references 137a specific device functionality, but has no meaning to the SAM EC. The 138(default) name of a client device is generated based on its UID. 139 140A driver instance can be registered via |ssam_device_driver_register| and 141unregistered via |ssam_device_driver_unregister|. For convenience, the 142|module_ssam_device_driver| macro may be used to define module init- and 143exit-functions registering the driver. 144 145The controller associated with a SSAM client device can be found in its 146:c:type:`struct ssam_device.ctrl <ssam_device>` member. This reference is 147guaranteed to be valid for at least as long as the client driver is bound, 148but should also be valid for as long as the client device exists. Note, 149however, that access outside of the bound client driver must ensure that the 150controller device is not suspended while making any requests or 151(un-)registering event notifiers (and thus should generally be avoided). This 152is guaranteed when the controller is accessed from inside the bound client 153driver. 154 155 156Making Synchronous Requests 157=========================== 158 159Synchronous requests are (currently) the main form of host-initiated 160communication with the EC. There are a couple of ways to define and execute 161such requests, however, most of them boil down to something similar as shown 162in the example below. This example defines a write-read request, meaning 163that the caller provides an argument to the SAM EC and receives a response. 164The caller needs to know the (maximum) length of the response payload and 165provide a buffer for it. 166 167Care must be taken to ensure that any command payload data passed to the SAM 168EC is provided in little-endian format and, similarly, any response payload 169data received from it is converted from little-endian to host endianness. 170 171.. code-block:: c 172 173 int perform_request(struct ssam_controller *ctrl, u32 arg, u32 *ret) 174 { 175 struct ssam_request rqst; 176 struct ssam_response resp; 177 int status; 178 179 /* Convert request argument to little-endian. */ 180 __le32 arg_le = cpu_to_le32(arg); 181 __le32 ret_le = cpu_to_le32(0); 182 183 /* 184 * Initialize request specification. Replace this with your values. 185 * The rqst.payload field may be NULL if rqst.length is zero, 186 * indicating that the request does not have any argument. 187 * 188 * Note: The request parameters used here are not valid, i.e. 189 * they do not correspond to an actual SAM/EC request. 190 */ 191 rqst.target_category = SSAM_SSH_TC_SAM; 192 rqst.target_id = 0x01; 193 rqst.command_id = 0x02; 194 rqst.instance_id = 0x03; 195 rqst.flags = SSAM_REQUEST_HAS_RESPONSE; 196 rqst.length = sizeof(arg_le); 197 rqst.payload = (u8 *)&arg_le; 198 199 /* Initialize request response. */ 200 resp.capacity = sizeof(ret_le); 201 resp.length = 0; 202 resp.pointer = (u8 *)&ret_le; 203 204 /* 205 * Perform actual request. The response pointer may be null in case 206 * the request does not have any response. This must be consistent 207 * with the SSAM_REQUEST_HAS_RESPONSE flag set in the specification 208 * above. 209 */ 210 status = ssam_request_sync(ctrl, &rqst, &resp); 211 212 /* 213 * Alternatively use 214 * 215 * ssam_request_sync_onstack(ctrl, &rqst, &resp, sizeof(arg_le)); 216 * 217 * to perform the request, allocating the message buffer directly 218 * on the stack as opposed to allocation via kzalloc(). 219 */ 220 221 /* 222 * Convert request response back to native format. Note that in the 223 * error case, this value is not touched by the SSAM core, i.e. 224 * 'ret_le' will be zero as specified in its initialization. 225 */ 226 *ret = le32_to_cpu(ret_le); 227 228 return status; 229 } 230 231Note that |ssam_request_sync| in its essence is a wrapper over lower-level 232request primitives, which may also be used to perform requests. Refer to its 233implementation and documentation for more details. 234 235An arguably more user-friendly way of defining such functions is by using 236one of the generator macros, for example via: 237 238.. code-block:: c 239 240 SSAM_DEFINE_SYNC_REQUEST_W(__ssam_tmp_perf_mode_set, __le32, { 241 .target_category = SSAM_SSH_TC_TMP, 242 .target_id = 0x01, 243 .command_id = 0x03, 244 .instance_id = 0x00, 245 }); 246 247This example defines a function 248 249.. code-block:: c 250 251 static int __ssam_tmp_perf_mode_set(struct ssam_controller *ctrl, const __le32 *arg); 252 253executing the specified request, with the controller passed in when calling 254said function. In this example, the argument is provided via the ``arg`` 255pointer. Note that the generated function allocates the message buffer on 256the stack. Thus, if the argument provided via the request is large, these 257kinds of macros should be avoided. Also note that, in contrast to the 258previous non-macro example, this function does not do any endianness 259conversion, which has to be handled by the caller. Apart from those 260differences the function generated by the macro is similar to the one 261provided in the non-macro example above. 262 263The full list of such function-generating macros is 264 265- :c:func:`SSAM_DEFINE_SYNC_REQUEST_N` for requests without return value and 266 without argument. 267- :c:func:`SSAM_DEFINE_SYNC_REQUEST_R` for requests with return value but no 268 argument. 269- :c:func:`SSAM_DEFINE_SYNC_REQUEST_W` for requests without return value but 270 with argument. 271 272Refer to their respective documentation for more details. For each one of 273these macros, a special variant is provided, which targets request types 274applicable to multiple instances of the same device type: 275 276- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_N` 277- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_R` 278- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_W` 279 280The difference of those macros to the previously mentioned versions is, that 281the device target and instance IDs are not fixed for the generated function, 282but instead have to be provided by the caller of said function. 283 284Additionally, variants for direct use with client devices, i.e. 285|ssam_device|, are also provided. These can, for example, be used as 286follows: 287 288.. code-block:: c 289 290 SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_sta, __le32, { 291 .target_category = SSAM_SSH_TC_BAT, 292 .command_id = 0x01, 293 }); 294 295This invocation of the macro defines a function 296 297.. code-block:: c 298 299 static int ssam_bat_get_sta(struct ssam_device *sdev, __le32 *ret); 300 301executing the specified request, using the device IDs and controller given 302in the client device. The full list of such macros for client devices is: 303 304- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_N` 305- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_R` 306- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_W` 307 308 309Handling Events 310=============== 311 312To receive events from the SAM EC, an event notifier must be registered for 313the desired event via |ssam_notifier_register|. The notifier must be 314unregistered via |ssam_notifier_unregister| once it is not required any 315more. 316 317Event notifiers are registered by providing (at minimum) a callback to call 318in case an event has been received, the registry specifying how the event 319should be enabled, an event ID specifying for which target category and, 320optionally and depending on the registry used, for which instance ID events 321should be enabled, and finally, flags describing how the EC will send these 322events. If the specific registry does not enable events by instance ID, the 323instance ID must be set to zero. Additionally, a priority for the respective 324notifier may be specified, which determines its order in relation to any 325other notifier registered for the same target category. 326 327By default, event notifiers will receive all events for the specific target 328category, regardless of the instance ID specified when registering the 329notifier. The core may be instructed to only call a notifier if the target 330ID or instance ID (or both) of the event match the ones implied by the 331notifier IDs (in case of target ID, the target ID of the registry), by 332providing an event mask (see |ssam_event_mask|). 333 334In general, the target ID of the registry is also the target ID of the 335enabled event (with the notable exception being keyboard input events on the 336Surface Laptop 1 and 2, which are enabled via a registry with target ID 1, 337but provide events with target ID 2). 338 339A full example for registering an event notifier and handling received 340events is provided below: 341 342.. code-block:: c 343 344 u32 notifier_callback(struct ssam_event_notifier *nf, 345 const struct ssam_event *event) 346 { 347 int status = ... 348 349 /* Handle the event here ... */ 350 351 /* Convert return value and indicate that we handled the event. */ 352 return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED; 353 } 354 355 int setup_notifier(struct ssam_device *sdev, 356 struct ssam_event_notifier *nf) 357 { 358 /* Set priority wrt. other handlers of same target category. */ 359 nf->base.priority = 1; 360 361 /* Set event/notifier callback. */ 362 nf->base.fn = notifier_callback; 363 364 /* Specify event registry, i.e. how events get enabled/disabled. */ 365 nf->event.reg = SSAM_EVENT_REGISTRY_KIP; 366 367 /* Specify which event to enable/disable */ 368 nf->event.id.target_category = sdev->uid.category; 369 nf->event.id.instance = sdev->uid.instance; 370 371 /* 372 * Specify for which events the notifier callback gets executed. 373 * This essentially tells the core if it can skip notifiers that 374 * don't have target or instance IDs matching those of the event. 375 */ 376 nf->event.mask = SSAM_EVENT_MASK_STRICT; 377 378 /* Specify event flags. */ 379 nf->event.flags = SSAM_EVENT_SEQUENCED; 380 381 return ssam_notifier_register(sdev->ctrl, nf); 382 } 383 384Multiple event notifiers can be registered for the same event. The event 385handler core takes care of enabling and disabling events when notifiers are 386registered and unregistered, by keeping track of how many notifiers for a 387specific event (combination of registry, event target category, and event 388instance ID) are currently registered. This means that a specific event will 389be enabled when the first notifier for it is being registered and disabled 390when the last notifier for it is being unregistered. Note that the event 391flags are therefore only used on the first registered notifier, however, one 392should take care that notifiers for a specific event are always registered 393with the same flag and it is considered a bug to do otherwise. 394