Commit d4201854 authored by Maximilian Luz's avatar Maximilian Luz Committed by Hans de Goede
Browse files

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: default avatarMaximilian Luz <luzmaximilian@gmail.com>
Link: https://lore.kernel.org/r/20220527023447.2460025-12-luzmaximilian@gmail.com


Reviewed-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
parent 58a4d884
Loading
Loading
Loading
Loading
+101 −2
Original line number Diff line number Diff line
@@ -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);
}