linux/drivers/platform/x86/asus-tf103c-dock.c

// SPDX-License-Identifier: GPL-2.0+
/*
 * This is a driver for the keyboard, touchpad and USB port of the
 * keyboard dock for the Asus TF103C 2-in-1 tablet.
 *
 * This keyboard dock has its own I2C attached embedded controller
 * and the keyboard and touchpad are also connected over I2C,
 * instead of using the usual USB connection. This means that the
 * keyboard dock requires this special driver to function.
 *
 * Copyright (C) 2021 Hans de Goede <[email protected]>
 */

#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
#include <linux/hid.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/mod_devicetable.h>
#include <linux/moduleparam.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/workqueue.h>
#include <asm/unaligned.h>

static bool fnlock;
module_param(fnlock, bool, 0644);
MODULE_PARM_DESC();

#define TF103C_DOCK_DEV_NAME

#define TF103C_DOCK_HPD_DEBOUNCE

/*** Touchpad I2C device defines ***/
#define TF103C_DOCK_TP_ADDR

/*** Keyboard I2C device defines **A*/
#define TF103C_DOCK_KBD_ADDR

#define TF103C_DOCK_KBD_DATA_REG
#define TF103C_DOCK_KBD_DATA_MIN_LENGTH
#define TF103C_DOCK_KBD_DATA_MAX_LENGTH
#define TF103C_DOCK_KBD_DATA_MODIFIERS
#define TF103C_DOCK_KBD_DATA_KEYS
#define TF103C_DOCK_KBD_CMD_REG

#define TF103C_DOCK_KBD_CMD_ENABLE

/*** EC innterrupt data I2C device defines ***/
#define TF103C_DOCK_INTR_ADDR
#define TF103C_DOCK_INTR_DATA_REG

#define TF103C_DOCK_INTR_DATA1_OBF_MASK
#define TF103C_DOCK_INTR_DATA1_KEY_MASK
#define TF103C_DOCK_INTR_DATA1_KBC_MASK
#define TF103C_DOCK_INTR_DATA1_AUX_MASK
#define TF103C_DOCK_INTR_DATA1_SCI_MASK
#define TF103C_DOCK_INTR_DATA1_SMI_MASK
/* Special values for the OOB data on kbd_client / tp_client */
#define TF103C_DOCK_INTR_DATA1_OOB_VALUE
#define TF103C_DOCK_INTR_DATA2_OOB_VALUE

#define TF103C_DOCK_SMI_AC_EVENT
#define TF103C_DOCK_SMI_HANDSHAKING
#define TF103C_DOCK_SMI_EC_WAKEUP
#define TF103C_DOCK_SMI_BOOTBLOCK_RESET
#define TF103C_DOCK_SMI_WATCHDOG_RESET
#define TF103C_DOCK_SMI_ADAPTER_CHANGE
#define TF103C_DOCK_SMI_DOCK_INSERT
#define TF103C_DOCK_SMI_DOCK_REMOVE
#define TF103C_DOCK_SMI_PAD_BL_CHANGE
#define TF103C_DOCK_SMI_HID_STATUS_CHANGED
#define TF103C_DOCK_SMI_HID_WAKEUP
#define TF103C_DOCK_SMI_S3
#define TF103C_DOCK_SMI_S5
#define TF103C_DOCK_SMI_NOTIFY_SHUTDOWN
#define TF103C_DOCK_SMI_RESUME

/*** EC (dockram) I2C device defines ***/
#define TF103C_DOCK_EC_ADDR

#define TF103C_DOCK_EC_CMD_REG
#define TF103C_DOCK_EC_CMD_LEN

enum {};

struct tf103c_dock_data {};

static struct gpiod_lookup_table tf103c_dock_gpios =;

/* Byte 0 is the length of the rest of the packet */
static const u8 tf103c_dock_enable_cmd[9] =;
static const u8 tf103c_dock_usb_enable_cmd[9] =;
static const u8 tf103c_dock_suspend_cmd[9] =;

/*** keyboard related code ***/

static u8 tf103c_dock_kbd_hid_desc[] =;

static int tf103c_dock_kbd_read(struct tf103c_dock_data *dock)
{}

static void tf103c_dock_kbd_write(struct tf103c_dock_data *dock, u16 cmd)
{}

/* HID ll_driver functions for forwarding input-reports from the kbd_client */
static int tf103c_dock_hid_parse(struct hid_device *hid)
{}

static int tf103c_dock_hid_start(struct hid_device *hid)
{}

