// 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 <luzmaximilian@gmail.com>
*/
#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 {
SAM_EVENT_CID_DTX_CONNECTION = 0x0c,
SAM_EVENT_CID_DTX_REQUEST = 0x0e,
SAM_EVENT_CID_DTX_CANCEL = 0x0f,
SAM_EVENT_CID_DTX_LATCH_STATUS = 0x11,
};
enum ssam_bas_base_state {
SSAM_BAS_BASE_STATE_DETACH_SUCCESS = 0x00,
SSAM_BAS_BASE_STATE_ATTACHED = 0x01,
SSAM_BAS_BASE_STATE_NOT_FEASIBLE = 0x02,
};
enum ssam_bas_latch_status {
SSAM_BAS_LATCH_STATUS_CLOSED = 0x00,
SSAM_BAS_LATCH_STATUS_OPENED = 0x01,
SSAM_BAS_LATCH_STATUS_FAILED_TO_OPEN = 0x02,
SSAM_BAS_LATCH_STATUS_FAILED_TO_REMAIN_OPEN = 0x03,
SSAM_BAS_LATCH_STATUS_FAILED_TO_CLOSE = 0x04,
};
enum ssam_bas_cancel_reason {
SSAM_BAS_CANCEL_REASON_NOT_FEASIBLE = 0x00, /* Low battery. */
SSAM_BAS_CANCEL_REASON_TIMEOUT = 0x02,
SSAM_BAS_CANCEL_REASON_FAILED_TO_OPEN = 0x03,
SSAM_BAS_CANCEL_REASON_FAILED_TO_REMAIN_OPEN = 0x04,
SSAM_BAS_CANCEL_REASON_FAILED_TO_CLOSE = 0x05,
};
struct ssam_bas_base_info {
u8 state;
u8 base_id;
} __packed;
static_assert(sizeof(struct ssam_bas_base_info) == 2);
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(