linux/drivers/platform/surface/surface_dtx.c

// SPDX-License-Identifier: GPL-2.0+
/*
 * Surface Book (gen. 2 and later) detachment system (DTX) driver.
 *
 * Provides a user-space interface to properly handle clipboard/tablet
 * (containing screen and processor) detachment from the base of the device
 * (containing the keyboard and optionally a discrete GPU). Allows to
 * acknowledge (to speed things up), abort (e.g. in case the dGPU is still in
 * use), or request detachment via user-space.
 *
 * Copyright (C) 2019-2022 Maximilian Luz <[email protected]>
 */

#include <linux/fs.h>
#include <linux/input.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/kfifo.h>
#include <linux/kref.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
#include <linux/workqueue.h>

#include <linux/surface_aggregator/controller.h>
#include <linux/surface_aggregator/device.h>
#include <linux/surface_aggregator/dtx.h>


/* -- SSAM interface. ------------------------------------------------------- */

enum sam_event_cid_bas {};

enum ssam_bas_base_state {};

enum ssam_bas_latch_status {};

enum ssam_bas_cancel_reason {};

struct ssam_bas_base_info {} __packed;

static_assert();

SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_lock, {
	.target_category = SSAM_SSH_TC_BAS,
	.target_id       = SSAM_SSH_TID_SAM,
	.command_id      = 0x06,
	.instance_id     = 0x00,
});

SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_unlock, {
	.target_category = SSAM_SSH_TC_BAS,
	.target_id       = SSAM_SSH_TID_SAM,
	.command_id      = 0x07,
	.instance_id     = 0x00,
});

SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_request, {
	.target_category = SSAM_SSH_TC_BAS,
	.target_id       = SSAM_SSH_TID_SAM,
	.command_id      = 0x08,
	.instance_id     = 0x00,
});

SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_confirm, {
	.target_category = SSAM_SSH_TC_BAS,
	.target_id       = SSAM_SSH_TID_SAM,
	.command_id      = 0x09,
	.instance_id     = 0x00,
});

SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_heartbeat, {
	.target_category = SSAM_SSH_TC_BAS,
	.target_id       = SSAM_SSH_TID_SAM,
	.command_id      = 0x0a,
	.instance_id     = 0x00,
});

SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_cancel, {
	.target_category = SSAM_SSH_TC_BAS,
	.target_id       = SSAM_SSH_TID_SAM,
	.command_id      = 0x0b,
	.instance_id     = 0x00,
});

SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_get_base, struct ssam_bas_base_info, {
	.target_category = SSAM_SSH_TC_BAS,
	.target_id       = SSAM_SSH_TID_SAM,
	.command_id      = 0x0c,
	.instance_id     = 0x00,
});

SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_get_device_mode, u8, {
	.target_category = SSAM_SSH_TC_BAS,
	.target_id       = SSAM_SSH_TID_SAM,
	.command_id      = 0x0d,
	.instance_id     = 0x00,
});

SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_get_latch_status, u8, {
	.target_category = SSAM_SSH_TC_BAS,
	.target_id       = SSAM_SSH_TID_SAM,
	.command_id      = 0x11,
	.instance_id     = 0x00,
});


/* -- Main structures. ------------------------------------------------------ */

enum sdtx_device_state {};

struct sdtx_device {};

enum sdtx_client_state {};

struct sdtx_client {};

static void __sdtx_device_release(struct kref *kref)
{}

static struct sdtx_device *sdtx_device_get(struct sdtx_device *ddev)
{}

static void sdtx_device_put(struct sdtx_device *ddev)
{}


/* -- Firmware value translations. ------------------------------------------ */

static u16 sdtx_translate_base_state(struct sdtx_device *ddev, u8 state)
{}

static u16 sdtx_translate_latch_status(struct sdtx_device *ddev, u8 status)
{}

static u16 sdtx_translate_cancel_reason(struct sdtx_device *ddev, u8 reason)
{}


/* -- IOCTLs. --------------------------------------------------------------- */

static int sdtx_ioctl_get_base_info(struct sdtx_device *ddev,
				    struct sdtx_base_info __user *buf)
{}

static int sdtx_ioctl_get_device_mode(struct sdtx_device *ddev, u16 __user *buf)
{}

static int sdtx_ioctl_get_latch_status(struct sdtx_device *ddev, u16 __user *buf)
{}

static long __surface_dtx_ioctl(struct sdtx_client *client, unsigned int cmd, unsigned long arg)
{}

static long surface_dtx_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{}


/* -- File operations. ------------------------------------------------------ */

static int surface_dtx_open(struct inode *inode, struct file *file)
{}

static int surface_dtx_release(struct inode *inode, struct file *file)
{}

