// 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(…) …;