Page MenuHomeFreeBSD

D39863.id121178.diff
No OneTemporary

D39863.id121178.diff

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

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)

Event Timeline