static ssize_t surface_dtx_read(struct file *file, char __user *buf, size_t count, loff_t *offs)
{}

static __poll_t surface_dtx_poll(struct file *file, struct poll_table_struct *pt)
{}

static int surface_dtx_fasync(int fd, struct file *file, int on)
{}

static const struct file_operations surface_dtx_fops =;


/* -- Event handling/forwarding. -------------------------------------------- */

/*
 * The device operation mode is not immediately updated on the EC when the
 * base has been connected, i.e. querying the device mode inside the
 * connection event callback yields an outdated value. Thus, we can only
 * determine the new tablet-mode switch and device mode values after some
 * time.
 *
 * These delays have been chosen by experimenting. We first delay on connect
 * events, then check and validate the device mode against the base state and
 * if invalid delay again by the "recheck" delay.
 */
#define SDTX_DEVICE_MODE_DELAY_CONNECT
#define SDTX_DEVICE_MODE_DELAY_RECHECK

struct sdtx_status_event {} __packed;

struct sdtx_base_info_event {} __packed;

sdtx_generic_event;

static void sdtx_update_device_mode(struct sdtx_device *ddev, unsigned long delay);

/* Must be executed with ddev->write_lock held. */
static void sdtx_push_event(struct sdtx_device *ddev, struct sdtx_event *evt)
{}

static u32 sdtx_notifier(struct ssam_event_notifier *nf, const struct ssam_event *in)
{}


/* -- State update functions. ----------------------------------------------- */

static bool sdtx_device_mode_invalid(u8 mode, u8 base_state)
{}

static void sdtx_device_mode_workfn(struct work_struct *work)
{}

static void sdtx_update_device_mode(struct sdtx_device *ddev, unsigned long delay)
{}

/* Must be executed with ddev->write_lock held. */
static void __sdtx_device_state_update_base(struct sdtx_device *ddev,
					    struct ssam_bas_base_info info)
{}

/* Must be executed with ddev->write_lock held. */
static void __sdtx_device_state_update_mode(struct sdtx_device *ddev, u8 mode)
{}

/* Must be executed with ddev->write_lock held. */
static void __sdtx_device_state_update_latch(struct sdtx_device *ddev, u8 status)
{}

static void sdtx_device_state_workfn(struct work_struct *work)
{}

static void sdtx_update_device_state(struct sdtx_device *ddev, unsigned long delay)
{}


/* -- Common device initialization. ----------------------------------------- */

static int sdtx_device_init(struct sdtx_device *ddev, struct device *dev,
			    struct ssam_controller *ctrl)
{}

static struct sdtx_device *sdtx_device_create(struct device *dev, struct ssam_controller *ctrl)
{}

static void sdtx_device_destroy(struct sdtx_device *ddev)
{}


/* -- PM ops. --------------------------------------------------------------- */

#ifdef CONFIG_PM_SLEEP

static void surface_dtx_pm_complete(struct device *dev)
{}

static const struct dev_pm_ops surface_dtx_pm_ops =;

#else /* CONFIG_PM_SLEEP */

static const struct dev_pm_ops surface_dtx_pm_ops = {};

#endif /* CONFIG_PM_SLEEP */


/* -- Platform driver. ------------------------------------------------------ */

static int surface_dtx_platform_probe(struct platform_device *pdev)
{}

static void surface_dtx_platform_remove(struct platform_device *pdev)
{}

static const struct acpi_device_id surface_dtx_acpi_match[] =;
MODULE_DEVICE_TABLE(acpi, surface_dtx_acpi_match);

static struct platform_driver surface_dtx_platform_driver =;


/* -- SSAM device driver. --------------------------------------------------- */

#ifdef CONFIG_SURFACE_AGGREGATOR_BUS

static int surface_dtx_ssam_probe(struct ssam_device *sdev)
{}

static void surface_dtx_ssam_remove(struct ssam_device *sdev)
{}

static const struct ssam_device_id surface_dtx_ssam_match[] =;
MODULE_DEVICE_TABLE(ssam, surface_dtx_ssam_match);

static struct ssam_device_driver surface_dtx_ssam_driver =;

static int ssam_dtx_driver_register(void)
{}

static void ssam_dtx_driver_unregister(void)
{}

#else /* CONFIG_SURFACE_AGGREGATOR_BUS */

static int ssam_dtx_driver_register(void)
{
	return 0;
}

static void ssam_dtx_driver_unregister(void)
{
}

#endif /* CONFIG_SURFACE_AGGREGATOR_BUS */


/* -- Module setup. --------------------------------------------------------- */

static int __init surface_dtx_init(void)
{}
module_init();

static void __exit surface_dtx_exit(void)
{}
module_exit(surface_dtx_exit);

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();