| 1 | |
| 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 | ======================
|
| 27 | Writing Client Drivers
|
| 28 | ======================
|
| 29 |
|
| 30 | For the API documentation, refer to:
|
| 31 |
|
| 32 | .. toctree::
|
| 33 | :maxdepth: 2
|
| 34 |
|
| 35 | client-api
|
| 36 |
|
| 37 |
|
| 38 | Overview
|
| 39 | ========
|
| 40 |
|
| 41 | Client drivers can be set up in two main ways, depending on how the
|
| 42 | corresponding device is made available to the system. We specifically
|
| 43 | differentiate between devices that are presented to the system via one of
|
| 44 | the conventional ways, e.g. as platform devices via ACPI, and devices that
|
| 45 | are non-discoverable and instead need to be explicitly provided by some
|
| 46 | other mechanism, as discussed further below.
|
| 47 |
|
| 48 |
|
| 49 | Non-SSAM Client Drivers
|
| 50 | =======================
|
| 51 |
|
| 52 | All communication with the SAM EC is handled via the |ssam_controller|
|
| 53 | representing that EC to the kernel. Drivers targeting a non-SSAM device (and
|
| 54 | thus not being a |ssam_device_driver|) need to explicitly establish a
|
| 55 | connection/relation to that controller. This can be done via the
|
| 56 | |ssam_client_bind| function. Said function returns a reference to the SSAM
|
| 57 | controller, but, more importantly, also establishes a device link between
|
| 58 | client device and controller (this can also be done separate via
|
| 59 | |ssam_client_link|). It is important to do this, as it, first, guarantees
|
| 60 | that the returned controller is valid for use in the client driver for as
|
| 61 | long as this driver is bound to its device, i.e. that the driver gets
|
| 62 | unbound before the controller ever becomes invalid, and, second, as it
|
| 63 | ensures correct suspend/resume ordering. This setup should be done in the
|
| 64 | driver's probe function, and may be used to defer probing in case the SSAM
|
| 65 | subsystem is not ready yet, for example:
|
| 66 |
|
| 67 | .. code-block:: c
|
| 68 |
|
| 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 |
|
| 82 | The controller may be separately obtained via |ssam_get_controller| and its
|
| 83 | lifetime be guaranteed via |ssam_controller_get| and |ssam_controller_put|.
|
| 84 | Note that none of these functions, however, guarantee that the controller
|
| 85 | will not be shut down or suspended. These functions essentially only operate
|
| 86 | on the reference, i.e. only guarantee a bare minimum of accessibility
|
| 87 | without any guarantees at all on practical operability.
|
| 88 |
|
| 89 |
|
| 90 | Adding SSAM Devices
|
| 91 | ===================
|
| 92 |
|
| 93 | If a device does not already exist/is not already provided via conventional
|
| 94 | means, it should be provided as |ssam_device| via the SSAM client device
|
| 95 | hub. New devices can be added to this hub by entering their UID into the
|
| 96 | corresponding registry. SSAM devices can also be manually allocated via
|
| 97 | |ssam_device_alloc|, subsequently to which they have to be added via
|
| 98 | |ssam_device_add| and eventually removed via |ssam_device_remove|. By
|
| 99 | default, the parent of the device is set to the controller device provided
|
| 100 | for allocation, however this may be changed before the device is added. Note
|
| 101 | that, when changing the parent device, care must be taken to ensure that the
|
| 102 | controller lifetime and suspend/resume ordering guarantees, in the default
|
| 103 | setup provided through the parent-child relation, are preserved. If
|
| 104 | necessary, by use of |ssam_client_link| as is done for non-SSAM client
|
| 105 | drivers and described in more detail above.
|
| 106 |
|
| 107 | A client device must always be removed by the party which added the
|
| 108 | respective device before the controller shuts down. Such removal can be
|
| 109 | guaranteed by linking the driver providing the SSAM device to the controller
|
| 110 | via |ssam_client_link|, causing it to unbind before the controller driver
|
| 111 | unbinds. Client devices registered with the controller as parent are
|
| 112 | automatically removed when the controller shuts down, but this should not be
|
| 113 | relied upon, especially as this does not extend to client devices with a
|
| 114 | different parent.
|
| 115 |
|
| 116 |
|
| 117 | SSAM Client Drivers
|
| 118 | ===================
|
| 119 |
|
| 120 | SSAM client device drivers are, in essence, no different than other device
|
| 121 | driver types. They are represented via |ssam_device_driver| and bind to a
|
| 122 | |ssam_device| via its UID (:c:type:`struct ssam_device.uid <ssam_device>`)
|
| 123 | member and the match table
|
| 124 | (:c:type:`struct ssam_device_driver.match_table <ssam_device_driver>`),
|
| 125 | which should be set when declaring the driver struct instance. Refer to the
|
| 126 | |SSAM_DEVICE| macro documentation for more details on how to define members
|
| 127 | of the driver's match table.
|
| 128 |
|
| 129 | The UID for SSAM client devices consists of a ``domain``, a ``category``,
|
| 130 | a ``target``, an ``instance``, and a ``function``. The ``domain`` is used
|
| 131 | differentiate between physical SAM devices
|
| 132 | (:c:type:`SSAM_DOMAIN_SERIALHUB <ssam_device_domain>`), i.e. devices that can
|
| 133 | be accessed via the Surface Serial Hub, and virtual ones
|
| 134 | (:c:type:`SSAM_DOMAIN_VIRTUAL <ssam_device_domain>`), such as client-device
|
| 135 | hubs, that have no real representation on the SAM EC and are solely used on
|
| 136 | the kernel/driver-side. For physical devices, ``category`` represents the
|
| 137 | target category, ``target`` the target ID, and ``instance`` the instance ID
|
| 138 | used to access the physical SAM device. In addition, ``function`` references
|
| 139 | a specific device functionality, but has no meaning to the SAM EC. The
|
| 140 | (default) name of a client device is generated based on its UID.
|
| 141 |
|
| 142 | A driver instance can be registered via |ssam_device_driver_register| and
|
| 143 | unregistered via |ssam_device_driver_unregister|. For convenience, the
|
| 144 | |module_ssam_device_driver| macro may be used to define module init- and
|
| 145 | exit-functions registering the driver.
|
| 146 |
|
| 147 | The controller associated with a SSAM client device can be found in its
|
| 148 | :c:type:`struct ssam_device.ctrl <ssam_device>` member. This reference is
|
| 149 | guaranteed to be valid for at least as long as the client driver is bound,
|
| 150 | but should also be valid for as long as the client device exists. Note,
|
| 151 | however, that access outside of the bound client driver must ensure that the
|
| 152 | controller device is not suspended while making any requests or
|
| 153 | (un-)registering event notifiers (and thus should generally be avoided). This
|
| 154 | is guaranteed when the controller is accessed from inside the bound client
|
| 155 | driver.
|
| 156 |
|
| 157 |
|
| 158 | Making Synchronous Requests
|
| 159 | ===========================
|
| 160 |
|
| 161 | Synchronous requests are (currently) the main form of host-initiated
|
| 162 | communication with the EC. There are a couple of ways to define and execute
|
| 163 | such requests, however, most of them boil down to something similar as shown
|
| 164 | in the example below. This example defines a write-read request, meaning
|
| 165 | that the caller provides an argument to the SAM EC and receives a response.
|
| 166 | The caller needs to know the (maximum) length of the response payload and
|
| 167 | provide a buffer for it.
|
| 168 |
|
| 169 | Care must be taken to ensure that any command payload data passed to the SAM
|
| 170 | EC is provided in little-endian format and, similarly, any response payload
|
| 171 | data received from it is converted from little-endian to host endianness.
|
| 172 |
|
| 173 | .. code-block:: c
|
| 174 |
|
| 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 case
|
| 208 | * the request does not have any response. This must be consistent
|
| 209 | * with the SSAM_REQUEST_HAS_RESPONSE flag set in the specification
|
| 210 | * above.
|
| 211 | */
|
| 212 | status = ssam_request_do_sync(ctrl, &rqst, &resp);
|
| 213 |
|
| 214 | /*
|
| 215 | * Alternatively use
|
| 216 | *
|
| 217 | * ssam_request_do_sync_onstack(ctrl, &rqst, &resp, sizeof(arg_le));
|
| 218 | *
|
| 219 | * to perform the request, allocating the message buffer directly
|
| 220 | * on the stack as opposed to allocation via kzalloc().
|
| 221 | */
|
| 222 |
|
| 223 | /*
|
| 224 | * Convert request response back to native format. Note that in the
|
| 225 | * 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 |
|
| 233 | Note that |ssam_request_do_sync| in its essence is a wrapper over lower-level
|
| 234 | request primitives, which may also be used to perform requests. Refer to its
|
| 235 | implementation and documentation for more details.
|
| 236 |
|
| 237 | An arguably more user-friendly way of defining such functions is by using
|
| 238 | one of the generator macros, for example via:
|
| 239 |
|
| 240 | .. code-block:: c
|
| 241 |
|
| 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 |
|
| 249 | This example defines a function
|
| 250 |
|
| 251 | .. code-block:: c
|
| 252 |
|
| 253 | static int __ssam_tmp_perf_mode_set(struct ssam_controller *ctrl, const __le32 *arg);
|
| 254 |
|
| 255 | executing the specified request, with the controller passed in when calling
|
| 256 | said function. In this example, the argument is provided via the ``arg``
|
| 257 | pointer. Note that the generated function allocates the message buffer on
|
| 258 | the stack. Thus, if the argument provided via the request is large, these
|
| 259 | kinds of macros should be avoided. Also note that, in contrast to the
|
| 260 | previous non-macro example, this function does not do any endianness
|
| 261 | conversion, which has to be handled by the caller. Apart from those
|
| 262 | differences the function generated by the macro is similar to the one
|
| 263 | provided in the non-macro example above.
|
| 264 |
|
| 265 | The full list of such function-generating macros is
|
| 266 |
|
| 267 | - :c:func:`SSAM_DEFINE_SYNC_REQUEST_N` for requests without return value and
|
| 268 | without argument.
|
| 269 | - :c:func:`SSAM_DEFINE_SYNC_REQUEST_R` for requests with return value but no
|
| 270 | argument.
|
| 271 | - :c:func:`SSAM_DEFINE_SYNC_REQUEST_W` for requests without return value but
|
| 272 | with argument.
|
| 273 |
|
| 274 | Refer to their respective documentation for more details. For each one of
|
| 275 | these macros, a special variant is provided, which targets request types
|
| 276 | applicable 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 |
|
| 282 | The difference of those macros to the previously mentioned versions is, that
|
| 283 | the device target and instance IDs are not fixed for the generated function,
|
| 284 | but instead have to be provided by the caller of said function.
|
| 285 |
|
| 286 | Additionally, variants for direct use with client devices, i.e.
|
| 287 | |ssam_device|, are also provided. These can, for example, be used as
|
| 288 | follows:
|
| 289 |
|
| 290 | .. code-block:: c
|
| 291 |
|
| 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 |
|
| 297 | This invocation of the macro defines a function
|
| 298 |
|
| 299 | .. code-block:: c
|
| 300 |
|
| 301 | static int ssam_bat_get_sta(struct ssam_device *sdev, __le32 *ret);
|
| 302 |
|
| 303 | executing the specified request, using the device IDs and controller given
|
| 304 | in 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 |
|
| 311 | Handling Events
|
| 312 | ===============
|
| 313 |
|
| 314 | To receive events from the SAM EC, an event notifier must be registered for
|
| 315 | the desired event via |ssam_notifier_register|. The notifier must be
|
| 316 | unregistered via |ssam_notifier_unregister| once it is not required any
|
| 317 | more. For |ssam_device| type clients, the |ssam_device_notifier_register| and
|
| 318 | |ssam_device_notifier_unregister| wrappers should be preferred as they properly
|
| 319 | handle hot-removal of client devices.
|
| 320 |
|
| 321 | Event notifiers are registered by providing (at minimum) a callback to call
|
| 322 | in case an event has been received, the registry specifying how the event
|
| 323 | should be enabled, an event ID specifying for which target category and,
|
| 324 | optionally and depending on the registry used, for which instance ID events
|
| 325 | should be enabled, and finally, flags describing how the EC will send these
|
| 326 | events. If the specific registry does not enable events by instance ID, the
|
| 327 | instance ID must be set to zero. Additionally, a priority for the respective
|
| 328 | notifier may be specified, which determines its order in relation to any
|
| 329 | other notifier registered for the same target category.
|
| 330 |
|
| 331 | By default, event notifiers will receive all events for the specific target
|
| 332 | category, regardless of the instance ID specified when registering the
|
| 333 | notifier. The core may be instructed to only call a notifier if the target
|
| 334 | ID or instance ID (or both) of the event match the ones implied by the
|
| 335 | notifier IDs (in case of target ID, the target ID of the registry), by
|
| 336 | providing an event mask (see |ssam_event_mask|).
|
| 337 |
|
| 338 | In general, the target ID of the registry is also the target ID of the
|
| 339 | enabled event (with the notable exception being keyboard input events on the
|
| 340 | Surface Laptop 1 and 2, which are enabled via a registry with target ID 1,
|
| 341 | but provide events with target ID 2).
|
| 342 |
|
| 343 | A full example for registering an event notifier and handling received
|
| 344 | events is provided below:
|
| 345 |
|
| 346 | .. code-block:: c
|
| 347 |
|
| 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 that
|
| 378 | * 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 |
|
| 388 | Multiple event notifiers can be registered for the same event. The event
|
| 389 | handler core takes care of enabling and disabling events when notifiers are
|
| 390 | registered and unregistered, by keeping track of how many notifiers for a
|
| 391 | specific event (combination of registry, event target category, and event
|
| 392 | instance ID) are currently registered. This means that a specific event will
|
| 393 | be enabled when the first notifier for it is being registered and disabled
|
| 394 | when the last notifier for it is being unregistered. Note that the event
|
| 395 | flags are therefore only used on the first registered notifier, however, one
|
| 396 | should take care that notifiers for a specific event are always registered
|
| 397 | with the same flag and it is considered a bug to do otherwise.
|