1111
1212#include <linux/acpi.h>
1313#include <linux/kernel.h>
14+ #include <linux/limits.h>
1415#include <linux/module.h>
16+ #include <linux/mutex.h>
1517#include <linux/platform_device.h>
1618#include <linux/property.h>
19+ #include <linux/types.h>
1720
1821#include <linux/surface_aggregator/controller.h>
1922#include <linux/surface_aggregator/device.h>
@@ -38,6 +41,12 @@ static const struct software_node ssam_node_root = {
3841 .name = "ssam_platform_hub" ,
3942};
4043
44+ /* Base device hub (devices attached to Surface Book 3 base). */
45+ static const struct software_node ssam_node_hub_base = {
46+ .name = "ssam:00:00:02:00:00" ,
47+ .parent = & ssam_node_root ,
48+ };
49+
4150/* Devices for Surface Book 2. */
4251static const struct software_node * ssam_node_group_sb2 [] = {
4352 & ssam_node_root ,
@@ -47,6 +56,7 @@ static const struct software_node *ssam_node_group_sb2[] = {
4756/* Devices for Surface Book 3. */
4857static const struct software_node * ssam_node_group_sb3 [] = {
4958 & ssam_node_root ,
59+ & ssam_node_hub_base ,
5060 NULL ,
5161};
5262
@@ -177,6 +187,230 @@ static int ssam_hub_add_devices(struct device *parent, struct ssam_controller *c
177187}
178188
179189
190+ /* -- SSAM base-hub driver. ------------------------------------------------- */
191+
192+ enum ssam_base_hub_state {
193+ SSAM_BASE_HUB_UNINITIALIZED ,
194+ SSAM_BASE_HUB_CONNECTED ,
195+ SSAM_BASE_HUB_DISCONNECTED ,
196+ };
197+
198+ struct ssam_base_hub {
199+ struct ssam_device * sdev ;
200+
201+ struct mutex lock ; /* Guards state update checks and transitions. */
202+ enum ssam_base_hub_state state ;
203+
204+ struct ssam_event_notifier notif ;
205+ };
206+
207+ static SSAM_DEFINE_SYNC_REQUEST_R (ssam_bas_query_opmode , u8 , {
208+ .target_category = SSAM_SSH_TC_BAS ,
209+ .target_id = 0x01 ,
210+ .command_id = 0x0d ,
211+ .instance_id = 0x00 ,
212+ }) ;
213+
214+ #define SSAM_BAS_OPMODE_TABLET 0x00
215+ #define SSAM_EVENT_BAS_CID_CONNECTION 0x0c
216+
217+ static int ssam_base_hub_query_state (struct ssam_base_hub * hub , enum ssam_base_hub_state * state )
218+ {
219+ u8 opmode ;
220+ int status ;
221+
222+ status = ssam_retry (ssam_bas_query_opmode , hub -> sdev -> ctrl , & opmode );
223+ if (status < 0 ) {
224+ dev_err (& hub -> sdev -> dev , "failed to query base state: %d\n" , status );
225+ return status ;
226+ }
227+
228+ if (opmode != SSAM_BAS_OPMODE_TABLET )
229+ * state = SSAM_BASE_HUB_CONNECTED ;
230+ else
231+ * state = SSAM_BASE_HUB_DISCONNECTED ;
232+
233+ return 0 ;
234+ }
235+
236+ static ssize_t ssam_base_hub_state_show (struct device * dev , struct device_attribute * attr ,
237+ char * buf )
238+ {
239+ struct ssam_base_hub * hub = dev_get_drvdata (dev );
240+ bool connected ;
241+
242+ mutex_lock (& hub -> lock );
243+ connected = hub -> state == SSAM_BASE_HUB_CONNECTED ;
244+ mutex_unlock (& hub -> lock );
245+
246+ return sysfs_emit (buf , "%d\n" , connected );
247+ }
248+
249+ static struct device_attribute ssam_base_hub_attr_state =
250+ __ATTR (state , 0444 , ssam_base_hub_state_show , NULL );
251+
252+ static struct attribute * ssam_base_hub_attrs [] = {
253+ & ssam_base_hub_attr_state .attr ,
254+ NULL ,
255+ };
256+
257+ const struct attribute_group ssam_base_hub_group = {
258+ .attrs = ssam_base_hub_attrs ,
259+ };
260+
261+ static int __ssam_base_hub_update (struct ssam_base_hub * hub , enum ssam_base_hub_state new )
262+ {
263+ struct fwnode_handle * node = dev_fwnode (& hub -> sdev -> dev );
264+ int status = 0 ;
265+
266+ lockdep_assert_held (& hub -> lock );
267+
268+ if (hub -> state == new )
269+ return 0 ;
270+ hub -> state = new ;
271+
272+ if (hub -> state == SSAM_BASE_HUB_CONNECTED )
273+ status = ssam_hub_add_devices (& hub -> sdev -> dev , hub -> sdev -> ctrl , node );
274+ else
275+ ssam_hub_remove_devices (& hub -> sdev -> dev );
276+
277+ if (status )
278+ dev_err (& hub -> sdev -> dev , "failed to update base-hub devices: %d\n" , status );
279+
280+ return status ;
281+ }
282+
283+ static int ssam_base_hub_update (struct ssam_base_hub * hub )
284+ {
285+ enum ssam_base_hub_state state ;
286+ int status ;
287+
288+ mutex_lock (& hub -> lock );
289+
290+ status = ssam_base_hub_query_state (hub , & state );
291+ if (!status )
292+ status = __ssam_base_hub_update (hub , state );
293+
294+ mutex_unlock (& hub -> lock );
295+ return status ;
296+ }
297+
298+ static u32 ssam_base_hub_notif (struct ssam_event_notifier * nf , const struct ssam_event * event )
299+ {
300+ struct ssam_base_hub * hub ;
301+ struct ssam_device * sdev ;
302+ enum ssam_base_hub_state new ;
303+
304+ hub = container_of (nf , struct ssam_base_hub , notif );
305+ sdev = hub -> sdev ;
306+
307+ if (event -> command_id != SSAM_EVENT_BAS_CID_CONNECTION )
308+ return 0 ;
309+
310+ if (event -> length < 1 ) {
311+ dev_err (& sdev -> dev , "unexpected payload size: %u\n" ,
312+ event -> length );
313+ return 0 ;
314+ }
315+
316+ if (event -> data [0 ])
317+ new = SSAM_BASE_HUB_CONNECTED ;
318+ else
319+ new = SSAM_BASE_HUB_DISCONNECTED ;
320+
321+ mutex_lock (& hub -> lock );
322+ __ssam_base_hub_update (hub , new );
323+ mutex_unlock (& hub -> lock );
324+
325+ /*
326+ * Do not return SSAM_NOTIF_HANDLED: The event should be picked up and
327+ * consumed by the detachment system driver. We're just a (more or less)
328+ * silent observer.
329+ */
330+ return 0 ;
331+ }
332+
333+ static int __maybe_unused ssam_base_hub_resume (struct device * dev )
334+ {
335+ return ssam_base_hub_update (dev_get_drvdata (dev ));
336+ }
337+ static SIMPLE_DEV_PM_OPS (ssam_base_hub_pm_ops , NULL, ssam_base_hub_resume ) ;
338+
339+ static int ssam_base_hub_probe (struct ssam_device * sdev )
340+ {
341+ struct ssam_base_hub * hub ;
342+ int status ;
343+
344+ hub = devm_kzalloc (& sdev -> dev , sizeof (* hub ), GFP_KERNEL );
345+ if (!hub )
346+ return - ENOMEM ;
347+
348+ mutex_init (& hub -> lock );
349+
350+ hub -> sdev = sdev ;
351+ hub -> state = SSAM_BASE_HUB_UNINITIALIZED ;
352+
353+ hub -> notif .base .priority = INT_MAX ; /* This notifier should run first. */
354+ hub -> notif .base .fn = ssam_base_hub_notif ;
355+ hub -> notif .event .reg = SSAM_EVENT_REGISTRY_SAM ;
356+ hub -> notif .event .id .target_category = SSAM_SSH_TC_BAS ,
357+ hub -> notif .event .id .instance = 0 ,
358+ hub -> notif .event .mask = SSAM_EVENT_MASK_NONE ;
359+ hub -> notif .event .flags = SSAM_EVENT_SEQUENCED ;
360+
361+ ssam_device_set_drvdata (sdev , hub );
362+
363+ status = ssam_notifier_register (sdev -> ctrl , & hub -> notif );
364+ if (status )
365+ goto err_register ;
366+
367+ status = ssam_base_hub_update (hub );
368+ if (status )
369+ goto err_update ;
370+
371+ status = sysfs_create_group (& sdev -> dev .kobj , & ssam_base_hub_group );
372+ if (status )
373+ goto err_update ;
374+
375+ return 0 ;
376+
377+ err_update :
378+ ssam_notifier_unregister (sdev -> ctrl , & hub -> notif );
379+ ssam_hub_remove_devices (& sdev -> dev );
380+ err_register :
381+ mutex_destroy (& hub -> lock );
382+ return status ;
383+ }
384+
385+ static void ssam_base_hub_remove (struct ssam_device * sdev )
386+ {
387+ struct ssam_base_hub * hub = ssam_device_get_drvdata (sdev );
388+
389+ sysfs_remove_group (& sdev -> dev .kobj , & ssam_base_hub_group );
390+
391+ ssam_notifier_unregister (sdev -> ctrl , & hub -> notif );
392+ ssam_hub_remove_devices (& sdev -> dev );
393+
394+ mutex_destroy (& hub -> lock );
395+ }
396+
397+ static const struct ssam_device_id ssam_base_hub_match [] = {
398+ { SSAM_VDEV (HUB , 0x02 , SSAM_ANY_IID , 0x00 ) },
399+ { },
400+ };
401+
402+ static struct ssam_device_driver ssam_base_hub_driver = {
403+ .probe = ssam_base_hub_probe ,
404+ .remove = ssam_base_hub_remove ,
405+ .match_table = ssam_base_hub_match ,
406+ .driver = {
407+ .name = "surface_aggregator_base_hub" ,
408+ .probe_type = PROBE_PREFER_ASYNCHRONOUS ,
409+ .pm = & ssam_base_hub_pm_ops ,
410+ },
411+ };
412+
413+
180414/* -- SSAM platform/meta-hub driver. ---------------------------------------- */
181415
182416static const struct acpi_device_id ssam_platform_hub_match [] = {
@@ -277,7 +511,32 @@ static struct platform_driver ssam_platform_hub_driver = {
277511 .probe_type = PROBE_PREFER_ASYNCHRONOUS ,
278512 },
279513};
280- module_platform_driver (ssam_platform_hub_driver );
514+
515+
516+ /* -- Module initialization. ------------------------------------------------ */
517+
518+ static int __init ssam_device_hub_init (void )
519+ {
520+ int status ;
521+
522+ status = platform_driver_register (& ssam_platform_hub_driver );
523+ if (status )
524+ return status ;
525+
526+ status = ssam_device_driver_register (& ssam_base_hub_driver );
527+ if (status )
528+ platform_driver_unregister (& ssam_platform_hub_driver );
529+
530+ return status ;
531+ }
532+ module_init (ssam_device_hub_init );
533+
534+ static void __exit ssam_device_hub_exit (void )
535+ {
536+ ssam_device_driver_unregister (& ssam_base_hub_driver );
537+ platform_driver_unregister (& ssam_platform_hub_driver );
538+ }
539+ module_exit (ssam_device_hub_exit );
281540
282541MODULE_AUTHOR ("Maximilian Luz <luzmaximilian@gmail.com>" );
283542MODULE_DESCRIPTION ("Device-registry for Surface System Aggregator Module" );
0 commit comments