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