Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/atopcase/atopcase.c
39507 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2021-2023 Val Packett <[email protected]>
5
* Copyright (c) 2023 Vladimir Kondratyev <[email protected]>
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", DEVICE_UNIT_ANY);
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
bus_attach_children(sc->sc_dev);
537
return (0);
538
539
err:
540
return (err);
541
}
542
543
int
544
atopcase_destroy(struct atopcase_softc *sc)
545
{
546
int err;
547
548
err = bus_generic_detach(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 *
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
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
590
atopcase_intr_unsetup(device_t dev, device_t child)
591
{
592
}
593
594
int
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
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
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
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
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
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
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
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
725