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 *
atopcase_get_child_by_device(struct atopcase_softc * sc,uint8_t device)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
atopcase_receive_status(struct atopcase_softc * sc)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
atopcase_process_message(struct atopcase_softc * sc,uint8_t device,void * msg,uint16_t msg_len)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
atopcase_receive_packet(struct atopcase_softc * sc)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
atopcase_send(struct atopcase_softc * sc,struct atopcase_packet * pkt)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
atopcase_create_message(struct atopcase_packet * pkt,uint8_t device,uint16_t type,uint8_t type_arg,const void * payload,uint8_t len,uint16_t resp_len)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
atopcase_request_desc(struct atopcase_softc * sc,uint16_t type,uint8_t device)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
atopcase_intr(struct atopcase_softc * sc)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
atopcase_add_child(struct atopcase_softc * sc,struct atopcase_child * ac,uint8_t device)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
atopcase_init(struct atopcase_softc * sc)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 bus_attach_children(sc->sc_dev);
537 return (0);
538
539 err:
540 return (err);
541 }
542
543 int
atopcase_destroy(struct atopcase_softc * sc)544 atopcase_destroy(struct atopcase_softc *sc)
545 {
546 int err;
547
548 err = device_delete_children(sc->sc_dev);
549 if (err)
550 return (err);
551
552 if (sc->sc_backlight)
553 backlight_destroy(sc->sc_backlight);
554
555 return (0);
556 }
557
558 static struct atopcase_child *
atopcase_get_child_by_hidbus(device_t child)559 atopcase_get_child_by_hidbus(device_t child)
560 {
561 device_t parent = device_get_parent(child);
562 struct atopcase_softc *sc = device_get_softc(parent);
563
564 if (child == sc->sc_kb.hidbus)
565 return (&sc->sc_kb);
566 if (child == sc->sc_tp.hidbus)
567 return (&sc->sc_tp);
568 panic("unknown child");
569 }
570
571 void
atopcase_intr_setup(device_t dev,device_t child,hid_intr_t intr,void * context,struct hid_rdesc_info * rdesc)572 atopcase_intr_setup(device_t dev, device_t child, hid_intr_t intr,
573 void *context, struct hid_rdesc_info *rdesc)
574 {
575 struct atopcase_child *ac = atopcase_get_child_by_hidbus(child);
576
577 if (intr == NULL)
578 return;
579
580 rdesc->rdsize = ATOPCASE_MSG_SIZE - sizeof(struct atopcase_header) - 2;
581 rdesc->grsize = 0;
582 rdesc->srsize = ATOPCASE_DATA_SIZE - sizeof(struct atopcase_header) - 2;
583 rdesc->wrsize = 0;
584
585 ac->intr_handler = intr;
586 ac->intr_ctx = context;
587 }
588
589 void
atopcase_intr_unsetup(device_t dev,device_t child)590 atopcase_intr_unsetup(device_t dev, device_t child)
591 {
592 }
593
594 int
atopcase_intr_start(device_t dev,device_t child)595 atopcase_intr_start(device_t dev, device_t child)
596 {
597 struct atopcase_softc *sc = device_get_softc(dev);
598 struct atopcase_child *ac = atopcase_get_child_by_hidbus(child);
599
600 if (ATOPCASE_IN_POLLING_MODE(sc))
601 sx_xlock(&sc->sc_write_sx);
602 else if (sc->sc_irq_ih != NULL)
603 mtx_lock(&sc->sc_mtx);
604 else
605 sx_xlock(&sc->sc_sx);
606 ac->open = true;
607 if (ATOPCASE_IN_POLLING_MODE(sc))
608 sx_xunlock(&sc->sc_write_sx);
609 else if (sc->sc_irq_ih != NULL)
610 mtx_unlock(&sc->sc_mtx);
611 else
612 sx_xunlock(&sc->sc_sx);
613
614 return (0);
615 }
616
617 int
atopcase_intr_stop(device_t dev,device_t child)618 atopcase_intr_stop(device_t dev, device_t child)
619 {
620 struct atopcase_softc *sc = device_get_softc(dev);
621 struct atopcase_child *ac = atopcase_get_child_by_hidbus(child);
622
623 if (ATOPCASE_IN_POLLING_MODE(sc))
624 sx_xlock(&sc->sc_write_sx);
625 else if (sc->sc_irq_ih != NULL)
626 mtx_lock(&sc->sc_mtx);
627 else
628 sx_xlock(&sc->sc_sx);
629 ac->open = false;
630 if (ATOPCASE_IN_POLLING_MODE(sc))
631 sx_xunlock(&sc->sc_write_sx);
632 else if (sc->sc_irq_ih != NULL)
633 mtx_unlock(&sc->sc_mtx);
634 else
635 sx_xunlock(&sc->sc_sx);
636
637 return (0);
638 }
639
640 void
atopcase_intr_poll(device_t dev,device_t child)641 atopcase_intr_poll(device_t dev, device_t child)
642 {
643 struct atopcase_softc *sc = device_get_softc(dev);
644
645 (void)atopcase_receive_packet(sc);
646 }
647
648 int
atopcase_get_rdesc(device_t dev,device_t child,void * buf,hid_size_t len)649 atopcase_get_rdesc(device_t dev, device_t child, void *buf, hid_size_t len)
650 {
651 struct atopcase_child *ac = atopcase_get_child_by_hidbus(child);
652
653 if (ac->rdesc_len != len)
654 return (ENXIO);
655 memcpy(buf, ac->rdesc, len);
656
657 return (0);
658 }
659
660 int
atopcase_set_report(device_t dev,device_t child,const void * buf,hid_size_t len,uint8_t type __unused,uint8_t id)661 atopcase_set_report(device_t dev, device_t child, const void *buf,
662 hid_size_t len, uint8_t type __unused, uint8_t id)
663 {
664 struct atopcase_softc *sc = device_get_softc(dev);
665 struct atopcase_child *ac = atopcase_get_child_by_hidbus(child);
666 int err;
667
668 if (len >= ATOPCASE_DATA_SIZE - sizeof(struct atopcase_header) - 2)
669 return (EINVAL);
670
671 DPRINTF("%s HID command SET_REPORT %d (len %d): %*D\n",
672 ac->name, id, len, len, buf, " ");
673
674 if (!ATOPCASE_IN_KDB())
675 sx_xlock(&sc->sc_write_sx);
676 atopcase_create_message(&sc->sc_buf, ac->device,
677 ATOPCASE_MSG_TYPE_SET_REPORT(ac->device, id), 0, buf, len, 0);
678 err = atopcase_send(sc, &sc->sc_buf);
679 if (!ATOPCASE_IN_KDB())
680 sx_xunlock(&sc->sc_write_sx);
681
682 return (err);
683 }
684
685 int
atopcase_backlight_update_status(device_t dev,struct backlight_props * props)686 atopcase_backlight_update_status(device_t dev, struct backlight_props *props)
687 {
688 struct atopcase_softc *sc = device_get_softc(dev);
689 struct atopcase_bl_payload payload = { 0 };
690
691 payload.report_id = ATOPCASE_BKL_REPORT_ID;
692 payload.device = ATOPCASE_DEV_KBRD;
693 /*
694 * Hardware range is 32-255 for visible backlight,
695 * convert from percentages
696 */
697 payload.level = (props->brightness == 0) ? 0 :
698 (32 + (223 * props->brightness / 100));
699 payload.status = (payload.level > 0) ? 0x01F4 : 0x1;
700
701 return (atopcase_set_report(dev, sc->sc_kb.hidbus, &payload,
702 sizeof(payload), HID_OUTPUT_REPORT, ATOPCASE_BKL_REPORT_ID));
703 }
704
705 int
atopcase_backlight_get_status(device_t dev,struct backlight_props * props)706 atopcase_backlight_get_status(device_t dev, struct backlight_props *props)
707 {
708 struct atopcase_softc *sc = device_get_softc(dev);
709
710 props->brightness = sc->sc_backlight_level;
711 props->nlevels = 0;
712
713 return (0);
714 }
715
716 int
atopcase_backlight_get_info(device_t dev,struct backlight_info * info)717 atopcase_backlight_get_info(device_t dev, struct backlight_info *info)
718 {
719 info->type = BACKLIGHT_TYPE_KEYBOARD;
720 strlcpy(info->name, "Apple MacBook Keyboard", BACKLIGHTMAXNAMELENGTH);
721
722 return (0);
723 }
724