From: joshua stein Subject: Re: aplhidev: enable on amd64, add keyboard backlight support To: tech@openbsd.org Date: Thu, 20 Nov 2025 10:46:08 -0600 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 > > > > 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 - * Copyright (c) 2013-2014 joshua stein - * - * 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 -#include -#include -#include -#include -#include - -#include - -#include - -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include - -#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 + * Copyright (c) 2013-2014 joshua stein + * + * 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 +#include +#include +#include +#include +#include + +#include + +#include + +#ifdef __HAVE_FDT +#include +#include +#include +#include +#endif + +#include "acpi.h" +#if NACPI > 0 && !defined(__HAVE_FDT) +#include +#include +#include +#include +#include +#endif + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#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