platform/surface: aggregator_registry: Add KIP device hub
Add a Surface System Aggregator Module (SSAM) client device hub for hot-removable devices managed via the KIP subsystem. The KIP subsystem (full name unknown, abbreviation has been obtained through reverse engineering) is a subsystem that manages hot-removable SSAM client devices. Specifically, it manages HID input devices contained in the detachable keyboard cover of the Surface Pro 8 and Surface Pro X. The KIP subsystem handles a single group of devices (e.g. all devices contained in the keyboard cover) and cannot handle devices individually. Thus we model it as a client device hub, which (hot-)removes all devices contained under it once removal of the hub (e.g. keyboard cover) has been detected and (re-)adds all devices once the physical hub device has been (re-)attached. To do this, use the previously generified SSAM subsystem hub framework. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com> Link: https://lore.kernel.org/r/20220527023447.2460025-12-luzmaximilian@gmail.com Reviewed-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
Родитель
58a4d884b5
Коммит
d420185489
|
@ -551,6 +551,93 @@ static struct ssam_device_driver ssam_base_hub_driver = {
|
|||
};
|
||||
|
||||
|
||||
/* -- SSAM KIP-subsystem hub driver. ---------------------------------------- */
|
||||
|
||||
/*
|
||||
* Some devices may need a bit of time to be fully usable after being
|
||||
* (re-)connected. This delay has been determined via experimentation.
|
||||
*/
|
||||
#define SSAM_KIP_UPDATE_CONNECT_DELAY msecs_to_jiffies(250)
|
||||
|
||||
#define SSAM_EVENT_KIP_CID_CONNECTION 0x2c
|
||||
|
||||
SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_connection_state, u8, {
|
||||
.target_category = SSAM_SSH_TC_KIP,
|
||||
.target_id = 0x01,
|
||||
.command_id = 0x2c,
|
||||
.instance_id = 0x00,
|
||||
});
|
||||
|
||||
static int ssam_kip_get_connection_state(struct ssam_hub *hub, enum ssam_hub_state *state)
|
||||
{
|
||||
int status;
|
||||
u8 connected;
|
||||
|
||||
status = ssam_retry(__ssam_kip_get_connection_state, hub->sdev->ctrl, &connected);
|
||||
if (status < 0) {
|
||||
dev_err(&hub->sdev->dev, "failed to query KIP connection state: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
*state = connected ? SSAM_HUB_CONNECTED : SSAM_HUB_DISCONNECTED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 ssam_kip_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event)
|
||||
{
|
||||
struct ssam_hub *hub = container_of(nf, struct ssam_hub, notif);
|
||||
|
||||
if (event->command_id != SSAM_EVENT_KIP_CID_CONNECTION)
|
||||
return 0; /* Return "unhandled". */
|
||||
|
||||
if (event->length < 1) {
|
||||
dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssam_hub_update(hub, event->data[0]);
|
||||
return SSAM_NOTIF_HANDLED;
|
||||
}
|
||||
|
||||
static int ssam_kip_hub_probe(struct ssam_device *sdev)
|
||||
{
|
||||
struct ssam_hub *hub;
|
||||
|
||||
hub = devm_kzalloc(&sdev->dev, sizeof(*hub), GFP_KERNEL);
|
||||
if (!hub)
|
||||
return -ENOMEM;
|
||||
|
||||
hub->notif.base.priority = INT_MAX; /* This notifier should run first. */
|
||||
hub->notif.base.fn = ssam_kip_hub_notif;
|
||||
hub->notif.event.reg = SSAM_EVENT_REGISTRY_SAM;
|
||||
hub->notif.event.id.target_category = SSAM_SSH_TC_KIP,
|
||||
hub->notif.event.id.instance = 0,
|
||||
hub->notif.event.mask = SSAM_EVENT_MASK_TARGET;
|
||||
hub->notif.event.flags = SSAM_EVENT_SEQUENCED;
|
||||
|
||||
hub->connect_delay = SSAM_KIP_UPDATE_CONNECT_DELAY;
|
||||
hub->get_state = ssam_kip_get_connection_state;
|
||||
|
||||
return ssam_hub_setup(sdev, hub);
|
||||
}
|
||||
|
||||
static const struct ssam_device_id ssam_kip_hub_match[] = {
|
||||
{ SSAM_VDEV(HUB, 0x01, SSAM_SSH_TC_KIP, 0x00) },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct ssam_device_driver ssam_kip_hub_driver = {
|
||||
.probe = ssam_kip_hub_probe,
|
||||
.remove = ssam_hub_remove,
|
||||
.match_table = ssam_kip_hub_match,
|
||||
.driver = {
|
||||
.name = "surface_kip_hub",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.pm = &ssam_hub_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/* -- SSAM platform/meta-hub driver. ---------------------------------------- */
|
||||
|
||||
static const struct acpi_device_id ssam_platform_hub_match[] = {
|
||||
|
@ -673,18 +760,30 @@ static int __init ssam_device_hub_init(void)
|
|||
|
||||
status = platform_driver_register(&ssam_platform_hub_driver);
|
||||
if (status)
|
||||
return status;
|
||||
goto err_platform;
|
||||
|
||||
status = ssam_device_driver_register(&ssam_base_hub_driver);
|
||||
if (status)
|
||||
platform_driver_unregister(&ssam_platform_hub_driver);
|
||||
goto err_base;
|
||||
|
||||
status = ssam_device_driver_register(&ssam_kip_hub_driver);
|
||||
if (status)
|
||||
goto err_kip;
|
||||
|
||||
return 0;
|
||||
|
||||
err_kip:
|
||||
ssam_device_driver_unregister(&ssam_base_hub_driver);
|
||||
err_base:
|
||||
platform_driver_unregister(&ssam_platform_hub_driver);
|
||||
err_platform:
|
||||
return status;
|
||||
}
|
||||
module_init(ssam_device_hub_init);
|
||||
|
||||
static void __exit ssam_device_hub_exit(void)
|
||||
{
|
||||
ssam_device_driver_unregister(&ssam_kip_hub_driver);
|
||||
ssam_device_driver_unregister(&ssam_base_hub_driver);
|
||||
platform_driver_unregister(&ssam_platform_hub_driver);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче