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