Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F167494568
D39863.id121178.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
39 KB
Referenced Files
None
Subscribers
None
D39863.id121178.diff
View Options
diff --git a/sys/dev/acpica/acpi_pci.c b/sys/dev/acpica/acpi_pci.c
--- a/sys/dev/acpica/acpi_pci.c
+++ b/sys/dev/acpica/acpi_pci.c
@@ -271,16 +271,22 @@
*/
child = acpi_get_device(handle);
if (child != NULL) {
- KASSERT(device_get_parent(child) ==
- devclass_get_device(devclass_find("acpi"), 0),
- ("%s: child (%s)'s parent is not acpi0", __func__,
- acpi_name(handle)));
- return;
+ // KASSERT(device_get_parent(child) ==
+ // devclass_get_device(devclass_find("acpi"), 0),
+ // ("%s: child (%s)'s parent is not acpi0", __func__,
+ // acpi_name(handle)));
+ // XXX: workaround :/
+ printf("%s: child (%s)'s parent (%s) is not acpi0", __func__,
+ acpi_name(handle), device_get_name(device_get_parent(child)));
+ AcpiDetachData(handle, acpi_fake_objhandler);
+ // return;
}
/*
* Update ACPI-CA to use the PCI enumerated device_t for this handle.
*/
+ printf("%s: attaching %s to %s\n", __func__,device_get_name(pci_child),
+ acpi_name(handle));
status = AcpiAttachData(handle, acpi_fake_objhandler, pci_child);
if (ACPI_FAILURE(status))
printf("WARNING: Unable to attach object data to %s - %s\n",
diff --git a/sys/dev/atopcase/atopcase.h b/sys/dev/atopcase/atopcase.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/atopcase/atopcase.h
@@ -0,0 +1,78 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021-2023 Val Packett <val@packett.cool>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEV_ATOPCASE_ATOPCASE_H_
+#define _DEV_ATOPCASE_ATOPCASE_H_
+
+#include <dev/hid/hid.h>
+
+/* Read == device-initiated, Write == host-initiated or reply to that */
+#define ATOPCASE_DIR_READ 0x20
+#define ATOPCASE_DIR_WRITE 0x40
+#define ATOPCASE_DIR_NOTHING 0x80
+
+#define ATOPCASE_DEV_KBRD 0x1
+#define ATOPCASE_DEV_TPAD 0x2
+#define ATOPCASE_DEV_INFO 0xD0
+
+#define ATOPCASE_INFO_DEVICE 0x1
+#define ATOPCASE_INFO_IFACE 0x2
+#define ATOPCASE_INFO_DESCRIPTOR 0x10
+
+#define ATOPCASE_MSG_TYPE_WRITE(dev) ((dev << 8) | 0x51)
+#define ATOPCASE_MSG_TYPE_SET_REPORT(dev) ((dev << 8) | 0x52)
+#define ATOPCASE_MSG_TYPE_REPORT(dev) ((dev << 8) | 0x10)
+#define ATOPCASE_MSG_TYPE_INFO(inf) ((inf << 8) | 20)
+
+struct atopcase_hid_softc {
+ device_t hid;
+
+ struct hid_device_info hw;
+
+ uint8_t device;
+
+ uint8_t desc[512];
+ size_t desc_len;
+
+ hid_intr_t *intr_handler;
+ void *intr_ctx;
+ uint8_t *intr_buf;
+ hid_size_t intr_bufsize;
+};
+
+void atopcase_lock(device_t dev);
+void atopcase_unlock(device_t dev);
+void atopcase_intr(device_t dev);
+
+void
+atopcase_enqueue_msg(device_t dev, uint8_t device,
+ uint16_t type, uint8_t type_arg,
+ const uint8_t *payload, uint8_t len, uint16_t resp_len);
+int
+atopcase_try_drain_queue(device_t dev);
+
+#endif
diff --git a/sys/dev/atopcase/atopcase.c b/sys/dev/atopcase/atopcase.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/atopcase/atopcase.c
@@ -0,0 +1,641 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021-2023 Val Packett <val@packett.cool>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_spi.h"
+#include "opt_hid.h"
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/mutex.h>
+#include <sys/crc16.h>
+
+#define HID_DEBUG_VAR atopcase_debug
+#include <dev/hid/hid.h>
+
+#include <dev/backlight/backlight.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include <dev/atopcase/atopcase.h>
+
+#include "spibus_if.h"
+#include "backlight_if.h"
+
+/* Tunables */
+static SYSCTL_NODE(_hw_hid, OID_AUTO, atopcase, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+ "Apple MacBook Topcase HID driver");
+
+#ifdef HID_DEBUG
+enum atopcase_log_level {
+ ATOPCASE_LLEVEL_DISABLED = 0,
+ ATOPCASE_LLEVEL_INFO,
+ ATOPCASE_LLEVEL_DEBUG, /* for troubleshooting */
+ ATOPCASE_LLEVEL_TRACE, /* log every packet */
+};
+static enum atopcase_log_level atopcase_debug = ATOPCASE_LLEVEL_INFO;
+
+SYSCTL_INT(_hw_hid_atopcase, OID_AUTO, debug, CTLFLAG_RWTUN,
+ &atopcase_debug, ATOPCASE_LLEVEL_INFO, "atopcase log level");
+#endif /* !HID_DEBUG */
+
+// static uint8_t booted[] = { 0xa0, 0x80, 0x00, 0x00 };
+static uint8_t status_ok[] = { 0xac, 0x27, 0x68, 0xd5 };
+
+struct atopcase_bl_payload {
+ uint16_t const0;
+ uint16_t level;
+ uint16_t status;
+} __packed;
+
+struct atopcase_device_info_payload {
+ uint16_t unknown[2];
+ uint16_t num_devs;
+ uint16_t vid;
+ uint16_t pid;
+ uint16_t ver;
+ uint16_t vendor_off;
+ uint16_t vendor_len;
+ uint16_t product_off;
+ uint16_t product_len;
+ uint16_t serial_off;
+ uint16_t serial_len;
+} __packed;
+
+struct atopcase_iface_info_payload {
+ uint8_t unknown0;
+ uint8_t iface_num;
+ uint8_t unknown1[3];
+ uint8_t country_code;
+ uint16_t max_input_report_len;
+ uint16_t max_output_report_len;
+ uint16_t max_control_report_len;
+ uint16_t name_off;
+ uint16_t name_len;
+} __packed;
+
+struct atopcase_header {
+ uint16_t type;
+ uint8_t type_arg; /* means "device" for ATOPCASE_MSG_TYPE_DESCRIPTOR */
+ uint8_t seq_no;
+ uint16_t resp_len;
+ uint16_t len;
+} __packed;
+
+struct atopcase_packet {
+ uint8_t direction;
+ uint8_t device;
+ uint16_t offset;
+ uint16_t remaining;
+ uint16_t length;
+ uint8_t data[246];
+ uint16_t checksum;
+} __packed;
+
+struct atopcase_softc {
+ struct mtx sc_mtx;
+ uint8_t sc_last_pkt_dir;
+ uint8_t sc_write_seq;
+
+ struct atopcase_hid_softc sc_kb_sc;
+ struct atopcase_hid_softc sc_tp_sc;
+
+ bool sc_intr_on;
+ bool sc_intr_on_saved;
+
+ struct cdev *sc_backlight;
+ uint32_t sc_backlight_level;
+
+ /*
+ * Writes are async (i.e. responses arrive via interrupt) and cannot be
+ * interleaved (no new writes until response arrives), so I made a queue.
+ *
+ * XXX: I have way, way overengineered this, right? We don't do too many writes.
+ * OpenBSD simply uses loops of 10 retries calling the intr handler with a delay until the result is achieved,
+ * like the second part of request_desc here, and doesn't seem to need anything else.
+ * On hid_set_report, Asahi doesn't seem to care about waiting for the response at all.
+ * Technically leaving a possibility for two writes in a row too soon?
+ * Also would be so nice to use wait_event_interruptible_timeout for those waits.
+ */
+ struct atopcase_packet sc_write_queue[8];
+ uint8_t sc_write_queue_len;
+ bool sc_write_in_flight;
+};
+
+int
+atopcase_try_drain_queue(device_t dev)
+{
+ struct atopcase_softc *sc = device_get_softc(dev);
+ struct spi_command cmd = SPI_COMMAND_INITIALIZER;
+ uint8_t rx_buffer[256] = { 0 };
+ uint8_t dummy_buffer[4] = { 0 };
+ uint8_t status_buffer[4] = { 0 };
+ int err;
+
+ if (sc->sc_write_in_flight || sc->sc_write_queue_len == 0)
+ return (EBUSY);
+
+ cmd.tx_cmd = (uint8_t*)&sc->sc_write_queue[0];
+ cmd.tx_cmd_sz = 256;
+ cmd.rx_cmd = rx_buffer;
+ cmd.rx_cmd_sz = 256;
+ cmd.flags = SPI_FLAG_KEEP_CS | SPI_FLAG_NO_SLEEP;
+ err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
+ if (err) {
+ device_printf(dev, "SPI error: %d\n", err);
+ return (err);
+ }
+#ifdef HID_DEBUG
+ if (atopcase_debug >= ATOPCASE_LLEVEL_DEBUG) {
+ device_printf(dev, "Sent packet:\n");
+ hexdump(cmd.tx_cmd, 256, 0, 0);
+ device_printf(dev, "RX buffer:\n");
+ hexdump(rx_buffer, 256, 0, 0);
+ }
+#endif
+
+ DELAY(100);
+
+ cmd.tx_cmd = dummy_buffer;
+ cmd.tx_cmd_sz = 4;
+ cmd.rx_cmd = status_buffer;
+ cmd.rx_cmd_sz = 4;
+ cmd.flags = SPI_FLAG_NO_SLEEP;
+ err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
+ if (err) {
+ device_printf(dev, "SPI error: %d\n", err);
+ return (err);
+ }
+
+#ifdef HID_DEBUG
+ if (atopcase_debug >= ATOPCASE_LLEVEL_TRACE) {
+ device_printf(dev, "Received status:\n");
+ hexdump(status_buffer, 256, 0, 0);
+ }
+#endif
+
+ if (memcmp(status_buffer, status_ok, sizeof(status_buffer))) {
+ device_printf(dev, "Failed to write command\n");
+ return (ENXIO);
+ } else {
+ sc->sc_write_queue_len--;
+ memmove((uint8_t*)&sc->sc_write_queue[0], (uint8_t*)&sc->sc_write_queue[1],
+ sizeof(struct atopcase_packet) * sc->sc_write_queue_len);
+ DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "Wrote command\n");
+ }
+
+ sc->sc_write_in_flight = true;
+ return (0);
+}
+
+void
+atopcase_enqueue_msg(device_t dev, uint8_t device,
+ uint16_t type, uint8_t type_arg,
+ const uint8_t *payload, uint8_t len, uint16_t resp_len)
+{
+ struct atopcase_softc *sc = device_get_softc(dev);
+ struct atopcase_packet *pkt;
+ struct atopcase_header *hdr;
+ uint16_t msg_checksum;
+
+ KASSERT(len + 8 + 2 <= sizeof(pkt->data), ("outgoing msg must be 1 packet"));
+
+ if (sc->sc_write_queue_len >= nitems(sc->sc_write_queue)) {
+ device_printf(dev, "Write queue overflow\n");
+ return;
+ }
+
+ pkt = &sc->sc_write_queue[sc->sc_write_queue_len++];
+ hdr = (struct atopcase_header *)pkt->data;
+
+ hdr->type = type;
+ hdr->type_arg = type_arg;
+ hdr->seq_no = sc->sc_write_seq++ % 256;
+ hdr->resp_len = (resp_len == 0) ? len : resp_len;
+ hdr->len = len;
+
+ pkt->direction = ATOPCASE_DIR_WRITE;
+ pkt->device = device;
+ pkt->length = sizeof(*hdr) + hdr->len + 2;
+
+ memcpy(pkt->data + sizeof(*hdr), payload, len);
+ msg_checksum = crc16(0, pkt->data, pkt->length - 2);
+ memcpy(pkt->data + sizeof(*hdr) + hdr->len,
+ (uint8_t*)&msg_checksum, 2);
+ pkt->checksum = crc16(0, (uint8_t*)pkt, sizeof(*pkt) - 2);
+
+ return;
+}
+
+static int
+atopcase_probe(device_t dev)
+{
+ /*
+ * On Intel MacBooks, atopcase_acpi explicitly creates us under a spibus.
+ * Otherwise, this function should be completely overridden.
+ */
+
+ device_set_desc(dev, "Apple MacBook SPI Topcase");
+
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+static void
+atopcase_receive_msg(device_t dev)
+{
+ struct atopcase_softc *sc = device_get_softc(dev);
+ struct atopcase_packet pkt = { 0 };
+ struct atopcase_header *hdr = (struct atopcase_header *)pkt.data;
+ struct spi_command cmd = SPI_COMMAND_INITIALIZER;
+ uint8_t tx_buffer[256] = { 0 };
+ int err;
+
+ cmd.tx_cmd = tx_buffer;
+ cmd.tx_cmd_sz = 256;
+ cmd.rx_cmd = (uint8_t*)&pkt;
+ cmd.rx_cmd_sz = 256;
+ cmd.flags = SPI_FLAG_NO_SLEEP;
+ err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
+ if (err) {
+ device_printf(dev, "SPI error: %d\n", err);
+ return;
+ }
+
+#ifdef HID_DEBUG
+ if (atopcase_debug >= ATOPCASE_LLEVEL_TRACE) {
+ device_printf(dev, "Incoming packet:\n");
+ hexdump((uint8_t*)&pkt, 256, 0, 0);
+ }
+#endif
+
+ if (pkt.checksum != crc16(0, (uint8_t*)&pkt, sizeof(pkt) - 2)) {
+ DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "packet with failed checksum\n");
+ return;
+ }
+
+ sc->sc_last_pkt_dir = pkt.direction;
+
+ /*
+ * When we poll and nothing has arrived we get a particular packet:
+ * '80 11 00 01 73 02 22 06 [zeros] a8 b0'
+ */
+ if (pkt.direction == ATOPCASE_DIR_NOTHING)
+ return;
+
+ if (pkt.direction != ATOPCASE_DIR_READ
+ && pkt.direction != ATOPCASE_DIR_WRITE) {
+ DPRINTFN(ATOPCASE_LLEVEL_DEBUG,
+ "unknown message direction 0x%x\n", pkt.direction);
+ return;
+ }
+
+ /*
+ * Multi-packet is only required for way too many fingers on the trackpad simultaneously.
+ * Support could be added in the future if someone really really needs it.
+ */
+ if (pkt.remaining != 0) {
+ DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "message with unuspported remainder\n");
+ return;
+ }
+
+ if (hdr->len + 8 + 2 >= sizeof(pkt.data)) {
+ DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "packet with length overflow\n");
+ return;
+ }
+
+ if (hdr->len > 0
+ && (pkt.length - 2 > sizeof(pkt.data)
+ || *(uint16_t*)(pkt.data + sizeof(*hdr) + hdr->len) !=
+ crc16(0, pkt.data, pkt.length - 2))) {
+ DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "message with failed checksum\n");
+ return;
+ }
+
+ if (pkt.device == ATOPCASE_DEV_KBRD
+ && hdr->type == ATOPCASE_MSG_TYPE_REPORT(ATOPCASE_DEV_KBRD)
+ && sc->sc_kb_sc.intr_handler) {
+ memcpy(sc->sc_kb_sc.intr_buf, (pkt.data + sizeof(*hdr)), hdr->len);
+ sc->sc_kb_sc.intr_handler(sc->sc_kb_sc.intr_ctx,
+ sc->sc_kb_sc.intr_buf, hdr->len);
+ } else if (pkt.device == ATOPCASE_DEV_TPAD
+ && hdr->type == ATOPCASE_MSG_TYPE_REPORT(ATOPCASE_DEV_TPAD)
+ && sc->sc_tp_sc.intr_handler) {
+ memcpy(sc->sc_tp_sc.intr_buf, (pkt.data + sizeof(*hdr)), hdr->len);
+ sc->sc_tp_sc.intr_handler(sc->sc_tp_sc.intr_ctx,
+ sc->sc_tp_sc.intr_buf, hdr->len);
+ } else if (pkt.device == ATOPCASE_DEV_INFO
+ && hdr->type == ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_DESCRIPTOR)) {
+ if (hdr->type_arg == ATOPCASE_DEV_KBRD) {
+ memcpy(sc->sc_kb_sc.desc, (pkt.data + sizeof(*hdr)), hdr->len);
+ sc->sc_kb_sc.desc_len = sc->sc_kb_sc.hw.rdescsize = hdr->len;
+ if (bootverbose) {
+ device_printf(dev, "Keyboard HID descriptor:\n");
+ hexdump(sc->sc_kb_sc.desc, sc->sc_kb_sc.desc_len, 0, 0);
+ }
+ } else if (hdr->type_arg == ATOPCASE_DEV_TPAD) {
+ memcpy(sc->sc_tp_sc.desc, (pkt.data + sizeof(*hdr)), hdr->len);
+ sc->sc_tp_sc.desc_len = sc->sc_tp_sc.hw.rdescsize = hdr->len;
+ if (sc->sc_tp_sc.desc_len == 110) {
+ sc->sc_tp_sc.hw.idVersion = sc->sc_tp_sc.desc[106];
+ } else {
+ device_printf(dev, "Unexpected trackpad descriptor len %zu"
+ ", not extracting model\n", sc->sc_tp_sc.desc_len);
+ }
+ if (bootverbose) {
+ device_printf(dev, "Trackpad HID descriptor:\n");
+ hexdump(sc->sc_tp_sc.desc, sc->sc_tp_sc.desc_len, 0, 0);
+ }
+ }
+ }
+
+ if (pkt.direction == ATOPCASE_DIR_WRITE) {
+ DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "write ack\n");
+ sc->sc_write_in_flight = false;
+ }
+
+ atopcase_try_drain_queue(dev);
+}
+
+void
+atopcase_intr(device_t dev)
+{
+ struct atopcase_softc *sc = device_get_softc(dev);
+
+ mtx_lock(&sc->sc_mtx);
+ if (sc->sc_intr_on)
+ atopcase_receive_msg(dev);
+ mtx_unlock(&sc->sc_mtx);
+}
+
+static int
+atopcase_request_desc(device_t dev, uint8_t device, size_t *len)
+{
+ int retry;
+
+ atopcase_enqueue_msg(dev, ATOPCASE_DEV_INFO,
+ ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_DESCRIPTOR), device,
+ NULL, 0, 0x200);
+
+ retry = 16;
+ while (retry-- > 0 && atopcase_try_drain_queue(dev) != 0) {
+ DELAY(100);
+ atopcase_receive_msg(dev);
+ }
+ if (retry == -1)
+ return (EIO);
+
+ retry = 16;
+ while (retry-- > 0 && *len == 0) {
+ DELAY(100);
+ atopcase_receive_msg(dev);
+ }
+ if (retry == -1)
+ return (EIO);
+
+ return (0);
+}
+
+static int
+atopcase_detach(device_t dev);
+
+static int
+atopcase_attach(device_t dev)
+{
+ struct atopcase_softc *sc = device_get_softc(dev);
+ device_t child;
+ int retry, err;
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ /* Poll until we know we're getting reasonable responses */
+ /* TODO: actually wait for the `booted` packet and not just dir nothing */
+ retry = 16;
+ while (retry-- > 0 && sc->sc_last_pkt_dir != ATOPCASE_DIR_NOTHING) {
+ DELAY(100);
+ atopcase_receive_msg(dev);
+ }
+ if (retry == -1) {
+ device_printf(dev, "could not establish communication\n");
+ err = EIO;
+ goto err;
+ }
+
+ /* TODO: request ATOPCASE_INFO_DEVICE to get vendor/product IDs instead of hardcoding them!!! (struct atopcase_device_info_payload) */
+ /* TODO: request ATOPCASE_INFO_IFACE/atopcase_iface_info_payload too for the country code */
+
+ /* Get the HID descriptors */
+ err = atopcase_request_desc(dev, ATOPCASE_DEV_TPAD, &sc->sc_tp_sc.desc_len);
+ if (err) {
+ device_printf(dev, "could not receive keyboard descriptors\n");
+ goto err;
+ }
+
+ err = atopcase_request_desc(dev, ATOPCASE_DEV_KBRD, &sc->sc_kb_sc.desc_len);
+ if (err) {
+ device_printf(dev, "could not receive keyboard descriptor\n");
+ goto err;
+ }
+
+ /* Add child devices that host hidbus instances */
+ child = device_add_child(dev, "atopcase_hid", -1);
+ if (child == NULL) {
+ device_printf(dev, "could not add child\n");
+ err = ENOMEM;
+ goto err;
+ }
+ device_set_softc(child, &sc->sc_kb_sc);
+ sc->sc_kb_sc.device = ATOPCASE_DEV_KBRD;
+
+ child = device_add_child(dev, "atopcase_hid", -1);
+ if (child == NULL) {
+ device_printf(dev, "could not add child\n");
+ err = ENOMEM;
+ goto err;
+ }
+ device_set_softc(child, &sc->sc_tp_sc);
+ sc->sc_tp_sc.device = ATOPCASE_DEV_TPAD;
+
+ /* TODO: skip on 2015 models where it's controlled by asmc */
+ sc->sc_backlight = backlight_register("atopcase", dev);
+ if (!sc->sc_backlight) {
+ device_printf(dev, "could not register backlight\n");
+ err = ENOMEM;
+ }
+
+ sc->sc_intr_on = true;
+
+ return (bus_generic_attach(dev));
+
+err:
+ atopcase_detach(dev);
+ return (err);
+}
+
+static int
+atopcase_detach(device_t dev)
+{
+ struct atopcase_softc *sc = device_get_softc(dev);
+ int err;
+
+ sc->sc_intr_on = false;
+
+ err = bus_generic_detach(dev);
+ if (err)
+ return (err);
+
+ if (sc->sc_backlight)
+ backlight_destroy(sc->sc_backlight);
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+void
+atopcase_lock(device_t dev)
+{
+ struct atopcase_softc *sc = device_get_softc(dev);
+
+ mtx_lock(&sc->sc_mtx);
+}
+
+void
+atopcase_unlock(device_t dev)
+{
+ struct atopcase_softc *sc = device_get_softc(dev);
+
+ mtx_unlock(&sc->sc_mtx);
+}
+
+static int
+atopcase_backlight_update_status(device_t dev, struct backlight_props *props)
+{
+ struct atopcase_softc *sc = device_get_softc(dev);
+ struct atopcase_bl_payload payload = { 0 };
+
+ payload.const0 = 0x01B0;
+ /* Hardware range is 32-255 for visible backlight, convert from percentages */
+ payload.level = (props->brightness == 0) ? 0 :
+ (32 + (223 * props->brightness / 100));
+ payload.status = (payload.level > 0) ? 0x01F4 : 0x1;
+
+ mtx_lock(&sc->sc_mtx);
+ atopcase_enqueue_msg(dev, ATOPCASE_DEV_KBRD,
+ ATOPCASE_MSG_TYPE_WRITE(0xB0), 0,
+ (const uint8_t*)&payload, sizeof(payload), 0);
+ atopcase_try_drain_queue(dev);
+ mtx_unlock(&sc->sc_mtx);
+
+ sc->sc_backlight_level = props->brightness;
+
+ return (0);
+}
+
+static int
+atopcase_backlight_get_status(device_t dev, struct backlight_props *props)
+{
+ struct atopcase_softc *sc = device_get_softc(dev);
+
+ props->brightness = sc->sc_backlight_level;
+ props->nlevels = 0;
+
+ return (0);
+}
+
+static int
+atopcase_backlight_get_info(device_t dev, struct backlight_info *info)
+{
+ info->type = BACKLIGHT_TYPE_KEYBOARD;
+ strlcpy(info->name, "Apple MacBook Keyboard", BACKLIGHTMAXNAMELENGTH);
+
+ return (0);
+}
+
+static int
+atopcase_suspend(device_t dev)
+{
+ struct atopcase_softc *sc = device_get_softc(dev);
+ int err;
+
+ err = bus_generic_suspend(dev);
+ if (err)
+ return (err);
+
+ mtx_lock(&sc->sc_mtx);
+ sc->sc_intr_on_saved = sc->sc_intr_on;
+ sc->sc_intr_on = false;
+ mtx_unlock(&sc->sc_mtx);
+
+ return (0);
+}
+
+static int
+atopcase_resume(device_t dev)
+{
+ struct atopcase_softc *sc = device_get_softc(dev);
+
+ mtx_lock(&sc->sc_mtx);
+ sc->sc_intr_on = sc->sc_intr_on_saved;
+ mtx_unlock(&sc->sc_mtx);
+
+ return (bus_generic_resume(dev));
+}
+
+
+static device_method_t atopcase_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, atopcase_probe),
+ DEVMETHOD(device_attach, atopcase_attach),
+ DEVMETHOD(device_detach, atopcase_detach),
+ DEVMETHOD(device_suspend, atopcase_suspend),
+ DEVMETHOD(device_resume, atopcase_resume),
+
+ /* Backlight interface */
+ DEVMETHOD(backlight_update_status, atopcase_backlight_update_status),
+ DEVMETHOD(backlight_get_status, atopcase_backlight_get_status),
+ DEVMETHOD(backlight_get_info, atopcase_backlight_get_info),
+
+ DEVMETHOD_END
+};
+
+static driver_t atopcase_driver = {
+ "atopcase",
+ atopcase_methods,
+ sizeof(struct atopcase_softc),
+};
+
+DRIVER_MODULE(atopcase, spibus, atopcase_driver, 0, 0);
+MODULE_DEPEND(atopcase, spibus, 1, 1, 1);
+MODULE_DEPEND(atopcase, backlight, 1, 1, 1);
+MODULE_DEPEND(atopcase, hid, 1, 1, 1);
+MODULE_VERSION(atopcase, 1);
diff --git a/sys/dev/atopcase/atopcase_acpi.c b/sys/dev/atopcase/atopcase_acpi.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/atopcase/atopcase_acpi.c
@@ -0,0 +1,346 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021-2023 Val Packett <val@packett.cool>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_acpi.h"
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+#include <contrib/dev/acpica/include/accommon.h>
+
+#include <dev/acpica/acpivar.h>
+#include <dev/acpica/acpiio.h>
+
+#include <dev/spibus/spibusvar.h>
+
+#include <dev/atopcase/atopcase.h>
+
+/*
+ * This driver does not inherit from atopcase, but instead manages an instance
+ * of atopcase under the correct spibus from a distance, as APP000D is a *sibling* of a spibus.
+ */
+
+/*
+ * XXX: The Linux driver only supports ACPI GPEs, but I only ever received interrupts in this driver on
+ * a MacBookPro12,1. My hypothesis is that this is because Linux responds to _OSI("Darwin") while we don't!
+ *
+ * If that is indeed confirmed on a couple more machines:
+ * - Remove all TESTING_GPE code
+ * - Leave a comment documenting this!!!
+ */
+#define TESTING_GPE
+
+static char *atopcase_ids[] = { "APP000D", NULL };
+
+struct atopcase_acpi_softc {
+ ACPI_HANDLE sc_handle;
+ device_t sc_dev;
+ device_t sc_spi;
+ device_t sc_atopcase;
+
+#ifdef TESTING_GPE
+ bool sc_printed_intr;
+ bool sc_printed_notify;
+ struct acpi_prw_data sc_prw;
+#endif
+ int sc_irq_rid;
+ struct resource *sc_irq_res;
+ void *sc_irq_ih;
+};
+
+static int
+atopcase_acpi_probe(device_t dev)
+{
+ int rv;
+
+ if (acpi_disabled("atopcase"))
+ return (ENXIO);
+
+ rv = ACPI_ID_PROBE(device_get_parent(dev), dev, atopcase_ids, NULL);
+ if (rv > 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Apple MacBook SPI Topcase Attachment");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+atopcase_acpi_set_comm_enabled(struct atopcase_acpi_softc *sc,
+ char *prop, const bool on)
+{
+ ACPI_OBJECT argobj;
+ ACPI_OBJECT_LIST args;
+
+ argobj.Type = ACPI_TYPE_INTEGER;
+ argobj.Integer.Value = on;
+ args.Count = 1;
+ args.Pointer = &argobj;
+
+ if (ACPI_FAILURE(
+ AcpiEvaluateObject(sc->sc_handle, prop, &args, NULL)))
+ return (ENXIO);
+
+ DELAY(100);
+
+ return (0);
+}
+
+#ifdef TESTING_GPE
+static UINT32
+atopcase_acpi_notify(ACPI_HANDLE h, UINT32 notify, void *ctx)
+{
+ struct atopcase_acpi_softc *sc = ctx;
+ if (!sc->sc_printed_notify) {
+ device_printf(sc->sc_dev, "Using ACPI GPE\n");
+ sc->sc_printed_notify = true;
+ }
+ device_printf(sc->sc_dev, "gpe\n");
+ // XXX This is run in the idle thread >_<
+ // atopcase_do_intr(sc->sc_atopcase);
+ return (ACPI_REENABLE_GPE);
+}
+#endif
+
+static void
+atopcase_acpi_intr(void *ctx)
+{
+ struct atopcase_acpi_softc *sc = ctx;
+#ifdef TESTING_GPE
+ if (!sc->sc_printed_intr) {
+ device_printf(sc->sc_dev, "Using interrupt\n");
+ sc->sc_printed_intr = true;
+ }
+#endif
+ if (device_is_attached(sc->sc_atopcase))
+ atopcase_intr(sc->sc_atopcase);
+}
+
+static int
+atopcase_acpi_attach(device_t dev)
+{
+ struct atopcase_acpi_softc *sc = device_get_softc(dev);
+ ACPI_HANDLE parent;
+
+ sc->sc_dev = dev;
+
+ /*
+ * We are \_SB.PCI0.SPI1.SPIT, our parent is intelspi,
+ * which has a spibus
+ */
+ sc->sc_handle = acpi_get_handle(dev);
+ if (ACPI_FAILURE(AcpiGetParent(sc->sc_handle, &parent))) {
+ device_printf(dev, "could not find ACPI parent\n");
+ return (ENXIO);
+ }
+
+ sc->sc_spi = acpi_get_device(parent);
+ if (!sc->sc_spi) {
+ device_printf(dev, "could not find device of ACPI parent\n");
+ return (ENXIO);
+ }
+
+ sc->sc_spi = device_find_child(sc->sc_spi, "spibus", -1);
+ if (!sc->sc_spi) {
+ device_printf(dev, "could not find spibus on parent device\n");
+ return (ENXIO);
+ }
+
+ /* no check because USB can be absent */
+ atopcase_acpi_set_comm_enabled(sc, "UIEN", false);
+
+ if (atopcase_acpi_set_comm_enabled(sc, "SIEN", true) != 0) {
+ device_printf(dev, "could not enable SPI communication\n");
+ return (ENXIO);
+ }
+
+ sc->sc_atopcase = BUS_ADD_CHILD(sc->sc_spi, 0, "atopcase", -1);
+ if (!sc->sc_atopcase) {
+ device_printf(dev, "could not attach atopcase\n");
+ return (ENXIO);
+ }
+
+ /*
+ * Apple encodes a CS delay in ACPI properties, but
+ * - they're encoded in a non-standard way that predates _DSD, and
+ * - they're only available if you respond to _OSI("Darwin") which we don't
+ * - because that has more side effects than we're prepared to handle
+ * - Apple makes a Windows driver and Windows is not Darwin
+ * - so presumably that one uses hardcoded values too
+ */
+ spibus_set_cs_delay(sc->sc_atopcase, 10);
+ spibus_set_clock(sc->sc_atopcase, 4000000);
+
+#ifdef TESTING_GPE
+ ACPI_BUFFER buf;
+ buf.Pointer = NULL;
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+ if (ACPI_SUCCESS(AcpiEvaluateObject(sc->sc_handle, "_GPE", NULL, &buf))) {
+ ACPI_OBJECT *obj = (ACPI_OBJECT *)buf.Pointer;
+ if (obj == NULL)
+ device_printf(dev, "_GPE acpi obj null\n");
+
+ switch (obj->Type) {
+ case ACPI_TYPE_INTEGER:
+ sc->sc_prw.gpe_handle = NULL;
+ sc->sc_prw.gpe_bit = obj->Integer.Value;
+ device_printf(dev, "_GPE int %d\n", sc->sc_prw.gpe_bit);
+ break;
+ case ACPI_TYPE_PACKAGE:
+ // if (!ACPI_PKG_VALID(obj, 2))
+ // goto out;
+ sc->sc_prw.gpe_handle = acpi_GetReference(NULL, &obj->Package.Elements[0]);
+ if (sc->sc_prw.gpe_handle == NULL ||
+ acpi_PkgInt32(obj, 1, &sc->sc_prw.gpe_bit) != 0)
+ device_printf(dev, "oops\n");
+ device_printf(dev, "_GPE w/h int %d\n", sc->sc_prw.gpe_bit);
+ break;
+ default:
+ device_printf(dev, "_GPE has invalid type %d\n", obj->Type);
+ }
+ }
+
+ // XXX: ACPI_DEVICE_NOTIFY?
+ // if (ACPI_SUCCESS(AcpiInstallNotifyHandler(sc->sc_handle, ACPI_ALL_NOTIFY,
+ // atopcase_acpi_notify, sc))) {
+ if (ACPI_SUCCESS(AcpiInstallGpeHandler(sc->sc_prw.gpe_handle, sc->sc_prw.gpe_bit,
+ ACPI_GPE_LEVEL_TRIGGERED, atopcase_acpi_notify, sc))) {
+ } else {
+ device_printf(dev, "could not install ACPI notification handler\n");
+ }
+ if (!ACPI_SUCCESS(AcpiEnableGpe(sc->sc_prw.gpe_handle, sc->sc_prw.gpe_bit)))
+ device_printf(dev, "could not enable ACPI notification\n");
+#endif
+
+ sc->sc_irq_res = bus_alloc_resource_any(sc->sc_dev,
+ SYS_RES_IRQ, &sc->sc_irq_rid, RF_ACTIVE);
+ if (sc->sc_irq_res) {
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, atopcase_acpi_intr, sc, &sc->sc_irq_ih)) {
+ device_printf(dev, "could not setup interrupt handler\n");
+ }
+ } else {
+ device_printf(dev, "could not allocate IRQ resource\n");
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+atopcase_acpi_detach(device_t dev)
+{
+ struct atopcase_acpi_softc *sc = device_get_softc(dev);
+
+ if (sc->sc_irq_ih)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_ih);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ,
+ sc->sc_irq_rid, sc->sc_irq_res);
+
+#ifdef TESTING_GPE
+ // if (ACPI_FAILURE(
+ // AcpiRemoveNotifyHandler(sc->sc_handle, ACPI_ALL_NOTIFY, atopcase_acpi_notify)))
+ // device_printf(dev, "could not remove ACPI notification handler\n");
+#endif
+
+ if (atopcase_acpi_set_comm_enabled(sc, "SIEN", false) != 0) {
+ device_printf(dev, "could not disable SPI communication\n");
+ }
+
+ /* no check because USB can be absent */
+ atopcase_acpi_set_comm_enabled(sc, "UIEN", true);
+
+ device_delete_child(sc->sc_spi, sc->sc_atopcase);
+
+ return (bus_generic_detach(dev));
+}
+
+static int
+atopcase_acpi_suspend(device_t dev)
+{
+ struct atopcase_acpi_softc *sc = device_get_softc(dev);
+
+#ifdef TESTING_GPE
+ if (sc->sc_prw.gpe_handle || sc->sc_prw.gpe_bit)
+ AcpiDisableGpe(sc->sc_prw.gpe_handle, sc->sc_prw.gpe_bit);
+#endif
+
+ if (atopcase_acpi_set_comm_enabled(sc, "SIEN", false) != 0) {
+ device_printf(dev, "could not disable SPI communication\n");
+ }
+
+ return (bus_generic_suspend(dev));
+}
+
+static int
+atopcase_acpi_resume(device_t dev)
+{
+ struct atopcase_acpi_softc *sc = device_get_softc(dev);
+
+#ifdef TESTING_GPE
+ if (sc->sc_prw.gpe_handle || sc->sc_prw.gpe_bit)
+ AcpiEnableGpe(sc->sc_prw.gpe_handle, sc->sc_prw.gpe_bit);
+#endif
+
+ if (atopcase_acpi_set_comm_enabled(sc, "SIEN", true) != 0) {
+ device_printf(dev, "could not enable SPI communication\n");
+ return (ENXIO);
+ }
+
+ return (bus_generic_resume(dev));
+}
+
+static device_method_t atopcase_acpi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, atopcase_acpi_probe),
+ DEVMETHOD(device_attach, atopcase_acpi_attach),
+ DEVMETHOD(device_detach, atopcase_acpi_detach),
+ DEVMETHOD(device_suspend, atopcase_acpi_suspend),
+ DEVMETHOD(device_resume, atopcase_acpi_resume),
+
+ DEVMETHOD_END
+};
+
+static driver_t atopcase_acpi_driver = {
+ "atopcase_acpi",
+ atopcase_acpi_methods,
+ sizeof(struct atopcase_acpi_softc),
+};
+
+DRIVER_MODULE(atopcase_acpi, acpi, atopcase_acpi_driver, 0, 0);
+MODULE_DEPEND(atopcase_acpi, acpi, 1, 1, 1);
+MODULE_DEPEND(atopcase_acpi, spibus, 1, 1, 1);
diff --git a/sys/dev/atopcase/atopcase_hid.c b/sys/dev/atopcase/atopcase_hid.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/atopcase/atopcase_hid.c
@@ -0,0 +1,240 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021-2023 Val Packett <val@packett.cool>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_hid.h"
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/malloc.h>
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+
+#include <dev/evdev/input.h>
+
+#include <dev/hid/hidquirk.h>
+
+#include <dev/atopcase/atopcase.h>
+
+#include "usbdevs.h"
+#include "hid_if.h"
+
+#define ATOPCASE_SIZE_MAX (246 - 6)
+
+static int
+atopcase_hid_probe(device_t dev)
+{
+ device_set_desc(dev, "Apple MacBook SPI Topcase HID Container");
+
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+static int
+atopcase_hid_attach(device_t dev)
+{
+ struct atopcase_hid_softc *sc = device_get_softc(dev);
+ device_t child;
+
+ sc->hw.idBus = BUS_SPI;
+ sc->hw.idVendor = USB_VENDOR_APPLE;
+ sc->hw.idProduct = USB_PRODUCT_APPLE_WELLSPRING9_ISO;
+ /* NOTE: idVersion is set for the trackpad by atopcase itself */
+ strlcpy(sc->hw.name, "Apple MacBook", sizeof(sc->hw.name));
+ strlcpy(sc->hw.serial, "", sizeof(sc->hw.serial));
+
+ child = device_add_child(dev, "hidbus", -1);
+ if (child == NULL) {
+ device_printf(dev, "Could not add HID device\n");
+ return (ENOMEM);
+ }
+ device_set_ivars(child, &sc->hw);
+
+ return (bus_generic_attach(dev));
+}
+
+static void
+atopcase_hid_intr_setup(device_t dev, hid_intr_t intr, void *context,
+ struct hid_rdesc_info *rdesc)
+{
+ struct atopcase_hid_softc *sc = device_get_softc(dev);
+
+ if (intr == NULL)
+ return;
+
+ rdesc->rdsize = rdesc->isize;
+ rdesc->grsize = rdesc->srsize = ATOPCASE_SIZE_MAX;
+ rdesc->wrsize = ATOPCASE_SIZE_MAX;
+
+ sc->intr_handler = intr;
+ sc->intr_ctx = context;
+ sc->intr_buf = malloc(rdesc->rdsize, M_DEVBUF, M_WAITOK | M_ZERO);
+ sc->intr_bufsize = rdesc->rdsize;
+}
+
+static void
+atopcase_hid_intr_unsetup(device_t dev)
+{
+ struct atopcase_hid_softc *sc = device_get_softc(dev);
+
+ free(sc->intr_buf, M_DEVBUF);
+ sc->intr_buf = NULL;
+ sc->intr_handler = NULL;
+ sc->intr_ctx = NULL;
+}
+
+static int
+atopcase_hid_intr_start(device_t dev)
+{
+ return (0);
+}
+
+static int
+atopcase_hid_intr_stop(device_t dev)
+{
+ return (0);
+}
+
+static void
+atopcase_hid_intr_poll(device_t dev)
+{
+ /* TODO: run atopcase_(do_?)intr? */
+}
+
+static int
+atopcase_hid_get_rdesc(device_t dev, void *buf, hid_size_t len)
+{
+ struct atopcase_hid_softc *sc = device_get_softc(dev);
+
+ if (sc->desc_len != len)
+ return (ENXIO);
+ memcpy(buf, sc->desc, len);
+
+ return (0);
+}
+
+static int
+atopcase_hid_read(device_t dev, void *buf, hid_size_t maxlen, hid_size_t *actlen)
+{
+ return (ENOTSUP);
+}
+
+static int
+atopcase_hid_write(device_t dev, const void *buf, hid_size_t len)
+{
+ struct atopcase_hid_softc *sc = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+
+ if (len >= ATOPCASE_SIZE_MAX)
+ return (EINVAL);
+
+ atopcase_lock(parent);
+ atopcase_enqueue_msg(parent, sc->device,
+ ATOPCASE_MSG_TYPE_WRITE(sc->device), 0, buf, len, 0);
+ atopcase_try_drain_queue(parent);
+ atopcase_unlock(parent);
+
+ return (0);
+}
+
+static int
+atopcase_hid_get_report(device_t dev, void *buf, hid_size_t maxlen,
+ hid_size_t *actlen, uint8_t type, uint8_t id)
+{
+ return (ENOTSUP);
+}
+
+static int
+atopcase_hid_set_report(device_t dev, const void *buf, hid_size_t len, uint8_t type,
+ uint8_t id)
+{
+ struct atopcase_hid_softc *sc = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+
+ if (len >= ATOPCASE_SIZE_MAX)
+ return (EINVAL);
+
+ atopcase_lock(parent);
+ atopcase_enqueue_msg(parent, sc->device,
+ ATOPCASE_MSG_TYPE_SET_REPORT(sc->device), 0, buf, len, 0);
+ atopcase_try_drain_queue(parent);
+ atopcase_unlock(parent);
+
+ return (0);
+}
+
+static int
+atopcase_hid_set_idle(device_t dev, uint16_t duration, uint8_t id)
+{
+ return (ENOTSUP);
+}
+
+static int
+atopcase_hid_set_protocol(device_t dev, uint16_t protocol)
+{
+ return (ENOTSUP);
+}
+
+static device_method_t atopcase_hid_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, atopcase_hid_probe),
+ DEVMETHOD(device_attach, atopcase_hid_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* HID interrupt interface */
+ DEVMETHOD(hid_intr_setup, atopcase_hid_intr_setup),
+ DEVMETHOD(hid_intr_unsetup, atopcase_hid_intr_unsetup),
+ DEVMETHOD(hid_intr_start, atopcase_hid_intr_start),
+ DEVMETHOD(hid_intr_stop, atopcase_hid_intr_stop),
+ DEVMETHOD(hid_intr_poll, atopcase_hid_intr_poll),
+
+ /* HID interface */
+ DEVMETHOD(hid_get_rdesc, atopcase_hid_get_rdesc),
+ DEVMETHOD(hid_read, atopcase_hid_read),
+ DEVMETHOD(hid_write, atopcase_hid_write),
+ DEVMETHOD(hid_get_report, atopcase_hid_get_report),
+ DEVMETHOD(hid_set_report, atopcase_hid_set_report),
+ DEVMETHOD(hid_set_idle, atopcase_hid_set_idle),
+ DEVMETHOD(hid_set_protocol, atopcase_hid_set_protocol),
+
+ DEVMETHOD_END
+};
+
+static driver_t atopcase_hid_driver = {
+ "atopcase_hid",
+ atopcase_hid_methods,
+ sizeof(struct atopcase_hid_softc),
+};
+
+DRIVER_MODULE(atopcase_hid, atopcase, atopcase_hid_driver, 0, 0);
+MODULE_DEPEND(atopcase_hid, hid, 1, 1, 1);
+MODULE_DEPEND(atopcase_hid, hidbus, 1, 1, 1);
diff --git a/sys/dev/hid/hidbus.c b/sys/dev/hid/hidbus.c
--- a/sys/dev/hid/hidbus.c
+++ b/sys/dev/hid/hidbus.c
@@ -927,3 +927,4 @@
DRIVER_MODULE(hidbus, hvhid, hidbus_driver, 0, 0);
DRIVER_MODULE(hidbus, iichid, hidbus_driver, 0, 0);
DRIVER_MODULE(hidbus, usbhid, hidbus_driver, 0, 0);
+DRIVER_MODULE(hidbus, atopcase_hid, hidbus_driver, 0, 0);
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -55,6 +55,7 @@
ath_main \
ath_rate \
ath_pci \
+ atopcase \
${_autofs} \
axgbe \
backlight \
diff --git a/sys/modules/atopcase/Makefile b/sys/modules/atopcase/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/atopcase/Makefile
@@ -0,0 +1,6 @@
+.PATH: ${SRCTOP}/sys/dev/atopcase
+KMOD= atopcase
+SRCS= atopcase_acpi.c atopcase_hid.c atopcase.c
+SRCS+= acpi_if.h backlight_if.h bus_if.h device_if.h hid_if.h opt_acpi.h opt_hid.h opt_spi.h spibus_if.h usbdevs.h
+
+.include <bsd.kmod.mk>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Aug 23, 6:44 AM (4 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37122769
Default Alt Text
D39863.id121178.diff (39 KB)
Attached To
Mode
D39863: Add atopcase, the Apple HID over SPI input driver [WIP: Someone w/ Hardware Needed]
Attached
Detach File
Event Timeline
Log In to Comment