linux/drivers/input/keyboard/applespi.c

// SPDX-License-Identifier: GPL-2.0
/*
 * MacBook (Pro) SPI keyboard and touchpad driver
 *
 * Copyright (c) 2015-2018 Federico Lorenzi
 * Copyright (c) 2017-2018 Ronald Tschalär
 */

/*
 * The keyboard and touchpad controller on the MacBookAir6, MacBookPro12,
 * MacBook8 and newer can be driven either by USB or SPI. However the USB
 * pins are only connected on the MacBookAir6 and 7 and the MacBookPro12.
 * All others need this driver. The interface is selected using ACPI methods:
 *
 * * UIEN ("USB Interface Enable"): If invoked with argument 1, disables SPI
 *   and enables USB. If invoked with argument 0, disables USB.
 * * UIST ("USB Interface Status"): Returns 1 if USB is enabled, 0 otherwise.
 * * SIEN ("SPI Interface Enable"): If invoked with argument 1, disables USB
 *   and enables SPI. If invoked with argument 0, disables SPI.
 * * SIST ("SPI Interface Status"): Returns 1 if SPI is enabled, 0 otherwise.
 * * ISOL: Resets the four GPIO pins used for SPI. Intended to be invoked with
 *   argument 1, then once more with argument 0.
 *
 * UIEN and UIST are only provided on models where the USB pins are connected.
 *
 * SPI-based Protocol
 * ------------------
 *
 * The device and driver exchange messages (struct message); each message is
 * encapsulated in one or more packets (struct spi_packet). There are two types
 * of exchanges: reads, and writes. A read is signaled by a GPE, upon which one
 * message can be read from the device. A write exchange consists of writing a
 * command message, immediately reading a short status packet, and then, upon
 * receiving a GPE, reading the response message. Write exchanges cannot be
 * interleaved, i.e. a new write exchange must not be started till the previous
 * write exchange is complete. Whether a received message is part of a read or
 * write exchange is indicated in the encapsulating packet's flags field.
 *
 * A single message may be too large to fit in a single packet (which has a
 * fixed, 256-byte size). In that case it will be split over multiple,
 * consecutive packets.
 */

#include <linux/acpi.h>
#include <linux/crc16.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/efi.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/ktime.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/spi/spi.h>
#include <linux/wait.h>
#include <linux/workqueue.h>

#include <asm/barrier.h>
#include <linux/unaligned.h>

#define CREATE_TRACE_POINTS
#include "applespi.h"
#include "applespi_trace.h"

#define APPLESPI_PACKET_SIZE
#define APPLESPI_STATUS_SIZE

#define PACKET_TYPE_READ
#define PACKET_TYPE_WRITE
#define PACKET_DEV_KEYB
#define PACKET_DEV_TPAD
#define PACKET_DEV_INFO

#define MAX_ROLLOVER

#define MAX_FINGERS
#define MAX_FINGER_ORIENTATION
#define MAX_PKTS_PER_MSG

#define KBD_BL_LEVEL_MIN
#define KBD_BL_LEVEL_MAX
#define KBD_BL_LEVEL_SCALE
#define KBD_BL_LEVEL_ADJ

#define EFI_BL_LEVEL_NAME
#define EFI_BL_LEVEL_GUID

#define APPLE_FLAG_FKEY

#define SPI_RW_CHG_DELAY_US

#define SYNAPTICS_VENDOR_ID

static unsigned int fnmode =;
module_param(fnmode, uint, 0644);
MODULE_PARM_DESC();

static unsigned int fnremap;
module_param(fnremap, uint, 0644);
MODULE_PARM_DESC();

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

static char touchpad_dimensions[40];
module_param_string();
MODULE_PARM_DESC();

/**
 * struct keyboard_protocol - keyboard message.
 * message.type = 0x0110, message.length = 0x000a
 *
 * @unknown1:		unknown
 * @modifiers:		bit-set of modifier/control keys pressed
 * @unknown2:		unknown
 * @keys_pressed:	the (non-modifier) keys currently pressed
 * @fn_pressed:		whether the fn key is currently pressed
 * @crc16:		crc over the whole message struct (message header +
 *			this struct) minus this @crc16 field
 */
struct keyboard_protocol {};

/**
 * struct tp_finger - single trackpad finger structure, le16-aligned
 *
 * @origin:		zero when switching track finger
 * @abs_x:		absolute x coordinate
 * @abs_y:		absolute y coordinate
 * @rel_x:		relative x coordinate
 * @rel_y:		relative y coordinate
 * @tool_major:		tool area, major axis
 * @tool_minor:		tool area, minor axis
 * @orientation:	16384 when point, else 15 bit angle
 * @touch_major:	touch area, major axis
 * @touch_minor:	touch area, minor axis
 * @unused:		zeros
 * @pressure:		pressure on forcetouch touchpad
 * @multi:		one finger: varies, more fingers: constant
 * @crc16:		on last finger: crc over the whole message struct
 *			(i.e. message header + this struct) minus the last
 *			@crc16 field; unknown on all other fingers.
 */
