/* * 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.
*/
staticbool iso_layout;
module_param(iso_layout, bool, 0644);
MODULE_PARM_DESC(iso_layout, "Enable/Disable hardcoded ISO-layout of the keyboard. ([0] = disabled, 1 = enabled)");
staticchar touchpad_dimensions[40];
module_param_string(touchpad_dimensions, touchpad_dimensions, sizeof(touchpad_dimensions), 0444);
MODULE_PARM_DESC(touchpad_dimensions, "The pixel dimensions of the touchpad, as XxY+W+H .");
/** * 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 {
u8 unknown1;
u8 modifiers;
u8 unknown2;
u8 keys_pressed[MAX_ROLLOVER];
u8 fn_pressed;
__le16 crc16;
};
/** * 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 {
__le16 origin;
__le16 abs_x;
__le16 abs_y;
__le16 rel_x;
__le16 rel_y;
__le16 tool_major;
__le16 tool_minor;
__le16 orientation;
__le16 touch_major;
__le16 touch_minor;
__le16 unused[2];
__le16 pressure;
__le16 multi;
__le16 crc16;
};
/** * 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 {
u8 unknown1[1];
u8 clicked;
u8 unknown2[28];
u8 number_of_fingers;
u8 clicked2;
u8 unknown3[16]; struct tp_finger fingers[];
};
/** * 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 {
__le16 crc16;
};
/** * 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 {
u8 unknown1[105];
u8 model_flags;
u8 model_no;
u8 unknown2[3];
__le16 crc16;
};
/** * 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 {
__le16 cmd;
__le16 crc16;
};
/** * 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 {
u8 unknown;
u8 led;
__le16 crc16;
};
/** * 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 {
__le16 const1;
__le16 level;
__le16 const2;
__le16 crc16;
};
/** * 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 {
__le16 type;
u8 zero;
u8 counter;
__le16 rsp_buf_len;
__le16 length; union { struct keyboard_protocol keyboard; struct touchpad_protocol touchpad; struct touchpad_info_protocol tp_info; struct command_protocol_tp_info tp_info_command; struct command_protocol_mt_init init_mt_command; struct command_protocol_capsl capsl_command; struct command_protocol_bl bl_command;
DECLARE_FLEX_ARRAY(u8, data);
};
};
/* type + zero + counter + rsp_buf_len + length */ #define MSG_HEADER_SIZE 8
/** * 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 {
u8 flags;
u8 device;
__le16 offset;
__le16 remaining;
__le16 length;
u8 data[246];
__le16 crc16;
};
/* * This must have exactly as many entries as there are bits in * struct keyboard_protocol.modifiers .
*/ staticconstunsignedchar applespi_controlcodes[] = {
KEY_LEFTCTRL,
KEY_LEFTSHIFT,
KEY_LEFTALT,
KEY_LEFTMETA,
0,
KEY_RIGHTSHIFT,
KEY_RIGHTALT,
KEY_RIGHTMETA
};
/* * All we need here is a delay at the beginning of the message before * asserting cs. But the current spi API doesn't support this, so we * end up with an extra unnecessary (but harmless) cs assertion and * deassertion.
*/
wt_t->delay.value = SPI_RW_CHG_DELAY_US;
wt_t->delay.unit = SPI_DELAY_UNIT_USECS;
wt_t->cs_change = 1;
/* check if SPI is already enabled, so we can skip the delay below */
acpi_sts = acpi_evaluate_integer(applespi->sist, NULL, NULL,
&spi_status); if (ACPI_SUCCESS(acpi_sts) && spi_status) return 0;
/* SIEN(1) will enable SPI communication */
acpi_sts = acpi_execute_simple_method(applespi->sien, NULL, 1); if (ACPI_FAILURE(acpi_sts)) {
dev_err(&applespi->spi->dev, "SIEN failed: %s\n",
acpi_format_exception(acpi_sts)); return -ENODEV;
}
/* * Allow the SPI interface to come up before returning. Without this * delay, the SPI commands to enable multitouch mode may not reach * the trackpad controller, causing pointer movement to break upon * resume from sleep.
*/
msleep(50);
if (!applespi_check_write_status(applespi, applespi->wr_m.status)) { /* * If we got an error, we presumably won't get the expected * response message either.
*/
applespi_msg_complete(applespi, true, false);
}
}
if (value == 0) {
applespi->want_bl_level = value;
} else { /* * The backlight does not turn on till level 32, so we scale * the range here so that from a user's perspective it turns * on at 1.
*/
applespi->want_bl_level =
((value * KBD_BL_LEVEL_ADJ) / KBD_BL_LEVEL_SCALE +
KBD_BL_LEVEL_MIN);
}
/* lifted from the BCM5974 driver and renamed from raw2int */ /* convert 16-bit little endian to signed integer */ staticinlineint le16_to_int(__le16 x)
{ return (signedshort)le16_to_cpu(x);
}
/* touchpad_input_dev is set async in worker */
input = smp_load_acquire(&applespi->touchpad_input_dev); if (!input) return; /* touchpad isn't initialized yet */
n = 0;
for (i = 0; i < t->number_of_fingers; i++) {
f = &t->fingers[i]; if (le16_to_int(f->touch_major) == 0) continue;
applespi->pos[n].x = le16_to_int(f->abs_x);
applespi->pos[n].y = tp_info->y_min + tp_info->y_max -
le16_to_int(f->abs_y);
n++;
if (applespi->debug_tp_dim)
applespi_debug_update_dimensions(applespi, f);
}
input_mt_assign_slots(input, applespi->slots, applespi->pos, n, 0);
for (i = 0; i < n; i++)
report_finger_data(input, applespi->slots[i],
&applespi->pos[i], &t->fingers[i]);
compiletime_assert(ARRAY_SIZE(applespi_controlcodes) ==
sizeof_field(struct keyboard_protocol, modifiers) * 8, "applespi_controlcodes has wrong number of entries");
/* check for rollover overflow, which is signalled by all keys == 1 */ if (!memchr_inv(keyboard_protocol->keys_pressed, 1, MAX_ROLLOVER)) return;
/* remap fn key if desired */
applespi_remap_fn_key(keyboard_protocol);
/* check released keys */ for (i = 0; i < MAX_ROLLOVER; i++) { if (memchr(keyboard_protocol->keys_pressed,
applespi->last_keys_pressed[i], MAX_ROLLOVER)) continue; /* key is still pressed */
/* set up touchpad dimensions */
tp_info = applespi_find_touchpad_info(rcvd_tp_info->model_no); if (!tp_info) {
dev_warn(&applespi->spi->dev, "Unknown touchpad model %x - falling back to MB8 touchpad\n",
rcvd_tp_info->model_no);
tp_info = &applespi_tp_models[0].tp_info;
}
applespi->tp_info = *tp_info;
if (touchpad_dimensions[0]) { int x, y, w, h;
sts = sscanf(touchpad_dimensions, "%dx%d+%u+%u", &x, &y, &w, &h); if (sts == 4) {
dev_info(&applespi->spi->dev, "Overriding touchpad dimensions from module param\n");
applespi->tp_info.x_min = x;
applespi->tp_info.y_min = y;
applespi->tp_info.x_max = x + w;
applespi->tp_info.y_max = y + h;
} else {
dev_warn(&applespi->spi->dev, "Invalid touchpad dimensions '%s': must be in the form XxY+W+H\n",
touchpad_dimensions);
touchpad_dimensions[0] = '\0';
}
} if (!touchpad_dimensions[0]) {
snprintf(touchpad_dimensions, sizeof(touchpad_dimensions), "%dx%d+%u+%u",
applespi->tp_info.x_min,
applespi->tp_info.y_min,
applespi->tp_info.x_max - applespi->tp_info.x_min,
applespi->tp_info.y_max - applespi->tp_info.y_min);
}
if (off + rem > MAX_PKTS_PER_MSG * APPLESPI_PACKET_SIZE) {
dev_warn_ratelimited(&applespi->spi->dev, "Received message too large (size %u)\n",
off + rem); goto msg_complete;
}
if (off + len > MAX_PKTS_PER_MSG * APPLESPI_PACKET_SIZE) {
dev_warn_ratelimited(&applespi->spi->dev, "Received message too large (size %u)\n",
off + len); goto msg_complete;
}
if (applespi->rd_m.status < 0) {
dev_warn(&applespi->spi->dev, "Error reading from device: %d\n",
applespi->rd_m.status); /* * We don't actually know if this was a pure read, or a response * to a write. But this is a rare error condition that should * never occur, so clearing both flags to avoid deadlock.
*/
applespi_msg_complete(applespi, true, true);
} else {
applespi_got_data(applespi);
}
/* check if the USB interface is present and enabled already */
acpi_sts = acpi_evaluate_integer(spi_handle, "UIST", NULL, &usb_status); if (ACPI_SUCCESS(acpi_sts) && usb_status) { /* let the USB driver take over instead */
dev_info(&spi->dev, "USB interface already enabled\n"); return -ENODEV;
}
/* allocate driver data */
applespi = devm_kzalloc(&spi->dev, sizeof(*applespi), GFP_KERNEL); if (!applespi) return -ENOMEM;
applespi->spi = spi;
INIT_WORK(&applespi->work, applespi_worker);
/* store the driver data */
spi_set_drvdata(spi, applespi);
for (i = 0; i < ARRAY_SIZE(applespi_scancodes); i++) if (applespi_scancodes[i])
input_set_capability(applespi->keyboard_input_dev,
EV_KEY, applespi_scancodes[i]);
for (i = 0; i < ARRAY_SIZE(applespi_controlcodes); i++) if (applespi_controlcodes[i])
input_set_capability(applespi->keyboard_input_dev,
EV_KEY, applespi_controlcodes[i]);
for (i = 0; i < ARRAY_SIZE(applespi_fn_codes); i++) if (applespi_fn_codes[i].to)
input_set_capability(applespi->keyboard_input_dev,
EV_KEY, applespi_fn_codes[i].to);
sts = input_register_device(applespi->keyboard_input_dev); if (sts) {
dev_err(&applespi->spi->dev, "Unable to register keyboard input device (%d)\n", sts); return -ENODEV;
}
/* * The applespi device doesn't send interrupts normally (as is described * in its DSDT), but rather seems to use ACPI GPEs.
*/
acpi_sts = acpi_evaluate_integer(spi_handle, "_GPE", NULL, &gpe); if (ACPI_FAILURE(acpi_sts)) {
dev_err(&applespi->spi->dev, "Failed to obtain GPE for SPI slave device: %s\n",
acpi_format_exception(acpi_sts)); return -ENODEV;
}
applespi->gpe = (int)gpe;
acpi_sts = acpi_install_gpe_handler(NULL, applespi->gpe,
ACPI_GPE_LEVEL_TRIGGERED,
applespi_notify, applespi); if (ACPI_FAILURE(acpi_sts)) {
dev_err(&applespi->spi->dev, "Failed to install GPE handler for GPE %d: %s\n",
applespi->gpe, acpi_format_exception(acpi_sts)); return -ENODEV;
}
applespi->suspended = false;
acpi_sts = acpi_enable_gpe(NULL, applespi->gpe); if (ACPI_FAILURE(acpi_sts)) {
dev_err(&applespi->spi->dev, "Failed to enable GPE handler for GPE %d: %s\n",
applespi->gpe, acpi_format_exception(acpi_sts));
acpi_remove_gpe_handler(NULL, applespi->gpe, applespi_notify); return -ENODEV;
}
/* * By default this device is not enabled for wakeup; but USB keyboards * generally are, so the expectation is that by default the keyboard * will wake the system.
*/
device_wakeup_enable(&spi->dev);
/* set up keyboard-backlight */
sts = applespi_get_saved_bl_level(applespi); if (sts >= 0)
applespi_set_bl_level(&applespi->backlight_info, sts);
sts = devm_led_classdev_register(&spi->dev, &applespi->backlight_info); if (sts)
dev_warn(&applespi->spi->dev, "Unable to register keyboard backlight class dev (%d)\n",
sts);
/* set up debugfs entries for touchpad dimensions logging */
applespi->debugfs_root = debugfs_create_dir("applespi", NULL);
/* turn off caps-lock - it'll stay on otherwise */
sts = applespi_set_capsl_led(applespi, false); if (sts)
dev_warn(&applespi->spi->dev, "Failed to turn off caps-lock led (%d)\n", sts);
applespi_drain_writes(applespi);
/* disable the interrupt */
acpi_sts = acpi_disable_gpe(NULL, applespi->gpe); if (ACPI_FAILURE(acpi_sts))
dev_err(&applespi->spi->dev, "Failed to disable GPE handler for GPE %d: %s\n",
applespi->gpe, acpi_format_exception(acpi_sts));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.