Commit | Line | Data |
---|---|---|
8d779282 ML |
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` | |
5c1e88b9 ML |
20 | .. |ssam_device_notifier_register| replace:: :c:func:`ssam_device_notifier_register` |
21 | .. |ssam_device_notifier_unregister| replace:: :c:func:`ssam_device_notifier_unregister` | |
b09ee1cd | 22 | .. |ssam_request_do_sync| replace:: :c:func:`ssam_request_do_sync` |
8d779282 ML |
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; | |
3f88b459 | 194 | rqst.target_id = SSAM_SSH_TID_SAM; |
8d779282 ML |
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 | */ | |
b09ee1cd | 212 | status = ssam_request_do_sync(ctrl, &rqst, &resp); |
8d779282 ML |
213 | |
214 | /* | |
215 | * Alternatively use | |
216 | * | |
b09ee1cd | 217 | * ssam_request_do_sync_onstack(ctrl, &rqst, &resp, sizeof(arg_le)); |
8d779282 ML |
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 | ||
b09ee1cd | 233 | Note that |ssam_request_do_sync| in its essence is a wrapper over lower-level |
8d779282 ML |
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, | |
3f88b459 | 244 | .target_id = SSAM_SSH_TID_SAM, |
8d779282 ML |
245 | .command_id = 0x03, |
246 | .instance_id = 0x00, | |
247 | }); | |
248 | ||
249 | This example defines a function | |
250 | ||
251 | .. code-block:: c | |
252 | ||
03ee3183 | 253 | static int __ssam_tmp_perf_mode_set(struct ssam_controller *ctrl, const __le32 *arg); |
8d779282 ML |
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 | ||
03ee3183 | 301 | static int ssam_bat_get_sta(struct ssam_device *sdev, __le32 *ret); |
8d779282 ML |
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 | |
5c1e88b9 ML |
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. | |
8d779282 ML |
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. |