xref: /openbsd-src/sys/arch/arm64/dev/aplhidev.c (revision 1ad61ae0a79a724d2d3ec69e69c8e1d1ff6b53a0)
1 /*	$OpenBSD: aplhidev.c,v 1.12 2023/07/02 21:44:04 bru Exp $	*/
2 /*
3  * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
4  * Copyright (c) 2013-2014 joshua stein <jcs@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/kernel.h>
22 #include <sys/device.h>
23 #include <sys/malloc.h>
24 #include <sys/timeout.h>
25 
26 #include <lib/libkern/crc16.h>
27 
28 #include <machine/fdt.h>
29 
30 #include <dev/spi/spivar.h>
31 
32 #include <dev/ofw/openfirm.h>
33 #include <dev/ofw/ofw_gpio.h>
34 #include <dev/ofw/ofw_pinctrl.h>
35 
36 #include <dev/usb/usbdevs.h>
37 
38 #include <dev/wscons/wsconsio.h>
39 #include <dev/wscons/wskbdvar.h>
40 #include <dev/wscons/wsksymdef.h>
41 #include <dev/wscons/wsmousevar.h>
42 
43 #include <dev/hid/hid.h>
44 #include <dev/hid/hidkbdsc.h>
45 #include <dev/hid/hidmsvar.h>
46 
47 #include "aplhidev.h"
48 
49 #define APLHIDEV_READ_PACKET	0x20
50 #define APLHIDEV_WRITE_PACKET	0x40
51 
52 #define APLHIDEV_KBD_DEVICE	1
53 #define APLHIDEV_TP_DEVICE	2
54 #define APLHIDEV_INFO_DEVICE	208
55 
56 #define APLHIDEV_GET_INFO	0x0120
57 #define APLHIDEV_GET_DESCRIPTOR	0x1020
58 #define  APLHIDEV_DESC_MAX	512
59 #define APLHIDEV_KBD_REPORT	0x0110
60 #define APLHIDEV_TP_REPORT	0x0210
61 #define APLHIDEV_SET_LEDS	0x0151
62 #define APLHIDEV_SET_MODE	0x0252
63 #define  APLHIDEV_MODE_HID	0x00
64 #define  APLHIDEV_MODE_RAW	0x01
65 
66 struct aplhidev_attach_args {
67 	uint8_t	aa_reportid;
68 	void	*aa_desc;
69 	size_t	aa_desclen;
70 };
71 
72 struct aplhidev_spi_packet {
73 	uint8_t		flags;
74 	uint8_t		device;
75 	uint16_t	offset;
76 	uint16_t	remaining;
77 	uint16_t	len;
78 	uint8_t		data[246];
79 	uint16_t	crc;
80 };
81 
82 struct aplhidev_spi_status {
83 	uint8_t		status[4];
84 };
85 
86 struct aplhidev_msghdr {
87 	uint16_t	type;
88 	uint8_t		device;
89 	uint8_t		msgid;
90 	uint16_t	rsplen;
91 	uint16_t	cmdlen;
92 };
93 
94 struct aplhidev_info_hdr {
95 	uint16_t	unknown[2];
96 	uint16_t	num_devices;
97 	uint16_t	vendor;
98 	uint16_t	product;
99 	uint16_t	version;
100 	uint16_t	vendor_str[2];
101 	uint16_t	product_str[2];
102 	uint16_t	serial_str[2];
103 };
104 
105 struct aplhidev_get_desc {
106 	struct aplhidev_msghdr	hdr;
107 	uint16_t		crc;
108 };
109 
110 struct aplhidev_set_leds {
111 	struct aplhidev_msghdr	hdr;
112 	uint8_t			reportid;
113 	uint8_t			leds;
114 	uint16_t		crc;
115 };
116 
117 struct aplhidev_set_mode {
118 	struct aplhidev_msghdr	hdr;
119 	uint8_t			reportid;
120 	uint8_t			mode;
121 	uint16_t		crc;
122 };
123 
124 struct aplhidev_softc {
125 	struct device		sc_dev;
126 	int			sc_node;
127 
128 	spi_tag_t		sc_spi_tag;
129 	struct spi_config	sc_spi_conf;
130 
131 	uint8_t			sc_msgid;
132 
133 	uint32_t		*sc_gpio;
134 	int			sc_gpiolen;
135 
136 	uint8_t			sc_mode;
137 	uint16_t		sc_vendor;
138 	uint16_t		sc_product;
139 
140 	struct device 		*sc_kbd;
141 	uint8_t			sc_kbddesc[APLHIDEV_DESC_MAX];
142 	size_t			sc_kbddesclen;
143 
144 	struct device		*sc_ms;
145 	uint8_t			sc_tpdesc[APLHIDEV_DESC_MAX];
146 	size_t			sc_tpdesclen;
147 };
148 
149 int	 aplhidev_match(struct device *, void *, void *);
150 void	 aplhidev_attach(struct device *, struct device *, void *);
151 
152 const struct cfattach aplhidev_ca = {
153 	sizeof(struct aplhidev_softc), aplhidev_match, aplhidev_attach
154 };
155 
156 struct cfdriver aplhidev_cd = {
157 	NULL, "aplhidev", DV_DULL
158 };
159 
160 void	aplhidev_get_info(struct aplhidev_softc *);
161 void	aplhidev_get_descriptor(struct aplhidev_softc *, uint8_t);
162 void	aplhidev_set_leds(struct aplhidev_softc *, uint8_t);
163 void	aplhidev_set_mode(struct aplhidev_softc *, uint8_t);
164 
165 int	aplhidev_intr(void *);
166 void	aplkbd_intr(struct device *, uint8_t *, size_t);
167 void	aplms_intr(struct device *, uint8_t *, size_t);
168 
169 int
170 aplhidev_match(struct device *parent, void *match, void *aux)
171 {
172 	struct spi_attach_args *sa = aux;
173 
174 	if (strcmp(sa->sa_name, "apple,spi-hid-transport") == 0)
175 		return 1;
176 
177 	return 0;
178 }
179 
180 void
181 aplhidev_attach(struct device *parent, struct device *self, void *aux)
182 {
183 	struct aplhidev_softc *sc = (struct aplhidev_softc *)self;
184 	struct spi_attach_args *sa = aux;
185 	struct aplhidev_attach_args aa;
186 	int retry;
187 
188 	sc->sc_spi_tag = sa->sa_tag;
189 	sc->sc_node = *(int *)sa->sa_cookie;
190 
191 	sc->sc_gpiolen = OF_getproplen(sc->sc_node, "spien-gpios");
192 	if (sc->sc_gpiolen > 0) {
193 		sc->sc_gpio = malloc(sc->sc_gpiolen, M_TEMP, M_WAITOK);
194 		OF_getpropintarray(sc->sc_node, "spien-gpios",
195 		    sc->sc_gpio, sc->sc_gpiolen);
196 		gpio_controller_config_pin(sc->sc_gpio, GPIO_CONFIG_OUTPUT);
197 
198 		/* Reset */
199 		gpio_controller_set_pin(sc->sc_gpio, 1);
200 		delay(5000);
201 		gpio_controller_set_pin(sc->sc_gpio, 0);
202 		delay(5000);
203 
204 		/* Enable. */
205 		gpio_controller_set_pin(sc->sc_gpio, 1);
206 		delay(50000);
207 	}
208 
209 	sc->sc_spi_conf.sc_bpw = 8;
210 	sc->sc_spi_conf.sc_freq = OF_getpropint(sc->sc_node,
211 	    "spi-max-frequency", 0);
212 	sc->sc_spi_conf.sc_cs = OF_getpropint(sc->sc_node, "reg", 0);
213 	sc->sc_spi_conf.sc_cs_delay = 100;
214 
215 	fdt_intr_establish(sc->sc_node, IPL_TTY,
216 	    aplhidev_intr, sc, sc->sc_dev.dv_xname);
217 
218 	aplhidev_get_info(sc);
219 	for (retry = 10; retry > 0; retry--) {
220 		aplhidev_intr(sc);
221 		delay(1000);
222 		if (sc->sc_vendor != 0 && sc->sc_product != 0)
223 			break;
224 	}
225 
226 	aplhidev_get_descriptor(sc, APLHIDEV_KBD_DEVICE);
227 	for (retry = 10; retry > 0; retry--) {
228 		aplhidev_intr(sc);
229 		delay(1000);
230 		if (sc->sc_kbddesclen > 0)
231 			break;
232 	}
233 
234 	aplhidev_get_descriptor(sc, APLHIDEV_TP_DEVICE);
235 	for (retry = 10; retry > 0; retry--) {
236 		aplhidev_intr(sc);
237 		delay(1000);
238 		if (sc->sc_tpdesclen > 0)
239 			break;
240 	}
241 
242 	sc->sc_mode = APLHIDEV_MODE_HID;
243 	aplhidev_set_mode(sc, APLHIDEV_MODE_RAW);
244 	for (retry = 10; retry > 0; retry--) {
245 		aplhidev_intr(sc);
246 		delay(1000);
247 		if (sc->sc_mode == APLHIDEV_MODE_RAW)
248 			break;
249 	}
250 
251 	printf("\n");
252 
253 	if (sc->sc_kbddesclen > 0) {
254 		aa.aa_reportid = APLHIDEV_KBD_DEVICE;
255 		aa.aa_desc = sc->sc_kbddesc;
256 		aa.aa_desclen = sc->sc_kbddesclen;
257 		sc->sc_kbd = config_found(self, &aa, NULL);
258 	}
259 
260 	if (sc->sc_tpdesclen > 0) {
261 		aa.aa_reportid = APLHIDEV_TP_DEVICE;
262 		aa.aa_desc = sc->sc_tpdesc;
263 		aa.aa_desclen = sc->sc_tpdesclen;
264 		sc->sc_ms = config_found(self, &aa, NULL);
265 	}
266 }
267 
268 void
269 aplhidev_get_info(struct aplhidev_softc *sc)
270 {
271 	struct aplhidev_spi_packet packet;
272 	struct aplhidev_get_desc *msg;
273 	struct aplhidev_spi_status status;
274 
275 	memset(&packet, 0, sizeof(packet));
276 	packet.flags = APLHIDEV_WRITE_PACKET;
277 	packet.device = APLHIDEV_INFO_DEVICE;
278 	packet.len = sizeof(*msg);
279 
280 	msg = (void *)&packet.data[0];
281 	msg->hdr.type = APLHIDEV_GET_INFO;
282 	msg->hdr.device = APLHIDEV_INFO_DEVICE;
283 	msg->hdr.msgid = sc->sc_msgid++;
284 	msg->hdr.cmdlen = 0;
285 	msg->hdr.rsplen = APLHIDEV_DESC_MAX;
286 	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
287 
288 	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
289 
290 	spi_acquire_bus(sc->sc_spi_tag, 0);
291 	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
292 	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
293 	    SPI_KEEP_CS);
294 	delay(100);
295 	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
296 	spi_release_bus(sc->sc_spi_tag, 0);
297 
298 	delay(1000);
299 }
300 
301 void
302 aplhidev_get_descriptor(struct aplhidev_softc *sc, uint8_t device)
303 {
304 	struct aplhidev_spi_packet packet;
305 	struct aplhidev_get_desc *msg;
306 	struct aplhidev_spi_status status;
307 
308 	memset(&packet, 0, sizeof(packet));
309 	packet.flags = APLHIDEV_WRITE_PACKET;
310 	packet.device = APLHIDEV_INFO_DEVICE;
311 	packet.len = sizeof(*msg);
312 
313 	msg = (void *)&packet.data[0];
314 	msg->hdr.type = APLHIDEV_GET_DESCRIPTOR;
315 	msg->hdr.device = device;
316 	msg->hdr.msgid = sc->sc_msgid++;
317 	msg->hdr.cmdlen = 0;
318 	msg->hdr.rsplen = APLHIDEV_DESC_MAX;
319 	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
320 
321 	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
322 
323 	spi_acquire_bus(sc->sc_spi_tag, 0);
324 	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
325 	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
326 	    SPI_KEEP_CS);
327 	delay(100);
328 	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
329 	spi_release_bus(sc->sc_spi_tag, 0);
330 
331 	delay(1000);
332 }
333 
334 void
335 aplhidev_set_leds(struct aplhidev_softc *sc, uint8_t leds)
336 {
337 	struct aplhidev_spi_packet packet;
338 	struct aplhidev_set_leds *msg;
339 	struct aplhidev_spi_status status;
340 
341 	memset(&packet, 0, sizeof(packet));
342 	packet.flags = APLHIDEV_WRITE_PACKET;
343 	packet.device = APLHIDEV_KBD_DEVICE;
344 	packet.len = sizeof(*msg);
345 
346 	msg = (void *)&packet.data[0];
347 	msg->hdr.type = APLHIDEV_SET_LEDS;
348 	msg->hdr.device = APLHIDEV_KBD_DEVICE;
349 	msg->hdr.msgid = sc->sc_msgid++;
350 	msg->hdr.cmdlen = sizeof(*msg) - sizeof(struct aplhidev_msghdr) - 2;
351 	msg->hdr.rsplen = msg->hdr.cmdlen;
352 	msg->reportid = APLHIDEV_KBD_DEVICE;
353 	msg->leds = leds;
354 	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
355 
356 	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
357 
358 	/*
359 	 * XXX Without a delay here, the command will fail.  Does the
360 	 * controller need a bit of time between sending us a keypress
361 	 * event and accepting a new command from us?
362 	 */
363 	delay(250);
364 
365 	spi_acquire_bus(sc->sc_spi_tag, 0);
366 	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
367 	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
368 	    SPI_KEEP_CS);
369 	delay(100);
370 	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
371 	spi_release_bus(sc->sc_spi_tag, 0);
372 }
373 
374 void
375 aplhidev_set_mode(struct aplhidev_softc *sc, uint8_t mode)
376 {
377 	struct aplhidev_spi_packet packet;
378 	struct aplhidev_set_mode *msg;
379 	struct aplhidev_spi_status status;
380 
381 	memset(&packet, 0, sizeof(packet));
382 	packet.flags = APLHIDEV_WRITE_PACKET;
383 	packet.device = APLHIDEV_TP_DEVICE;
384 	packet.len = sizeof(*msg);
385 
386 	msg = (void *)&packet.data[0];
387 	msg->hdr.type = APLHIDEV_SET_MODE;
388 	msg->hdr.device = APLHIDEV_TP_DEVICE;
389 	msg->hdr.msgid = sc->sc_msgid++;
390 	msg->hdr.cmdlen = sizeof(*msg) - sizeof(struct aplhidev_msghdr) - 2;
391 	msg->hdr.rsplen = msg->hdr.cmdlen;
392 	msg->reportid = APLHIDEV_TP_DEVICE;
393 	msg->mode = mode;
394 	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
395 
396 	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
397 
398 	spi_acquire_bus(sc->sc_spi_tag, 0);
399 	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
400 	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
401 	    SPI_KEEP_CS);
402 	delay(100);
403 	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
404 	spi_release_bus(sc->sc_spi_tag, 0);
405 
406 	delay(1000);
407 }
408 
409 int
410 aplhidev_intr(void *arg)
411 {
412 	struct aplhidev_softc *sc = arg;
413 	struct aplhidev_spi_packet packet;
414 	struct aplhidev_msghdr *hdr = (struct aplhidev_msghdr *)&packet.data[0];
415 
416 	memset(&packet, 0, sizeof(packet));
417 	spi_acquire_bus(sc->sc_spi_tag, 0);
418 	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
419 	spi_read(sc->sc_spi_tag, (char *)&packet, sizeof(packet));
420 	spi_release_bus(sc->sc_spi_tag, 0);
421 
422 	/* Treat empty packets as spurious interrupts. */
423 	if (packet.flags == 0 && packet.device == 0 && packet.crc == 0)
424 		return 0;
425 
426 	if (crc16(0, (uint8_t *)&packet, sizeof(packet)))
427 		return 1;
428 
429 	/* Keyboard input. */
430 	if (packet.flags == APLHIDEV_READ_PACKET &&
431 	    packet.device == APLHIDEV_KBD_DEVICE &&
432 	    hdr->type == APLHIDEV_KBD_REPORT) {
433 		if (sc->sc_kbd)
434 			aplkbd_intr(sc->sc_kbd, &packet.data[8], hdr->cmdlen);
435 		return 1;
436 	}
437 
438 	/* Touchpad input. */
439 	if (packet.flags == APLHIDEV_READ_PACKET &&
440 	    packet.device == APLHIDEV_TP_DEVICE &&
441 	    hdr->type == APLHIDEV_TP_REPORT) {
442 		if (sc->sc_ms)
443 			aplms_intr(sc->sc_ms, &packet.data[8], hdr->cmdlen);
444 		return 1;
445 	}
446 
447 	/* Replies to commands we sent. */
448 	if (packet.flags == APLHIDEV_WRITE_PACKET &&
449 	    packet.device == APLHIDEV_INFO_DEVICE &&
450 	    hdr->type == APLHIDEV_GET_INFO) {
451 		struct aplhidev_info_hdr *info =
452 		    (struct aplhidev_info_hdr *)&packet.data[8];
453 		sc->sc_vendor = info->vendor;
454 		sc->sc_product = info->product;
455 		return 1;
456 	}
457 	if (packet.flags == APLHIDEV_WRITE_PACKET &&
458 	    packet.device == APLHIDEV_INFO_DEVICE &&
459 	    hdr->type == APLHIDEV_GET_DESCRIPTOR) {
460 		switch (hdr->device) {
461 		case APLHIDEV_KBD_DEVICE:
462 			memcpy(sc->sc_kbddesc, &packet.data[8], hdr->cmdlen);
463 			sc->sc_kbddesclen = hdr->cmdlen;
464 			break;
465 		case APLHIDEV_TP_DEVICE:
466 			memcpy(sc->sc_tpdesc, &packet.data[8], hdr->cmdlen);
467 			sc->sc_tpdesclen = hdr->cmdlen;
468 			break;
469 		}
470 
471 		return 1;
472 	}
473 	if (packet.flags == APLHIDEV_WRITE_PACKET &&
474 	    packet.device == APLHIDEV_TP_DEVICE &&
475 	    hdr->type == APLHIDEV_SET_MODE) {
476 		sc->sc_mode = APLHIDEV_MODE_RAW;
477 		return 1;
478 	}
479 
480 	/* Valid, but unrecognized packet; ignore for now. */
481 	return 1;
482 }
483 
484 /* Keyboard */
485 
486 struct aplkbd_softc {
487 	struct device		sc_dev;
488 	struct aplhidev_softc	*sc_hidev;
489 	struct hidkbd		sc_kbd;
490 	int			sc_spl;
491 };
492 
493 void	aplkbd_cngetc(void *, u_int *, int *);
494 void	aplkbd_cnpollc(void *, int);
495 void	aplkbd_cnbell(void *, u_int, u_int, u_int);
496 
497 const struct wskbd_consops aplkbd_consops = {
498 	aplkbd_cngetc,
499 	aplkbd_cnpollc,
500 	aplkbd_cnbell,
501 };
502 
503 int	aplkbd_enable(void *, int);
504 void	aplkbd_set_leds(void *, int);
505 int	aplkbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
506 
507 const struct wskbd_accessops aplkbd_accessops = {
508 	.enable = aplkbd_enable,
509 	.ioctl = aplkbd_ioctl,
510 	.set_leds = aplkbd_set_leds,
511 };
512 
513 int	 aplkbd_match(struct device *, void *, void *);
514 void	 aplkbd_attach(struct device *, struct device *, void *);
515 
516 const struct cfattach aplkbd_ca = {
517 	sizeof(struct aplkbd_softc), aplkbd_match, aplkbd_attach
518 };
519 
520 struct cfdriver aplkbd_cd = {
521 	NULL, "aplkbd", DV_DULL
522 };
523 
524 int
525 aplkbd_match(struct device *parent, void *match, void *aux)
526 {
527 	struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux;
528 
529 	return (aa->aa_reportid == APLHIDEV_KBD_DEVICE);
530 }
531 
532 void
533 aplkbd_attach(struct device *parent, struct device *self, void *aux)
534 {
535 	struct aplkbd_softc *sc = (struct aplkbd_softc *)self;
536 	struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux;
537 	struct hidkbd *kbd = &sc->sc_kbd;
538 
539 	sc->sc_hidev = (struct aplhidev_softc *)parent;
540 	if (hidkbd_attach(self, kbd, 1, 0, APLHIDEV_KBD_DEVICE,
541 	    aa->aa_desc, aa->aa_desclen))
542 		return;
543 
544 	printf("\n");
545 
546 	if (hid_locate(aa->aa_desc, aa->aa_desclen, HID_USAGE2(HUP_APPLE, HUG_FN_KEY),
547 	    1, hid_input, &kbd->sc_fn, NULL)) {
548 		switch (sc->sc_hidev->sc_product) {
549 		case USB_PRODUCT_APPLE_WELLSPRINGM1_J293:
550 			kbd->sc_munge = hidkbd_apple_tb_munge;
551 			break;
552 		default:
553 			kbd->sc_munge = hidkbd_apple_munge;
554 			break;
555 		}
556 	}
557 
558 	if (kbd->sc_console_keyboard) {
559 		extern struct wskbd_mapdata ukbd_keymapdata;
560 
561 		ukbd_keymapdata.layout = KB_US | KB_DEFAULT;
562 		wskbd_cnattach(&aplkbd_consops, sc, &ukbd_keymapdata);
563 		aplkbd_enable(sc, 1);
564 	}
565 
566 	hidkbd_attach_wskbd(kbd, KB_US | KB_DEFAULT, &aplkbd_accessops);
567 }
568 
569 void
570 aplkbd_intr(struct device *self, uint8_t *packet, size_t packetlen)
571 {
572 	struct aplkbd_softc *sc = (struct aplkbd_softc *)self;
573 	struct hidkbd *kbd = &sc->sc_kbd;
574 
575 	if (kbd->sc_enabled)
576 		hidkbd_input(kbd, &packet[1], packetlen - 1);
577 }
578 
579 int
580 aplkbd_enable(void *v, int on)
581 {
582 	struct aplkbd_softc *sc = v;
583 	struct hidkbd *kbd = &sc->sc_kbd;
584 
585 	return hidkbd_enable(kbd, on);
586 }
587 
588 int
589 aplkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
590 {
591 	struct aplkbd_softc *sc = v;
592 	struct hidkbd *kbd = &sc->sc_kbd;
593 
594 	switch (cmd) {
595 	case WSKBDIO_GTYPE:
596 		/* XXX: should we set something else? */
597 		*(u_int *)data = WSKBD_TYPE_USB;
598 		return 0;
599 	case WSKBDIO_SETLEDS:
600 		aplkbd_set_leds(v, *(int *)data);
601 		return 0;
602 	default:
603 		return hidkbd_ioctl(kbd, cmd, data, flag, p);
604 	}
605 }
606 
607 void
608 aplkbd_set_leds(void *v, int leds)
609 {
610 	struct aplkbd_softc *sc = v;
611 	struct hidkbd *kbd = &sc->sc_kbd;
612 	uint8_t res;
613 
614 	if (hidkbd_set_leds(kbd, leds, &res))
615 		aplhidev_set_leds(sc->sc_hidev, res);
616 }
617 
618 /* Console interface. */
619 void
620 aplkbd_cngetc(void *v, u_int *type, int *data)
621 {
622 	struct aplkbd_softc *sc = v;
623 	struct hidkbd *kbd = &sc->sc_kbd;
624 
625 	kbd->sc_polling = 1;
626 	while (kbd->sc_npollchar <= 0) {
627 		aplhidev_intr(sc->sc_dev.dv_parent);
628 		delay(1000);
629 	}
630 	kbd->sc_polling = 0;
631 	hidkbd_cngetc(kbd, type, data);
632 }
633 
634 void
635 aplkbd_cnpollc(void *v, int on)
636 {
637 	struct aplkbd_softc *sc = v;
638 
639 	if (on)
640 		sc->sc_spl = spltty();
641 	else
642 		splx(sc->sc_spl);
643 }
644 
645 void
646 aplkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume)
647 {
648 	hidkbd_bell(pitch, period, volume, 1);
649 }
650 
651 #if NAPLMS > 0
652 
653 /* Touchpad */
654 
655 /*
656  * The contents of the touchpad event packets is identical to those
657  * used by the ubcmtp(4) driver.  The relevant definitions and the
658  * code to decode the packets is replicated here.
659  */
660 
661 struct ubcmtp_finger {
662 	uint16_t	origin;
663 	uint16_t	abs_x;
664 	uint16_t	abs_y;
665 	uint16_t	rel_x;
666 	uint16_t	rel_y;
667 	uint16_t	tool_major;
668 	uint16_t	tool_minor;
669 	uint16_t	orientation;
670 	uint16_t	touch_major;
671 	uint16_t	touch_minor;
672 	uint16_t	unused[2];
673 	uint16_t	pressure;
674 	uint16_t	multi;
675 } __packed __attribute((aligned(2)));
676 
677 #define UBCMTP_MAX_FINGERS	16
678 
679 #define UBCMTP_TYPE4_TPOFF	(24 * sizeof(uint16_t))
680 #define UBCMTP_TYPE4_BTOFF	31
681 #define UBCMTP_TYPE4_FINGERPAD	(1 * sizeof(uint16_t))
682 
683 /* Use a constant, synaptics-compatible pressure value for now. */
684 #define DEFAULT_PRESSURE	40
685 
686 static struct wsmouse_param aplms_wsmousecfg[] = {
687 	{ WSMOUSECFG_MTBTN_MAXDIST, 0 }, /* 0: Compute a default value. */
688 };
689 
690 struct aplms_softc {
691 	struct device	sc_dev;
692 	struct device	*sc_wsmousedev;
693 
694 	int		sc_enabled;
695 
696 	int		tp_offset;
697 	int		tp_fingerpad;
698 
699 	struct mtpoint	frame[UBCMTP_MAX_FINGERS];
700 	int		contacts;
701 	int		btn;
702 };
703 
704 int	aplms_enable(void *);
705 void	aplms_disable(void *);
706 int	aplms_ioctl(void *, u_long, caddr_t, int, struct proc *);
707 
708 const struct wsmouse_accessops aplms_accessops = {
709 	.enable = aplms_enable,
710 	.disable = aplms_disable,
711 	.ioctl = aplms_ioctl,
712 };
713 
714 int	 aplms_match(struct device *, void *, void *);
715 void	 aplms_attach(struct device *, struct device *, void *);
716 
717 const struct cfattach aplms_ca = {
718 	sizeof(struct aplms_softc), aplms_match, aplms_attach
719 };
720 
721 struct cfdriver aplms_cd = {
722 	NULL, "aplms", DV_DULL
723 };
724 
725 int	aplms_configure(struct aplms_softc *);
726 
727 int
728 aplms_match(struct device *parent, void *match, void *aux)
729 {
730 	struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux;
731 
732 	return (aa->aa_reportid == APLHIDEV_TP_DEVICE);
733 }
734 
735 void
736 aplms_attach(struct device *parent, struct device *self, void *aux)
737 {
738 	struct aplms_softc *sc = (struct aplms_softc *)self;
739 	struct wsmousedev_attach_args aa;
740 
741 	printf("\n");
742 
743 	sc->tp_offset = UBCMTP_TYPE4_TPOFF;
744 	sc->tp_fingerpad = UBCMTP_TYPE4_FINGERPAD;
745 
746 	aa.accessops = &aplms_accessops;
747 	aa.accesscookie = sc;
748 
749 	sc->sc_wsmousedev = config_found(self, &aa, wsmousedevprint);
750 	if (sc->sc_wsmousedev != NULL && aplms_configure(sc))
751 		aplms_disable(sc);
752 }
753 
754 int
755 aplms_configure(struct aplms_softc *sc)
756 {
757 	struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev);
758 
759 	/* The values below are for the MacBookPro17,1 */
760 	hw->type = WSMOUSE_TYPE_TOUCHPAD;
761 	hw->hw_type = WSMOUSEHW_CLICKPAD;
762 	hw->x_min = -6046;
763 	hw->x_max = 6536;
764 	hw->y_min = -164;
765 	hw->y_max = 7439;
766 	hw->mt_slots = UBCMTP_MAX_FINGERS;
767 	hw->flags = WSMOUSEHW_MT_TRACKING;
768 
769 	return wsmouse_configure(sc->sc_wsmousedev,
770 	    aplms_wsmousecfg, nitems(aplms_wsmousecfg));
771 }
772 
773 void
774 aplms_intr(struct device *self, uint8_t *packet, size_t packetlen)
775 {
776 	struct aplms_softc *sc = (struct aplms_softc *)self;
777 	struct ubcmtp_finger *finger;
778 	int off, s, btn, contacts;
779 
780 	if (!sc->sc_enabled)
781 		return;
782 
783 	contacts = 0;
784 	for (off = sc->tp_offset; off < packetlen;
785 	    off += (sizeof(struct ubcmtp_finger) + sc->tp_fingerpad)) {
786 		finger = (struct ubcmtp_finger *)(packet + off);
787 
788 		if ((int16_t)letoh16(finger->touch_major) == 0)
789 			continue; /* finger lifted */
790 
791 		sc->frame[contacts].x = (int16_t)letoh16(finger->abs_x);
792 		sc->frame[contacts].y = (int16_t)letoh16(finger->abs_y);
793 		sc->frame[contacts].pressure = DEFAULT_PRESSURE;
794 		contacts++;
795 	}
796 
797 	btn = sc->btn;
798 	sc->btn = !!((int16_t)letoh16(packet[UBCMTP_TYPE4_BTOFF]));
799 
800 	if (contacts || sc->contacts || sc->btn != btn) {
801 		sc->contacts = contacts;
802 		s = spltty();
803 		wsmouse_buttons(sc->sc_wsmousedev, sc->btn);
804 		wsmouse_mtframe(sc->sc_wsmousedev, sc->frame, contacts);
805 		wsmouse_input_sync(sc->sc_wsmousedev);
806 		splx(s);
807 	}
808 }
809 
810 int
811 aplms_enable(void *v)
812 {
813 	struct aplms_softc *sc = v;
814 
815 	if (sc->sc_enabled)
816 		return EBUSY;
817 
818 	sc->sc_enabled = 1;
819 	return 0;
820 }
821 
822 void
823 aplms_disable(void *v)
824 {
825 	struct aplms_softc *sc = v;
826 
827 	sc->sc_enabled = 0;
828 }
829 
830 int
831 aplms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
832 {
833 	struct aplms_softc *sc = v;
834 	struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev);
835 	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
836 	int wsmode;
837 
838 	switch (cmd) {
839 	case WSMOUSEIO_GTYPE:
840 		*(u_int *)data = hw->type;
841 		break;
842 
843 	case WSMOUSEIO_GCALIBCOORDS:
844 		wsmc->minx = hw->x_min;
845 		wsmc->maxx = hw->x_max;
846 		wsmc->miny = hw->y_min;
847 		wsmc->maxy = hw->y_max;
848 		wsmc->swapxy = 0;
849 		wsmc->resx = 0;
850 		wsmc->resy = 0;
851 		break;
852 
853 	case WSMOUSEIO_SETMODE:
854 		wsmode = *(u_int *)data;
855 		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) {
856 			printf("%s: invalid mode %d\n", sc->sc_dev.dv_xname,
857 			    wsmode);
858 			return (EINVAL);
859 		}
860 		wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
861 		break;
862 
863 	default:
864 		return -1;
865 	}
866 
867 	return 0;
868 }
869 
870 #else
871 
872 void
873 aplms_intr(struct device *self, uint8_t *packet, size_t packetlen)
874 {
875 }
876 
877 #endif
878