Index | Thread | Search

From:
joshua stein <jcs@jcs.org>
Subject:
Re: aplhidev: enable on amd64, add keyboard backlight support
To:
tech@openbsd.org
Date:
Thu, 20 Nov 2025 10:46:08 -0600

Download raw body.

Thread
  • Mark Kettenis:

    aplhidev: enable on amd64, add keyboard backlight support

  • On Thu, 20 Nov 2025 at 13:40:53 +0100, Mark Kettenis wrote:
    > > Date: Thu, 13 Nov 2025 20:15:16 -0600
    > > From: joshua stein <jcs@jcs.org>
    > > 
    > > This moves aplhidev out of arch/arm64 and into dev/spi, adds 
    > > attachment via ACPI through ispi, enables it on amd64, and adds 
    > > support for adjusting keyboard backlight through 'wsconsctl 
    > > keyboard.backlight'.
    > > 
    > > Tested on a 2017 MacBook 12".  Hopefully it didn't break anything on 
    > > Apple M* arm64 laptops and I'm curious if the keyboard backlight 
    > > support also works there.
    > > 
    > > Individual commits are here but this is just one consolidated diff:
    > > 
    > > https://github.com/jcs/openbsd-src/commit/5cc8811f104532139877139b6852ae627963a602
    > > https://github.com/jcs/openbsd-src/commit/3a57a3b8440efc45d4e75a36e843328f8e5ff015
    > > https://github.com/jcs/openbsd-src/commit/852de258b6625c33641bdf36303699ba157f3d55
    > > 
    > > I also tested it in RAMDISK_CD so the keyboard works during 
    > > installation but that's not included here.
    > 
    > Diff doesn't apply because of the file rename.  I guess it is helpful
    > to show the changes you made, and I can move the file by hand.
    > 
    > But if I do so, things don't compile on arm64:
    
    Here is an updated diff that was tested on amd64 and compiles on 
    arm64.  The wskbd backlight change is now only done on amd64.
    
    
    Index: arch/amd64/conf/GENERIC
    ===================================================================
    RCS file: /cvs/src/sys/arch/amd64/conf/GENERIC,v
    retrieving revision 1.536
    diff -u -p -u -r1.536 GENERIC
    --- arch/amd64/conf/GENERIC	14 Nov 2025 01:55:07 -0000	1.536
    +++ arch/amd64/conf/GENERIC	20 Nov 2025 16:42:41 -0000
    @@ -208,6 +208,11 @@ gpio* at skgpio?
     
     ispi*	at acpi?		# Intel LPSS SPI controller
     ispi*	at pci?
    +aplhidev* at spi?		# Apple SPI HID
    +aplkbd* at aplhidev?
    +wskbd*	at aplkbd? mux 1
    +aplms*	at aplhidev?
    +wsmouse* at aplms? mux 0
     
     #option PCMCIAVERBOSE
     
    Index: arch/amd64/conf/files.amd64
    ===================================================================
    RCS file: /cvs/src/sys/arch/amd64/conf/files.amd64,v
    retrieving revision 1.117
    diff -u -p -u -r1.117 files.amd64
    --- arch/amd64/conf/files.amd64	12 Nov 2025 11:34:36 -0000	1.117
    +++ arch/amd64/conf/files.amd64	20 Nov 2025 16:42:41 -0000
    @@ -281,6 +281,17 @@ include "dev/onewire/files.onewire"
     attach	ipmi at mainbus
     
     #
    +# SPI
    +#
    +device	aplhidev {}
    +attach	aplhidev at spi
    +device	aplkbd: hid, hidkbd, wskbddev
    +attach	aplkbd at aplhidev
    +device	aplms: hid, hidms, wsmousedev
    +attach	aplms at aplhidev
    +file	dev/spi/aplhidev.c			aplhidev | aplkbd | aplms needs-flag
    +
    +#
     # device major numbers
     #
     
    Index: arch/arm64/conf/files.arm64
    ===================================================================
    RCS file: /cvs/src/sys/arch/arm64/conf/files.arm64,v
    retrieving revision 1.79
    diff -u -p -u -r1.79 files.arm64
    --- arch/arm64/conf/files.arm64	17 Sep 2025 09:32:55 -0000	1.79
    +++ arch/arm64/conf/files.arm64	20 Nov 2025 16:42:41 -0000
    @@ -186,7 +186,7 @@ device	aplkbd: hid, hidkbd, wskbddev
     attach	aplkbd at aplhidev
     device	aplms: hid, hidms, wsmousedev
     attach	aplms at aplhidev
    -file	arch/arm64/dev/aplhidev.c		aplhidev | aplkbd | aplms needs-flag
    +file	dev/spi/aplhidev.c			aplhidev | aplkbd | aplms needs-flag
     
     device	aplmbox
     attach	aplmbox at fdt
    Index: arch/arm64/dev/aplhidev.c
    ===================================================================
    RCS file: arch/arm64/dev/aplhidev.c
    diff -N arch/arm64/dev/aplhidev.c
    --- arch/arm64/dev/aplhidev.c	15 Jan 2024 13:27:20 -0000	1.13
    +++ /dev/null	1 Jan 1970 00:00:00 -0000
    @@ -1,959 +0,0 @@
    -/*	$OpenBSD: aplhidev.c,v 1.13 2024/01/15 13:27:20 kettenis Exp $	*/
    -/*
    - * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
    - * Copyright (c) 2013-2014 joshua stein <jcs@openbsd.org>
    - *
    - * Permission to use, copy, modify, and distribute this software for any
    - * purpose with or without fee is hereby granted, provided that the above
    - * copyright notice and this permission notice appear in all copies.
    - *
    - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    - */
    -
    -#include <sys/param.h>
    -#include <sys/systm.h>
    -#include <sys/kernel.h>
    -#include <sys/device.h>
    -#include <sys/malloc.h>
    -#include <sys/timeout.h>
    -
    -#include <lib/libkern/crc16.h>
    -
    -#include <machine/fdt.h>
    -
    -#include <dev/spi/spivar.h>
    -
    -#include <dev/ofw/openfirm.h>
    -#include <dev/ofw/ofw_gpio.h>
    -#include <dev/ofw/ofw_pinctrl.h>
    -
    -#include <dev/usb/usbdevs.h>
    -
    -#include <dev/wscons/wsconsio.h>
    -#include <dev/wscons/wskbdvar.h>
    -#include <dev/wscons/wsksymdef.h>
    -#include <dev/wscons/wsmousevar.h>
    -
    -#include <dev/hid/hid.h>
    -#include <dev/hid/hidkbdsc.h>
    -#include <dev/hid/hidmsvar.h>
    -
    -#include "aplhidev.h"
    -
    -#define APLHIDEV_READ_PACKET	0x20
    -#define APLHIDEV_WRITE_PACKET	0x40
    -
    -#define APLHIDEV_KBD_DEVICE	1
    -#define APLHIDEV_TP_DEVICE	2
    -#define APLHIDEV_INFO_DEVICE	208
    -
    -#define APLHIDEV_GET_INFO	0x0120
    -#define APLHIDEV_GET_DESCRIPTOR	0x1020
    -#define  APLHIDEV_DESC_MAX	512
    -#define APLHIDEV_KBD_REPORT	0x0110
    -#define APLHIDEV_TP_REPORT	0x0210
    -#define APLHIDEV_SET_LEDS	0x0151
    -#define APLHIDEV_SET_MODE	0x0252
    -#define  APLHIDEV_MODE_HID	0x00
    -#define  APLHIDEV_MODE_RAW	0x01
    -#define APLHIDEV_GET_DIMENSIONS	0xd932
    -
    -struct aplhidev_dim {
    -	uint32_t width;
    -	uint32_t height;
    -	int16_t x_min;
    -	int16_t y_min;
    -	int16_t x_max;
    -	int16_t y_max;
    -};
    -
    -struct aplhidev_attach_args {
    -	uint8_t	aa_reportid;
    -	void	*aa_desc;
    -	size_t	aa_desclen;
    -};
    -
    -struct aplhidev_spi_packet {
    -	uint8_t		flags;
    -	uint8_t		device;
    -	uint16_t	offset;
    -	uint16_t	remaining;
    -	uint16_t	len;
    -	uint8_t		data[246];
    -	uint16_t	crc;
    -};
    -
    -struct aplhidev_spi_status {
    -	uint8_t		status[4];
    -};
    -
    -struct aplhidev_msghdr {
    -	uint16_t	type;
    -	uint8_t		device;
    -	uint8_t		msgid;
    -	uint16_t	rsplen;
    -	uint16_t	cmdlen;
    -};
    -
    -struct aplhidev_info_hdr {
    -	uint16_t	unknown[2];
    -	uint16_t	num_devices;
    -	uint16_t	vendor;
    -	uint16_t	product;
    -	uint16_t	version;
    -	uint16_t	vendor_str[2];
    -	uint16_t	product_str[2];
    -	uint16_t	serial_str[2];
    -};
    -
    -struct aplhidev_get_desc {
    -	struct aplhidev_msghdr	hdr;
    -	uint16_t		crc;
    -};
    -
    -struct aplhidev_set_leds {
    -	struct aplhidev_msghdr	hdr;
    -	uint8_t			reportid;
    -	uint8_t			leds;
    -	uint16_t		crc;
    -};
    -
    -struct aplhidev_set_mode {
    -	struct aplhidev_msghdr	hdr;
    -	uint8_t			reportid;
    -	uint8_t			mode;
    -	uint16_t		crc;
    -};
    -
    -struct aplhidev_softc {
    -	struct device		sc_dev;
    -	int			sc_node;
    -
    -	spi_tag_t		sc_spi_tag;
    -	struct spi_config	sc_spi_conf;
    -
    -	uint8_t			sc_msgid;
    -
    -	uint32_t		*sc_gpio;
    -	int			sc_gpiolen;
    -
    -	uint8_t			sc_mode;
    -	uint16_t		sc_vendor;
    -	uint16_t		sc_product;
    -
    -	struct device 		*sc_kbd;
    -	uint8_t			sc_kbddesc[APLHIDEV_DESC_MAX];
    -	size_t			sc_kbddesclen;
    -
    -	struct device		*sc_ms;
    -	uint8_t			sc_tpdesc[APLHIDEV_DESC_MAX];
    -	size_t			sc_tpdesclen;
    -	uint8_t			sc_dimdesc[APLHIDEV_DESC_MAX];
    -	size_t			sc_dimdesclen;
    -	int			sc_x_min;
    -	int			sc_x_max;
    -	int			sc_y_min;
    -	int			sc_y_max;
    -	int			sc_h_res;
    -	int			sc_v_res;
    -};
    -
    -int	 aplhidev_match(struct device *, void *, void *);
    -void	 aplhidev_attach(struct device *, struct device *, void *);
    -
    -const struct cfattach aplhidev_ca = {
    -	sizeof(struct aplhidev_softc), aplhidev_match, aplhidev_attach
    -};
    -
    -struct cfdriver aplhidev_cd = {
    -	NULL, "aplhidev", DV_DULL
    -};
    -
    -void	aplhidev_get_info(struct aplhidev_softc *);
    -void	aplhidev_get_descriptor(struct aplhidev_softc *, uint8_t);
    -void	aplhidev_set_leds(struct aplhidev_softc *, uint8_t);
    -void	aplhidev_set_mode(struct aplhidev_softc *, uint8_t);
    -void	aplhidev_get_dimensions(struct aplhidev_softc *);
    -
    -int	aplhidev_intr(void *);
    -void	aplkbd_intr(struct device *, uint8_t *, size_t);
    -void	aplms_intr(struct device *, uint8_t *, size_t);
    -
    -int
    -aplhidev_match(struct device *parent, void *match, void *aux)
    -{
    -	struct spi_attach_args *sa = aux;
    -
    -	if (strcmp(sa->sa_name, "apple,spi-hid-transport") == 0)
    -		return 1;
    -
    -	return 0;
    -}
    -
    -void
    -aplhidev_attach(struct device *parent, struct device *self, void *aux)
    -{
    -	struct aplhidev_softc *sc = (struct aplhidev_softc *)self;
    -	struct spi_attach_args *sa = aux;
    -	struct aplhidev_attach_args aa;
    -	struct aplhidev_dim dim;
    -	int retry;
    -
    -	sc->sc_spi_tag = sa->sa_tag;
    -	sc->sc_node = *(int *)sa->sa_cookie;
    -
    -	sc->sc_gpiolen = OF_getproplen(sc->sc_node, "spien-gpios");
    -	if (sc->sc_gpiolen > 0) {
    -		sc->sc_gpio = malloc(sc->sc_gpiolen, M_TEMP, M_WAITOK);
    -		OF_getpropintarray(sc->sc_node, "spien-gpios",
    -		    sc->sc_gpio, sc->sc_gpiolen);
    -		gpio_controller_config_pin(sc->sc_gpio, GPIO_CONFIG_OUTPUT);
    -
    -		/* Reset */
    -		gpio_controller_set_pin(sc->sc_gpio, 1);
    -		delay(5000);
    -		gpio_controller_set_pin(sc->sc_gpio, 0);
    -		delay(5000);
    -
    -		/* Enable. */
    -		gpio_controller_set_pin(sc->sc_gpio, 1);
    -		delay(50000);
    -	}
    -
    -	sc->sc_spi_conf.sc_bpw = 8;
    -	sc->sc_spi_conf.sc_freq = OF_getpropint(sc->sc_node,
    -	    "spi-max-frequency", 0);
    -	sc->sc_spi_conf.sc_cs = OF_getpropint(sc->sc_node, "reg", 0);
    -	sc->sc_spi_conf.sc_cs_delay = 100;
    -
    -	fdt_intr_establish(sc->sc_node, IPL_TTY,
    -	    aplhidev_intr, sc, sc->sc_dev.dv_xname);
    -
    -	aplhidev_get_info(sc);
    -	for (retry = 10; retry > 0; retry--) {
    -		aplhidev_intr(sc);
    -		delay(1000);
    -		if (sc->sc_vendor != 0 && sc->sc_product != 0)
    -			break;
    -	}
    -
    -	aplhidev_get_descriptor(sc, APLHIDEV_KBD_DEVICE);
    -	for (retry = 10; retry > 0; retry--) {
    -		aplhidev_intr(sc);
    -		delay(1000);
    -		if (sc->sc_kbddesclen > 0)
    -			break;
    -	}
    -
    -	aplhidev_get_descriptor(sc, APLHIDEV_TP_DEVICE);
    -	for (retry = 10; retry > 0; retry--) {
    -		aplhidev_intr(sc);
    -		delay(1000);
    -		if (sc->sc_tpdesclen > 0)
    -			break;
    -	}
    -
    -	sc->sc_mode = APLHIDEV_MODE_HID;
    -	aplhidev_set_mode(sc, APLHIDEV_MODE_RAW);
    -	for (retry = 10; retry > 0; retry--) {
    -		aplhidev_intr(sc);
    -		delay(1000);
    -		if (sc->sc_mode == APLHIDEV_MODE_RAW)
    -			break;
    -	}
    -
    -	aplhidev_get_dimensions(sc);
    -	for (retry = 10; retry > 0; retry--) {
    -		aplhidev_intr(sc);
    -		delay(1000);
    -		if (sc->sc_dimdesclen > 0)
    -			break;
    -	}
    -
    -	printf("\n");
    -
    -	if (sc->sc_dimdesclen == sizeof(dim) + 1) {
    -		memcpy(&dim, &sc->sc_dimdesc[1], sizeof(dim));
    -		sc->sc_x_min = dim.x_min;
    -		sc->sc_x_max = dim.x_max;
    -		sc->sc_y_min = dim.y_min;
    -		sc->sc_y_max = dim.y_max;
    -		sc->sc_h_res = (100 * (dim.x_max - dim.x_min)) / dim.width;
    -		sc->sc_v_res = (100 * (dim.y_max - dim.y_min)) / dim.height;
    -	}
    -
    -	if (sc->sc_kbddesclen > 0) {
    -		aa.aa_reportid = APLHIDEV_KBD_DEVICE;
    -		aa.aa_desc = sc->sc_kbddesc;
    -		aa.aa_desclen = sc->sc_kbddesclen;
    -		sc->sc_kbd = config_found(self, &aa, NULL);
    -	}
    -
    -	if (sc->sc_tpdesclen > 0) {
    -		aa.aa_reportid = APLHIDEV_TP_DEVICE;
    -		aa.aa_desc = sc->sc_tpdesc;
    -		aa.aa_desclen = sc->sc_tpdesclen;
    -		sc->sc_ms = config_found(self, &aa, NULL);
    -	}
    -}
    -
    -void
    -aplhidev_get_info(struct aplhidev_softc *sc)
    -{
    -	struct aplhidev_spi_packet packet;
    -	struct aplhidev_get_desc *msg;
    -	struct aplhidev_spi_status status;
    -
    -	memset(&packet, 0, sizeof(packet));
    -	packet.flags = APLHIDEV_WRITE_PACKET;
    -	packet.device = APLHIDEV_INFO_DEVICE;
    -	packet.len = sizeof(*msg);
    -
    -	msg = (void *)&packet.data[0];
    -	msg->hdr.type = APLHIDEV_GET_INFO;
    -	msg->hdr.device = APLHIDEV_INFO_DEVICE;
    -	msg->hdr.msgid = sc->sc_msgid++;
    -	msg->hdr.cmdlen = 0;
    -	msg->hdr.rsplen = APLHIDEV_DESC_MAX;
    -	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
    -
    -	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
    -
    -	spi_acquire_bus(sc->sc_spi_tag, 0);
    -	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
    -	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
    -	    SPI_KEEP_CS);
    -	delay(100);
    -	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
    -	spi_release_bus(sc->sc_spi_tag, 0);
    -
    -	delay(1000);
    -}
    -
    -void
    -aplhidev_get_descriptor(struct aplhidev_softc *sc, uint8_t device)
    -{
    -	struct aplhidev_spi_packet packet;
    -	struct aplhidev_get_desc *msg;
    -	struct aplhidev_spi_status status;
    -
    -	memset(&packet, 0, sizeof(packet));
    -	packet.flags = APLHIDEV_WRITE_PACKET;
    -	packet.device = APLHIDEV_INFO_DEVICE;
    -	packet.len = sizeof(*msg);
    -
    -	msg = (void *)&packet.data[0];
    -	msg->hdr.type = APLHIDEV_GET_DESCRIPTOR;
    -	msg->hdr.device = device;
    -	msg->hdr.msgid = sc->sc_msgid++;
    -	msg->hdr.cmdlen = 0;
    -	msg->hdr.rsplen = APLHIDEV_DESC_MAX;
    -	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
    -
    -	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
    -
    -	spi_acquire_bus(sc->sc_spi_tag, 0);
    -	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
    -	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
    -	    SPI_KEEP_CS);
    -	delay(100);
    -	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
    -	spi_release_bus(sc->sc_spi_tag, 0);
    -
    -	delay(1000);
    -}
    -
    -void
    -aplhidev_set_leds(struct aplhidev_softc *sc, uint8_t leds)
    -{
    -	struct aplhidev_spi_packet packet;
    -	struct aplhidev_set_leds *msg;
    -	struct aplhidev_spi_status status;
    -
    -	memset(&packet, 0, sizeof(packet));
    -	packet.flags = APLHIDEV_WRITE_PACKET;
    -	packet.device = APLHIDEV_KBD_DEVICE;
    -	packet.len = sizeof(*msg);
    -
    -	msg = (void *)&packet.data[0];
    -	msg->hdr.type = APLHIDEV_SET_LEDS;
    -	msg->hdr.device = APLHIDEV_KBD_DEVICE;
    -	msg->hdr.msgid = sc->sc_msgid++;
    -	msg->hdr.cmdlen = sizeof(*msg) - sizeof(struct aplhidev_msghdr) - 2;
    -	msg->hdr.rsplen = msg->hdr.cmdlen;
    -	msg->reportid = APLHIDEV_KBD_DEVICE;
    -	msg->leds = leds;
    -	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
    -
    -	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
    -
    -	/*
    -	 * XXX Without a delay here, the command will fail.  Does the
    -	 * controller need a bit of time between sending us a keypress
    -	 * event and accepting a new command from us?
    -	 */
    -	delay(250);
    -
    -	spi_acquire_bus(sc->sc_spi_tag, 0);
    -	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
    -	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
    -	    SPI_KEEP_CS);
    -	delay(100);
    -	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
    -	spi_release_bus(sc->sc_spi_tag, 0);
    -}
    -
    -void
    -aplhidev_set_mode(struct aplhidev_softc *sc, uint8_t mode)
    -{
    -	struct aplhidev_spi_packet packet;
    -	struct aplhidev_set_mode *msg;
    -	struct aplhidev_spi_status status;
    -
    -	memset(&packet, 0, sizeof(packet));
    -	packet.flags = APLHIDEV_WRITE_PACKET;
    -	packet.device = APLHIDEV_TP_DEVICE;
    -	packet.len = sizeof(*msg);
    -
    -	msg = (void *)&packet.data[0];
    -	msg->hdr.type = APLHIDEV_SET_MODE;
    -	msg->hdr.device = APLHIDEV_TP_DEVICE;
    -	msg->hdr.msgid = sc->sc_msgid++;
    -	msg->hdr.cmdlen = sizeof(*msg) - sizeof(struct aplhidev_msghdr) - 2;
    -	msg->hdr.rsplen = msg->hdr.cmdlen;
    -	msg->reportid = APLHIDEV_TP_DEVICE;
    -	msg->mode = mode;
    -	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
    -
    -	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
    -
    -	spi_acquire_bus(sc->sc_spi_tag, 0);
    -	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
    -	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
    -	    SPI_KEEP_CS);
    -	delay(100);
    -	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
    -	spi_release_bus(sc->sc_spi_tag, 0);
    -
    -	delay(1000);
    -}
    -
    -void
    -aplhidev_get_dimensions(struct aplhidev_softc *sc)
    -{
    -	struct aplhidev_spi_packet packet;
    -	struct aplhidev_get_desc *msg;
    -	struct aplhidev_spi_status status;
    -
    -	memset(&packet, 0, sizeof(packet));
    -	packet.flags = APLHIDEV_WRITE_PACKET;
    -	packet.device = APLHIDEV_TP_DEVICE;
    -	packet.len = sizeof(*msg);
    -
    -	msg = (void *)&packet.data[0];
    -	msg->hdr.type = APLHIDEV_GET_DIMENSIONS;
    -	msg->hdr.device = 0;
    -	msg->hdr.msgid = sc->sc_msgid++;
    -	msg->hdr.cmdlen = 0;
    -	msg->hdr.rsplen = APLHIDEV_DESC_MAX;
    -	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
    -
    -	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
    -
    -	spi_acquire_bus(sc->sc_spi_tag, 0);
    -	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
    -	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
    -	    SPI_KEEP_CS);
    -	delay(100);
    -	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
    -	spi_release_bus(sc->sc_spi_tag, 0);
    -
    -	delay(1000);
    -}
    -
    -int
    -aplhidev_intr(void *arg)
    -{
    -	struct aplhidev_softc *sc = arg;
    -	struct aplhidev_spi_packet packet;
    -	struct aplhidev_msghdr *hdr = (struct aplhidev_msghdr *)&packet.data[0];
    -
    -	memset(&packet, 0, sizeof(packet));
    -	spi_acquire_bus(sc->sc_spi_tag, 0);
    -	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
    -	spi_read(sc->sc_spi_tag, (char *)&packet, sizeof(packet));
    -	spi_release_bus(sc->sc_spi_tag, 0);
    -
    -	/* Treat empty packets as spurious interrupts. */
    -	if (packet.flags == 0 && packet.device == 0 && packet.crc == 0)
    -		return 0;
    -
    -	if (crc16(0, (uint8_t *)&packet, sizeof(packet)))
    -		return 1;
    -
    -	/* Keyboard input. */
    -	if (packet.flags == APLHIDEV_READ_PACKET &&
    -	    packet.device == APLHIDEV_KBD_DEVICE &&
    -	    hdr->type == APLHIDEV_KBD_REPORT) {
    -		if (sc->sc_kbd)
    -			aplkbd_intr(sc->sc_kbd, &packet.data[8], hdr->cmdlen);
    -		return 1;
    -	}
    -
    -	/* Touchpad input. */
    -	if (packet.flags == APLHIDEV_READ_PACKET &&
    -	    packet.device == APLHIDEV_TP_DEVICE &&
    -	    hdr->type == APLHIDEV_TP_REPORT) {
    -		if (sc->sc_ms)
    -			aplms_intr(sc->sc_ms, &packet.data[8], hdr->cmdlen);
    -		return 1;
    -	}
    -
    -	/* Replies to commands we sent. */
    -	if (packet.flags == APLHIDEV_WRITE_PACKET &&
    -	    packet.device == APLHIDEV_INFO_DEVICE &&
    -	    hdr->type == APLHIDEV_GET_INFO) {
    -		struct aplhidev_info_hdr *info =
    -		    (struct aplhidev_info_hdr *)&packet.data[8];
    -		sc->sc_vendor = info->vendor;
    -		sc->sc_product = info->product;
    -		return 1;
    -	}
    -	if (packet.flags == APLHIDEV_WRITE_PACKET &&
    -	    packet.device == APLHIDEV_INFO_DEVICE &&
    -	    hdr->type == APLHIDEV_GET_DESCRIPTOR) {
    -		switch (hdr->device) {
    -		case APLHIDEV_KBD_DEVICE:
    -			memcpy(sc->sc_kbddesc, &packet.data[8], hdr->cmdlen);
    -			sc->sc_kbddesclen = hdr->cmdlen;
    -			break;
    -		case APLHIDEV_TP_DEVICE:
    -			memcpy(sc->sc_tpdesc, &packet.data[8], hdr->cmdlen);
    -			sc->sc_tpdesclen = hdr->cmdlen;
    -			break;
    -		}
    -
    -		return 1;
    -	}
    -	if (packet.flags == APLHIDEV_WRITE_PACKET &&
    -	    packet.device == APLHIDEV_TP_DEVICE &&
    -	    hdr->type == APLHIDEV_SET_MODE) {
    -		sc->sc_mode = APLHIDEV_MODE_RAW;
    -		return 1;
    -	}
    -	if (packet.flags == APLHIDEV_WRITE_PACKET &&
    -	    packet.device == APLHIDEV_TP_DEVICE &&
    -	    hdr->type == APLHIDEV_GET_DIMENSIONS) {
    -		memcpy(sc->sc_dimdesc, &packet.data[8], hdr->cmdlen);
    -		sc->sc_dimdesclen = hdr->cmdlen;
    -		return 1;
    -	}
    -
    -	/* Valid, but unrecognized packet; ignore for now. */
    -	return 1;
    -}
    -
    -/* Keyboard */
    -
    -struct aplkbd_softc {
    -	struct device		sc_dev;
    -	struct aplhidev_softc	*sc_hidev;
    -	struct hidkbd		sc_kbd;
    -	int			sc_spl;
    -};
    -
    -void	aplkbd_cngetc(void *, u_int *, int *);
    -void	aplkbd_cnpollc(void *, int);
    -void	aplkbd_cnbell(void *, u_int, u_int, u_int);
    -
    -const struct wskbd_consops aplkbd_consops = {
    -	aplkbd_cngetc,
    -	aplkbd_cnpollc,
    -	aplkbd_cnbell,
    -};
    -
    -int	aplkbd_enable(void *, int);
    -void	aplkbd_set_leds(void *, int);
    -int	aplkbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
    -
    -const struct wskbd_accessops aplkbd_accessops = {
    -	.enable = aplkbd_enable,
    -	.ioctl = aplkbd_ioctl,
    -	.set_leds = aplkbd_set_leds,
    -};
    -
    -int	 aplkbd_match(struct device *, void *, void *);
    -void	 aplkbd_attach(struct device *, struct device *, void *);
    -
    -const struct cfattach aplkbd_ca = {
    -	sizeof(struct aplkbd_softc), aplkbd_match, aplkbd_attach
    -};
    -
    -struct cfdriver aplkbd_cd = {
    -	NULL, "aplkbd", DV_DULL
    -};
    -
    -int
    -aplkbd_match(struct device *parent, void *match, void *aux)
    -{
    -	struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux;
    -
    -	return (aa->aa_reportid == APLHIDEV_KBD_DEVICE);
    -}
    -
    -void
    -aplkbd_attach(struct device *parent, struct device *self, void *aux)
    -{
    -	struct aplkbd_softc *sc = (struct aplkbd_softc *)self;
    -	struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux;
    -	struct hidkbd *kbd = &sc->sc_kbd;
    -
    -	sc->sc_hidev = (struct aplhidev_softc *)parent;
    -	if (hidkbd_attach(self, kbd, 1, 0, APLHIDEV_KBD_DEVICE,
    -	    aa->aa_desc, aa->aa_desclen))
    -		return;
    -
    -	printf("\n");
    -
    -	if (hid_locate(aa->aa_desc, aa->aa_desclen, HID_USAGE2(HUP_APPLE, HUG_FN_KEY),
    -	    1, hid_input, &kbd->sc_fn, NULL)) {
    -		switch (sc->sc_hidev->sc_product) {
    -		case USB_PRODUCT_APPLE_WELLSPRINGM1_J293:
    -			kbd->sc_munge = hidkbd_apple_tb_munge;
    -			break;
    -		default:
    -			kbd->sc_munge = hidkbd_apple_munge;
    -			break;
    -		}
    -	}
    -
    -	if (kbd->sc_console_keyboard) {
    -		extern struct wskbd_mapdata ukbd_keymapdata;
    -
    -		ukbd_keymapdata.layout = KB_US | KB_DEFAULT;
    -		wskbd_cnattach(&aplkbd_consops, sc, &ukbd_keymapdata);
    -		aplkbd_enable(sc, 1);
    -	}
    -
    -	hidkbd_attach_wskbd(kbd, KB_US | KB_DEFAULT, &aplkbd_accessops);
    -}
    -
    -void
    -aplkbd_intr(struct device *self, uint8_t *packet, size_t packetlen)
    -{
    -	struct aplkbd_softc *sc = (struct aplkbd_softc *)self;
    -	struct hidkbd *kbd = &sc->sc_kbd;
    -
    -	if (kbd->sc_enabled)
    -		hidkbd_input(kbd, &packet[1], packetlen - 1);
    -}
    -
    -int
    -aplkbd_enable(void *v, int on)
    -{
    -	struct aplkbd_softc *sc = v;
    -	struct hidkbd *kbd = &sc->sc_kbd;
    -
    -	return hidkbd_enable(kbd, on);
    -}
    -
    -int
    -aplkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
    -{
    -	struct aplkbd_softc *sc = v;
    -	struct hidkbd *kbd = &sc->sc_kbd;
    -
    -	switch (cmd) {
    -	case WSKBDIO_GTYPE:
    -		/* XXX: should we set something else? */
    -		*(u_int *)data = WSKBD_TYPE_USB;
    -		return 0;
    -	case WSKBDIO_SETLEDS:
    -		aplkbd_set_leds(v, *(int *)data);
    -		return 0;
    -	default:
    -		return hidkbd_ioctl(kbd, cmd, data, flag, p);
    -	}
    -}
    -
    -void
    -aplkbd_set_leds(void *v, int leds)
    -{
    -	struct aplkbd_softc *sc = v;
    -	struct hidkbd *kbd = &sc->sc_kbd;
    -	uint8_t res;
    -
    -	if (hidkbd_set_leds(kbd, leds, &res))
    -		aplhidev_set_leds(sc->sc_hidev, res);
    -}
    -
    -/* Console interface. */
    -void
    -aplkbd_cngetc(void *v, u_int *type, int *data)
    -{
    -	struct aplkbd_softc *sc = v;
    -	struct hidkbd *kbd = &sc->sc_kbd;
    -
    -	kbd->sc_polling = 1;
    -	while (kbd->sc_npollchar <= 0) {
    -		aplhidev_intr(sc->sc_dev.dv_parent);
    -		delay(1000);
    -	}
    -	kbd->sc_polling = 0;
    -	hidkbd_cngetc(kbd, type, data);
    -}
    -
    -void
    -aplkbd_cnpollc(void *v, int on)
    -{
    -	struct aplkbd_softc *sc = v;
    -
    -	if (on)
    -		sc->sc_spl = spltty();
    -	else
    -		splx(sc->sc_spl);
    -}
    -
    -void
    -aplkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume)
    -{
    -	hidkbd_bell(pitch, period, volume, 1);
    -}
    -
    -#if NAPLMS > 0
    -
    -/* Touchpad */
    -
    -/*
    - * The contents of the touchpad event packets is identical to those
    - * used by the ubcmtp(4) driver.  The relevant definitions and the
    - * code to decode the packets is replicated here.
    - */
    -
    -struct ubcmtp_finger {
    -	uint16_t	origin;
    -	uint16_t	abs_x;
    -	uint16_t	abs_y;
    -	uint16_t	rel_x;
    -	uint16_t	rel_y;
    -	uint16_t	tool_major;
    -	uint16_t	tool_minor;
    -	uint16_t	orientation;
    -	uint16_t	touch_major;
    -	uint16_t	touch_minor;
    -	uint16_t	unused[2];
    -	uint16_t	pressure;
    -	uint16_t	multi;
    -} __packed __attribute((aligned(2)));
    -
    -#define UBCMTP_MAX_FINGERS	16
    -
    -#define UBCMTP_TYPE4_TPOFF	(24 * sizeof(uint16_t))
    -#define UBCMTP_TYPE4_BTOFF	31
    -#define UBCMTP_TYPE4_FINGERPAD	(1 * sizeof(uint16_t))
    -
    -/* Use a constant, synaptics-compatible pressure value for now. */
    -#define DEFAULT_PRESSURE	40
    -
    -static struct wsmouse_param aplms_wsmousecfg[] = {
    -	{ WSMOUSECFG_MTBTN_MAXDIST, 0 }, /* 0: Compute a default value. */
    -};
    -
    -struct aplms_softc {
    -	struct device		sc_dev;
    -	struct aplhidev_softc	*sc_hidev;
    -	struct device		*sc_wsmousedev;
    -
    -	int			sc_enabled;
    -
    -	int			tp_offset;
    -	int			tp_fingerpad;
    -
    -	struct mtpoint		frame[UBCMTP_MAX_FINGERS];
    -	int			contacts;
    -	int			btn;
    -};
    -
    -int	aplms_enable(void *);
    -void	aplms_disable(void *);
    -int	aplms_ioctl(void *, u_long, caddr_t, int, struct proc *);
    -
    -const struct wsmouse_accessops aplms_accessops = {
    -	.enable = aplms_enable,
    -	.disable = aplms_disable,
    -	.ioctl = aplms_ioctl,
    -};
    -
    -int	 aplms_match(struct device *, void *, void *);
    -void	 aplms_attach(struct device *, struct device *, void *);
    -
    -const struct cfattach aplms_ca = {
    -	sizeof(struct aplms_softc), aplms_match, aplms_attach
    -};
    -
    -struct cfdriver aplms_cd = {
    -	NULL, "aplms", DV_DULL
    -};
    -
    -int	aplms_configure(struct aplms_softc *);
    -
    -int
    -aplms_match(struct device *parent, void *match, void *aux)
    -{
    -	struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux;
    -
    -	return (aa->aa_reportid == APLHIDEV_TP_DEVICE);
    -}
    -
    -void
    -aplms_attach(struct device *parent, struct device *self, void *aux)
    -{
    -	struct aplms_softc *sc = (struct aplms_softc *)self;
    -	struct wsmousedev_attach_args aa;
    -
    -	sc->sc_hidev = (struct aplhidev_softc *)parent;
    -
    -	printf("\n");
    -
    -	sc->tp_offset = UBCMTP_TYPE4_TPOFF;
    -	sc->tp_fingerpad = UBCMTP_TYPE4_FINGERPAD;
    -
    -	aa.accessops = &aplms_accessops;
    -	aa.accesscookie = sc;
    -
    -	sc->sc_wsmousedev = config_found(self, &aa, wsmousedevprint);
    -	if (sc->sc_wsmousedev != NULL && aplms_configure(sc))
    -		aplms_disable(sc);
    -}
    -
    -int
    -aplms_configure(struct aplms_softc *sc)
    -{
    -	struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev);
    -
    -	hw->type = WSMOUSE_TYPE_TOUCHPAD;
    -	hw->hw_type = WSMOUSEHW_CLICKPAD;
    -	hw->x_min = sc->sc_hidev->sc_x_min;
    -	hw->x_max = sc->sc_hidev->sc_x_max;
    -	hw->y_min = sc->sc_hidev->sc_y_min;
    -	hw->y_max = sc->sc_hidev->sc_y_max;
    -	hw->h_res = sc->sc_hidev->sc_h_res;
    -	hw->v_res = sc->sc_hidev->sc_v_res;
    -	hw->mt_slots = UBCMTP_MAX_FINGERS;
    -	hw->flags = WSMOUSEHW_MT_TRACKING;
    -
    -	return wsmouse_configure(sc->sc_wsmousedev,
    -	    aplms_wsmousecfg, nitems(aplms_wsmousecfg));
    -}
    -
    -void
    -aplms_intr(struct device *self, uint8_t *packet, size_t packetlen)
    -{
    -	struct aplms_softc *sc = (struct aplms_softc *)self;
    -	struct ubcmtp_finger *finger;
    -	int off, s, btn, contacts;
    -
    -	if (!sc->sc_enabled)
    -		return;
    -
    -	contacts = 0;
    -	for (off = sc->tp_offset; off < packetlen;
    -	    off += (sizeof(struct ubcmtp_finger) + sc->tp_fingerpad)) {
    -		finger = (struct ubcmtp_finger *)(packet + off);
    -
    -		if ((int16_t)letoh16(finger->touch_major) == 0)
    -			continue; /* finger lifted */
    -
    -		sc->frame[contacts].x = (int16_t)letoh16(finger->abs_x);
    -		sc->frame[contacts].y = (int16_t)letoh16(finger->abs_y);
    -		sc->frame[contacts].pressure = DEFAULT_PRESSURE;
    -		contacts++;
    -	}
    -
    -	btn = sc->btn;
    -	sc->btn = !!((int16_t)letoh16(packet[UBCMTP_TYPE4_BTOFF]));
    -
    -	if (contacts || sc->contacts || sc->btn != btn) {
    -		sc->contacts = contacts;
    -		s = spltty();
    -		wsmouse_buttons(sc->sc_wsmousedev, sc->btn);
    -		wsmouse_mtframe(sc->sc_wsmousedev, sc->frame, contacts);
    -		wsmouse_input_sync(sc->sc_wsmousedev);
    -		splx(s);
    -	}
    -}
    -
    -int
    -aplms_enable(void *v)
    -{
    -	struct aplms_softc *sc = v;
    -
    -	if (sc->sc_enabled)
    -		return EBUSY;
    -
    -	sc->sc_enabled = 1;
    -	return 0;
    -}
    -
    -void
    -aplms_disable(void *v)
    -{
    -	struct aplms_softc *sc = v;
    -
    -	sc->sc_enabled = 0;
    -}
    -
    -int
    -aplms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
    -{
    -	struct aplms_softc *sc = v;
    -	struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev);
    -	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
    -	int wsmode;
    -
    -	switch (cmd) {
    -	case WSMOUSEIO_GTYPE:
    -		*(u_int *)data = hw->type;
    -		break;
    -
    -	case WSMOUSEIO_GCALIBCOORDS:
    -		wsmc->minx = hw->x_min;
    -		wsmc->maxx = hw->x_max;
    -		wsmc->miny = hw->y_min;
    -		wsmc->maxy = hw->y_max;
    -		wsmc->swapxy = 0;
    -		wsmc->resx = hw->h_res;
    -		wsmc->resy = hw->v_res;
    -		break;
    -
    -	case WSMOUSEIO_SETMODE:
    -		wsmode = *(u_int *)data;
    -		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) {
    -			printf("%s: invalid mode %d\n", sc->sc_dev.dv_xname,
    -			    wsmode);
    -			return (EINVAL);
    -		}
    -		wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
    -		break;
    -
    -	default:
    -		return -1;
    -	}
    -
    -	return 0;
    -}
    -
    -#else
    -
    -void
    -aplms_intr(struct device *self, uint8_t *packet, size_t packetlen)
    -{
    -}
    -
    -#endif
    Index: dev/acpi/ispi_acpi.c
    ===================================================================
    RCS file: /cvs/src/sys/dev/acpi/ispi_acpi.c,v
    retrieving revision 1.1
    diff -u -p -u -r1.1 ispi_acpi.c
    --- dev/acpi/ispi_acpi.c	14 Nov 2025 01:55:07 -0000	1.1
    +++ dev/acpi/ispi_acpi.c	20 Nov 2025 16:42:41 -0000
    @@ -92,6 +92,7 @@ int
     ispi_acpi_found_hid(struct aml_node *node, void *arg)
     {
     	struct ispi_softc *sc = (struct ispi_softc *)arg;
    +	struct spi_attach_args sa;
     	int64_t sta;
     	char cdev[32], dev[32];
     
    @@ -110,7 +111,17 @@ ispi_acpi_found_hid(struct aml_node *nod
     
     	acpi_attach_deps(acpi_softc, node->parent);
     
    -	/* TODO */
    +	if (strcmp(cdev, "apple-spi-topcase") == 0) {
    +		memset(&sa, 0, sizeof(sa));
    +		sa.sa_tag = &sc->sc_spi_tag;
    +		sa.sa_name = cdev;
    +		sa.sa_cookie = node->parent;
    +
    +		if (config_found(&sc->sc_dev, &sa, ispi_spi_print)) {
    +			node->attached = 1;
    +			return 1;
    +		}
    +	}
     
     	return 0;
     }
    Index: dev/spi/aplhidev.c
    ===================================================================
    RCS file: dev/spi/aplhidev.c
    diff -N dev/spi/aplhidev.c
    --- /dev/null	1 Jan 1970 00:00:00 -0000
    +++ dev/spi/aplhidev.c	20 Nov 2025 16:42:42 -0000
    @@ -0,0 +1,1271 @@
    +/*	$OpenBSD: aplhidev.c,v 1.13 2024/01/15 13:27:20 kettenis Exp $	*/
    +/*
    + * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
    + * Copyright (c) 2013-2014 joshua stein <jcs@openbsd.org>
    + *
    + * Permission to use, copy, modify, and distribute this software for any
    + * purpose with or without fee is hereby granted, provided that the above
    + * copyright notice and this permission notice appear in all copies.
    + *
    + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    + */
    +
    +#include <sys/param.h>
    +#include <sys/systm.h>
    +#include <sys/kernel.h>
    +#include <sys/device.h>
    +#include <sys/malloc.h>
    +#include <sys/timeout.h>
    +
    +#include <lib/libkern/crc16.h>
    +
    +#include <dev/spi/spivar.h>
    +
    +#ifdef __HAVE_FDT
    +#include <machine/fdt.h>
    +#include <dev/ofw/openfirm.h>
    +#include <dev/ofw/ofw_gpio.h>
    +#include <dev/ofw/ofw_pinctrl.h>
    +#endif
    +
    +#include "acpi.h"
    +#if NACPI > 0 && !defined(__HAVE_FDT)
    +#include <dev/acpi/acpireg.h>
    +#include <dev/acpi/acpivar.h>
    +#include <dev/acpi/acpidev.h>
    +#include <dev/acpi/amltypes.h>
    +#include <dev/acpi/dsdt.h>
    +#endif
    +
    +#include <dev/usb/usbdevs.h>
    +
    +#include <dev/wscons/wsconsio.h>
    +#include <dev/wscons/wskbdvar.h>
    +#include <dev/wscons/wsksymdef.h>
    +#include <dev/wscons/wsmousevar.h>
    +
    +#include <dev/hid/hid.h>
    +#include <dev/hid/hidkbdsc.h>
    +#include <dev/hid/hidmsvar.h>
    +
    +#include "aplhidev.h"
    +
    +/* #define APLHIDEV_DEBUG 1 */
    +
    +#ifdef APLHIDEV_DEBUG
    +#define DPRINTF(x) printf x
    +#else
    +#define DPRINTF(x)
    +#endif
    +
    +#define APLHIDEV_READ_PACKET	0x20
    +#define APLHIDEV_WRITE_PACKET	0x40
    +
    +#define APLHIDEV_KBD_DEVICE	1
    +#define APLHIDEV_TP_DEVICE	2
    +#define APLHIDEV_INFO_DEVICE	208
    +
    +#define APLHIDEV_GET_INFO	0x0120
    +#define APLHIDEV_GET_DESCRIPTOR	0x1020
    +#define  APLHIDEV_DESC_MAX	512
    +#define APLHIDEV_KBD_REPORT	0x0110
    +#define APLHIDEV_TP_REPORT	0x0210
    +#define APLHIDEV_SET_LEDS	0x0151
    +#define APLHIDEV_SET_MODE	0x0252
    +#define  APLHIDEV_MODE_HID	0x00
    +#define  APLHIDEV_MODE_RAW	0x01
    +#define APLHIDEV_GET_DIMENSIONS	0xd932
    +#define APLHIDEV_SET_BACKLIGHT	0xb051
    +#define  APLHIDEV_BACKLIGHT_MAGIC 0x01b0
    +#define  APLHIDEV_BACKLIGHT_MIN	32
    +#define  APLHIDEV_BACKLIGHT_MAX	255
    +#define  APLHIDEV_BACKLIGHT_ON	0x01f4
    +#define  APLHIDEV_BACKLIGHT_OFF	0x0001
    +
    +struct aplhidev_dim {
    +	uint32_t width;
    +	uint32_t height;
    +	int16_t x_min;
    +	int16_t y_min;
    +	int16_t x_max;
    +	int16_t y_max;
    +};
    +
    +struct aplhidev_attach_args {
    +	uint8_t	aa_reportid;
    +	void	*aa_desc;
    +	size_t	aa_desclen;
    +};
    +
    +struct aplhidev_spi_packet {
    +	uint8_t		flags;
    +	uint8_t		device;
    +	uint16_t	offset;
    +	uint16_t	remaining;
    +	uint16_t	len;
    +	uint8_t		data[246];
    +	uint16_t	crc;
    +};
    +
    +struct aplhidev_spi_status {
    +	uint8_t		status[4];
    +};
    +
    +struct aplhidev_msghdr {
    +	uint16_t	type;
    +	uint8_t		device;
    +	uint8_t		msgid;
    +	uint16_t	rsplen;
    +	uint16_t	cmdlen;
    +};
    +
    +struct aplhidev_info_hdr {
    +	uint16_t	unknown[2];
    +	uint16_t	num_devices;
    +	uint16_t	vendor;
    +	uint16_t	product;
    +	uint16_t	version;
    +	uint16_t	vendor_str[2];
    +	uint16_t	product_str[2];
    +	uint16_t	serial_str[2];
    +};
    +
    +struct aplhidev_get_desc {
    +	struct aplhidev_msghdr	hdr;
    +	uint16_t		crc;
    +};
    +
    +struct aplhidev_set_leds {
    +	struct aplhidev_msghdr	hdr;
    +	uint8_t			reportid;
    +	uint8_t			leds;
    +	uint16_t		crc;
    +};
    +
    +struct aplhidev_set_mode {
    +	struct aplhidev_msghdr	hdr;
    +	uint8_t			reportid;
    +	uint8_t			mode;
    +	uint16_t		crc;
    +};
    +
    +struct aplhidev_set_backlight {
    +	struct aplhidev_msghdr	hdr;
    +	uint16_t		magic;
    +	uint16_t		level;
    +	uint16_t		on_off;
    +	uint16_t		crc;
    +};
    +
    +struct aplhidev_softc {
    +	struct device		sc_dev;
    +#if NACPI > 0 && !defined(__HAVE_FDT)
    +	struct aml_node		*sc_dev_node;
    +#endif
    +
    +	spi_tag_t		sc_spi_tag;
    +	struct spi_config	sc_spi_conf;
    +
    +	uint8_t			sc_msgid;
    +
    +	uint32_t		*sc_gpio;
    +	int			sc_gpiolen;
    +
    +	uint8_t			sc_mode;
    +	uint16_t		sc_vendor;
    +	uint16_t		sc_product;
    +
    +	struct device 		*sc_kbd;
    +	uint8_t			sc_kbddesc[APLHIDEV_DESC_MAX];
    +	size_t			sc_kbddesclen;
    +
    +	struct device		*sc_ms;
    +	uint8_t			sc_tpdesc[APLHIDEV_DESC_MAX];
    +	size_t			sc_tpdesclen;
    +	uint8_t			sc_dimdesc[APLHIDEV_DESC_MAX];
    +	size_t			sc_dimdesclen;
    +	int			sc_x_min;
    +	int			sc_x_max;
    +	int			sc_y_min;
    +	int			sc_y_max;
    +	int			sc_h_res;
    +	int			sc_v_res;
    +};
    +
    +int	 aplhidev_match(struct device *, void *, void *);
    +void	 aplhidev_attach(struct device *, struct device *, void *);
    +#ifdef __HAVE_FDT
    +void	 aplhidev_fdt_init(struct aplhidev_softc *, void *);
    +#elif NACPI > 0
    +int	 aplhidev_acpi_init(struct aplhidev_softc *);
    +int	 aplhidev_enable_spi(struct aplhidev_softc *);
    +int	 aplhidev_gpe_intr(struct aml_node *, int, void *);
    +#endif
    +
    +const struct cfattach aplhidev_ca = {
    +	sizeof(struct aplhidev_softc), aplhidev_match, aplhidev_attach
    +};
    +
    +struct cfdriver aplhidev_cd = {
    +	NULL, "aplhidev", DV_DULL
    +};
    +
    +void	aplhidev_get_info(struct aplhidev_softc *);
    +void	aplhidev_get_descriptor(struct aplhidev_softc *, uint8_t);
    +void	aplhidev_set_leds(struct aplhidev_softc *, uint8_t);
    +void	aplhidev_set_backlight(struct aplhidev_softc *, uint8_t);
    +void	aplhidev_set_mode(struct aplhidev_softc *, uint8_t);
    +void	aplhidev_get_dimensions(struct aplhidev_softc *);
    +
    +void	aplkbd_set_backlight(void *);
    +extern int (*wskbd_get_backlight)(struct wskbd_backlight *);
    +extern int (*wskbd_set_backlight)(struct wskbd_backlight *);
    +int	aplkbd_wskbd_get_backlight(struct wskbd_backlight *);
    +int	aplkbd_wskbd_set_backlight(struct wskbd_backlight *);
    +
    +int	aplhidev_intr(void *);
    +void	aplkbd_intr(struct device *, uint8_t *, size_t);
    +void	aplms_intr(struct device *, uint8_t *, size_t);
    +
    +int
    +aplhidev_match(struct device *parent, void *match, void *aux)
    +{
    +	struct spi_attach_args *sa = aux;
    +#if NACPI > 0 && !defined(__HAVE_FDT)
    +	struct aml_node *node = (struct aml_node *)sa->sa_cookie;
    +	uint64_t val;
    +#endif
    +
    +#if defined(__HAVE_FDT)
    +	if (strcmp(sa->sa_name, "apple,spi-hid-transport") == 0)
    +		return 1;
    +
    +#elif NACPI > 0
    +	if (strcmp(sa->sa_name, "apple-spi-topcase") == 0) {
    +		/* don't attach if USB interface is present */
    +		if (aml_evalinteger(acpi_softc, node, "UIST", 0, NULL,
    +		    &val) == 0 && val)
    +			return 0;
    +
    +		return 1;
    +	}
    +#endif
    +
    +	return 0;
    +}
    +
    +void
    +aplhidev_attach(struct device *parent, struct device *self, void *aux)
    +{
    +	struct aplhidev_softc *sc = (struct aplhidev_softc *)self;
    +	struct spi_attach_args *sa = aux;
    +	struct aplhidev_attach_args aa;
    +	struct aplhidev_dim dim;
    +	int retry;
    +
    +	sc->sc_spi_tag = sa->sa_tag;
    +
    +#ifdef __HAVE_FDT
    +	aplhidev_fdt_init(sc, sa->sa_cookie);
    +
    +#elif NACPI > 0
    +	sc->sc_dev_node = (struct aml_node *)sa->sa_cookie;
    +	if (aplhidev_acpi_init(sc) != 0)
    +		return;
    +#endif
    +
    +	aplhidev_get_info(sc);
    +	for (retry = 10; retry > 0; retry--) {
    +		aplhidev_intr(sc);
    +		delay(1000);
    +		if (sc->sc_vendor != 0 && sc->sc_product != 0)
    +			break;
    +	}
    +
    +	aplhidev_get_descriptor(sc, APLHIDEV_KBD_DEVICE);
    +	for (retry = 10; retry > 0; retry--) {
    +		aplhidev_intr(sc);
    +		delay(1000);
    +		if (sc->sc_kbddesclen > 0)
    +			break;
    +	}
    +
    +	aplhidev_get_descriptor(sc, APLHIDEV_TP_DEVICE);
    +	for (retry = 10; retry > 0; retry--) {
    +		aplhidev_intr(sc);
    +		delay(1000);
    +		if (sc->sc_tpdesclen > 0)
    +			break;
    +	}
    +
    +	sc->sc_mode = APLHIDEV_MODE_HID;
    +	aplhidev_set_mode(sc, APLHIDEV_MODE_RAW);
    +	for (retry = 10; retry > 0; retry--) {
    +		aplhidev_intr(sc);
    +		delay(1000);
    +		if (sc->sc_mode == APLHIDEV_MODE_RAW)
    +			break;
    +	}
    +
    +	aplhidev_get_dimensions(sc);
    +	for (retry = 10; retry > 0; retry--) {
    +		aplhidev_intr(sc);
    +		delay(1000);
    +		if (sc->sc_dimdesclen > 0)
    +			break;
    +	}
    +
    +	printf("\n");
    +
    +	if (sc->sc_dimdesclen == sizeof(dim) + 1) {
    +		memcpy(&dim, &sc->sc_dimdesc[1], sizeof(dim));
    +		sc->sc_x_min = dim.x_min;
    +		sc->sc_x_max = dim.x_max;
    +		sc->sc_y_min = dim.y_min;
    +		sc->sc_y_max = dim.y_max;
    +		sc->sc_h_res = (100 * (dim.x_max - dim.x_min)) / dim.width;
    +		sc->sc_v_res = (100 * (dim.y_max - dim.y_min)) / dim.height;
    +	}
    +
    +	if (sc->sc_kbddesclen > 0) {
    +		aa.aa_reportid = APLHIDEV_KBD_DEVICE;
    +		aa.aa_desc = sc->sc_kbddesc;
    +		aa.aa_desclen = sc->sc_kbddesclen;
    +		sc->sc_kbd = config_found(self, &aa, NULL);
    +	}
    +
    +	if (sc->sc_tpdesclen > 0) {
    +		aa.aa_reportid = APLHIDEV_TP_DEVICE;
    +		aa.aa_desc = sc->sc_tpdesc;
    +		aa.aa_desclen = sc->sc_tpdesclen;
    +		sc->sc_ms = config_found(self, &aa, NULL);
    +	}
    +}
    +
    +#ifdef __HAVE_FDT
    +void
    +aplhidev_fdt_init(struct aplhidev_softc *sc, void *cookie)
    +{
    +	int node = *(int *)cookie;
    +
    +	sc->sc_gpiolen = OF_getproplen(node, "spien-gpios");
    +	if (sc->sc_gpiolen > 0) {
    +		sc->sc_gpio = malloc(sc->sc_gpiolen, M_TEMP, M_WAITOK);
    +		OF_getpropintarray(node, "spien-gpios",
    +		    sc->sc_gpio, sc->sc_gpiolen);
    +		gpio_controller_config_pin(sc->sc_gpio, GPIO_CONFIG_OUTPUT);
    +
    +		/* Reset */
    +		gpio_controller_set_pin(sc->sc_gpio, 1);
    +		delay(5000);
    +		gpio_controller_set_pin(sc->sc_gpio, 0);
    +		delay(5000);
    +
    +		/* Enable. */
    +		gpio_controller_set_pin(sc->sc_gpio, 1);
    +		delay(50000);
    +	}
    +
    +	sc->sc_spi_conf.sc_bpw = 8;
    +	sc->sc_spi_conf.sc_freq = OF_getpropint(node, "spi-max-frequency", 0);
    +	sc->sc_spi_conf.sc_cs = OF_getpropint(node, "reg", 0);
    +	sc->sc_spi_conf.sc_cs_delay = 100;
    +
    +	fdt_intr_establish(node, IPL_TTY, aplhidev_intr, sc,
    +	    sc->sc_dev.dv_xname);
    +}
    +#endif
    +
    +#if NACPI > 0 && !defined(__HAVE_FDT)
    +int
    +aplhidev_acpi_init(struct aplhidev_softc *sc)
    +{
    +	/* a0b5b7c6-1318-441c-b0c9-fe695eaf949b */
    +	static uint8_t guid[] = {
    +		0xC6, 0xB7, 0xB5, 0xA0, 0x18, 0x13, 0x1C, 0x44,
    +		0xB0, 0xC9, 0xFE, 0x69, 0x5E, 0xAF, 0x94, 0x9B,
    +	};
    +	struct aml_value cmd[4], res;
    +	struct aml_node *node;
    +	uint64_t val;
    +	int i;
    +
    +	/*
    +	 * On newer Apple hardware where we claim an OSI of Darwin, _CRS
    +	 * doesn't return a useful SpiSerialBusV2 object but instead returns
    +	 * parameters from a _DSM method when called with a particular UUID
    +	 * which macOS does.
    +	 */
    +	if (!aml_searchname(sc->sc_dev_node, "_DSM")) {
    +		printf("%s: couldn't find _DSM at %s\n", sc->sc_dev.dv_xname,
    +		    aml_nodename(sc->sc_dev_node));
    +		return 1;
    +	}
    +
    +	bzero(&cmd, sizeof(cmd));
    +	cmd[0].type = AML_OBJTYPE_BUFFER;
    +	cmd[0].v_buffer = (uint8_t *)&guid;
    +	cmd[0].length = sizeof(guid);
    +	cmd[1].type = AML_OBJTYPE_INTEGER;
    +	cmd[1].v_integer = 1;
    +	cmd[1].length = 1;
    +	cmd[2].type = AML_OBJTYPE_INTEGER;
    +	cmd[2].v_integer = 1;
    +	cmd[2].length = 1;
    +	cmd[3].type = AML_OBJTYPE_BUFFER;
    +	cmd[3].length = 0;
    +
    +	if (aml_evalname(acpi_softc, sc->sc_dev_node, "_DSM", 4, cmd, &res)) {
    +		printf("%s: eval of _DSM at %s failed\n",
    +		    sc->sc_dev.dv_xname, aml_nodename(sc->sc_dev_node));
    +		return 1;
    +	}
    +
    +	if (res.type != AML_OBJTYPE_PACKAGE) {
    +		printf("%s: bad _DSM result at %s: %d\n",
    +		    sc->sc_dev.dv_xname, aml_nodename(sc->sc_dev_node),
    +		    res.type);
    +		aml_freevalue(&res);
    +		return 1;
    +	}
    +
    +	if (res.length % 2 != 0) {
    +		printf("%s: _DSM length %d not even\n", sc->sc_dev.dv_xname,
    +		    res.length);
    +		aml_freevalue(&res);
    +		return 1;
    +	}
    +
    +	for (i = 0; i < res.length; i += 2) {
    +		char *k;
    +
    +		if (res.v_package[i]->type != AML_OBJTYPE_STRING ||
    +		    res.v_package[i + 1]->type != AML_OBJTYPE_BUFFER) {
    +			printf("%s: expected string+buffer, got %d+%d\n",
    +			    sc->sc_dev.dv_xname, res.v_package[i]->type,
    +			    res.v_package[i + 1]->type);
    +			aml_freevalue(&res);
    +			return 1;
    +		}
    +
    +		k = res.v_package[i]->v_string;
    +		val = aml_val2int(res.v_package[i + 1]);
    +
    +		DPRINTF(("%s: %s = %lld\n", sc->sc_dev.dv_xname, k, val));
    +
    +		if (strcmp(k, "spiSclkPeriod") == 0) {
    +			sc->sc_spi_conf.sc_freq = 1000000000 / val;
    +		} else if (strcmp(k, "spiWordSize") == 0) {
    +			sc->sc_spi_conf.sc_bpw = val;
    +		} else if (strcmp(k, "spiSPO") == 0) {
    +			if (val)
    +				sc->sc_spi_conf.sc_flags |= SPI_CONFIG_CPOL;
    +		} else if (strcmp(k, "spiSPH") == 0) {
    +			if (val)
    +				sc->sc_spi_conf.sc_flags |= SPI_CONFIG_CPHA;
    +		} else if (strcmp(k, "spiCSDelay") == 0) {
    +			sc->sc_spi_conf.sc_cs_delay = val;
    +		} else {
    +			DPRINTF(("%s: unused _DSM key %s\n",
    +			    sc->sc_dev.dv_xname, k));
    +		}
    +	}
    +	aml_freevalue(&res);
    +
    +	if (aplhidev_enable_spi(sc) != 0)
    +		return 1;
    +
    +	node = aml_searchname(sc->sc_dev_node, "_GPE");
    +	if (node)
    +		aml_register_notify(sc->sc_dev_node, NULL, aplhidev_gpe_intr,
    +		    sc, ACPIDEV_NOPOLL);
    +
    +	return 0;
    +}
    +
    +int
    +aplhidev_enable_spi(struct aplhidev_softc *sc)
    +{
    +	struct aml_value arg;
    +	uint64_t val;
    +
    +	/* if SPI is not enabled, enable it */
    +	if (aml_evalinteger(acpi_softc, sc->sc_dev_node, "SIST", 0, NULL,
    +	    &val) == 0 && !val) {
    +	    	DPRINTF(("%s: SIST is %lld\n", sc->sc_dev.dv_xname, val));
    +
    +		bzero(&arg, sizeof(arg));
    +		arg.type = AML_OBJTYPE_INTEGER;
    +		arg.v_integer = 1;
    +		arg.length = 1;
    +
    +		if (aml_evalname(acpi_softc, sc->sc_dev_node, "SIEN", 1, &arg,
    +		    NULL) != 0) {
    +			DPRINTF(("%s: couldn't enable SPI mode\n", __func__));
    +			return 1;
    +		}
    +
    +		DELAY(500);
    +	} else {
    +	    	DPRINTF(("%s: SIST is already %lld\n", sc->sc_dev.dv_xname,
    +		    val));
    +	}
    +
    +	return 0;
    +}
    +#endif
    +
    +void
    +aplhidev_get_info(struct aplhidev_softc *sc)
    +{
    +	struct aplhidev_spi_packet packet;
    +	struct aplhidev_get_desc *msg;
    +	struct aplhidev_spi_status status;
    +
    +	memset(&packet, 0, sizeof(packet));
    +	packet.flags = APLHIDEV_WRITE_PACKET;
    +	packet.device = APLHIDEV_INFO_DEVICE;
    +	packet.len = sizeof(*msg);
    +
    +	msg = (void *)&packet.data[0];
    +	msg->hdr.type = APLHIDEV_GET_INFO;
    +	msg->hdr.device = APLHIDEV_INFO_DEVICE;
    +	msg->hdr.msgid = sc->sc_msgid++;
    +	msg->hdr.cmdlen = 0;
    +	msg->hdr.rsplen = APLHIDEV_DESC_MAX;
    +	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
    +
    +	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
    +
    +	spi_acquire_bus(sc->sc_spi_tag, 0);
    +	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
    +	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
    +	    SPI_KEEP_CS);
    +	delay(100);
    +	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
    +	spi_release_bus(sc->sc_spi_tag, 0);
    +
    +	delay(1000);
    +}
    +
    +void
    +aplhidev_get_descriptor(struct aplhidev_softc *sc, uint8_t device)
    +{
    +	struct aplhidev_spi_packet packet;
    +	struct aplhidev_get_desc *msg;
    +	struct aplhidev_spi_status status;
    +
    +	memset(&packet, 0, sizeof(packet));
    +	packet.flags = APLHIDEV_WRITE_PACKET;
    +	packet.device = APLHIDEV_INFO_DEVICE;
    +	packet.len = sizeof(*msg);
    +
    +	msg = (void *)&packet.data[0];
    +	msg->hdr.type = APLHIDEV_GET_DESCRIPTOR;
    +	msg->hdr.device = device;
    +	msg->hdr.msgid = sc->sc_msgid++;
    +	msg->hdr.cmdlen = 0;
    +	msg->hdr.rsplen = APLHIDEV_DESC_MAX;
    +	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
    +
    +	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
    +
    +	spi_acquire_bus(sc->sc_spi_tag, 0);
    +	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
    +	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
    +	    SPI_KEEP_CS);
    +	delay(100);
    +	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
    +	spi_release_bus(sc->sc_spi_tag, 0);
    +
    +	delay(1000);
    +}
    +
    +void
    +aplhidev_set_leds(struct aplhidev_softc *sc, uint8_t leds)
    +{
    +	struct aplhidev_spi_packet packet;
    +	struct aplhidev_set_leds *msg;
    +	struct aplhidev_spi_status status;
    +
    +	memset(&packet, 0, sizeof(packet));
    +	packet.flags = APLHIDEV_WRITE_PACKET;
    +	packet.device = APLHIDEV_KBD_DEVICE;
    +	packet.len = sizeof(*msg);
    +
    +	msg = (void *)&packet.data[0];
    +	msg->hdr.type = APLHIDEV_SET_LEDS;
    +	msg->hdr.device = APLHIDEV_KBD_DEVICE;
    +	msg->hdr.msgid = sc->sc_msgid++;
    +	msg->hdr.cmdlen = sizeof(*msg) - sizeof(struct aplhidev_msghdr) - 2;
    +	msg->hdr.rsplen = msg->hdr.cmdlen;
    +	msg->reportid = APLHIDEV_KBD_DEVICE;
    +	msg->leds = leds;
    +	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
    +
    +	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
    +
    +	/*
    +	 * XXX Without a delay here, the command will fail.  Does the
    +	 * controller need a bit of time between sending us a keypress
    +	 * event and accepting a new command from us?
    +	 */
    +	delay(250);
    +
    +	spi_acquire_bus(sc->sc_spi_tag, 0);
    +	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
    +	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
    +	    SPI_KEEP_CS);
    +	delay(100);
    +	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
    +	spi_release_bus(sc->sc_spi_tag, 0);
    +}
    +
    +void
    +aplhidev_set_backlight(struct aplhidev_softc *sc, uint8_t level)
    +{
    +	struct aplhidev_spi_packet packet;
    +	struct aplhidev_set_backlight *msg;
    +	struct aplhidev_spi_status status;
    +
    +	memset(&packet, 0, sizeof(packet));
    +	packet.flags = APLHIDEV_WRITE_PACKET;
    +	packet.device = APLHIDEV_KBD_DEVICE;
    +	packet.len = sizeof(*msg);
    +
    +	msg = (void *)&packet.data[0];
    +	msg->hdr.type = APLHIDEV_SET_BACKLIGHT;
    +	msg->hdr.device = APLHIDEV_KBD_DEVICE;
    +	msg->hdr.msgid = sc->sc_msgid++;
    +	msg->hdr.cmdlen = sizeof(*msg) - sizeof(struct aplhidev_msghdr) - 2;
    +	msg->hdr.rsplen = msg->hdr.cmdlen;
    +	msg->magic = htole16(APLHIDEV_BACKLIGHT_MAGIC);
    +	if (level <= APLHIDEV_BACKLIGHT_MIN) {
    +		msg->level = 0;
    +		msg->on_off = htole16(APLHIDEV_BACKLIGHT_OFF);
    +	} else {
    +		msg->level = htole16(level);
    +		msg->on_off = htole16(APLHIDEV_BACKLIGHT_ON);
    +	}
    +	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
    +
    +	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
    +
    +	spi_acquire_bus(sc->sc_spi_tag, 0);
    +	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
    +	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
    +	    SPI_KEEP_CS);
    +	delay(100);
    +	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
    +	spi_release_bus(sc->sc_spi_tag, 0);
    +}
    +
    +void
    +aplhidev_set_mode(struct aplhidev_softc *sc, uint8_t mode)
    +{
    +	struct aplhidev_spi_packet packet;
    +	struct aplhidev_set_mode *msg;
    +	struct aplhidev_spi_status status;
    +
    +	memset(&packet, 0, sizeof(packet));
    +	packet.flags = APLHIDEV_WRITE_PACKET;
    +	packet.device = APLHIDEV_TP_DEVICE;
    +	packet.len = sizeof(*msg);
    +
    +	msg = (void *)&packet.data[0];
    +	msg->hdr.type = APLHIDEV_SET_MODE;
    +	msg->hdr.device = APLHIDEV_TP_DEVICE;
    +	msg->hdr.msgid = sc->sc_msgid++;
    +	msg->hdr.cmdlen = sizeof(*msg) - sizeof(struct aplhidev_msghdr) - 2;
    +	msg->hdr.rsplen = msg->hdr.cmdlen;
    +	msg->reportid = APLHIDEV_TP_DEVICE;
    +	msg->mode = mode;
    +	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
    +
    +	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
    +
    +	spi_acquire_bus(sc->sc_spi_tag, 0);
    +	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
    +	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
    +	    SPI_KEEP_CS);
    +	delay(100);
    +	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
    +	spi_release_bus(sc->sc_spi_tag, 0);
    +
    +	delay(1000);
    +}
    +
    +void
    +aplhidev_get_dimensions(struct aplhidev_softc *sc)
    +{
    +	struct aplhidev_spi_packet packet;
    +	struct aplhidev_get_desc *msg;
    +	struct aplhidev_spi_status status;
    +
    +	memset(&packet, 0, sizeof(packet));
    +	packet.flags = APLHIDEV_WRITE_PACKET;
    +	packet.device = APLHIDEV_TP_DEVICE;
    +	packet.len = sizeof(*msg);
    +
    +	msg = (void *)&packet.data[0];
    +	msg->hdr.type = APLHIDEV_GET_DIMENSIONS;
    +	msg->hdr.device = 0;
    +	msg->hdr.msgid = sc->sc_msgid++;
    +	msg->hdr.cmdlen = 0;
    +	msg->hdr.rsplen = APLHIDEV_DESC_MAX;
    +	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
    +
    +	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
    +
    +	spi_acquire_bus(sc->sc_spi_tag, 0);
    +	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
    +	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
    +	    SPI_KEEP_CS);
    +	delay(100);
    +	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
    +	spi_release_bus(sc->sc_spi_tag, 0);
    +
    +	delay(1000);
    +}
    +
    +#if NACPI > 0 && !defined(__HAVE_FDT)
    +int
    +aplhidev_gpe_intr(struct aml_node *node, int gpe, void *arg)
    +{
    +	struct aplhidev_softc *sc = arg;
    +
    +	return aplhidev_intr(sc);
    +}
    +#endif
    +
    +int
    +aplhidev_intr(void *arg)
    +{
    +	struct aplhidev_softc *sc = arg;
    +	struct aplhidev_spi_packet packet;
    +	struct aplhidev_msghdr *hdr = (struct aplhidev_msghdr *)&packet.data[0];
    +
    +	memset(&packet, 0, sizeof(packet));
    +	spi_acquire_bus(sc->sc_spi_tag, 0);
    +	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
    +	spi_read(sc->sc_spi_tag, (char *)&packet, sizeof(packet));
    +	spi_release_bus(sc->sc_spi_tag, 0);
    +
    +	/* Treat empty packets as spurious interrupts. */
    +	if (packet.flags == 0 && packet.device == 0 && packet.crc == 0)
    +		return 0;
    +
    +	if (crc16(0, (uint8_t *)&packet, sizeof(packet)))
    +		return 1;
    +
    +	/* Keyboard input. */
    +	if (packet.flags == APLHIDEV_READ_PACKET &&
    +	    packet.device == APLHIDEV_KBD_DEVICE &&
    +	    hdr->type == APLHIDEV_KBD_REPORT) {
    +		if (sc->sc_kbd)
    +			aplkbd_intr(sc->sc_kbd, &packet.data[8], hdr->cmdlen);
    +		return 1;
    +	}
    +
    +	/* Touchpad input. */
    +	if (packet.flags == APLHIDEV_READ_PACKET &&
    +	    packet.device == APLHIDEV_TP_DEVICE &&
    +	    hdr->type == APLHIDEV_TP_REPORT) {
    +		if (sc->sc_ms)
    +			aplms_intr(sc->sc_ms, &packet.data[8], hdr->cmdlen);
    +		return 1;
    +	}
    +
    +	/* Replies to commands we sent. */
    +	if (packet.flags == APLHIDEV_WRITE_PACKET &&
    +	    packet.device == APLHIDEV_INFO_DEVICE &&
    +	    hdr->type == APLHIDEV_GET_INFO) {
    +		struct aplhidev_info_hdr *info =
    +		    (struct aplhidev_info_hdr *)&packet.data[8];
    +		sc->sc_vendor = info->vendor;
    +		sc->sc_product = info->product;
    +		return 1;
    +	}
    +	if (packet.flags == APLHIDEV_WRITE_PACKET &&
    +	    packet.device == APLHIDEV_INFO_DEVICE &&
    +	    hdr->type == APLHIDEV_GET_DESCRIPTOR) {
    +		switch (hdr->device) {
    +		case APLHIDEV_KBD_DEVICE:
    +			memcpy(sc->sc_kbddesc, &packet.data[8], hdr->cmdlen);
    +			sc->sc_kbddesclen = hdr->cmdlen;
    +			break;
    +		case APLHIDEV_TP_DEVICE:
    +			memcpy(sc->sc_tpdesc, &packet.data[8], hdr->cmdlen);
    +			sc->sc_tpdesclen = hdr->cmdlen;
    +			break;
    +		}
    +
    +		return 1;
    +	}
    +	if (packet.flags == APLHIDEV_WRITE_PACKET &&
    +	    packet.device == APLHIDEV_TP_DEVICE &&
    +	    hdr->type == APLHIDEV_SET_MODE) {
    +		sc->sc_mode = APLHIDEV_MODE_RAW;
    +		return 1;
    +	}
    +	if (packet.flags == APLHIDEV_WRITE_PACKET &&
    +	    packet.device == APLHIDEV_TP_DEVICE &&
    +	    hdr->type == APLHIDEV_GET_DIMENSIONS) {
    +		memcpy(sc->sc_dimdesc, &packet.data[8], hdr->cmdlen);
    +		sc->sc_dimdesclen = hdr->cmdlen;
    +		return 1;
    +	}
    +
    +	/* Valid, but unrecognized packet; ignore for now. */
    +	return 1;
    +}
    +
    +/* Keyboard */
    +
    +struct aplkbd_softc {
    +	struct device		sc_dev;
    +	struct aplhidev_softc	*sc_hidev;
    +	struct hidkbd		sc_kbd;
    +	int			sc_spl;
    +	int			sc_backlight;
    +};
    +
    +void	aplkbd_cngetc(void *, u_int *, int *);
    +void	aplkbd_cnpollc(void *, int);
    +void	aplkbd_cnbell(void *, u_int, u_int, u_int);
    +
    +const struct wskbd_consops aplkbd_consops = {
    +	aplkbd_cngetc,
    +	aplkbd_cnpollc,
    +	aplkbd_cnbell,
    +};
    +
    +int	aplkbd_enable(void *, int);
    +void	aplkbd_set_leds(void *, int);
    +int	aplkbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
    +
    +const struct wskbd_accessops aplkbd_accessops = {
    +	.enable = aplkbd_enable,
    +	.ioctl = aplkbd_ioctl,
    +	.set_leds = aplkbd_set_leds,
    +};
    +
    +int	 aplkbd_match(struct device *, void *, void *);
    +void	 aplkbd_attach(struct device *, struct device *, void *);
    +
    +const struct cfattach aplkbd_ca = {
    +	sizeof(struct aplkbd_softc), aplkbd_match, aplkbd_attach
    +};
    +
    +struct cfdriver aplkbd_cd = {
    +	NULL, "aplkbd", DV_DULL
    +};
    +
    +int
    +aplkbd_match(struct device *parent, void *match, void *aux)
    +{
    +	struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux;
    +
    +	return (aa->aa_reportid == APLHIDEV_KBD_DEVICE);
    +}
    +
    +void
    +aplkbd_attach(struct device *parent, struct device *self, void *aux)
    +{
    +	struct aplkbd_softc *sc = (struct aplkbd_softc *)self;
    +	struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux;
    +	struct hidkbd *kbd = &sc->sc_kbd;
    +
    +	sc->sc_hidev = (struct aplhidev_softc *)parent;
    +	if (hidkbd_attach(self, kbd, 1, 0, APLHIDEV_KBD_DEVICE,
    +	    aa->aa_desc, aa->aa_desclen))
    +		return;
    +
    +	printf("\n");
    +
    +	if (hid_locate(aa->aa_desc, aa->aa_desclen, HID_USAGE2(HUP_APPLE, HUG_FN_KEY),
    +	    1, hid_input, &kbd->sc_fn, NULL)) {
    +		switch (sc->sc_hidev->sc_product) {
    +		case USB_PRODUCT_APPLE_WELLSPRINGM1_J293:
    +			kbd->sc_munge = hidkbd_apple_tb_munge;
    +			break;
    +		default:
    +			kbd->sc_munge = hidkbd_apple_munge;
    +			break;
    +		}
    +	}
    +
    +	if (kbd->sc_console_keyboard) {
    +		extern struct wskbd_mapdata ukbd_keymapdata;
    +
    +		ukbd_keymapdata.layout = KB_US | KB_DEFAULT;
    +		wskbd_cnattach(&aplkbd_consops, sc, &ukbd_keymapdata);
    +		aplkbd_enable(sc, 1);
    +	}
    +
    +
    +	hidkbd_attach_wskbd(kbd, KB_US | KB_DEFAULT, &aplkbd_accessops);
    +
    +#if NACPI > 0 && !defined(__HAVE_FDT)
    +	sc->sc_backlight = APLHIDEV_BACKLIGHT_MIN;
    +	wskbd_get_backlight = aplkbd_wskbd_get_backlight;
    +	wskbd_set_backlight = aplkbd_wskbd_set_backlight;
    +#endif
    +}
    +
    +void
    +aplkbd_intr(struct device *self, uint8_t *packet, size_t packetlen)
    +{
    +	struct aplkbd_softc *sc = (struct aplkbd_softc *)self;
    +	struct hidkbd *kbd = &sc->sc_kbd;
    +
    +	if (kbd->sc_enabled)
    +		hidkbd_input(kbd, &packet[1], packetlen - 1);
    +}
    +
    +int
    +aplkbd_enable(void *v, int on)
    +{
    +	struct aplkbd_softc *sc = v;
    +	struct hidkbd *kbd = &sc->sc_kbd;
    +
    +	return hidkbd_enable(kbd, on);
    +}
    +
    +int
    +aplkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
    +{
    +	struct aplkbd_softc *sc = v;
    +	struct hidkbd *kbd = &sc->sc_kbd;
    +
    +	switch (cmd) {
    +	case WSKBDIO_GTYPE:
    +		/* XXX: should we set something else? */
    +		*(u_int *)data = WSKBD_TYPE_USB;
    +		return 0;
    +	case WSKBDIO_SETLEDS:
    +		aplkbd_set_leds(v, *(int *)data);
    +		return 0;
    +	default:
    +		return hidkbd_ioctl(kbd, cmd, data, flag, p);
    +	}
    +}
    +
    +void
    +aplkbd_set_leds(void *v, int leds)
    +{
    +	struct aplkbd_softc *sc = v;
    +	struct hidkbd *kbd = &sc->sc_kbd;
    +	uint8_t res;
    +
    +	if (hidkbd_set_leds(kbd, leds, &res))
    +		aplhidev_set_leds(sc->sc_hidev, res);
    +}
    +
    +int
    +aplkbd_wskbd_get_backlight(struct wskbd_backlight *kbl)
    +{
    +	struct aplkbd_softc *sc = aplkbd_cd.cd_devs[0];
    +
    +	if (sc == NULL)
    +		return 0;
    +
    +	kbl->min = APLHIDEV_BACKLIGHT_MIN;
    +	kbl->max = APLHIDEV_BACKLIGHT_MAX;
    +	kbl->curval = sc->sc_backlight;
    +
    +	return 0;
    +}
    +
    +int
    +aplkbd_wskbd_set_backlight(struct wskbd_backlight *kbl)
    +{
    +	struct aplkbd_softc *sc = aplkbd_cd.cd_devs[0];
    +	int val;
    +
    +	if (sc == NULL)
    +		return -1;
    +
    +	val = kbl->curval;
    +	if (val < APLHIDEV_BACKLIGHT_MIN)
    +		val = APLHIDEV_BACKLIGHT_MIN;
    +	if (val > APLHIDEV_BACKLIGHT_MAX)
    +		val = APLHIDEV_BACKLIGHT_MAX;
    +
    +	sc->sc_backlight = val;
    +	aplhidev_set_backlight((struct aplhidev_softc *)sc->sc_dev.dv_parent,
    +	    sc->sc_backlight);
    +
    +	return 0;
    +}
    +
    +/* Console interface. */
    +void
    +aplkbd_cngetc(void *v, u_int *type, int *data)
    +{
    +	struct aplkbd_softc *sc = v;
    +	struct hidkbd *kbd = &sc->sc_kbd;
    +
    +	kbd->sc_polling = 1;
    +	while (kbd->sc_npollchar <= 0) {
    +		aplhidev_intr(sc->sc_dev.dv_parent);
    +		delay(1000);
    +	}
    +	kbd->sc_polling = 0;
    +	hidkbd_cngetc(kbd, type, data);
    +}
    +
    +void
    +aplkbd_cnpollc(void *v, int on)
    +{
    +	struct aplkbd_softc *sc = v;
    +
    +	if (on)
    +		sc->sc_spl = spltty();
    +	else
    +		splx(sc->sc_spl);
    +}
    +
    +void
    +aplkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume)
    +{
    +	hidkbd_bell(pitch, period, volume, 1);
    +}
    +
    +#if NAPLMS > 0
    +
    +/* Touchpad */
    +
    +/*
    + * The contents of the touchpad event packets is identical to those
    + * used by the ubcmtp(4) driver.  The relevant definitions and the
    + * code to decode the packets is replicated here.
    + */
    +
    +struct ubcmtp_finger {
    +	uint16_t	origin;
    +	uint16_t	abs_x;
    +	uint16_t	abs_y;
    +	uint16_t	rel_x;
    +	uint16_t	rel_y;
    +	uint16_t	tool_major;
    +	uint16_t	tool_minor;
    +	uint16_t	orientation;
    +	uint16_t	touch_major;
    +	uint16_t	touch_minor;
    +	uint16_t	unused[2];
    +	uint16_t	pressure;
    +	uint16_t	multi;
    +} __packed __attribute((aligned(2)));
    +
    +#define UBCMTP_MAX_FINGERS	16
    +
    +#define UBCMTP_TYPE4_TPOFF	(24 * sizeof(uint16_t))
    +#define UBCMTP_TYPE4_BTOFF	31
    +#define UBCMTP_TYPE4_FINGERPAD	(1 * sizeof(uint16_t))
    +
    +/* Use a constant, synaptics-compatible pressure value for now. */
    +#define DEFAULT_PRESSURE	40
    +
    +static struct wsmouse_param aplms_wsmousecfg[] = {
    +	{ WSMOUSECFG_MTBTN_MAXDIST, 0 }, /* 0: Compute a default value. */
    +};
    +
    +struct aplms_softc {
    +	struct device		sc_dev;
    +	struct aplhidev_softc	*sc_hidev;
    +	struct device		*sc_wsmousedev;
    +
    +	int			sc_enabled;
    +
    +	int			tp_offset;
    +	int			tp_fingerpad;
    +
    +	struct mtpoint		frame[UBCMTP_MAX_FINGERS];
    +	int			contacts;
    +	int			btn;
    +};
    +
    +int	aplms_enable(void *);
    +void	aplms_disable(void *);
    +int	aplms_ioctl(void *, u_long, caddr_t, int, struct proc *);
    +
    +const struct wsmouse_accessops aplms_accessops = {
    +	.enable = aplms_enable,
    +	.disable = aplms_disable,
    +	.ioctl = aplms_ioctl,
    +};
    +
    +int	 aplms_match(struct device *, void *, void *);
    +void	 aplms_attach(struct device *, struct device *, void *);
    +
    +const struct cfattach aplms_ca = {
    +	sizeof(struct aplms_softc), aplms_match, aplms_attach
    +};
    +
    +struct cfdriver aplms_cd = {
    +	NULL, "aplms", DV_DULL
    +};
    +
    +int	aplms_configure(struct aplms_softc *);
    +
    +int
    +aplms_match(struct device *parent, void *match, void *aux)
    +{
    +	struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux;
    +
    +	return (aa->aa_reportid == APLHIDEV_TP_DEVICE);
    +}
    +
    +void
    +aplms_attach(struct device *parent, struct device *self, void *aux)
    +{
    +	struct aplms_softc *sc = (struct aplms_softc *)self;
    +	struct wsmousedev_attach_args aa;
    +
    +	sc->sc_hidev = (struct aplhidev_softc *)parent;
    +
    +	printf("\n");
    +
    +	sc->tp_offset = UBCMTP_TYPE4_TPOFF;
    +	sc->tp_fingerpad = UBCMTP_TYPE4_FINGERPAD;
    +
    +	aa.accessops = &aplms_accessops;
    +	aa.accesscookie = sc;
    +
    +	sc->sc_wsmousedev = config_found(self, &aa, wsmousedevprint);
    +	if (sc->sc_wsmousedev != NULL && aplms_configure(sc))
    +		aplms_disable(sc);
    +}
    +
    +int
    +aplms_configure(struct aplms_softc *sc)
    +{
    +	struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev);
    +
    +	hw->type = WSMOUSE_TYPE_TOUCHPAD;
    +	hw->hw_type = WSMOUSEHW_CLICKPAD;
    +	hw->x_min = sc->sc_hidev->sc_x_min;
    +	hw->x_max = sc->sc_hidev->sc_x_max;
    +	hw->y_min = sc->sc_hidev->sc_y_min;
    +	hw->y_max = sc->sc_hidev->sc_y_max;
    +	hw->h_res = sc->sc_hidev->sc_h_res;
    +	hw->v_res = sc->sc_hidev->sc_v_res;
    +	hw->mt_slots = UBCMTP_MAX_FINGERS;
    +	hw->flags = WSMOUSEHW_MT_TRACKING;
    +
    +	return wsmouse_configure(sc->sc_wsmousedev,
    +	    aplms_wsmousecfg, nitems(aplms_wsmousecfg));
    +}
    +
    +void
    +aplms_intr(struct device *self, uint8_t *packet, size_t packetlen)
    +{
    +	struct aplms_softc *sc = (struct aplms_softc *)self;
    +	struct ubcmtp_finger *finger;
    +	int off, s, btn, contacts;
    +
    +	if (!sc->sc_enabled)
    +		return;
    +
    +	contacts = 0;
    +	for (off = sc->tp_offset; off < packetlen;
    +	    off += (sizeof(struct ubcmtp_finger) + sc->tp_fingerpad)) {
    +		finger = (struct ubcmtp_finger *)(packet + off);
    +
    +		if ((int16_t)letoh16(finger->touch_major) == 0)
    +			continue; /* finger lifted */
    +
    +		sc->frame[contacts].x = (int16_t)letoh16(finger->abs_x);
    +		sc->frame[contacts].y = (int16_t)letoh16(finger->abs_y);
    +		sc->frame[contacts].pressure = DEFAULT_PRESSURE;
    +		contacts++;
    +	}
    +
    +	btn = sc->btn;
    +	sc->btn = !!((int16_t)letoh16(packet[UBCMTP_TYPE4_BTOFF]));
    +
    +	if (contacts || sc->contacts || sc->btn != btn) {
    +		sc->contacts = contacts;
    +		s = spltty();
    +		wsmouse_buttons(sc->sc_wsmousedev, sc->btn);
    +		wsmouse_mtframe(sc->sc_wsmousedev, sc->frame, contacts);
    +		wsmouse_input_sync(sc->sc_wsmousedev);
    +		splx(s);
    +	}
    +}
    +
    +int
    +aplms_enable(void *v)
    +{
    +	struct aplms_softc *sc = v;
    +
    +	if (sc->sc_enabled)
    +		return EBUSY;
    +
    +	sc->sc_enabled = 1;
    +	return 0;
    +}
    +
    +void
    +aplms_disable(void *v)
    +{
    +	struct aplms_softc *sc = v;
    +
    +	sc->sc_enabled = 0;
    +}
    +
    +int
    +aplms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
    +{
    +	struct aplms_softc *sc = v;
    +	struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev);
    +	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
    +	int wsmode;
    +
    +	switch (cmd) {
    +	case WSMOUSEIO_GTYPE:
    +		*(u_int *)data = hw->type;
    +		break;
    +
    +	case WSMOUSEIO_GCALIBCOORDS:
    +		wsmc->minx = hw->x_min;
    +		wsmc->maxx = hw->x_max;
    +		wsmc->miny = hw->y_min;
    +		wsmc->maxy = hw->y_max;
    +		wsmc->swapxy = 0;
    +		wsmc->resx = hw->h_res;
    +		wsmc->resy = hw->v_res;
    +		break;
    +
    +	case WSMOUSEIO_SETMODE:
    +		wsmode = *(u_int *)data;
    +		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) {
    +			printf("%s: invalid mode %d\n", sc->sc_dev.dv_xname,
    +			    wsmode);
    +			return (EINVAL);
    +		}
    +		wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
    +		break;
    +
    +	default:
    +		return -1;
    +	}
    +
    +	return 0;
    +}
    +
    +#else
    +
    +void
    +aplms_intr(struct device *self, uint8_t *packet, size_t packetlen)
    +{
    +}
    +
    +#endif
    
    
  • Mark Kettenis:

    aplhidev: enable on amd64, add keyboard backlight support