struct tp_finger {};

/**
 * struct touchpad_protocol - touchpad message.
 * message.type = 0x0210
 *
 * @unknown1:		unknown
 * @clicked:		1 if a button-click was detected, 0 otherwise
 * @unknown2:		unknown
 * @number_of_fingers:	the number of fingers being reported in @fingers
 * @clicked2:		same as @clicked
 * @unknown3:		unknown
 * @fingers:		the data for each finger
 */
struct touchpad_protocol {};

/**
 * struct command_protocol_tp_info - get touchpad info.
 * message.type = 0x1020, message.length = 0x0000
 *
 * @crc16:		crc over the whole message struct (message header +
 *			this struct) minus this @crc16 field
 */
struct command_protocol_tp_info {};

/**
 * struct touchpad_info_protocol - touchpad info response.
 * message.type = 0x1020, message.length = 0x006e
 *
 * @unknown1:		unknown
 * @model_flags:	flags (vary by model number, but significance otherwise
 *			unknown)
 * @model_no:		the touchpad model number
 * @unknown2:		unknown
 * @crc16:		crc over the whole message struct (message header +
 *			this struct) minus this @crc16 field
 */
struct touchpad_info_protocol {};

/**
 * struct command_protocol_mt_init - initialize multitouch.
 * message.type = 0x0252, message.length = 0x0002
 *
 * @cmd:		value: 0x0102
 * @crc16:		crc over the whole message struct (message header +
 *			this struct) minus this @crc16 field
 */
struct command_protocol_mt_init {};

/**
 * struct command_protocol_capsl - toggle caps-lock led
 * message.type = 0x0151, message.length = 0x0002
 *
 * @unknown:		value: 0x01 (length?)
 * @led:		0 off, 2 on
 * @crc16:		crc over the whole message struct (message header +
 *			this struct) minus this @crc16 field
 */
struct command_protocol_capsl {};

/**
 * struct command_protocol_bl - set keyboard backlight brightness
 * message.type = 0xB051, message.length = 0x0006
 *
 * @const1:		value: 0x01B0
 * @level:		the brightness level to set
 * @const2:		value: 0x0001 (backlight off), 0x01F4 (backlight on)
 * @crc16:		crc over the whole message struct (message header +
 *			this struct) minus this @crc16 field
 */
struct command_protocol_bl {};

/**
 * struct message - a complete spi message.
 *
 * Each message begins with fixed header, followed by a message-type specific
 * payload, and ends with a 16-bit crc. Because of the varying lengths of the
 * payload, the crc is defined at the end of each payload struct, rather than
 * in this struct.
 *
 * @type:	the message type
 * @zero:	always 0
 * @counter:	incremented on each message, rolls over after 255; there is a
 *		separate counter for each message type.
 * @rsp_buf_len:response buffer length (the exact nature of this field is quite
 *		speculative). On a request/write this is often the same as
 *		@length, though in some cases it has been seen to be much larger
 *		(e.g. 0x400); on a response/read this the same as on the
 *		request; for reads that are not responses it is 0.
 * @length:	length of the remainder of the data in the whole message
 *		structure (after re-assembly in case of being split over
 *		multiple spi-packets), minus the trailing crc. The total size
 *		of the message struct is therefore @length + 10.
 *
 * @keyboard:		Keyboard message
 * @touchpad:		Touchpad message
 * @tp_info:		Touchpad info (response)
 * @tp_info_command:	Touchpad info (CRC)
 * @init_mt_command:	Initialise Multitouch
 * @capsl_command:	Toggle caps-lock LED
 * @bl_command:		Keyboard brightness
 * @data:		Buffer data
 */
struct message {};

/* type + zero + counter + rsp_buf_len + length */
#define MSG_HEADER_SIZE

/**
 * struct spi_packet - a complete spi packet; always 256 bytes. This carries
 * the (parts of the) message in the data. But note that this does not
 * necessarily contain a complete message, as in some cases (e.g. many
 * fingers pressed) the message is split over multiple packets (see the
 * @offset, @remaining, and @length fields). In general the data parts in
 * spi_packet's are concatenated until @remaining is 0, and the result is an
 * message.
 *
 * @flags:	0x40 = write (to device), 0x20 = read (from device); note that
 *		the response to a write still has 0x40.
 * @device:	1 = keyboard, 2 = touchpad
 * @offset:	specifies the offset of this packet's data in the complete
 *		message; i.e. > 0 indicates this is a continuation packet (in
 *		the second packet for a message split over multiple packets
 *		this would then be the same as the @length in the first packet)
 * @remaining:	number of message bytes remaining in subsequents packets (in
 *		the first packet of a message split over two packets this would
 *		then be the same as the @length in the second packet)
 * @length:	length of the valid data in the @data in this packet
 * @data:	all or part of a message
 * @crc16:	crc over this whole structure minus this @crc16 field. This
 *		covers just this packet, even on multi-packet messages (in
 *		contrast to the crc in the message).
 */
struct spi_packet {};