static void tf103c_dock_hid_stop(struct hid_device *hid)
{}

static int tf103c_dock_hid_open(struct hid_device *hid)
{}

static void tf103c_dock_hid_close(struct hid_device *hid)
{}

/* Mandatory, but not used */
static int tf103c_dock_hid_raw_request(struct hid_device *hid, u8 reportnum,
				       u8 *buf, size_t len, u8 rtype, int reqtype)
{}

static const struct hid_ll_driver tf103c_dock_hid_ll_driver =;

static const int tf103c_dock_toprow_codes[13][2] =;

static void tf103c_dock_report_toprow_kbd_hook(struct tf103c_dock_data *dock)
{}

static void tf103c_dock_toprow_press(struct tf103c_dock_data *dock, int key_code)
{}

static void tf103c_dock_toprow_release(struct tf103c_dock_data *dock, int key_code)
{}

static void tf103c_dock_toprow_event(struct tf103c_dock_data *dock,
					    int toprow_index, int *last_press)
{}

/*
 * The keyboard sends what appears to be standard I2C-HID input-reports,
 * except that a 16 bit register address of where the I2C-HID format
 * input-reports are stored must be send before reading it in a single
 * (I2C repeated-start) I2C transaction.
 *
 * Its unknown how to get the HID descriptors but they are easy to reconstruct:
 *
 * Input report id 0x11 is 8 bytes long and contain standard USB HID intf-class,
 * Boot Interface Subclass reports.
 * Input report id 0x13 is 2 bytes long and sends Consumer Control events
 * Input report id 0x14 is 1 byte long and sends System Control events
 *
 * However the top row keys (where a normal keyboard has F1-F12 + Print-Screen)
 * are a mess, using a mix of the 0x13 and 0x14 input reports as well as EC SCI
 * events; and these need special handling to allow actually sending F1-F12,
 * since the Fn key on the keyboard only works on the cursor keys and the top
 * row keys always send their special "Multimedia hotkey" codes.
 *
 * So only forward the 0x11 reports to HID and handle the top-row keys here.
 */
static void tf103c_dock_kbd_interrupt(struct tf103c_dock_data *dock)
{}

/*** touchpad related code ***/

static const struct property_entry tf103c_dock_touchpad_props[] =;

static const struct software_node tf103c_dock_touchpad_sw_node =;

/*
 * tf103c_enable_touchpad() is only called from the threaded interrupt handler
 * and tf103c_disable_touchpad() is only called after the irq is disabled,
 * so no locking is necessary.
 */
static void tf103c_dock_enable_touchpad(struct tf103c_dock_data *dock)
{}

static void tf103c_dock_disable_touchpad(struct tf103c_dock_data *dock)
{}

/*** interrupt handling code ***/
static void tf103c_dock_ec_cmd(struct tf103c_dock_data *dock, const u8 *cmd)
{}

static void tf103c_dock_sci(struct tf103c_dock_data *dock, u8 val)
{}

static void tf103c_dock_smi(struct tf103c_dock_data *dock, u8 val)
{}

static irqreturn_t tf103c_dock_irq(int irq, void *data)
{}

/*
 * tf103c_dock_[dis|en]able only run from hpd_work or at times when
 * hpd_work cannot run (hpd_irq disabled), so no locking is necessary.
 */
static void tf103c_dock_enable(struct tf103c_dock_data *dock)
{}

static void tf103c_dock_disable(struct tf103c_dock_data *dock)
{}

static void tf103c_dock_hpd_work(struct work_struct *work)
{}

static irqreturn_t tf103c_dock_hpd_irq(int irq, void *data)
{}

static void tf103c_dock_start_hpd(struct tf103c_dock_data *dock)
{}

static void tf103c_dock_stop_hpd(struct tf103c_dock_data *dock)
{}

/*** probe ***/

static const struct dmi_system_id tf103c_dock_dmi_ids[] =;

static void tf103c_dock_non_devm_cleanup(void *data)
{}

static int tf103c_dock_probe(struct i2c_client *client)
{}

static void tf103c_dock_remove(struct i2c_client *client)
{}

static int __maybe_unused tf103c_dock_suspend(struct device *dev)
{}

static int __maybe_unused tf103c_dock_resume(struct device *dev)
{}

static SIMPLE_DEV_PM_OPS(tf103c_dock_pm_ops, tf103c_dock_suspend, tf103c_dock_resume);

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

static struct i2c_driver tf103c_dock_driver =;
module_i2c_driver();

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