xref: /freebsd/sys/dev/atopcase/atopcase.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021-2023 Val Packett <val@packett.cool>
5  * Copyright (c) 2023 Vladimir Kondratyev <wulf@FreeBSD.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include "opt_hid.h"
30 #include "opt_spi.h"
31 
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/crc16.h>
35 #include <sys/endian.h>
36 #include <sys/kdb.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/mutex.h>
40 #include <sys/module.h>
41 #include <sys/proc.h>
42 #include <sys/rman.h>
43 #include <sys/sysctl.h>
44 #include <sys/sx.h>
45 #include <sys/taskqueue.h>
46 
47 #include <dev/backlight/backlight.h>
48 
49 #include <dev/evdev/input.h>
50 
51 #define	HID_DEBUG_VAR atopcase_debug
52 #include <dev/hid/hid.h>
53 #include <dev/hid/hidquirk.h>
54 
55 #include <dev/spibus/spi.h>
56 #include <dev/spibus/spibusvar.h>
57 
58 #include "spibus_if.h"
59 
60 #include "atopcase_reg.h"
61 #include "atopcase_var.h"
62 
63 #define	ATOPCASE_IN_KDB()	(SCHEDULER_STOPPED() || kdb_active)
64 #define	ATOPCASE_IN_POLLING_MODE(sc)					\
65 	(((sc)->sc_gpe_bit == 0 && ((sc)->sc_irq_ih == NULL)) || cold ||\
66 	ATOPCASE_IN_KDB())
67 #define	ATOPCASE_WAKEUP(sc, chan) do {					\
68 	if (!ATOPCASE_IN_POLLING_MODE(sc)) {				\
69 		DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "wakeup: %p\n", chan);	\
70 		wakeup(chan);						\
71 	}								\
72 } while (0)
73 #define	ATOPCASE_SPI_PAUSE()	DELAY(100)
74 #define	ATOPCASE_SPI_NO_SLEEP_FLAG(sc)					\
75 	((sc)->sc_irq_ih != NULL ? SPI_FLAG_NO_SLEEP : 0)
76 
77 /* Tunables */
78 static SYSCTL_NODE(_hw_hid, OID_AUTO, atopcase, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
79     "Apple MacBook Topcase HID driver");
80 
81 #ifdef HID_DEBUG
82 enum atopcase_log_level atopcase_debug = ATOPCASE_LLEVEL_DISABLED;
83 
84 SYSCTL_INT(_hw_hid_atopcase, OID_AUTO, debug, CTLFLAG_RWTUN,
85     &atopcase_debug, ATOPCASE_LLEVEL_DISABLED, "atopcase log level");
86 #endif /* !HID_DEBUG */
87 
88 static const uint8_t booted[] = { 0xa0, 0x80, 0x00, 0x00 };
89 static const uint8_t status_ok[] = { 0xac, 0x27, 0x68, 0xd5 };
90 
91 static inline struct atopcase_child *
92 atopcase_get_child_by_device(struct atopcase_softc *sc, uint8_t device)
93 {
94 	switch (device) {
95 	case ATOPCASE_DEV_KBRD:
96 		return (&sc->sc_kb);
97 	case ATOPCASE_DEV_TPAD:
98 		return (&sc->sc_tp);
99 	default:
100 		return (NULL);
101 	}
102 }
103 
104 static int
105 atopcase_receive_status(struct atopcase_softc *sc)
106 {
107 	struct spi_command cmd = SPI_COMMAND_INITIALIZER;
108 	uint8_t dummy_buffer[4] = { 0 };
109 	uint8_t status_buffer[4] = { 0 };
110 	int err;
111 
112 	cmd.tx_cmd = dummy_buffer;
113 	cmd.tx_cmd_sz = sizeof(dummy_buffer);
114 	cmd.rx_cmd = status_buffer;
115 	cmd.rx_cmd_sz = sizeof(status_buffer);
116 	cmd.flags = ATOPCASE_SPI_NO_SLEEP_FLAG(sc);
117 
118 	err = SPIBUS_TRANSFER(device_get_parent(sc->sc_dev), sc->sc_dev, &cmd);
119 	ATOPCASE_SPI_PAUSE();
120 	if (err) {
121 		device_printf(sc->sc_dev, "SPI error: %d\n", err);
122 		return (err);
123 	}
124 
125 	DPRINTFN(ATOPCASE_LLEVEL_TRACE, "Status: %*D\n", 4, status_buffer, " ");
126 
127 	if (memcmp(status_buffer, status_ok, sizeof(status_ok)) == 0) {
128 		DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "Wrote command\n");
129 		ATOPCASE_WAKEUP(sc, sc->sc_dev);
130 	} else {
131 		device_printf(sc->sc_dev, "Failed to write command\n");
132 		return (EIO);
133 	}
134 
135 	return (0);
136 }
137 
138 static int
139 atopcase_process_message(struct atopcase_softc *sc, uint8_t device, void *msg,
140     uint16_t msg_len)
141 {
142 	struct atopcase_header *hdr = msg;
143 	struct atopcase_child *ac;
144 	void *payload;
145 	uint16_t pl_len, crc;
146 
147 	payload = (uint8_t *)msg + sizeof(*hdr);
148 	pl_len = le16toh(hdr->len);
149 
150 	if (pl_len + sizeof(*hdr) + sizeof(crc) != msg_len) {
151 		DPRINTFN(ATOPCASE_LLEVEL_DEBUG,
152 		    "message with length overflow\n");
153 		return (EIO);
154 	}
155 
156 	crc = le16toh(*(uint16_t *)((uint8_t *)payload + pl_len));
157 	if (crc != crc16(0, msg, msg_len - sizeof(crc))) {
158 		DPRINTFN(ATOPCASE_LLEVEL_DEBUG,
159 		    "message with failed checksum\n");
160 		return (EIO);
161 	}
162 
163 #define CPOFF(dst, len, off)	do {					\
164 	unsigned _len = le16toh(len);					\
165 	unsigned _off = le16toh(off);					\
166 	if (pl_len >= _len + _off) {					\
167 		memcpy(dst, (uint8_t*)payload + _off, MIN(_len, sizeof(dst)));\
168 		(dst)[MIN(_len, sizeof(dst) - 1)] = '\0';		\
169 	}} while (0);
170 
171 	if ((ac = atopcase_get_child_by_device(sc, device)) != NULL
172 	    && hdr->type == ATOPCASE_MSG_TYPE_REPORT(device)) {
173 		if (ac->open)
174 			ac->intr_handler(ac->intr_ctx, payload, pl_len);
175 	} else if (device == ATOPCASE_DEV_INFO
176 	    && hdr->type == ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_IFACE)
177 	    && (ac = atopcase_get_child_by_device(sc, hdr->type_arg)) != NULL) {
178 		struct atopcase_iface_info_payload *iface = payload;
179 		CPOFF(ac->name, iface->name_len, iface->name_off);
180 		DPRINTF("Interface #%d name: %s\n", ac->device, ac->name);
181 	} else if (device == ATOPCASE_DEV_INFO
182 	    && hdr->type == ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_DESCRIPTOR)
183 	    && (ac = atopcase_get_child_by_device(sc, hdr->type_arg)) != NULL) {
184 		memcpy(ac->rdesc, payload, pl_len);
185 		ac->rdesc_len = ac->hw.rdescsize = pl_len;
186 		DPRINTF("%s HID report descriptor: %*D\n", ac->name,
187 		    (int) ac->hw.rdescsize, ac->rdesc, " ");
188 	} else if (device == ATOPCASE_DEV_INFO
189 	    && hdr->type == ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_DEVICE)
190 	    && hdr->type_arg == ATOPCASE_INFO_DEVICE) {
191 		struct atopcase_device_info_payload *dev = payload;
192 		sc->sc_vid = le16toh(dev->vid);
193 		sc->sc_pid = le16toh(dev->pid);
194 		sc->sc_ver = le16toh(dev->ver);
195 		CPOFF(sc->sc_vendor, dev->vendor_len, dev->vendor_off);
196 		CPOFF(sc->sc_product, dev->product_len, dev->product_off);
197 		CPOFF(sc->sc_serial, dev->serial_len, dev->serial_off);
198 		if (bootverbose) {
199 			device_printf(sc->sc_dev, "Device info descriptor:\n");
200 			printf("  Vendor:  %s\n", sc->sc_vendor);
201 			printf("  Product: %s\n", sc->sc_product);
202 			printf("  Serial:  %s\n", sc->sc_serial);
203 		}
204 	}
205 
206 	return (0);
207 }
208 
209 int
210 atopcase_receive_packet(struct atopcase_softc *sc)
211 {
212 	struct atopcase_packet pkt = { 0 };
213 	struct spi_command cmd = SPI_COMMAND_INITIALIZER;
214 	void *msg;
215 	int err;
216 	uint16_t length, remaining, offset, msg_len;
217 
218 	bzero(&sc->sc_junk, sizeof(struct atopcase_packet));
219 	cmd.tx_cmd = &sc->sc_junk;
220 	cmd.tx_cmd_sz = sizeof(struct atopcase_packet);
221 	cmd.rx_cmd = &pkt;
222 	cmd.rx_cmd_sz = sizeof(struct atopcase_packet);
223 	cmd.flags = ATOPCASE_SPI_NO_SLEEP_FLAG(sc);
224 	err = SPIBUS_TRANSFER(device_get_parent(sc->sc_dev), sc->sc_dev, &cmd);
225 	ATOPCASE_SPI_PAUSE();
226 	if (err) {
227 		device_printf(sc->sc_dev, "SPI error: %d\n", err);
228 		return (err);
229 	}
230 
231 	DPRINTFN(ATOPCASE_LLEVEL_TRACE, "Response: %*D\n", 256, &pkt, " ");
232 
233 	if (le16toh(pkt.checksum) != crc16(0, &pkt, sizeof(pkt) - 2)) {
234 		DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "packet with failed checksum\n");
235 		return (EIO);
236 	}
237 
238 	/*
239 	 * When we poll and nothing has arrived we get a particular packet
240 	 * starting with '80 11 00 01'
241 	 */
242 	if (pkt.direction == ATOPCASE_DIR_NOTHING) {
243 		DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "'Nothing' packet: %*D\n", 4,
244 		    &pkt, " ");
245 		return (EAGAIN);
246 	}
247 
248 	if (pkt.direction != ATOPCASE_DIR_READ &&
249 	    pkt.direction != ATOPCASE_DIR_WRITE) {
250 		DPRINTFN(ATOPCASE_LLEVEL_DEBUG,
251 		         "unknown message direction 0x%x\n", pkt.direction);
252 		return (EIO);
253 	}
254 
255 	length = le16toh(pkt.length);
256 	remaining = le16toh(pkt.remaining);
257 	offset = le16toh(pkt.offset);
258 
259 	if (length > sizeof(pkt.data)) {
260 		DPRINTFN(ATOPCASE_LLEVEL_DEBUG,
261 		    "packet with length overflow: %u\n", length);
262 		return (EIO);
263 	}
264 
265 	if (pkt.direction == ATOPCASE_DIR_READ &&
266 	    pkt.device == ATOPCASE_DEV_INFO &&
267 	    length == sizeof(booted) &&
268 	    memcmp(pkt.data, booted, length) == 0) {
269 		DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "GPE boot packet\n");
270 		sc->sc_booted = true;
271 		ATOPCASE_WAKEUP(sc, sc);
272 		return (0);
273 	}
274 
275 	/* handle multi-packet messages */
276 	if (remaining != 0 || offset != 0) {
277 		if (offset != sc->sc_msg_len) {
278 			DPRINTFN(ATOPCASE_LLEVEL_DEBUG,
279 			    "Unexpected offset (got %u, expected %u)\n",
280 			    offset, sc->sc_msg_len);
281 			sc->sc_msg_len = 0;
282 			return (EIO);
283 		}
284 
285 		if ((size_t)remaining + length + offset > sizeof(sc->sc_msg)) {
286 			DPRINTFN(ATOPCASE_LLEVEL_DEBUG,
287 			    "Message with length overflow: %zu\n",
288 			    (size_t)remaining + length + offset);
289 			sc->sc_msg_len = 0;
290 			return (EIO);
291 		}
292 
293 		memcpy(sc->sc_msg + offset, &pkt.data, length);
294 		sc->sc_msg_len += length;
295 
296 		if (remaining != 0)
297 			return (0);
298 
299 		msg = sc->sc_msg;
300 		msg_len = sc->sc_msg_len;
301 	} else {
302 		msg = pkt.data;
303 		msg_len = length;
304 	}
305 	sc->sc_msg_len = 0;
306 
307 	err = atopcase_process_message(sc, pkt.device, msg, msg_len);
308 	if (err == 0 && pkt.direction == ATOPCASE_DIR_WRITE) {
309 		DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "Write ack\n");
310 		ATOPCASE_WAKEUP(sc, sc);
311 	}
312 
313 	return (err);
314 }
315 
316 static int
317 atopcase_send(struct atopcase_softc *sc, struct atopcase_packet *pkt)
318 {
319 	struct spi_command cmd = SPI_COMMAND_INITIALIZER;
320 	int err, retries;
321 
322 	cmd.tx_cmd = pkt;
323 	cmd.tx_cmd_sz = sizeof(struct atopcase_packet);
324 	cmd.rx_cmd = &sc->sc_junk;
325 	cmd.rx_cmd_sz = sizeof(struct atopcase_packet);
326 	cmd.flags = SPI_FLAG_KEEP_CS | ATOPCASE_SPI_NO_SLEEP_FLAG(sc);
327 
328 	DPRINTFN(ATOPCASE_LLEVEL_TRACE, "Request: %*D\n",
329 	    (int)sizeof(struct atopcase_packet), cmd.tx_cmd, " ");
330 
331 	if (!ATOPCASE_IN_POLLING_MODE(sc)) {
332 		if (sc->sc_irq_ih != NULL)
333 			mtx_lock(&sc->sc_mtx);
334 		else
335 			sx_xlock(&sc->sc_sx);
336 	}
337 	sc->sc_wait_for_status = true;
338 	err = SPIBUS_TRANSFER(device_get_parent(sc->sc_dev), sc->sc_dev, &cmd);
339 	ATOPCASE_SPI_PAUSE();
340 	if (!ATOPCASE_IN_POLLING_MODE(sc)) {
341 		if (sc->sc_irq_ih != NULL)
342 			mtx_unlock(&sc->sc_mtx);
343 		else
344 			sx_xunlock(&sc->sc_sx);
345 	}
346 	if (err != 0) {
347 		device_printf(sc->sc_dev, "SPI error: %d\n", err);
348 		goto exit;
349 	}
350 
351 	if (ATOPCASE_IN_POLLING_MODE(sc)) {
352 		err = atopcase_receive_status(sc);
353 	} else {
354 		DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "wait for: %p\n", sc->sc_dev);
355 		err = tsleep(sc->sc_dev, 0, "atcstat", hz / 10);
356 	}
357 	sc->sc_wait_for_status = false;
358 	if (err != 0) {
359 		DPRINTF("Write status read failed: %d\n", err);
360 		goto exit;
361 	}
362 
363 	if (ATOPCASE_IN_POLLING_MODE(sc)) {
364 		/* Backlight setting may require a lot of time */
365 		retries = 20;
366 		while ((err = atopcase_receive_packet(sc)) == EAGAIN &&
367 		    --retries != 0)
368 			DELAY(1000);
369 	} else {
370 		DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "wait for: %p\n", sc);
371 		err = tsleep(sc, 0, "atcack", hz / 10);
372 	}
373 	if (err != 0)
374 		DPRINTF("Write ack read failed: %d\n", err);
375 
376 exit:
377 	if (err == EWOULDBLOCK)
378 		err = EIO;
379 
380 	return (err);
381 }
382 
383 static void
384 atopcase_create_message(struct atopcase_packet *pkt, uint8_t device,
385     uint16_t type, uint8_t type_arg, const void *payload, uint8_t len,
386     uint16_t resp_len)
387 {
388 	struct atopcase_header *hdr = (struct atopcase_header *)pkt->data;
389 	uint16_t msg_checksum;
390 	static uint8_t seq_no;
391 
392 	KASSERT(len <= ATOPCASE_DATA_SIZE - sizeof(struct atopcase_header),
393 	    ("outgoing msg must be 1 packet"));
394 
395 	bzero(pkt, sizeof(struct atopcase_packet));
396 	pkt->direction = ATOPCASE_DIR_WRITE;
397 	pkt->device = device;
398 	pkt->length = htole16(sizeof(*hdr) + len + 2);
399 
400 	hdr->type = htole16(type);
401 	hdr->type_arg = type_arg;
402 	hdr->seq_no = seq_no++;
403 	hdr->resp_len = htole16((resp_len == 0) ? len : resp_len);
404 	hdr->len = htole16(len);
405 
406 	memcpy(pkt->data + sizeof(*hdr), payload, len);
407 	msg_checksum = htole16(crc16(0, pkt->data, pkt->length - 2));
408 	memcpy(pkt->data + sizeof(*hdr) + len, &msg_checksum, 2);
409 	pkt->checksum = htole16(crc16(0, (uint8_t*)pkt, sizeof(*pkt) - 2));
410 
411 	return;
412 }
413 
414 static int
415 atopcase_request_desc(struct atopcase_softc *sc, uint16_t type, uint8_t device)
416 {
417 	atopcase_create_message(
418 	   &sc->sc_buf, ATOPCASE_DEV_INFO, type, device, NULL, 0, 0x200);
419 	return (atopcase_send(sc, &sc->sc_buf));
420 }
421 
422 int
423 atopcase_intr(struct atopcase_softc *sc)
424 {
425 	int err;
426 
427 	DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "Interrupt event\n");
428 
429 	if (sc->sc_wait_for_status) {
430 		err = atopcase_receive_status(sc);
431 		sc->sc_wait_for_status = false;
432 	} else
433 		err = atopcase_receive_packet(sc);
434 
435 	return (err);
436 }
437 
438 static int
439 atopcase_add_child(struct atopcase_softc *sc, struct atopcase_child *ac,
440     uint8_t device)
441 {
442 	device_t hidbus;
443 	int err = 0;
444 
445 	ac->device = device;
446 
447 	/* fill device info */
448 	strlcpy(ac->hw.name, "Apple MacBook", sizeof(ac->hw.name));
449 	ac->hw.idBus = BUS_SPI;
450 	ac->hw.idVendor = sc->sc_vid;
451 	ac->hw.idProduct = sc->sc_pid;
452 	ac->hw.idVersion = sc->sc_ver;
453 	strlcpy(ac->hw.idPnP, sc->sc_hid, sizeof(ac->hw.idPnP));
454 	strlcpy(ac->hw.serial, sc->sc_serial, sizeof(ac->hw.serial));
455 	/*
456 	 * HID write and set_report methods executed on Apple SPI topcase
457 	 * hardware do the same request on SPI layer. Set HQ_NOWRITE quirk to
458 	 * force hidmap to convert writes to set_reports. That makes HID bus
459 	 * write handler unnecessary and reduces code duplication.
460 	 */
461 	hid_add_dynamic_quirk(&ac->hw, HQ_NOWRITE);
462 
463 	DPRINTF("Get the interface #%d descriptor\n", device);
464 	err = atopcase_request_desc(sc,
465 	    ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_IFACE), device);
466 	if (err) {
467 		device_printf(sc->sc_dev, "can't receive iface descriptor\n");
468 		goto exit;
469 	}
470 
471 	DPRINTF("Get the \"%s\" HID report descriptor\n", ac->name);
472 	err = atopcase_request_desc(sc,
473 	    ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_DESCRIPTOR), device);
474 	if (err) {
475 		device_printf(sc->sc_dev, "can't receive report descriptor\n");
476 		goto exit;
477 	}
478 
479 	hidbus = device_add_child(sc->sc_dev, "hidbus", -1);
480 	if (hidbus == NULL) {
481 		device_printf(sc->sc_dev, "can't add child\n");
482 		err = ENOMEM;
483 		goto exit;
484 	}
485 	device_set_ivars(hidbus, &ac->hw);
486 	ac->hidbus = hidbus;
487 
488 exit:
489 	return (err);
490 }
491 
492 int
493 atopcase_init(struct atopcase_softc *sc)
494 {
495 	int err;
496 
497 	/* Wait until we know we're getting reasonable responses */
498 	if(!sc->sc_booted && tsleep(sc, 0, "atcboot", hz / 20) != 0) {
499 		device_printf(sc->sc_dev, "can't establish communication\n");
500 		err = EIO;
501 		goto err;
502 	}
503 
504 	/*
505 	 * Management device may send a message on first boot after power off.
506 	 * Let interrupt handler to read and discard it.
507 	 */
508 	DELAY(2000);
509 
510 	DPRINTF("Get the device descriptor\n");
511 	err = atopcase_request_desc(sc,
512 	    ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_DEVICE),
513 	    ATOPCASE_INFO_DEVICE);
514 	if (err) {
515 		device_printf(sc->sc_dev, "can't receive device descriptor\n");
516 		goto err;
517 	}
518 
519 	err = atopcase_add_child(sc, &sc->sc_kb, ATOPCASE_DEV_KBRD);
520 	if (err != 0)
521 		goto err;
522 	err = atopcase_add_child(sc, &sc->sc_tp, ATOPCASE_DEV_TPAD);
523 	if (err != 0)
524 		goto err;
525 
526 	/* TODO: skip on 2015 models where it's controlled by asmc */
527 	sc->sc_backlight = backlight_register("atopcase", sc->sc_dev);
528 	if (!sc->sc_backlight) {
529 		device_printf(sc->sc_dev, "can't register backlight\n");
530 		err = ENOMEM;
531 	}
532 
533 	if (sc->sc_tq != NULL)
534 		taskqueue_enqueue_timeout(sc->sc_tq, &sc->sc_task, hz / 120);
535 
536 	return (bus_generic_attach(sc->sc_dev));
537 
538 err:
539 	return (err);
540 }
541 
542 int
543 atopcase_destroy(struct atopcase_softc *sc)
544 {
545 	int err;
546 
547 	err = device_delete_children(sc->sc_dev);
548 	if (err)
549 		return (err);
550 
551 	if (sc->sc_backlight)
552 		backlight_destroy(sc->sc_backlight);
553 
554 	return (0);
555 }
556 
557 static struct atopcase_child *
558 atopcase_get_child_by_hidbus(device_t child)
559 {
560 	device_t parent = device_get_parent(child);
561 	struct atopcase_softc *sc = device_get_softc(parent);
562 
563 	if (child == sc->sc_kb.hidbus)
564 		return (&sc->sc_kb);
565 	if (child == sc->sc_tp.hidbus)
566 		return (&sc->sc_tp);
567 	panic("unknown child");
568 }
569 
570 void
571 atopcase_intr_setup(device_t dev, device_t child, hid_intr_t intr,
572     void *context, struct hid_rdesc_info *rdesc)
573 {
574 	struct atopcase_child *ac = atopcase_get_child_by_hidbus(child);
575 
576 	if (intr == NULL)
577 		return;
578 
579 	rdesc->rdsize = ATOPCASE_MSG_SIZE - sizeof(struct atopcase_header) - 2;
580 	rdesc->grsize = 0;
581 	rdesc->srsize = ATOPCASE_DATA_SIZE - sizeof(struct atopcase_header) - 2;
582 	rdesc->wrsize = 0;
583 
584 	ac->intr_handler = intr;
585 	ac->intr_ctx = context;
586 }
587 
588 void
589 atopcase_intr_unsetup(device_t dev, device_t child)
590 {
591 }
592 
593 int
594 atopcase_intr_start(device_t dev, device_t child)
595 {
596 	struct atopcase_softc *sc = device_get_softc(dev);
597 	struct atopcase_child *ac = atopcase_get_child_by_hidbus(child);
598 
599 	if (ATOPCASE_IN_POLLING_MODE(sc))
600 		sx_xlock(&sc->sc_write_sx);
601 	else if (sc->sc_irq_ih != NULL)
602 		mtx_lock(&sc->sc_mtx);
603 	else
604 		sx_xlock(&sc->sc_sx);
605 	ac->open = true;
606 	if (ATOPCASE_IN_POLLING_MODE(sc))
607 		sx_xunlock(&sc->sc_write_sx);
608 	else if (sc->sc_irq_ih != NULL)
609 		mtx_unlock(&sc->sc_mtx);
610 	else
611 		sx_xunlock(&sc->sc_sx);
612 
613 	return (0);
614 }
615 
616 int
617 atopcase_intr_stop(device_t dev, device_t child)
618 {
619 	struct atopcase_softc *sc = device_get_softc(dev);
620 	struct atopcase_child *ac = atopcase_get_child_by_hidbus(child);
621 
622 	if (ATOPCASE_IN_POLLING_MODE(sc))
623 		sx_xlock(&sc->sc_write_sx);
624 	else if (sc->sc_irq_ih != NULL)
625 		mtx_lock(&sc->sc_mtx);
626 	else
627 		sx_xlock(&sc->sc_sx);
628 	ac->open = false;
629 	if (ATOPCASE_IN_POLLING_MODE(sc))
630 		sx_xunlock(&sc->sc_write_sx);
631 	else if (sc->sc_irq_ih != NULL)
632 		mtx_unlock(&sc->sc_mtx);
633 	else
634 		sx_xunlock(&sc->sc_sx);
635 
636 	return (0);
637 }
638 
639 void
640 atopcase_intr_poll(device_t dev, device_t child)
641 {
642 	struct atopcase_softc *sc = device_get_softc(dev);
643 
644 	(void)atopcase_receive_packet(sc);
645 }
646 
647 int
648 atopcase_get_rdesc(device_t dev, device_t child, void *buf, hid_size_t len)
649 {
650 	struct atopcase_child *ac = atopcase_get_child_by_hidbus(child);
651 
652 	if (ac->rdesc_len != len)
653 		return (ENXIO);
654 	memcpy(buf, ac->rdesc, len);
655 
656 	return (0);
657 }
658 
659 int
660 atopcase_set_report(device_t dev, device_t child, const void *buf,
661     hid_size_t len, uint8_t type __unused, uint8_t id)
662 {
663 	struct atopcase_softc *sc = device_get_softc(dev);
664 	struct atopcase_child *ac = atopcase_get_child_by_hidbus(child);
665 	int err;
666 
667 	if (len >= ATOPCASE_DATA_SIZE - sizeof(struct atopcase_header) - 2)
668 		return (EINVAL);
669 
670 	DPRINTF("%s HID command SET_REPORT %d (len %d): %*D\n",
671 	    ac->name, id, len, len, buf, " ");
672 
673 	if (!ATOPCASE_IN_KDB())
674 		sx_xlock(&sc->sc_write_sx);
675 	atopcase_create_message(&sc->sc_buf, ac->device,
676 	    ATOPCASE_MSG_TYPE_SET_REPORT(ac->device, id), 0, buf, len, 0);
677 	err = atopcase_send(sc, &sc->sc_buf);
678 	if (!ATOPCASE_IN_KDB())
679 		sx_xunlock(&sc->sc_write_sx);
680 
681 	return (err);
682 }
683 
684 int
685 atopcase_backlight_update_status(device_t dev, struct backlight_props *props)
686 {
687 	struct atopcase_softc *sc = device_get_softc(dev);
688 	struct atopcase_bl_payload payload = { 0 };
689 
690 	payload.report_id = ATOPCASE_BKL_REPORT_ID;
691 	payload.device = ATOPCASE_DEV_KBRD;
692 	/*
693 	 * Hardware range is 32-255 for visible backlight,
694 	 * convert from percentages
695 	 */
696 	payload.level = (props->brightness == 0) ? 0 :
697 		(32 + (223 * props->brightness / 100));
698 	payload.status = (payload.level > 0) ? 0x01F4 : 0x1;
699 
700 	return (atopcase_set_report(dev, sc->sc_kb.hidbus, &payload,
701 	    sizeof(payload), HID_OUTPUT_REPORT, ATOPCASE_BKL_REPORT_ID));
702 }
703 
704 int
705 atopcase_backlight_get_status(device_t dev, struct backlight_props *props)
706 {
707 	struct atopcase_softc *sc = device_get_softc(dev);
708 
709 	props->brightness = sc->sc_backlight_level;
710 	props->nlevels = 0;
711 
712 	return (0);
713 }
714 
715 int
716 atopcase_backlight_get_info(device_t dev, struct backlight_info *info)
717 {
718 	info->type = BACKLIGHT_TYPE_KEYBOARD;
719 	strlcpy(info->name, "Apple MacBook Keyboard", BACKLIGHTMAXNAMELENGTH);
720 
721 	return (0);
722 }
723