struct spi_settings {};

/* this mimics struct drm_rect */
struct applespi_tp_info {};

struct applespi_data {};

static const unsigned char applespi_scancodes[] =;

/*
 * This must have exactly as many entries as there are bits in
 * struct keyboard_protocol.modifiers .
 */
static const unsigned char applespi_controlcodes[] =;

struct applespi_key_translation {};

static const struct applespi_key_translation applespi_fn_codes[] =;

static const struct applespi_key_translation apple_iso_keyboard[] =;

struct applespi_tp_model_info {};

static const struct applespi_tp_model_info applespi_tp_models[] =;

applespi_trace_fun;

static applespi_trace_fun applespi_get_trace_fun(enum applespi_evt_type type)
{}

static void applespi_setup_read_txfrs(struct applespi_data *applespi)
{}

static void applespi_setup_write_txfrs(struct applespi_data *applespi)
{}

static int applespi_async(struct applespi_data *applespi,
			  struct spi_message *message, void (*complete)(void *))
{}

static inline bool applespi_check_write_status(struct applespi_data *applespi,
					       int sts)
{}

static int applespi_get_spi_settings(struct applespi_data *applespi)
{}

static int applespi_setup_spi(struct applespi_data *applespi)
{}

static int applespi_enable_spi(struct applespi_data *applespi)
{}

static int applespi_send_cmd_msg(struct applespi_data *applespi);

static void applespi_msg_complete(struct applespi_data *applespi,
				  bool is_write_msg, bool is_read_compl)
{}

static void applespi_async_write_complete(void *context)
{}

static int applespi_send_cmd_msg(struct applespi_data *applespi)
{}

static void applespi_init(struct applespi_data *applespi, bool is_resume)
{}

static int applespi_set_capsl_led(struct applespi_data *applespi,
				  bool capslock_on)
{}

static void applespi_set_bl_level(struct led_classdev *led_cdev,
				  enum led_brightness value)
{}

static int applespi_event(struct input_dev *dev, unsigned int type,
			  unsigned int code, int value)
{}

/* lifted from the BCM5974 driver and renamed from raw2int */
/* convert 16-bit little endian to signed integer */
static inline int le16_to_int(__le16 x)
{}

static void applespi_debug_update_dimensions(struct applespi_data *applespi,
					     const struct tp_finger *f)
{}

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

static ssize_t applespi_tp_dim_read(struct file *file, char __user *buf,
				    size_t len, loff_t *off)
{}

static const struct file_operations applespi_tp_dim_fops =;

static void report_finger_data(struct input_dev *input, int slot,
			       const struct input_mt_pos *pos,
			       const struct tp_finger *f)
{}

static void report_tp_state(struct applespi_data *applespi,
			    struct touchpad_protocol *t)
{}

static const struct applespi_key_translation *
applespi_find_translation(const struct applespi_key_translation *table, u16 key)
{}

static unsigned int applespi_translate_fn_key(unsigned int key, int fn_pressed)
{}

static unsigned int applespi_translate_iso_layout(unsigned int key)
{}

static unsigned int applespi_code_to_key(u8 code, int fn_pressed)
{}

static void
applespi_remap_fn_key(struct keyboard_protocol *keyboard_protocol)
{}

static void
applespi_handle_keyboard_event(struct applespi_data *applespi,
			       struct keyboard_protocol *keyboard_protocol)
{}

static const struct applespi_tp_info *applespi_find_touchpad_info(u8 model)
{}

static int
applespi_register_touchpad_device(struct applespi_data *applespi,
				  struct touchpad_info_protocol *rcvd_tp_info)
{}

static void applespi_worker(struct work_struct *work)
{}

static void applespi_handle_cmd_response(struct applespi_data *applespi,
					 struct spi_packet *packet,
					 struct message *message)
{}

static bool applespi_verify_crc(struct applespi_data *applespi, u8 *buffer,
				size_t buflen)
{}

static void applespi_debug_print_read_packet(struct applespi_data *applespi,
					     struct spi_packet *packet)
{}

static void applespi_got_data(struct applespi_data *applespi)
{}

static void applespi_async_read_complete(void *context)
{}

static u32 applespi_notify(acpi_handle gpe_device, u32 gpe, void *context)
{}

static int applespi_get_saved_bl_level(struct applespi_data *applespi)
{}

static void applespi_save_bl_level(struct applespi_data *applespi,
				   unsigned int level)
{}

static int applespi_probe(struct spi_device *spi)
{}

static void applespi_drain_writes(struct applespi_data *applespi)
{}

static void applespi_drain_reads(struct applespi_data *applespi)
{}

static void applespi_remove(struct spi_device *spi)
{}

static void applespi_shutdown(struct spi_device *spi)
{}

static int applespi_poweroff_late(struct device *dev)
{}

static int applespi_suspend(struct device *dev)
{}

static int applespi_resume(struct device *dev)
{}

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

static const struct dev_pm_ops applespi_pm_ops =;

static struct spi_driver applespi_driver =;

module_spi_driver()

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