0005-surface-sam.patch 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492
  1. From 2bb8c3daadfa4daa1f2d0f35b7753f850d0f0b62 Mon Sep 17 00:00:00 2001
  2. From: Maximilian Luz <luzmaximilian@gmail.com>
  3. Date: Tue, 8 Jun 2021 00:24:47 +0200
  4. Subject: [PATCH] platform/surface: aggregator: Allow devices to be marked as
  5. hot-removed
  6. Some SSAM devices, notably the keyboard cover (keyboard and touchpad) on
  7. the Surface Pro 8, can be hot-removed. When this occurs, communication
  8. with the device may fail and time out. This timeout can unnecessarily
  9. block and slow down device removal and even cause issues when the
  10. devices are detached and re-attached quickly. Thus, communication should
  11. generally be avoided once hot-removal is detected.
  12. While we already remove a device as soon as we detect its (hot-)removal,
  13. the corresponding device driver may still attempt to communicate with
  14. the device during teardown. This is especially critical as communication
  15. failure may also extend to disabling of events, which is typically done
  16. at that stage.
  17. Add a flag to allow marking devices as hot-removed. This can then be
  18. used during client driver teardown to check if any communication
  19. attempts should be avoided.
  20. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  21. Patchset: surface-sam
  22. ---
  23. drivers/platform/surface/aggregator/bus.c | 3 ++
  24. include/linux/surface_aggregator/device.h | 48 +++++++++++++++++++++--
  25. 2 files changed, 48 insertions(+), 3 deletions(-)
  26. diff --git a/drivers/platform/surface/aggregator/bus.c b/drivers/platform/surface/aggregator/bus.c
  27. index abbbb5b08b07..2b003dcbfc4b 100644
  28. --- a/drivers/platform/surface/aggregator/bus.c
  29. +++ b/drivers/platform/surface/aggregator/bus.c
  30. @@ -388,6 +388,9 @@ void ssam_remove_clients(struct device *dev)
  31. }
  32. EXPORT_SYMBOL_GPL(ssam_remove_clients);
  33. +
  34. +/* -- Bus registration. ----------------------------------------------------- */
  35. +
  36. /**
  37. * ssam_bus_register() - Register and set-up the SSAM client device bus.
  38. */
  39. diff --git a/include/linux/surface_aggregator/device.h b/include/linux/surface_aggregator/device.h
  40. index cc257097eb05..491aa7e9f4bc 100644
  41. --- a/include/linux/surface_aggregator/device.h
  42. +++ b/include/linux/surface_aggregator/device.h
  43. @@ -148,17 +148,30 @@ struct ssam_device_uid {
  44. #define SSAM_SDEV(cat, tid, iid, fun) \
  45. SSAM_DEVICE(SSAM_DOMAIN_SERIALHUB, SSAM_SSH_TC_##cat, tid, iid, fun)
  46. +/*
  47. + * enum ssam_device_flags - Flags for SSAM client devices.
  48. + * @SSAM_DEVICE_HOT_REMOVED_BIT:
  49. + * The device has been hot-removed. Further communication with it may time
  50. + * out and should be avoided.
  51. + */
  52. +enum ssam_device_flags {
  53. + SSAM_DEVICE_HOT_REMOVED_BIT = 0,
  54. +};
  55. +
  56. /**
  57. * struct ssam_device - SSAM client device.
  58. - * @dev: Driver model representation of the device.
  59. - * @ctrl: SSAM controller managing this device.
  60. - * @uid: UID identifying the device.
  61. + * @dev: Driver model representation of the device.
  62. + * @ctrl: SSAM controller managing this device.
  63. + * @uid: UID identifying the device.
  64. + * @flags: Device state flags, see &enum ssam_device_flags.
  65. */
  66. struct ssam_device {
  67. struct device dev;
  68. struct ssam_controller *ctrl;
  69. struct ssam_device_uid uid;
  70. +
  71. + unsigned long flags;
  72. };
  73. /**
  74. @@ -240,6 +253,35 @@ struct ssam_device *ssam_device_alloc(struct ssam_controller *ctrl,
  75. int ssam_device_add(struct ssam_device *sdev);
  76. void ssam_device_remove(struct ssam_device *sdev);
  77. +/**
  78. + * ssam_device_mark_hot_removed() - Mark the given device as hot-removed.
  79. + * @sdev: The device to mark as hot-removed.
  80. + *
  81. + * Mark the device as having been hot-removed. This signals drivers using the
  82. + * device that communication with the device should be avoided and may lead to
  83. + * timeouts.
  84. + */
  85. +static inline void ssam_device_mark_hot_removed(struct ssam_device *sdev)
  86. +{
  87. + dev_dbg(&sdev->dev, "marking device as hot-removed\n");
  88. + set_bit(SSAM_DEVICE_HOT_REMOVED_BIT, &sdev->flags);
  89. +}
  90. +
  91. +/**
  92. + * ssam_device_is_hot_removed() - Check if the given device has been
  93. + * hot-removed.
  94. + * @sdev: The device to check.
  95. + *
  96. + * Checks if the given device has been marked as hot-removed. See
  97. + * ssam_device_mark_hot_removed() for more details.
  98. + *
  99. + * Return: Returns ``true`` if the device has been marked as hot-removed.
  100. + */
  101. +static inline bool ssam_device_is_hot_removed(struct ssam_device *sdev)
  102. +{
  103. + return test_bit(SSAM_DEVICE_HOT_REMOVED_BIT, &sdev->flags);
  104. +}
  105. +
  106. /**
  107. * ssam_device_get() - Increment reference count of SSAM client device.
  108. * @sdev: The device to increment the reference count of.
  109. --
  110. 2.36.0
  111. From e1da5a5135346e7091651f7fe2aa8c43b9d03f7d Mon Sep 17 00:00:00 2001
  112. From: Maximilian Luz <luzmaximilian@gmail.com>
  113. Date: Tue, 8 Jun 2021 00:48:22 +0200
  114. Subject: [PATCH] platform/surface: aggregator: Allow notifiers to avoid
  115. communication on unregistering
  116. When SSAM client devices have been (physically) hot-removed,
  117. communication attempts with those devices may fail and time out. This
  118. can even extend to event notifiers, due to which timeouts may occur
  119. during device removal, slowing down that process.
  120. Add a flag to the notifier unregister function that allows skipping
  121. communication with the EC to prevent this. Furthermore, add wrappers for
  122. registering and unregistering notifiers belonging to SSAM client devices
  123. that automatically check if the device has been marked as hot-removed
  124. and communication should be avoided.
  125. Note that non-SSAM client devices can generally not be hot-removed, so
  126. also add a convenience wrapper for those, defaulting to allow
  127. communication.
  128. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  129. Patchset: surface-sam
  130. ---
  131. .../driver-api/surface_aggregator/client.rst | 6 +-
  132. .../platform/surface/aggregator/controller.c | 53 ++++++++++------
  133. include/linux/surface_aggregator/controller.h | 24 +++++++-
  134. include/linux/surface_aggregator/device.h | 60 +++++++++++++++++++
  135. 4 files changed, 122 insertions(+), 21 deletions(-)
  136. diff --git a/Documentation/driver-api/surface_aggregator/client.rst b/Documentation/driver-api/surface_aggregator/client.rst
  137. index e519d374c378..27f95abdbe99 100644
  138. --- a/Documentation/driver-api/surface_aggregator/client.rst
  139. +++ b/Documentation/driver-api/surface_aggregator/client.rst
  140. @@ -17,6 +17,8 @@
  141. .. |SSAM_DEVICE| replace:: :c:func:`SSAM_DEVICE`
  142. .. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register`
  143. .. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister`
  144. +.. |ssam_device_notifier_register| replace:: :c:func:`ssam_device_notifier_register`
  145. +.. |ssam_device_notifier_unregister| replace:: :c:func:`ssam_device_notifier_unregister`
  146. .. |ssam_request_sync| replace:: :c:func:`ssam_request_sync`
  147. .. |ssam_event_mask| replace:: :c:type:`enum ssam_event_mask <ssam_event_mask>`
  148. @@ -312,7 +314,9 @@ Handling Events
  149. To receive events from the SAM EC, an event notifier must be registered for
  150. the desired event via |ssam_notifier_register|. The notifier must be
  151. unregistered via |ssam_notifier_unregister| once it is not required any
  152. -more.
  153. +more. For |ssam_device| type clients, the |ssam_device_notifier_register| and
  154. +|ssam_device_notifier_unregister| wrappers should be preferred as they properly
  155. +handle hot-removal of client devices.
  156. Event notifiers are registered by providing (at minimum) a callback to call
  157. in case an event has been received, the registry specifying how the event
  158. diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c
  159. index b8c377b3f932..6de834b52b63 100644
  160. --- a/drivers/platform/surface/aggregator/controller.c
  161. +++ b/drivers/platform/surface/aggregator/controller.c
  162. @@ -2199,16 +2199,26 @@ static int ssam_nf_refcount_enable(struct ssam_controller *ctrl,
  163. }
  164. /**
  165. - * ssam_nf_refcount_disable_free() - Disable event for reference count entry if it is
  166. - * no longer in use and free the corresponding entry.
  167. + * ssam_nf_refcount_disable_free() - Disable event for reference count entry if
  168. + * it is no longer in use and free the corresponding entry.
  169. * @ctrl: The controller to disable the event on.
  170. * @entry: The reference count entry for the event to be disabled.
  171. * @flags: The flags used for enabling the event on the EC.
  172. + * @ec: Flag specifying if the event should actually be disabled on the EC.
  173. *
  174. - * If the reference count equals zero, i.e. the event is no longer requested by
  175. - * any client, the event will be disabled and the corresponding reference count
  176. - * entry freed. The reference count entry must not be used any more after a
  177. - * call to this function.
  178. + * If ``ec`` equals ``true`` and the reference count equals zero (i.e. the
  179. + * event is no longer requested by any client), the specified event will be
  180. + * disabled on the EC via the corresponding request.
  181. + *
  182. + * If ``ec`` equals ``false``, no request will be sent to the EC and the event
  183. + * can be considered in a detached state (i.e. no longer used but still
  184. + * enabled). Disabling an event via this method may be required for
  185. + * hot-removable devices, where event disable requests may time out after the
  186. + * device has been physically removed.
  187. + *
  188. + * In both cases, if the reference count equals zero, the corresponding
  189. + * reference count entry will be freed. The reference count entry must not be
  190. + * used any more after a call to this function.
  191. *
  192. * Also checks if the flags used for disabling the event match the flags used
  193. * for enabling the event and warns if they do not (regardless of reference
  194. @@ -2223,7 +2233,7 @@ static int ssam_nf_refcount_enable(struct ssam_controller *ctrl,
  195. * returns the status of the event-enable EC command.
  196. */
  197. static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl,
  198. - struct ssam_nf_refcount_entry *entry, u8 flags)
  199. + struct ssam_nf_refcount_entry *entry, u8 flags, bool ec)
  200. {
  201. const struct ssam_event_registry reg = entry->key.reg;
  202. const struct ssam_event_id id = entry->key.id;
  203. @@ -2232,8 +2242,9 @@ static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl,
  204. lockdep_assert_held(&nf->lock);
  205. - ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
  206. - reg.target_category, id.target_category, id.instance, entry->refcount);
  207. + ssam_dbg(ctrl, "%s event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
  208. + ec ? "disabling" : "detaching", reg.target_category, id.target_category,
  209. + id.instance, entry->refcount);
  210. if (entry->flags != flags) {
  211. ssam_warn(ctrl,
  212. @@ -2242,7 +2253,7 @@ static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl,
  213. id.instance);
  214. }
  215. - if (entry->refcount == 0) {
  216. + if (ec && entry->refcount == 0) {
  217. status = ssam_ssh_event_disable(ctrl, reg, id, flags);
  218. kfree(entry);
  219. }
  220. @@ -2322,20 +2333,26 @@ int ssam_notifier_register(struct ssam_controller *ctrl, struct ssam_event_notif
  221. EXPORT_SYMBOL_GPL(ssam_notifier_register);
  222. /**
  223. - * ssam_notifier_unregister() - Unregister an event notifier.
  224. - * @ctrl: The controller the notifier has been registered on.
  225. - * @n: The event notifier to unregister.
  226. + * __ssam_notifier_unregister() - Unregister an event notifier.
  227. + * @ctrl: The controller the notifier has been registered on.
  228. + * @n: The event notifier to unregister.
  229. + * @disable: Whether to disable the corresponding event on the EC.
  230. *
  231. * Unregister an event notifier. Decrement the usage counter of the associated
  232. * SAM event if the notifier is not marked as an observer. If the usage counter
  233. - * reaches zero, the event will be disabled.
  234. + * reaches zero and ``disable`` equals ``true``, the event will be disabled.
  235. + *
  236. + * Useful for hot-removable devices, where communication may fail once the
  237. + * device has been physically removed. In that case, specifying ``disable`` as
  238. + * ``false`` avoids communication with the EC.
  239. *
  240. * Return: Returns zero on success, %-ENOENT if the given notifier block has
  241. * not been registered on the controller. If the given notifier block was the
  242. * last one associated with its specific event, returns the status of the
  243. * event-disable EC-command.
  244. */
  245. -int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n)
  246. +int __ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n,
  247. + bool disable)
  248. {
  249. u16 rqid = ssh_tc_to_rqid(n->event.id.target_category);
  250. struct ssam_nf_refcount_entry *entry;
  251. @@ -2373,7 +2390,7 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_not
  252. goto remove;
  253. }
  254. - status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags);
  255. + status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags, disable);
  256. }
  257. remove:
  258. @@ -2383,7 +2400,7 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_not
  259. return status;
  260. }
  261. -EXPORT_SYMBOL_GPL(ssam_notifier_unregister);
  262. +EXPORT_SYMBOL_GPL(__ssam_notifier_unregister);
  263. /**
  264. * ssam_controller_event_enable() - Enable the specified event.
  265. @@ -2477,7 +2494,7 @@ int ssam_controller_event_disable(struct ssam_controller *ctrl,
  266. return -ENOENT;
  267. }
  268. - status = ssam_nf_refcount_disable_free(ctrl, entry, flags);
  269. + status = ssam_nf_refcount_disable_free(ctrl, entry, flags, true);
  270. mutex_unlock(&nf->lock);
  271. return status;
  272. diff --git a/include/linux/surface_aggregator/controller.h b/include/linux/surface_aggregator/controller.h
  273. index 74bfdffaf7b0..50a2b4926c06 100644
  274. --- a/include/linux/surface_aggregator/controller.h
  275. +++ b/include/linux/surface_aggregator/controller.h
  276. @@ -835,8 +835,28 @@ struct ssam_event_notifier {
  277. int ssam_notifier_register(struct ssam_controller *ctrl,
  278. struct ssam_event_notifier *n);
  279. -int ssam_notifier_unregister(struct ssam_controller *ctrl,
  280. - struct ssam_event_notifier *n);
  281. +int __ssam_notifier_unregister(struct ssam_controller *ctrl,
  282. + struct ssam_event_notifier *n, bool disable);
  283. +
  284. +/**
  285. + * ssam_notifier_unregister() - Unregister an event notifier.
  286. + * @ctrl: The controller the notifier has been registered on.
  287. + * @n: The event notifier to unregister.
  288. + *
  289. + * Unregister an event notifier. Decrement the usage counter of the associated
  290. + * SAM event if the notifier is not marked as an observer. If the usage counter
  291. + * reaches zero, the event will be disabled.
  292. + *
  293. + * Return: Returns zero on success, %-ENOENT if the given notifier block has
  294. + * not been registered on the controller. If the given notifier block was the
  295. + * last one associated with its specific event, returns the status of the
  296. + * event-disable EC-command.
  297. + */
  298. +static inline int ssam_notifier_unregister(struct ssam_controller *ctrl,
  299. + struct ssam_event_notifier *n)
  300. +{
  301. + return __ssam_notifier_unregister(ctrl, n, true);
  302. +}
  303. int ssam_controller_event_enable(struct ssam_controller *ctrl,
  304. struct ssam_event_registry reg,
  305. diff --git a/include/linux/surface_aggregator/device.h b/include/linux/surface_aggregator/device.h
  306. index 491aa7e9f4bc..16816c34da3e 100644
  307. --- a/include/linux/surface_aggregator/device.h
  308. +++ b/include/linux/surface_aggregator/device.h
  309. @@ -472,4 +472,64 @@ static inline void ssam_remove_clients(struct device *dev) {}
  310. sdev->uid.instance, ret); \
  311. }
  312. +
  313. +/* -- Helpers for client-device notifiers. ---------------------------------- */
  314. +
  315. +/**
  316. + * ssam_device_notifier_register() - Register an event notifier for the
  317. + * specified client device.
  318. + * @sdev: The device the notifier should be registered on.
  319. + * @n: The event notifier to register.
  320. + *
  321. + * Register an event notifier. Increment the usage counter of the associated
  322. + * SAM event if the notifier is not marked as an observer. If the event is not
  323. + * marked as an observer and is currently not enabled, it will be enabled
  324. + * during this call. If the notifier is marked as an observer, no attempt will
  325. + * be made at enabling any event and no reference count will be modified.
  326. + *
  327. + * Notifiers marked as observers do not need to be associated with one specific
  328. + * event, i.e. as long as no event matching is performed, only the event target
  329. + * category needs to be set.
  330. + *
  331. + * Return: Returns zero on success, %-ENOSPC if there have already been
  332. + * %INT_MAX notifiers for the event ID/type associated with the notifier block
  333. + * registered, %-ENOMEM if the corresponding event entry could not be
  334. + * allocated, %-ENODEV if the device is marked as hot-removed. If this is the
  335. + * first time that a notifier block is registered for the specific associated
  336. + * event, returns the status of the event-enable EC-command.
  337. + */
  338. +static inline int ssam_device_notifier_register(struct ssam_device *sdev,
  339. + struct ssam_event_notifier *n)
  340. +{
  341. + if (ssam_device_is_hot_removed(sdev))
  342. + return -ENODEV;
  343. +
  344. + return ssam_notifier_register(sdev->ctrl, n);
  345. +}
  346. +
  347. +/**
  348. + * ssam_device_notifier_unregister() - Unregister an event notifier for the
  349. + * specified client device.
  350. + * @sdev: The device the notifier has been registered on.
  351. + * @n: The event notifier to unregister.
  352. + *
  353. + * Unregister an event notifier. Decrement the usage counter of the associated
  354. + * SAM event if the notifier is not marked as an observer. If the usage counter
  355. + * reaches zero, the event will be disabled.
  356. + *
  357. + * In case the device has been marked as hot-removed, the event will not be
  358. + * disabled on the EC, as in those cases any attempt at doing so may time out.
  359. + *
  360. + * Return: Returns zero on success, %-ENOENT if the given notifier block has
  361. + * not been registered on the controller. If the given notifier block was the
  362. + * last one associated with its specific event, returns the status of the
  363. + * event-disable EC-command.
  364. + */
  365. +static inline int ssam_device_notifier_unregister(struct ssam_device *sdev,
  366. + struct ssam_event_notifier *n)
  367. +{
  368. + return __ssam_notifier_unregister(sdev->ctrl, n,
  369. + !ssam_device_is_hot_removed(sdev));
  370. +}
  371. +
  372. #endif /* _LINUX_SURFACE_AGGREGATOR_DEVICE_H */
  373. --
  374. 2.36.0
  375. From bf9269161d0e15076508e0ca776a31d860eacb7f Mon Sep 17 00:00:00 2001
  376. From: Maximilian Luz <luzmaximilian@gmail.com>
  377. Date: Tue, 8 Jun 2021 01:20:49 +0200
  378. Subject: [PATCH] platform/surface: aggregator_registry: Use client device
  379. wrappers for notifier registration
  380. Use newly introduced client device wrapper functions for notifier
  381. registration and unregistration.
  382. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  383. Patchset: surface-sam
  384. ---
  385. drivers/platform/surface/surface_aggregator_registry.c | 6 +++---
  386. 1 file changed, 3 insertions(+), 3 deletions(-)
  387. diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
  388. index ce2bd88feeaa..9f630e890ff7 100644
  389. --- a/drivers/platform/surface/surface_aggregator_registry.c
  390. +++ b/drivers/platform/surface/surface_aggregator_registry.c
  391. @@ -468,7 +468,7 @@ static int ssam_base_hub_probe(struct ssam_device *sdev)
  392. ssam_device_set_drvdata(sdev, hub);
  393. - status = ssam_notifier_register(sdev->ctrl, &hub->notif);
  394. + status = ssam_device_notifier_register(sdev, &hub->notif);
  395. if (status)
  396. return status;
  397. @@ -480,7 +480,7 @@ static int ssam_base_hub_probe(struct ssam_device *sdev)
  398. return 0;
  399. err:
  400. - ssam_notifier_unregister(sdev->ctrl, &hub->notif);
  401. + ssam_device_notifier_unregister(sdev, &hub->notif);
  402. cancel_delayed_work_sync(&hub->update_work);
  403. ssam_remove_clients(&sdev->dev);
  404. return status;
  405. @@ -492,7 +492,7 @@ static void ssam_base_hub_remove(struct ssam_device *sdev)
  406. sysfs_remove_group(&sdev->dev.kobj, &ssam_base_hub_group);
  407. - ssam_notifier_unregister(sdev->ctrl, &hub->notif);
  408. + ssam_device_notifier_unregister(sdev, &hub->notif);
  409. cancel_delayed_work_sync(&hub->update_work);
  410. ssam_remove_clients(&sdev->dev);
  411. }
  412. --
  413. 2.36.0
  414. From 7dffc0d5aa0b675201815f0db11d4207ed1bcf47 Mon Sep 17 00:00:00 2001
  415. From: Maximilian Luz <luzmaximilian@gmail.com>
  416. Date: Thu, 28 Oct 2021 03:37:06 +0200
  417. Subject: [PATCH] power/supply: surface_charger: Use client device wrappers for
  418. notifier registration
  419. Use newly introduced client device wrapper functions for notifier
  420. registration and unregistration.
  421. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  422. Patchset: surface-sam
  423. ---
  424. drivers/power/supply/surface_charger.c | 4 ++--
  425. 1 file changed, 2 insertions(+), 2 deletions(-)
  426. diff --git a/drivers/power/supply/surface_charger.c b/drivers/power/supply/surface_charger.c
  427. index a060c36c7766..59182d55742d 100644
  428. --- a/drivers/power/supply/surface_charger.c
  429. +++ b/drivers/power/supply/surface_charger.c
  430. @@ -216,7 +216,7 @@ static int spwr_ac_register(struct spwr_ac_device *ac)
  431. if (IS_ERR(ac->psy))
  432. return PTR_ERR(ac->psy);
  433. - return ssam_notifier_register(ac->sdev->ctrl, &ac->notif);
  434. + return ssam_device_notifier_register(ac->sdev, &ac->notif);
  435. }
  436. @@ -251,7 +251,7 @@ static void surface_ac_remove(struct ssam_device *sdev)
  437. {
  438. struct spwr_ac_device *ac = ssam_device_get_drvdata(sdev);
  439. - ssam_notifier_unregister(sdev->ctrl, &ac->notif);
  440. + ssam_device_notifier_unregister(sdev, &ac->notif);
  441. }
  442. static const struct spwr_psy_properties spwr_psy_props_adp1 = {
  443. --
  444. 2.36.0
  445. From bf04f91eb86cd24faed47f078d4c7cba0d3afb0a Mon Sep 17 00:00:00 2001
  446. From: Maximilian Luz <luzmaximilian@gmail.com>
  447. Date: Thu, 28 Oct 2021 03:38:09 +0200
  448. Subject: [PATCH] power/supply: surface_battery: Use client device wrappers for
  449. notifier registration
  450. Use newly introduced client device wrapper functions for notifier
  451. registration and unregistration.
  452. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  453. Patchset: surface-sam
  454. ---
  455. drivers/power/supply/surface_battery.c | 4 ++--
  456. 1 file changed, 2 insertions(+), 2 deletions(-)
  457. diff --git a/drivers/power/supply/surface_battery.c b/drivers/power/supply/surface_battery.c
  458. index 5ec2e6bb2465..540707882bb0 100644
  459. --- a/drivers/power/supply/surface_battery.c
  460. +++ b/drivers/power/supply/surface_battery.c
  461. @@ -802,7 +802,7 @@ static int spwr_battery_register(struct spwr_battery_device *bat)
  462. if (IS_ERR(bat->psy))
  463. return PTR_ERR(bat->psy);
  464. - return ssam_notifier_register(bat->sdev->ctrl, &bat->notif);
  465. + return ssam_device_notifier_register(bat->sdev, &bat->notif);
  466. }
  467. @@ -837,7 +837,7 @@ static void surface_battery_remove(struct ssam_device *sdev)
  468. {
  469. struct spwr_battery_device *bat = ssam_device_get_drvdata(sdev);
  470. - ssam_notifier_unregister(sdev->ctrl, &bat->notif);
  471. + ssam_device_notifier_unregister(sdev, &bat->notif);
  472. cancel_delayed_work_sync(&bat->update_work);
  473. }
  474. --
  475. 2.36.0
  476. From ec5de9e26ab336abeb4510101843ca9215f306ac Mon Sep 17 00:00:00 2001
  477. From: Maximilian Luz <luzmaximilian@gmail.com>
  478. Date: Tue, 8 Jun 2021 01:33:02 +0200
  479. Subject: [PATCH] HID: surface-hid: Add support for hot-removal
  480. Add support for hot-removal of SSAM HID client devices.
  481. Once a device has been hot-removed, further communication with it should
  482. be avoided as it may fail and time out. While the device will be removed
  483. as soon as we detect hot-removal, communication may still occur during
  484. teardown, especially when unregistering notifiers.
  485. While hot-removal is a surprise event that can happen any time, try to
  486. avoid communication as much as possible once it has been detected to
  487. prevent timeouts that can slow down device removal and cause issues,
  488. e.g. when quickly re-attaching the device.
  489. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  490. Patchset: surface-sam
  491. ---
  492. drivers/hid/surface-hid/surface_hid_core.c | 38 +++++++++++++++++++++-
  493. 1 file changed, 37 insertions(+), 1 deletion(-)
  494. diff --git a/drivers/hid/surface-hid/surface_hid_core.c b/drivers/hid/surface-hid/surface_hid_core.c
  495. index e46330b2e561..87637f813de2 100644
  496. --- a/drivers/hid/surface-hid/surface_hid_core.c
  497. +++ b/drivers/hid/surface-hid/surface_hid_core.c
  498. @@ -19,12 +19,30 @@
  499. #include "surface_hid_core.h"
  500. +/* -- Utility functions. ---------------------------------------------------- */
  501. +
  502. +static bool surface_hid_is_hot_removed(struct surface_hid_device *shid)
  503. +{
  504. + /*
  505. + * Non-ssam client devices, i.e. platform client devices, cannot be
  506. + * hot-removed.
  507. + */
  508. + if (!is_ssam_device(shid->dev))
  509. + return false;
  510. +
  511. + return ssam_device_is_hot_removed(to_ssam_device(shid->dev));
  512. +}
  513. +
  514. +
  515. /* -- Device descriptor access. --------------------------------------------- */
  516. static int surface_hid_load_hid_descriptor(struct surface_hid_device *shid)
  517. {
  518. int status;
  519. + if (surface_hid_is_hot_removed(shid))
  520. + return -ENODEV;
  521. +
  522. status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_HID,
  523. (u8 *)&shid->hid_desc, sizeof(shid->hid_desc));
  524. if (status)
  525. @@ -61,6 +79,9 @@ static int surface_hid_load_device_attributes(struct surface_hid_device *shid)
  526. {
  527. int status;
  528. + if (surface_hid_is_hot_removed(shid))
  529. + return -ENODEV;
  530. +
  531. status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_ATTRS,
  532. (u8 *)&shid->attrs, sizeof(shid->attrs));
  533. if (status)
  534. @@ -88,9 +109,18 @@ static int surface_hid_start(struct hid_device *hid)
  535. static void surface_hid_stop(struct hid_device *hid)
  536. {
  537. struct surface_hid_device *shid = hid->driver_data;
  538. + bool hot_removed;
  539. +
  540. + /*
  541. + * Communication may fail for devices that have been hot-removed. This
  542. + * also includes unregistration of HID events, so we need to check this
  543. + * here. Only if the device has not been marked as hot-removed, we can
  544. + * safely disable events.
  545. + */
  546. + hot_removed = surface_hid_is_hot_removed(shid);
  547. /* Note: This call will log errors for us, so ignore them here. */
  548. - ssam_notifier_unregister(shid->ctrl, &shid->notif);
  549. + __ssam_notifier_unregister(shid->ctrl, &shid->notif, !hot_removed);
  550. }
  551. static int surface_hid_open(struct hid_device *hid)
  552. @@ -109,6 +139,9 @@ static int surface_hid_parse(struct hid_device *hid)
  553. u8 *buf;
  554. int status;
  555. + if (surface_hid_is_hot_removed(shid))
  556. + return -ENODEV;
  557. +
  558. buf = kzalloc(len, GFP_KERNEL);
  559. if (!buf)
  560. return -ENOMEM;
  561. @@ -126,6 +159,9 @@ static int surface_hid_raw_request(struct hid_device *hid, unsigned char reportn
  562. {
  563. struct surface_hid_device *shid = hid->driver_data;
  564. + if (surface_hid_is_hot_removed(shid))
  565. + return -ENODEV;
  566. +
  567. if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT)
  568. return shid->ops.output_report(shid, reportnum, buf, len);
  569. --
  570. 2.36.0
  571. From 4bdf54b718375e303f8fe6745ffa90c4b153e20c Mon Sep 17 00:00:00 2001
  572. From: Maximilian Luz <luzmaximilian@gmail.com>
  573. Date: Sun, 31 Oct 2021 12:34:08 +0100
  574. Subject: [PATCH] platform/surface: aggregator: Add comment for KIP subsystem
  575. category
  576. The KIP subsystem (full name unknown, abbreviation has been obtained
  577. through reverse engineering) handles detachable peripherals such as the
  578. keyboard cover on the Surface Pro X and Surface Pro 8.
  579. It is currently not entirely clear what this subsystem entails, but at
  580. the very least it provides event notifications for when the keyboard
  581. cover on the Surface Pro X and Surface Pro 8 have been detached or
  582. re-attached, as well as the state that the keyboard cover is currently
  583. in (e.g. folded-back, folded laptop-like, closed, etc.).
  584. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  585. Patchset: surface-sam
  586. ---
  587. include/linux/surface_aggregator/serial_hub.h | 2 +-
  588. 1 file changed, 1 insertion(+), 1 deletion(-)
  589. diff --git a/include/linux/surface_aggregator/serial_hub.h b/include/linux/surface_aggregator/serial_hub.h
  590. index c3de43edcffa..d1efac85caf1 100644
  591. --- a/include/linux/surface_aggregator/serial_hub.h
  592. +++ b/include/linux/surface_aggregator/serial_hub.h
  593. @@ -306,7 +306,7 @@ enum ssam_ssh_tc {
  594. SSAM_SSH_TC_LPC = 0x0b,
  595. SSAM_SSH_TC_TCL = 0x0c,
  596. SSAM_SSH_TC_SFL = 0x0d,
  597. - SSAM_SSH_TC_KIP = 0x0e,
  598. + SSAM_SSH_TC_KIP = 0x0e, /* Manages detachable peripherals (Pro X/8 keyboard cover) */
  599. SSAM_SSH_TC_EXT = 0x0f,
  600. SSAM_SSH_TC_BLD = 0x10,
  601. SSAM_SSH_TC_BAS = 0x11, /* Detachment system (Surface Book 2/3). */
  602. --
  603. 2.36.0
  604. From b05a57a23fb0dce8c1be11eda511116493d1e114 Mon Sep 17 00:00:00 2001
  605. From: Maximilian Luz <luzmaximilian@gmail.com>
  606. Date: Sun, 10 Oct 2021 23:56:23 +0200
  607. Subject: [PATCH] platform/surface: aggregator_registry: Add KIP device hub
  608. Add a Surface System Aggregator Module (SSAM) client device hub for
  609. hot-removable devices managed via the KIP subsystem.
  610. The KIP subsystem (full name unknown, abbreviation has been obtained
  611. through reverse engineering) is a subsystem that manages hot-removable
  612. SSAM client devices. Specifically, it manages HID input devices
  613. contained in the detachable keyboard cover of the Surface Pro 8 and
  614. Surface Pro X.
  615. To properly handle detachable devices, we need to remove their kernel
  616. representation when the physical device has been detached and (re-)add
  617. and (re-)initialize said representation when the physical device has
  618. been (re-)attached. Note that we need to hot-remove those devices, as
  619. communication (especially during event notifier unregistration) may time
  620. out when the physical device is no longer present, which would lead to
  621. an unnecessary delay. This delay might become problematic when devices
  622. are detached and re-attached quickly.
  623. The KIP subsystem handles a single group of devices (e.g. all devices
  624. contained in the keyboard cover) and cannot handle devices individually.
  625. Thus we model it as a client device hub, which removes all devices
  626. contained under it once removal of the hub (e.g. keyboard cover) has
  627. been detected and (re-)adds all devices once the physical hub device has
  628. been (re-)attached.
  629. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  630. Patchset: surface-sam
  631. ---
  632. .../surface/surface_aggregator_registry.c | 247 +++++++++++++++++-
  633. 1 file changed, 245 insertions(+), 2 deletions(-)
  634. diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
  635. index 9f630e890ff7..4838ce6519a6 100644
  636. --- a/drivers/platform/surface/surface_aggregator_registry.c
  637. +++ b/drivers/platform/surface/surface_aggregator_registry.c
  638. @@ -514,6 +514,237 @@ static struct ssam_device_driver ssam_base_hub_driver = {
  639. };
  640. +/* -- SSAM KIP-subsystem hub driver. ---------------------------------------- */
  641. +
  642. +/*
  643. + * Some devices may need a bit of time to be fully usable after being
  644. + * (re-)connected. This delay has been determined via experimentation.
  645. + */
  646. +#define SSAM_KIP_UPDATE_CONNECT_DELAY msecs_to_jiffies(250)
  647. +
  648. +#define SSAM_EVENT_KIP_CID_CONNECTION 0x2c
  649. +
  650. +enum ssam_kip_hub_state {
  651. + SSAM_KIP_HUB_UNINITIALIZED,
  652. + SSAM_KIP_HUB_CONNECTED,
  653. + SSAM_KIP_HUB_DISCONNECTED,
  654. +};
  655. +
  656. +struct ssam_kip_hub {
  657. + struct ssam_device *sdev;
  658. +
  659. + enum ssam_kip_hub_state state;
  660. + struct delayed_work update_work;
  661. +
  662. + struct ssam_event_notifier notif;
  663. +};
  664. +
  665. +SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_connection_state, u8, {
  666. + .target_category = SSAM_SSH_TC_KIP,
  667. + .target_id = 0x01,
  668. + .command_id = 0x2c,
  669. + .instance_id = 0x00,
  670. +});
  671. +
  672. +static int ssam_kip_get_connection_state(struct ssam_kip_hub *hub, enum ssam_kip_hub_state *state)
  673. +{
  674. + int status;
  675. + u8 connected;
  676. +
  677. + status = ssam_retry(__ssam_kip_get_connection_state, hub->sdev->ctrl, &connected);
  678. + if (status < 0) {
  679. + dev_err(&hub->sdev->dev, "failed to query KIP connection state: %d\n", status);
  680. + return status;
  681. + }
  682. +
  683. + *state = connected ? SSAM_KIP_HUB_CONNECTED : SSAM_KIP_HUB_DISCONNECTED;
  684. + return 0;
  685. +}
  686. +
  687. +static ssize_t ssam_kip_hub_state_show(struct device *dev, struct device_attribute *attr, char *buf)
  688. +{
  689. + struct ssam_kip_hub *hub = dev_get_drvdata(dev);
  690. + const char *state;
  691. +
  692. + switch (hub->state) {
  693. + case SSAM_KIP_HUB_UNINITIALIZED:
  694. + state = "uninitialized";
  695. + break;
  696. +
  697. + case SSAM_KIP_HUB_CONNECTED:
  698. + state = "connected";
  699. + break;
  700. +
  701. + case SSAM_KIP_HUB_DISCONNECTED:
  702. + state = "disconnected";
  703. + break;
  704. +
  705. + default:
  706. + /*
  707. + * Any value not handled in the above cases is invalid and
  708. + * should never have been set. Thus this case should be
  709. + * impossible to reach.
  710. + */
  711. + WARN(1, "invalid KIP hub state: %d\n", hub->state);
  712. + state = "<invalid>";
  713. + break;
  714. + }
  715. +
  716. + return sysfs_emit(buf, "%s\n", state);
  717. +}
  718. +
  719. +static struct device_attribute ssam_kip_hub_attr_state =
  720. + __ATTR(state, 0444, ssam_kip_hub_state_show, NULL);
  721. +
  722. +static struct attribute *ssam_kip_hub_attrs[] = {
  723. + &ssam_kip_hub_attr_state.attr,
  724. + NULL,
  725. +};
  726. +
  727. +static const struct attribute_group ssam_kip_hub_group = {
  728. + .attrs = ssam_kip_hub_attrs,
  729. +};
  730. +
  731. +static int ssam_kip_hub_mark_hot_removed(struct device *dev, void *_data)
  732. +{
  733. + struct ssam_device *sdev = to_ssam_device(dev);
  734. +
  735. + if (is_ssam_device(dev))
  736. + ssam_device_mark_hot_removed(sdev);
  737. +
  738. + return 0;
  739. +}
  740. +
  741. +static void ssam_kip_hub_update_workfn(struct work_struct *work)
  742. +{
  743. + struct ssam_kip_hub *hub = container_of(work, struct ssam_kip_hub, update_work.work);
  744. + struct fwnode_handle *node = dev_fwnode(&hub->sdev->dev);
  745. + enum ssam_kip_hub_state state;
  746. + int status = 0;
  747. +
  748. + status = ssam_kip_get_connection_state(hub, &state);
  749. + if (status)
  750. + return;
  751. +
  752. + if (hub->state == state)
  753. + return;
  754. + hub->state = state;
  755. +
  756. + if (hub->state == SSAM_KIP_HUB_CONNECTED)
  757. + status = ssam_hub_register_clients(&hub->sdev->dev, hub->sdev->ctrl, node);
  758. + else
  759. + ssam_remove_clients(&hub->sdev->dev);
  760. +
  761. + if (status)
  762. + dev_err(&hub->sdev->dev, "failed to update KIP-hub devices: %d\n", status);
  763. +}
  764. +
  765. +static u32 ssam_kip_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event)
  766. +{
  767. + struct ssam_kip_hub *hub = container_of(nf, struct ssam_kip_hub, notif);
  768. + unsigned long delay;
  769. +
  770. + if (event->command_id != SSAM_EVENT_KIP_CID_CONNECTION)
  771. + return 0; /* Return "unhandled". */
  772. +
  773. + if (event->length < 1) {
  774. + dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length);
  775. + return 0;
  776. + }
  777. +
  778. + /* Mark devices as hot-removed before we remove any */
  779. + if (!event->data[0])
  780. + device_for_each_child_reverse(&hub->sdev->dev, NULL, ssam_kip_hub_mark_hot_removed);
  781. +
  782. + /*
  783. + * Delay update when KIP devices are being connected to give devices/EC
  784. + * some time to set up.
  785. + */
  786. + delay = event->data[0] ? SSAM_KIP_UPDATE_CONNECT_DELAY : 0;
  787. +
  788. + schedule_delayed_work(&hub->update_work, delay);
  789. + return SSAM_NOTIF_HANDLED;
  790. +}
  791. +
  792. +static int __maybe_unused ssam_kip_hub_resume(struct device *dev)
  793. +{
  794. + struct ssam_kip_hub *hub = dev_get_drvdata(dev);
  795. +
  796. + schedule_delayed_work(&hub->update_work, 0);
  797. + return 0;
  798. +}
  799. +static SIMPLE_DEV_PM_OPS(ssam_kip_hub_pm_ops, NULL, ssam_kip_hub_resume);
  800. +
  801. +static int ssam_kip_hub_probe(struct ssam_device *sdev)
  802. +{
  803. + struct ssam_kip_hub *hub;
  804. + int status;
  805. +
  806. + hub = devm_kzalloc(&sdev->dev, sizeof(*hub), GFP_KERNEL);
  807. + if (!hub)
  808. + return -ENOMEM;
  809. +
  810. + hub->sdev = sdev;
  811. + hub->state = SSAM_KIP_HUB_UNINITIALIZED;
  812. +
  813. + hub->notif.base.priority = INT_MAX; /* This notifier should run first. */
  814. + hub->notif.base.fn = ssam_kip_hub_notif;
  815. + hub->notif.event.reg = SSAM_EVENT_REGISTRY_SAM;
  816. + hub->notif.event.id.target_category = SSAM_SSH_TC_KIP,
  817. + hub->notif.event.id.instance = 0,
  818. + hub->notif.event.mask = SSAM_EVENT_MASK_TARGET;
  819. + hub->notif.event.flags = SSAM_EVENT_SEQUENCED;
  820. +
  821. + INIT_DELAYED_WORK(&hub->update_work, ssam_kip_hub_update_workfn);
  822. +
  823. + ssam_device_set_drvdata(sdev, hub);
  824. +
  825. + status = ssam_device_notifier_register(sdev, &hub->notif);
  826. + if (status)
  827. + return status;
  828. +
  829. + status = sysfs_create_group(&sdev->dev.kobj, &ssam_kip_hub_group);
  830. + if (status)
  831. + goto err;
  832. +
  833. + schedule_delayed_work(&hub->update_work, 0);
  834. + return 0;
  835. +
  836. +err:
  837. + ssam_device_notifier_unregister(sdev, &hub->notif);
  838. + cancel_delayed_work_sync(&hub->update_work);
  839. + ssam_remove_clients(&sdev->dev);
  840. + return status;
  841. +}
  842. +
  843. +static void ssam_kip_hub_remove(struct ssam_device *sdev)
  844. +{
  845. + struct ssam_kip_hub *hub = ssam_device_get_drvdata(sdev);
  846. +
  847. + sysfs_remove_group(&sdev->dev.kobj, &ssam_kip_hub_group);
  848. +
  849. + ssam_device_notifier_unregister(sdev, &hub->notif);
  850. + cancel_delayed_work_sync(&hub->update_work);
  851. + ssam_remove_clients(&sdev->dev);
  852. +}
  853. +
  854. +static const struct ssam_device_id ssam_kip_hub_match[] = {
  855. + { SSAM_SDEV(KIP, 0x01, 0x00, 0x00) },
  856. + { },
  857. +};
  858. +
  859. +static struct ssam_device_driver ssam_kip_hub_driver = {
  860. + .probe = ssam_kip_hub_probe,
  861. + .remove = ssam_kip_hub_remove,
  862. + .match_table = ssam_kip_hub_match,
  863. + .driver = {
  864. + .name = "surface_kip_hub",
  865. + .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  866. + .pm = &ssam_kip_hub_pm_ops,
  867. + },
  868. +};
  869. +
  870. +
  871. /* -- SSAM platform/meta-hub driver. ---------------------------------------- */
  872. static const struct acpi_device_id ssam_platform_hub_match[] = {
  873. @@ -636,18 +867,30 @@ static int __init ssam_device_hub_init(void)
  874. status = platform_driver_register(&ssam_platform_hub_driver);
  875. if (status)
  876. - return status;
  877. + goto err_platform;
  878. status = ssam_device_driver_register(&ssam_base_hub_driver);
  879. if (status)
  880. - platform_driver_unregister(&ssam_platform_hub_driver);
  881. + goto err_base;
  882. +
  883. + status = ssam_device_driver_register(&ssam_kip_hub_driver);
  884. + if (status)
  885. + goto err_kip;
  886. + return 0;
  887. +
  888. +err_kip:
  889. + ssam_device_driver_unregister(&ssam_base_hub_driver);
  890. +err_base:
  891. + platform_driver_unregister(&ssam_platform_hub_driver);
  892. +err_platform:
  893. return status;
  894. }
  895. module_init(ssam_device_hub_init);
  896. static void __exit ssam_device_hub_exit(void)
  897. {
  898. + ssam_device_driver_unregister(&ssam_kip_hub_driver);
  899. ssam_device_driver_unregister(&ssam_base_hub_driver);
  900. platform_driver_unregister(&ssam_platform_hub_driver);
  901. }
  902. --
  903. 2.36.0
  904. From 0e443bc77964f90eed651cb32af45398060ba42e Mon Sep 17 00:00:00 2001
  905. From: Maximilian Luz <luzmaximilian@gmail.com>
  906. Date: Wed, 27 Oct 2021 22:33:03 +0200
  907. Subject: [PATCH] platform/surface: aggregator_registry: Add support for
  908. keyboard cover on Surface Pro 8
  909. Add support for the detachable keyboard cover on the Surface Pro 8.
  910. The keyboard cover on the Surface Pro 8 is, unlike the keyboard covers
  911. of earlier Surface Pro generations, handled via the Surface System
  912. Aggregator Module (SSAM). The keyboard and touchpad (as well as other
  913. HID input devices) of this cover are standard SSAM HID client devices
  914. (just like keyboard and touchpad on e.g. the Surface Laptop 3 and 4),
  915. however, some care needs to be taken as they can be physically detached
  916. (similarly to the Surface Book 3). Specifically, the respective SSAM
  917. client devices need to be removed when the keyboard cover has been
  918. detached and (re-)initialized when the keyboard cover has been
  919. (re-)attached.
  920. On the Surface Pro 8, detachment of the keyboard cover (and by extension
  921. its devices) is managed via the KIP subsystem. Therefore, said devices
  922. need to be registered under the KIP device hub, which in turn will
  923. remove and re-create/re-initialize those devices as needed.
  924. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  925. Patchset: surface-sam
  926. ---
  927. .../surface/surface_aggregator_registry.c | 37 ++++++++++++++++++-
  928. 1 file changed, 36 insertions(+), 1 deletion(-)
  929. diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
  930. index 4838ce6519a6..c0e29c0514df 100644
  931. --- a/drivers/platform/surface/surface_aggregator_registry.c
  932. +++ b/drivers/platform/surface/surface_aggregator_registry.c
  933. @@ -47,6 +47,12 @@ static const struct software_node ssam_node_hub_base = {
  934. .parent = &ssam_node_root,
  935. };
  936. +/* KIP device hub (connects keyboard cover devices on Surface Pro 8). */
  937. +static const struct software_node ssam_node_hub_kip = {
  938. + .name = "ssam:01:0e:01:00:00",
  939. + .parent = &ssam_node_root,
  940. +};
  941. +
  942. /* AC adapter. */
  943. static const struct software_node ssam_node_bat_ac = {
  944. .name = "ssam:01:02:01:01:01",
  945. @@ -155,6 +161,30 @@ static const struct software_node ssam_node_hid_base_iid6 = {
  946. .parent = &ssam_node_hub_base,
  947. };
  948. +/* HID keyboard (KIP hub). */
  949. +static const struct software_node ssam_node_hid_kip_keyboard = {
  950. + .name = "ssam:01:15:02:01:00",
  951. + .parent = &ssam_node_hub_kip,
  952. +};
  953. +
  954. +/* HID pen stash (KIP hub; pen taken / stashed away evens). */
  955. +static const struct software_node ssam_node_hid_kip_penstash = {
  956. + .name = "ssam:01:15:02:02:00",
  957. + .parent = &ssam_node_hub_kip,
  958. +};
  959. +
  960. +/* HID touchpad (KIP hub). */
  961. +static const struct software_node ssam_node_hid_kip_touchpad = {
  962. + .name = "ssam:01:15:02:03:00",
  963. + .parent = &ssam_node_hub_kip,
  964. +};
  965. +
  966. +/* HID device instance 5 (KIP hub, unknown HID device). */
  967. +static const struct software_node ssam_node_hid_kip_iid5 = {
  968. + .name = "ssam:01:15:02:05:00",
  969. + .parent = &ssam_node_hub_kip,
  970. +};
  971. +
  972. /*
  973. * Devices for 5th- and 6th-generations models:
  974. * - Surface Book 2,
  975. @@ -230,10 +260,15 @@ static const struct software_node *ssam_node_group_sp7[] = {
  976. static const struct software_node *ssam_node_group_sp8[] = {
  977. &ssam_node_root,
  978. + &ssam_node_hub_kip,
  979. &ssam_node_bat_ac,
  980. &ssam_node_bat_main,
  981. &ssam_node_tmp_pprof,
  982. - /* TODO: Add support for keyboard cover. */
  983. + &ssam_node_hid_kip_keyboard,
  984. + &ssam_node_hid_kip_penstash,
  985. + &ssam_node_hid_kip_touchpad,
  986. + &ssam_node_hid_kip_iid5,
  987. + /* TODO: Add support for tablet mode switch. */
  988. NULL,
  989. };
  990. --
  991. 2.36.0
  992. From f3ff67b118c39ab15a3443f953f93fa933c1d957 Mon Sep 17 00:00:00 2001
  993. From: Maximilian Luz <luzmaximilian@gmail.com>
  994. Date: Tue, 8 Jun 2021 03:19:20 +0200
  995. Subject: [PATCH] platform/surface: Add KIP tablet-mode switch
  996. Add a driver providing a tablet-mode switch input device for Surface
  997. models using the KIP subsystem to manage detachable peripherals.
  998. The Surface Pro 8 has a detachable keyboard cover. Unlike the keyboard
  999. covers of previous generation Surface Pro models, this cover is fully
  1000. handled by the Surface System Aggregator Module (SSAM). The SSAM KIP
  1001. subsystem (full name unknown, abbreviation found through reverse
  1002. engineering) provides notifications for mode changes of the cover.
  1003. Specifically, it allows us to know when the cover has been folded back,
  1004. detached, or whether it is in laptop mode.
  1005. The driver introduced with this change captures these events and
  1006. translates them to standard SW_TABLET_MODE input events.
  1007. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  1008. Patchset: surface-sam
  1009. ---
  1010. MAINTAINERS | 6 +
  1011. drivers/platform/surface/Kconfig | 22 ++
  1012. drivers/platform/surface/Makefile | 1 +
  1013. .../surface/surface_kip_tablet_switch.c | 245 ++++++++++++++++++
  1014. 4 files changed, 274 insertions(+)
  1015. create mode 100644 drivers/platform/surface/surface_kip_tablet_switch.c
  1016. diff --git a/MAINTAINERS b/MAINTAINERS
  1017. index d9b2f1731ee0..4d83cd26e299 100644
  1018. --- a/MAINTAINERS
  1019. +++ b/MAINTAINERS
  1020. @@ -12823,6 +12823,12 @@ L: platform-driver-x86@vger.kernel.org
  1021. S: Maintained
  1022. F: drivers/platform/surface/surface_hotplug.c
  1023. +MICROSOFT SURFACE KIP TABLET-MODE SWITCH
  1024. +M: Maximilian Luz <luzmaximilian@gmail.com>
  1025. +L: platform-driver-x86@vger.kernel.org
  1026. +S: Maintained
  1027. +F: drivers/platform/surface/surface_kip_tablet_switch.c
  1028. +
  1029. MICROSOFT SURFACE PLATFORM PROFILE DRIVER
  1030. M: Maximilian Luz <luzmaximilian@gmail.com>
  1031. L: platform-driver-x86@vger.kernel.org
  1032. diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig
  1033. index 463f1ec5c14e..9c228090c35b 100644
  1034. --- a/drivers/platform/surface/Kconfig
  1035. +++ b/drivers/platform/surface/Kconfig
  1036. @@ -156,6 +156,28 @@ config SURFACE_HOTPLUG
  1037. Select M or Y here, if you want to (fully) support hot-plugging of
  1038. dGPU devices on the Surface Book 2 and/or 3 during D3cold.
  1039. +config SURFACE_KIP_TABLET_SWITCH
  1040. + tristate "Surface KIP Tablet-Mode Switch Driver"
  1041. + depends on SURFACE_AGGREGATOR
  1042. + depends on SURFACE_AGGREGATOR_BUS
  1043. + depends on INPUT
  1044. + help
  1045. + Provides a tablet-mode switch input device on Microsoft Surface models
  1046. + using the KIP subsystem for detachable keyboards (e.g. keyboard
  1047. + covers).
  1048. +
  1049. + The KIP subsystem is used on newer Surface generations to handle
  1050. + detachable input peripherals, specifically the keyboard cover
  1051. + (containing keyboard and touchpad) on the Surface Pro 8. This module
  1052. + provides a driver to let user-space know when the device should be
  1053. + considered in tablet-mode due to the keyboard cover being detached or
  1054. + folded back (essentially signaling when the keyboard is not available
  1055. + for input). It does so by creating a tablet-mode switch input device,
  1056. + sending the standard SW_TABLET_MODE event on mode change.
  1057. +
  1058. + Select M or Y here, if you want to provide tablet-mode switch input
  1059. + events on the Surface Pro 8.
  1060. +
  1061. config SURFACE_PLATFORM_PROFILE
  1062. tristate "Surface Platform Profile Driver"
  1063. depends on ACPI
  1064. diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile
  1065. index 32889482de55..6d9291c993c4 100644
  1066. --- a/drivers/platform/surface/Makefile
  1067. +++ b/drivers/platform/surface/Makefile
  1068. @@ -14,5 +14,6 @@ obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o
  1069. obj-$(CONFIG_SURFACE_DTX) += surface_dtx.o
  1070. obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o
  1071. obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o
  1072. +obj-$(CONFIG_SURFACE_KIP_TABLET_SWITCH) += surface_kip_tablet_switch.o
  1073. obj-$(CONFIG_SURFACE_PLATFORM_PROFILE) += surface_platform_profile.o
  1074. obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
  1075. diff --git a/drivers/platform/surface/surface_kip_tablet_switch.c b/drivers/platform/surface/surface_kip_tablet_switch.c
  1076. new file mode 100644
  1077. index 000000000000..458470067579
  1078. --- /dev/null
  1079. +++ b/drivers/platform/surface/surface_kip_tablet_switch.c
  1080. @@ -0,0 +1,245 @@
  1081. +// SPDX-License-Identifier: GPL-2.0+
  1082. +/*
  1083. + * Surface System Aggregator Module (SSAM) tablet mode switch via KIP
  1084. + * subsystem.
  1085. + *
  1086. + * Copyright (C) 2021 Maximilian Luz <luzmaximilian@gmail.com>
  1087. + */
  1088. +
  1089. +#include <linux/input.h>
  1090. +#include <linux/kernel.h>
  1091. +#include <linux/module.h>
  1092. +#include <linux/platform_device.h>
  1093. +#include <linux/types.h>
  1094. +#include <linux/workqueue.h>
  1095. +
  1096. +#include <linux/surface_aggregator/controller.h>
  1097. +#include <linux/surface_aggregator/device.h>
  1098. +
  1099. +#define SSAM_EVENT_KIP_CID_LID_STATE 0x1d
  1100. +
  1101. +enum ssam_kip_lid_state {
  1102. + SSAM_KIP_LID_STATE_DISCONNECTED = 0x01,
  1103. + SSAM_KIP_LID_STATE_CLOSED = 0x02,
  1104. + SSAM_KIP_LID_STATE_LAPTOP = 0x03,
  1105. + SSAM_KIP_LID_STATE_FOLDED_CANVAS = 0x04,
  1106. + SSAM_KIP_LID_STATE_FOLDED_BACK = 0x05,
  1107. +};
  1108. +
  1109. +struct ssam_kip_sw {
  1110. + struct ssam_device *sdev;
  1111. +
  1112. + enum ssam_kip_lid_state state;
  1113. + struct work_struct update_work;
  1114. + struct input_dev *mode_switch;
  1115. +
  1116. + struct ssam_event_notifier notif;
  1117. +};
  1118. +
  1119. +SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_lid_state, u8, {
  1120. + .target_category = SSAM_SSH_TC_KIP,
  1121. + .target_id = 0x01,
  1122. + .command_id = 0x1d,
  1123. + .instance_id = 0x00,
  1124. +});
  1125. +
  1126. +static int ssam_kip_get_lid_state(struct ssam_kip_sw *sw, enum ssam_kip_lid_state *state)
  1127. +{
  1128. + int status;
  1129. + u8 raw;
  1130. +
  1131. + status = ssam_retry(__ssam_kip_get_lid_state, sw->sdev->ctrl, &raw);
  1132. + if (status < 0) {
  1133. + dev_err(&sw->sdev->dev, "failed to query KIP lid state: %d\n", status);
  1134. + return status;
  1135. + }
  1136. +
  1137. + *state = raw;
  1138. + return 0;
  1139. +}
  1140. +
  1141. +static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf)
  1142. +{
  1143. + struct ssam_kip_sw *sw = dev_get_drvdata(dev);
  1144. + const char *state;
  1145. +
  1146. + switch (sw->state) {
  1147. + case SSAM_KIP_LID_STATE_DISCONNECTED:
  1148. + state = "disconnected";
  1149. + break;
  1150. +
  1151. + case SSAM_KIP_LID_STATE_CLOSED:
  1152. + state = "closed";
  1153. + break;
  1154. +
  1155. + case SSAM_KIP_LID_STATE_LAPTOP:
  1156. + state = "laptop";
  1157. + break;
  1158. +
  1159. + case SSAM_KIP_LID_STATE_FOLDED_CANVAS:
  1160. + state = "folded-canvas";
  1161. + break;
  1162. +
  1163. + case SSAM_KIP_LID_STATE_FOLDED_BACK:
  1164. + state = "folded-back";
  1165. + break;
  1166. +
  1167. + default:
  1168. + state = "<unknown>";
  1169. + dev_warn(dev, "unknown KIP lid state: %d\n", sw->state);
  1170. + break;
  1171. + }
  1172. +
  1173. + return sysfs_emit(buf, "%s\n", state);
  1174. +}
  1175. +static DEVICE_ATTR_RO(state);
  1176. +
  1177. +static struct attribute *ssam_kip_sw_attrs[] = {
  1178. + &dev_attr_state.attr,
  1179. + NULL,
  1180. +};
  1181. +
  1182. +static const struct attribute_group ssam_kip_sw_group = {
  1183. + .attrs = ssam_kip_sw_attrs,
  1184. +};
  1185. +
  1186. +static void ssam_kip_sw_update_workfn(struct work_struct *work)
  1187. +{
  1188. + struct ssam_kip_sw *sw = container_of(work, struct ssam_kip_sw, update_work);
  1189. + enum ssam_kip_lid_state state;
  1190. + int tablet, status;
  1191. +
  1192. + status = ssam_kip_get_lid_state(sw, &state);
  1193. + if (status)
  1194. + return;
  1195. +
  1196. + if (sw->state == state)
  1197. + return;
  1198. + sw->state = state;
  1199. +
  1200. + /* Send SW_TABLET_MODE event. */
  1201. + tablet = state != SSAM_KIP_LID_STATE_LAPTOP;
  1202. + input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet);
  1203. + input_sync(sw->mode_switch);
  1204. +}
  1205. +
  1206. +static u32 ssam_kip_sw_notif(struct ssam_event_notifier *nf, const struct ssam_event *event)
  1207. +{
  1208. + struct ssam_kip_sw *sw = container_of(nf, struct ssam_kip_sw, notif);
  1209. +
  1210. + if (event->command_id != SSAM_EVENT_KIP_CID_LID_STATE)
  1211. + return 0; /* Return "unhandled". */
  1212. +
  1213. + if (event->length < 1) {
  1214. + dev_err(&sw->sdev->dev, "unexpected payload size: %u\n", event->length);
  1215. + return 0;
  1216. + }
  1217. +
  1218. + schedule_work(&sw->update_work);
  1219. + return SSAM_NOTIF_HANDLED;
  1220. +}
  1221. +
  1222. +static int __maybe_unused ssam_kip_sw_resume(struct device *dev)
  1223. +{
  1224. + struct ssam_kip_sw *sw = dev_get_drvdata(dev);
  1225. +
  1226. + schedule_work(&sw->update_work);
  1227. + return 0;
  1228. +}
  1229. +static SIMPLE_DEV_PM_OPS(ssam_kip_sw_pm_ops, NULL, ssam_kip_sw_resume);
  1230. +
  1231. +static int ssam_kip_sw_probe(struct ssam_device *sdev)
  1232. +{
  1233. + struct ssam_kip_sw *sw;
  1234. + int tablet, status;
  1235. +
  1236. + sw = devm_kzalloc(&sdev->dev, sizeof(*sw), GFP_KERNEL);
  1237. + if (!sw)
  1238. + return -ENOMEM;
  1239. +
  1240. + sw->sdev = sdev;
  1241. + INIT_WORK(&sw->update_work, ssam_kip_sw_update_workfn);
  1242. +
  1243. + ssam_device_set_drvdata(sdev, sw);
  1244. +
  1245. + /* Get initial state. */
  1246. + status = ssam_kip_get_lid_state(sw, &sw->state);
  1247. + if (status)
  1248. + return status;
  1249. +
  1250. + /* Set up tablet mode switch. */
  1251. + sw->mode_switch = devm_input_allocate_device(&sdev->dev);
  1252. + if (!sw->mode_switch)
  1253. + return -ENOMEM;
  1254. +
  1255. + sw->mode_switch->name = "Microsoft Surface KIP Tablet Mode Switch";
  1256. + sw->mode_switch->phys = "ssam/01:0e:01:00:01/input0";
  1257. + sw->mode_switch->id.bustype = BUS_HOST;
  1258. + sw->mode_switch->dev.parent = &sdev->dev;
  1259. +
  1260. + tablet = sw->state != SSAM_KIP_LID_STATE_LAPTOP;
  1261. + input_set_capability(sw->mode_switch, EV_SW, SW_TABLET_MODE);
  1262. + input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet);
  1263. +
  1264. + status = input_register_device(sw->mode_switch);
  1265. + if (status)
  1266. + return status;
  1267. +
  1268. + /* Set up notifier. */
  1269. + sw->notif.base.priority = 0;
  1270. + sw->notif.base.fn = ssam_kip_sw_notif;
  1271. + sw->notif.event.reg = SSAM_EVENT_REGISTRY_SAM;
  1272. + sw->notif.event.id.target_category = SSAM_SSH_TC_KIP,
  1273. + sw->notif.event.id.instance = 0,
  1274. + sw->notif.event.mask = SSAM_EVENT_MASK_TARGET;
  1275. + sw->notif.event.flags = SSAM_EVENT_SEQUENCED;
  1276. +
  1277. + status = ssam_device_notifier_register(sdev, &sw->notif);
  1278. + if (status)
  1279. + return status;
  1280. +
  1281. + status = sysfs_create_group(&sdev->dev.kobj, &ssam_kip_sw_group);
  1282. + if (status)
  1283. + goto err;
  1284. +
  1285. + /* We might have missed events during setup, so check again. */
  1286. + schedule_work(&sw->update_work);
  1287. + return 0;
  1288. +
  1289. +err:
  1290. + ssam_device_notifier_unregister(sdev, &sw->notif);
  1291. + cancel_work_sync(&sw->update_work);
  1292. + return status;
  1293. +}
  1294. +
  1295. +static void ssam_kip_sw_remove(struct ssam_device *sdev)
  1296. +{
  1297. + struct ssam_kip_sw *sw = ssam_device_get_drvdata(sdev);
  1298. +
  1299. + sysfs_remove_group(&sdev->dev.kobj, &ssam_kip_sw_group);
  1300. +
  1301. + ssam_device_notifier_unregister(sdev, &sw->notif);
  1302. + cancel_work_sync(&sw->update_work);
  1303. +}
  1304. +
  1305. +static const struct ssam_device_id ssam_kip_sw_match[] = {
  1306. + { SSAM_SDEV(KIP, 0x01, 0x00, 0x01) },
  1307. + { },
  1308. +};
  1309. +MODULE_DEVICE_TABLE(ssam, ssam_kip_sw_match);
  1310. +
  1311. +static struct ssam_device_driver ssam_kip_sw_driver = {
  1312. + .probe = ssam_kip_sw_probe,
  1313. + .remove = ssam_kip_sw_remove,
  1314. + .match_table = ssam_kip_sw_match,
  1315. + .driver = {
  1316. + .name = "surface_kip_tablet_mode_switch",
  1317. + .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  1318. + .pm = &ssam_kip_sw_pm_ops,
  1319. + },
  1320. +};
  1321. +module_ssam_device_driver(ssam_kip_sw_driver);
  1322. +
  1323. +MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
  1324. +MODULE_DESCRIPTION("Tablet mode switch driver for Surface devices using KIP subsystem");
  1325. +MODULE_LICENSE("GPL");
  1326. --
  1327. 2.36.0
  1328. From 228072592809b63575ea69a4d2bb9192beb74b02 Mon Sep 17 00:00:00 2001
  1329. From: Maximilian Luz <luzmaximilian@gmail.com>
  1330. Date: Wed, 27 Oct 2021 22:33:03 +0200
  1331. Subject: [PATCH] platform/surface: aggregator_registry: Add support for tablet
  1332. mode switch on Surface Pro 8
  1333. Add a KIP subsystem tablet-mode switch device for the Surface Pro 8.
  1334. The respective driver for this device provides SW_TABLET_MODE input
  1335. events for user-space based on the state of the keyboard cover (e.g.
  1336. detached, folded-back, normal/laptop mode).
  1337. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  1338. Patchset: surface-sam
  1339. ---
  1340. drivers/platform/surface/surface_aggregator_registry.c | 8 +++++++-
  1341. 1 file changed, 7 insertions(+), 1 deletion(-)
  1342. diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
  1343. index c0e29c0514df..eaf0054627a5 100644
  1344. --- a/drivers/platform/surface/surface_aggregator_registry.c
  1345. +++ b/drivers/platform/surface/surface_aggregator_registry.c
  1346. @@ -77,6 +77,12 @@ static const struct software_node ssam_node_tmp_pprof = {
  1347. .parent = &ssam_node_root,
  1348. };
  1349. +/* Tablet-mode switch via KIP subsystem. */
  1350. +static const struct software_node ssam_node_kip_tablet_switch = {
  1351. + .name = "ssam:01:0e:01:00:01",
  1352. + .parent = &ssam_node_root,
  1353. +};
  1354. +
  1355. /* DTX / detachment-system device (Surface Book 3). */
  1356. static const struct software_node ssam_node_bas_dtx = {
  1357. .name = "ssam:01:11:01:00:00",
  1358. @@ -264,11 +270,11 @@ static const struct software_node *ssam_node_group_sp8[] = {
  1359. &ssam_node_bat_ac,
  1360. &ssam_node_bat_main,
  1361. &ssam_node_tmp_pprof,
  1362. + &ssam_node_kip_tablet_switch,
  1363. &ssam_node_hid_kip_keyboard,
  1364. &ssam_node_hid_kip_penstash,
  1365. &ssam_node_hid_kip_touchpad,
  1366. &ssam_node_hid_kip_iid5,
  1367. - /* TODO: Add support for tablet mode switch. */
  1368. NULL,
  1369. };
  1370. --
  1371. 2.36.0