1/*	$OpenBSD: apldc.c,v 1.12 2024/01/20 08:00:59 kettenis Exp $	*/
2/*
3 * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/param.h>
19#include <sys/systm.h>
20#include <sys/device.h>
21#include <sys/evcount.h>
22#include <sys/malloc.h>
23#include <sys/task.h>
24#include <sys/timeout.h>
25
26#include <machine/bus.h>
27#include <machine/fdt.h>
28
29#include <dev/ofw/openfirm.h>
30#include <dev/ofw/ofw_gpio.h>
31#include <dev/ofw/fdt.h>
32
33#include <dev/wscons/wsconsio.h>
34#include <dev/wscons/wskbdvar.h>
35#include <dev/wscons/wsksymdef.h>
36#include <dev/wscons/wsmousevar.h>
37
38#include <dev/hid/hid.h>
39#include <dev/hid/hidkbdsc.h>
40#include <dev/hid/hidmsvar.h>
41
42#include <arm64/dev/rtkit.h>
43#include <machine/simplebusvar.h>
44
45#include "apldc.h"
46
47#define DC_IRQ_MASK		0x0000
48#define DC_IRQ_STAT		0x0004
49
50#define DC_CONFIG_TX_THRESH	0x0000
51#define DC_CONFIG_RX_THRESH	0x0004
52
53#define DC_DATA_TX8		0x0004
54#define DC_DATA_TX32		0x0010
55#define DC_DATA_TX_FREE		0x0014
56#define DC_DATA_RX8		0x001c
57#define  DC_DATA_RX8_COUNT(d)	((d) & 0x7f)
58#define  DC_DATA_RX8_DATA(d)	(((d) >> 8) & 0xff)
59#define DC_DATA_RX32		0x0028
60#define DC_DATA_RX_COUNT	0x002c
61
62#define APLDC_MAX_INTR		32
63
64#define HREAD4(sc, reg)							\
65	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
66#define HWRITE4(sc, reg, val)						\
67	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
68#define HSET4(sc, reg, bits)						\
69	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
70#define HCLR4(sc, reg, bits)						\
71	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
72
73struct apldchidev_attach_args {
74	const char *aa_name;
75	void	*aa_desc;
76	size_t	aa_desclen;
77};
78
79struct intrhand {
80	int (*ih_func)(void *);
81	void *ih_arg;
82	int ih_ipl;
83	int ih_irq;
84	int ih_level;
85	struct evcount ih_count;
86	char *ih_name;
87	void *ih_sc;
88};
89
90struct apldc_softc {
91	struct simplebus_softc	sc_sbus;
92	bus_space_tag_t		sc_iot;
93	bus_space_handle_t	sc_ioh;
94
95	void			*sc_ih;
96	struct intrhand		*sc_handlers[APLDC_MAX_INTR];
97	struct interrupt_controller sc_ic;
98};
99
100int	apldc_match(struct device *, void *, void *);
101void	apldc_attach(struct device *, struct device *, void *);
102
103const struct cfattach apldc_ca = {
104	sizeof (struct apldc_softc), apldc_match, apldc_attach
105};
106
107struct cfdriver apldc_cd = {
108	NULL, "apldc", DV_DULL
109};
110
111int	apldc_intr(void *);
112void	*apldc_intr_establish(void *, int *, int, struct cpu_info *,
113	    int (*)(void *), void *, char *);
114void	apldc_intr_enable(void *);
115void	apldc_intr_disable(void *);
116void	apldc_intr_barrier(void *);
117
118int
119apldc_match(struct device *parent, void *match, void *aux)
120{
121	struct fdt_attach_args *faa = aux;
122
123	return OF_is_compatible(faa->fa_node, "apple,dockchannel");
124}
125
126void
127apldc_attach(struct device *parent, struct device *self, void *aux)
128{
129	struct apldc_softc *sc = (struct apldc_softc *)self;
130	struct fdt_attach_args *faa = aux;
131
132	if (faa->fa_nreg < 1) {
133		printf(": no registers\n");
134		return;
135	}
136
137	sc->sc_iot = faa->fa_iot;
138	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
139	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
140		printf(": can't map registers\n");
141		return;
142	}
143
144	/* Disable and clear all interrupts. */
145	HWRITE4(sc, DC_IRQ_MASK, 0);
146	HWRITE4(sc, DC_IRQ_STAT, 0xffffffff);
147
148	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_TTY,
149	    apldc_intr, sc, sc->sc_sbus.sc_dev.dv_xname);
150
151	sc->sc_ic.ic_node = faa->fa_node;
152	sc->sc_ic.ic_cookie = sc;
153	sc->sc_ic.ic_establish = apldc_intr_establish;
154	sc->sc_ic.ic_enable = apldc_intr_enable;
155	sc->sc_ic.ic_disable = apldc_intr_disable;
156	sc->sc_ic.ic_barrier = apldc_intr_barrier;
157	fdt_intr_register(&sc->sc_ic);
158
159	simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa);
160}
161
162int
163apldc_intr(void *arg)
164{
165	struct apldc_softc *sc = arg;
166	struct intrhand *ih;
167	uint32_t stat, pending;
168	int irq, s;
169
170	stat = HREAD4(sc, DC_IRQ_STAT);
171
172	pending = stat;
173	while (pending) {
174		irq = ffs(pending) - 1;
175		ih = sc->sc_handlers[irq];
176		if (ih) {
177			s = splraise(ih->ih_ipl);
178			if (ih->ih_func(ih->ih_arg))
179				ih->ih_count.ec_count++;
180			splx(s);
181		}
182
183		pending &= ~(1 << irq);
184	}
185
186	HWRITE4(sc, DC_IRQ_STAT, stat);
187
188	return 1;
189}
190
191void *
192apldc_intr_establish(void *cookie, int *cells, int ipl,
193    struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
194{
195	struct apldc_softc *sc = cookie;
196	struct intrhand *ih;
197	int irq = cells[0];
198	int level = cells[1];
199
200	if (irq < 0 || irq >= APLDC_MAX_INTR)
201		return NULL;
202
203	if (ipl != IPL_TTY)
204		return NULL;
205
206	if (ci != NULL && !CPU_IS_PRIMARY(ci))
207		return NULL;
208
209	if (sc->sc_handlers[irq])
210		return NULL;
211
212	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
213	ih->ih_func = func;
214	ih->ih_arg = arg;
215	ih->ih_ipl = ipl & IPL_IRQMASK;
216	ih->ih_irq = irq;
217	ih->ih_name = name;
218	ih->ih_level = level;
219	ih->ih_sc = sc;
220
221	sc->sc_handlers[irq] = ih;
222
223	if (name != NULL)
224		evcount_attach(&ih->ih_count, name, &ih->ih_irq);
225
226	return ih;
227}
228
229void
230apldc_intr_enable(void *cookie)
231{
232	struct intrhand	*ih = cookie;
233	struct apldc_softc *sc = ih->ih_sc;
234
235	HSET4(sc, DC_IRQ_MASK, 1 << ih->ih_irq);
236}
237
238void
239apldc_intr_disable(void *cookie)
240{
241	struct intrhand	*ih = cookie;
242	struct apldc_softc *sc = ih->ih_sc;
243
244	HCLR4(sc, DC_IRQ_MASK, 1 << ih->ih_irq);
245}
246
247void
248apldc_intr_barrier(void *cookie)
249{
250	struct intrhand	*ih = cookie;
251	struct apldc_softc *sc = ih->ih_sc;
252
253	intr_barrier(sc->sc_ih);
254}
255
256#define APLDCHIDEV_DESC_MAX	512
257#define APLDCHIDEV_PKT_MAX	1024
258#define APLDCHIDEV_GPIO_MAX	4
259
260#define APLDCHIDEV_NUM_GPIOS	16
261
262struct apldchidev_gpio {
263	struct apldchidev_softc	*ag_sc;
264	uint8_t			ag_id;
265	uint8_t			ag_iface;
266	uint32_t		ag_gpio[APLDCHIDEV_GPIO_MAX];
267	struct task		ag_task;
268};
269
270struct apldchidev_softc {
271	struct device		sc_dev;
272	bus_space_tag_t		sc_iot;
273	bus_space_handle_t	sc_cfg_ioh;
274	bus_space_handle_t	sc_data_ioh;
275
276	bus_dma_tag_t		sc_dmat;
277	int			sc_node;
278
279	void			*sc_rx_ih;
280
281	uint8_t			sc_seq_comm;
282
283	uint8_t			sc_iface_stm;
284	uint8_t			sc_seq_stm;
285	uint8_t			sc_stmdesc[APLDCHIDEV_DESC_MAX];
286	size_t			sc_stmdesclen;
287	int			sc_stm_ready;
288
289	uint8_t			sc_iface_kbd;
290	uint8_t			sc_seq_kbd;
291	struct device		*sc_kbd;
292	uint8_t			sc_kbddesc[APLDCHIDEV_DESC_MAX];
293	size_t			sc_kbddesclen;
294	int			sc_kbd_ready;
295
296	uint8_t			sc_iface_mt;
297	uint8_t			sc_seq_mt;
298	struct device		*sc_mt;
299	uint8_t			sc_mtdesc[APLDCHIDEV_DESC_MAX];
300	size_t			sc_mtdesclen;
301	int			sc_mt_ready;
302	int			sc_x_min;
303	int			sc_x_max;
304	int			sc_y_min;
305	int			sc_y_max;
306	int			sc_h_res;
307	int			sc_v_res;
308
309	struct apldchidev_gpio	sc_gpio[APLDCHIDEV_NUM_GPIOS];
310	u_int			sc_ngpios;
311	uint8_t			sc_gpio_cmd[APLDCHIDEV_PKT_MAX];
312	size_t			sc_gpio_cmd_len;
313
314	uint8_t			sc_cmd_iface;
315	uint8_t			sc_cmd_seq;
316	uint8_t			sc_data[APLDCHIDEV_DESC_MAX];
317	size_t			sc_data_len;
318	uint32_t		sc_retcode;
319	int			sc_busy;
320};
321
322int	apldchidev_match(struct device *, void *, void *);
323void	apldchidev_attach(struct device *, struct device *, void *);
324
325const struct cfattach apldchidev_ca = {
326	sizeof(struct apldchidev_softc), apldchidev_match, apldchidev_attach
327};
328
329struct cfdriver apldchidev_cd = {
330	NULL, "apldchidev", DV_DULL
331};
332
333void	apldchidev_attachhook(struct device *);
334void	apldchidev_cmd(struct apldchidev_softc *, uint8_t, uint8_t,
335	    void *, size_t);
336void	apldchidev_wait(struct apldchidev_softc *);
337int	apldchidev_send_firmware(struct apldchidev_softc *, int,
338	    void *, size_t);
339void	apldchidev_enable(struct apldchidev_softc *, uint8_t);
340void	apldchidev_reset(struct apldchidev_softc *, uint8_t, uint8_t);
341int	apldchidev_rx_intr(void *);
342void	apldchidev_gpio_task(void *);
343
344int
345apldchidev_match(struct device *parent, void *cfdata, void *aux)
346{
347	struct fdt_attach_args *faa = aux;
348
349	return OF_is_compatible(faa->fa_node, "apple,dockchannel-hid");
350}
351
352void
353apldchidev_attach(struct device *parent, struct device *self, void *aux)
354{
355	struct apldchidev_softc *sc = (struct apldchidev_softc *)self;
356	struct fdt_attach_args *faa = aux;
357	struct apldchidev_attach_args aa;
358	uint32_t phandle;
359	int error, idx, retry;
360
361	if (faa->fa_nreg < 2) {
362		printf(": no registers\n");
363		return;
364	}
365
366	sc->sc_iot = faa->fa_iot;
367	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
368	    faa->fa_reg[0].size, 0, &sc->sc_cfg_ioh)) {
369		printf(": can't map registers\n");
370		return;
371	}
372	if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
373	    faa->fa_reg[1].size, 0, &sc->sc_data_ioh)) {
374		printf(": can't map registers\n");
375		return;
376	}
377
378	sc->sc_dmat = faa->fa_dmat;
379	sc->sc_node = faa->fa_node;
380
381	idx = OF_getindex(faa->fa_node, "rx", "interrupt-names");
382	if (idx < 0) {
383		printf(": no rx interrupt\n");
384		return;
385	}
386	sc->sc_rx_ih = fdt_intr_establish_idx(faa->fa_node, idx, IPL_TTY,
387	    apldchidev_rx_intr, sc, sc->sc_dev.dv_xname);
388	if (sc->sc_rx_ih == NULL) {
389		printf(": can't establish interrupt\n");
390		return;
391	}
392
393	phandle = OF_getpropint(faa->fa_node, "apple,helper-cpu", 0);
394	if (phandle) {
395		error = aplrtk_start(phandle);
396		if (error) {
397			printf(": can't start helper CPU\n");
398			return;
399		}
400	}
401
402	printf("\n");
403
404	/* Poll until we have received the STM HID descriptor. */
405	for (retry = 10; retry > 0; retry--) {
406		if (sc->sc_stmdesclen > 0)
407			break;
408		apldchidev_rx_intr(sc);
409		delay(1000);
410	}
411
412	if (sc->sc_stmdesclen > 0) {
413		/* Enable interface. */
414		apldchidev_enable(sc, sc->sc_iface_stm);
415	}
416
417	/* Poll until we have received the keyboard HID descriptor. */
418	for (retry = 10; retry > 0; retry--) {
419		if (sc->sc_kbddesclen > 0)
420			break;
421		apldchidev_rx_intr(sc);
422		delay(1000);
423	}
424
425	if (sc->sc_kbddesclen > 0) {
426		/* Enable interface. */
427		apldchidev_enable(sc, sc->sc_iface_kbd);
428
429		aa.aa_name = "keyboard";
430		aa.aa_desc = sc->sc_kbddesc;
431		aa.aa_desclen = sc->sc_kbddesclen;
432		sc->sc_kbd = config_found(self, &aa, NULL);
433	}
434
435	bus_space_write_4(sc->sc_iot, sc->sc_cfg_ioh, DC_CONFIG_RX_THRESH, 8);
436	fdt_intr_enable(sc->sc_rx_ih);
437
438#if NAPLDCMS > 0
439	config_mountroot(self, apldchidev_attachhook);
440#endif
441}
442
443int
444apldchidev_read(struct apldchidev_softc *sc, void *buf, size_t len,
445    uint32_t *checksum)
446{
447	uint8_t *dst = buf;
448	uint32_t data;
449	int shift = 0;
450
451	while (len > 0) {
452		data = bus_space_read_4(sc->sc_iot, sc->sc_data_ioh,
453		    DC_DATA_RX8);
454		if (DC_DATA_RX8_COUNT(data) > 0) {
455			*dst++ = DC_DATA_RX8_DATA(data);
456			*checksum += (DC_DATA_RX8_DATA(data) << shift);
457			shift += 8;
458			if (shift > 24)
459				shift = 0;
460			len--;
461		} else {
462			delay(10);
463		}
464	}
465
466	return 0;
467}
468
469int
470apldchidev_write(struct apldchidev_softc *sc, const void *buf, size_t len,
471    uint32_t *checksum)
472{
473	const uint8_t *src = buf;
474	uint32_t free;
475	int shift = 0;
476
477	while (len > 0) {
478		free = bus_space_read_4(sc->sc_iot, sc->sc_data_ioh,
479		    DC_DATA_TX_FREE);
480		if (free > 0) {
481			if (checksum)
482				*checksum -= *src << shift;
483			bus_space_write_4(sc->sc_iot, sc->sc_data_ioh,
484			    DC_DATA_TX8, *src++);
485			shift += 8;
486			if (shift > 24)
487				shift = 0;
488			len--;
489		} else {
490			delay(10);
491		}
492	}
493
494	return 0;
495}
496
497struct mtp_hdr {
498	uint8_t hdr_len;
499	uint8_t chan;
500#define MTP_CHAN_CMD		0x11
501#define MTP_CHAN_REPORT		0x12
502	uint16_t pkt_len;
503	uint8_t seq;
504	uint8_t iface;
505#define MTP_IFACE_COMM		0
506	uint16_t pad;
507} __packed;
508
509struct mtp_subhdr {
510	uint8_t flags;
511#define MTP_GROUP_SHIFT	6
512#define MTP_GROUP(x)		((x >> 6) & 0x3)
513#define MTP_GROUP_INPUT	0
514#define MTP_GROUP_OUTPUT	1
515#define MTP_GROUP_CMD		2
516#define MTP_REQ_SHIFT		0
517#define MTP_REQ(x)		((x >> 0) & 0x3f)
518#define MTP_REQ_SET_REPORT	0
519#define MTP_REQ_GET_REPORT	1
520	uint8_t unk;
521	uint16_t len;
522	uint32_t retcode;
523} __packed;
524
525struct mtp_init_hdr {
526	uint8_t type;
527#define MTP_EVENT_GPIO_CMD	0xa0
528#define MTP_EVENT_INIT	0xf0
529#define MTP_EVENT_READY	0xf1
530	uint8_t unk1;
531	uint8_t unk2;
532	uint8_t iface;
533	char name[16];
534} __packed;
535
536struct mtp_init_block_hdr {
537	uint16_t type;
538#define MTP_BLOCK_DESCRIPTOR	0
539#define MTP_BLOCK_GPIO_REQ	1
540#define MTP_BLOCK_END		2
541	uint16_t subtype;
542	uint16_t len;
543} __packed;
544
545struct mtp_gpio_req {
546	uint16_t unk;
547	uint16_t id;
548	char name[32];
549} __packed;
550
551struct mtp_gpio_cmd {
552	uint8_t type;
553	uint8_t iface;
554	uint8_t id;
555	uint8_t unk;
556	uint8_t cmd;
557#define MTP_GPIO_CMD_TOGGLE	0x03
558} __packed;
559
560struct mtp_gpio_ack {
561	uint8_t type;
562	uint32_t retcode;
563	uint8_t cmd[512];
564} __packed;
565
566struct mtp_dim {
567	uint32_t width;
568	uint32_t height;
569	int16_t x_min;
570	int16_t y_min;
571	int16_t x_max;
572	int16_t y_max;
573};
574
575#define MTP_CMD_RESET_INTERFACE	0x40
576#define MTP_CMD_SEND_FIRMWARE		0x95
577#define MTP_CMD_ENABLE_INTERFACE	0xb4
578#define MTP_CMD_ACK_GPIO_CMD		0xa1
579#define MTP_CMD_GET_DIMENSIONS		0xd9
580
581void
582apldchidev_handle_gpio_req(struct apldchidev_softc *sc, uint8_t iface,
583    void *buf, size_t len)
584{
585	struct mtp_gpio_req *req = buf;
586	uint32_t gpio[APLDCHIDEV_GPIO_MAX];
587	char name[64];
588	int node = -1;
589
590	if (len < sizeof(*req))
591		return;
592
593	if (sc->sc_ngpios >= APLDCHIDEV_NUM_GPIOS)
594		return;
595
596	node = sc->sc_node;
597	snprintf(name, sizeof(name), "apple,%s-gpios", req->name);
598	len = OF_getproplen(node, name);
599	if (len <= 0 || len > sizeof(gpio)) {
600		/* XXX: older device trees store gpios in sub-nodes */
601		if (iface == sc->sc_iface_mt)
602			node = OF_getnodebyname(sc->sc_node, "multi-touch");
603		else if (iface == sc->sc_iface_stm)
604			node = OF_getnodebyname(sc->sc_node, "stm");
605		if (node == -1)
606			return;
607		len = OF_getproplen(node, name);
608		if (len <= 0 || len > sizeof(gpio))
609			return;
610	}
611
612	OF_getpropintarray(node, name, gpio, len);
613	gpio_controller_config_pin(gpio, GPIO_CONFIG_OUTPUT);
614	gpio_controller_set_pin(gpio, 0);
615
616	sc->sc_gpio[sc->sc_ngpios].ag_sc = sc;
617	sc->sc_gpio[sc->sc_ngpios].ag_id = req->id;
618	sc->sc_gpio[sc->sc_ngpios].ag_iface = iface;
619	memcpy(sc->sc_gpio[sc->sc_ngpios].ag_gpio, gpio, len);
620	task_set(&sc->sc_gpio[sc->sc_ngpios].ag_task,
621	    apldchidev_gpio_task, &sc->sc_gpio[sc->sc_ngpios]);
622	sc->sc_ngpios++;
623}
624
625void
626apldchidev_handle_init(struct apldchidev_softc *sc, uint8_t iface,
627    void *buf, size_t len)
628{
629	struct mtp_init_block_hdr *bhdr = buf;
630
631	for (;;) {
632		if (len < sizeof(*bhdr))
633			return;
634		len -= sizeof(*bhdr);
635
636		if (len < bhdr->len)
637			return;
638		len -= bhdr->len;
639
640		switch (bhdr->type) {
641		case MTP_BLOCK_DESCRIPTOR:
642			if (iface == sc->sc_iface_kbd &&
643			    bhdr->len <= sizeof(sc->sc_kbddesc)) {
644				memcpy(sc->sc_kbddesc, bhdr + 1, bhdr->len);
645				sc->sc_kbddesclen = bhdr->len;
646			} else if (iface == sc->sc_iface_mt &&
647			    bhdr->len <= sizeof(sc->sc_mtdesc)) {
648				memcpy(sc->sc_mtdesc, bhdr + 1, bhdr->len);
649				sc->sc_mtdesclen = bhdr->len;
650			} else if (iface == sc->sc_iface_stm &&
651			    bhdr->len <= sizeof(sc->sc_stmdesc)) {
652				memcpy(sc->sc_stmdesc, bhdr + 1, bhdr->len);
653				sc->sc_stmdesclen = bhdr->len;
654			}
655			break;
656		case MTP_BLOCK_GPIO_REQ:
657			apldchidev_handle_gpio_req(sc, iface,
658			    bhdr + 1, bhdr->len);
659			break;
660		case MTP_BLOCK_END:
661			return;
662		default:
663			printf("%s: unhandled block type 0x%04x\n",
664			    sc->sc_dev.dv_xname, bhdr->type);
665			break;
666		}
667
668		bhdr = (struct mtp_init_block_hdr *)
669		    ((uint8_t *)(bhdr + 1) + bhdr->len);
670	}
671}
672
673void
674apldchidev_handle_comm(struct apldchidev_softc *sc, void *buf, size_t len)
675{
676	struct mtp_init_hdr *ihdr = buf;
677	struct mtp_gpio_cmd *cmd = buf;
678	uint8_t iface;
679	int i;
680
681	switch (ihdr->type) {
682	case MTP_EVENT_INIT:
683		if (strcmp(ihdr->name, "keyboard") == 0) {
684			sc->sc_iface_kbd = ihdr->iface;
685			apldchidev_handle_init(sc, ihdr->iface,
686			    ihdr + 1, len - sizeof(*ihdr));
687		}
688		if (strcmp(ihdr->name, "multi-touch") == 0) {
689			sc->sc_iface_mt = ihdr->iface;
690			apldchidev_handle_init(sc, ihdr->iface,
691			    ihdr + 1, len - sizeof(*ihdr));
692		}
693		if (strcmp(ihdr->name, "stm") == 0) {
694			sc->sc_iface_stm = ihdr->iface;
695			apldchidev_handle_init(sc, ihdr->iface,
696			    ihdr + 1, len - sizeof(*ihdr));
697		}
698		break;
699	case MTP_EVENT_READY:
700		iface = ihdr->unk1;
701		if (iface == sc->sc_iface_stm)
702			sc->sc_stm_ready = 1;
703		if (iface == sc->sc_iface_kbd)
704			sc->sc_kbd_ready = 1;
705		if (iface == sc->sc_iface_mt)
706			sc->sc_mt_ready = 1;
707		break;
708	case MTP_EVENT_GPIO_CMD:
709		for (i =0; i < sc->sc_ngpios; i++) {
710			if (cmd->id == sc->sc_gpio[i].ag_id &&
711			    cmd->iface == sc->sc_gpio[i].ag_iface &&
712			    cmd->cmd == MTP_GPIO_CMD_TOGGLE) {
713				/* Stash the command for the reply. */
714				KASSERT(len < sizeof(sc->sc_gpio_cmd));
715				memcpy(sc->sc_gpio_cmd, buf, len);
716				sc->sc_gpio_cmd_len = len;
717				task_add(systq, &sc->sc_gpio[i].ag_task);
718				return;
719			}
720		}
721		printf("%s: unhandled gpio id %d iface %d cmd 0x%02x\n",
722		       sc->sc_dev.dv_xname, cmd->id, cmd->iface, cmd->cmd);
723		break;
724	default:
725		printf("%s: unhandled comm event 0x%02x\n",
726		    sc->sc_dev.dv_xname, ihdr->type);
727		break;
728	}
729}
730
731void
732apldchidev_gpio_task(void *arg)
733{
734	struct apldchidev_gpio *ag = arg;
735	struct apldchidev_softc *sc = ag->ag_sc;
736	struct mtp_gpio_ack *ack;
737	uint8_t flags;
738	size_t len;
739
740	gpio_controller_set_pin(ag->ag_gpio, 1);
741	delay(10000);
742	gpio_controller_set_pin(ag->ag_gpio, 0);
743
744	len = sizeof(*ack) + sc->sc_gpio_cmd_len;
745	ack = malloc(len, M_TEMP, M_WAITOK);
746	ack->type = MTP_CMD_ACK_GPIO_CMD;
747	ack->retcode = 0;
748	memcpy(ack->cmd, sc->sc_gpio_cmd, sc->sc_gpio_cmd_len);
749
750	flags = MTP_GROUP_CMD << MTP_GROUP_SHIFT;
751	flags |= MTP_REQ_SET_REPORT << MTP_REQ_SHIFT;
752	apldchidev_cmd(sc, MTP_IFACE_COMM, flags, ack, len);
753
754	free(ack, M_TEMP, len);
755}
756
757void apldckbd_intr(struct device *, uint8_t *, size_t);
758void apldcms_intr(struct device *, uint8_t *, size_t);
759
760int
761apldchidev_rx_intr(void *arg)
762{
763	struct apldchidev_softc *sc = arg;
764	struct mtp_hdr hdr;
765	struct mtp_subhdr *shdr;
766	uint32_t checksum = 0;
767	char buf[APLDCHIDEV_PKT_MAX];
768
769	apldchidev_read(sc, &hdr, sizeof(hdr), &checksum);
770	apldchidev_read(sc, buf, hdr.pkt_len + 4, &checksum);
771	if (checksum != 0xffffffff) {
772		printf("%s: packet checksum error\n", sc->sc_dev.dv_xname);
773		return 1;
774	}
775	if (hdr.pkt_len < sizeof(*shdr)) {
776		printf("%s: packet too small\n", sc->sc_dev.dv_xname);
777		return 1;
778	}
779
780	shdr = (struct mtp_subhdr *)buf;
781	if (MTP_GROUP(shdr->flags) == MTP_GROUP_OUTPUT ||
782	    MTP_GROUP(shdr->flags) == MTP_GROUP_CMD) {
783		if (hdr.iface != sc->sc_cmd_iface) {
784			printf("%s: got ack for unexpected iface\n",
785			    sc->sc_dev.dv_xname);
786		}
787		if (hdr.seq != sc->sc_cmd_seq) {
788			printf("%s: got ack with unexpected seq\n",
789			    sc->sc_dev.dv_xname);
790		}
791		if (MTP_REQ(shdr->flags) == MTP_REQ_GET_REPORT &&
792		    shdr->len <= sizeof(sc->sc_data)) {
793			memcpy(sc->sc_data, (shdr + 1), shdr->len);
794			sc->sc_data_len = shdr->len;
795		} else {
796			sc->sc_data_len = 0;
797		}
798		sc->sc_retcode = shdr->retcode;
799		sc->sc_busy = 0;
800		wakeup(sc);
801		return 1;
802	}
803	if (MTP_GROUP(shdr->flags) != MTP_GROUP_INPUT) {
804		printf("%s: unhandled group 0x%02x\n",
805		    sc->sc_dev.dv_xname, shdr->flags);
806		return 1;
807	}
808
809	if (hdr.iface == MTP_IFACE_COMM)
810		apldchidev_handle_comm(sc, shdr + 1, shdr->len);
811	else if (hdr.iface == sc->sc_iface_kbd && sc->sc_kbd)
812		apldckbd_intr(sc->sc_kbd, (uint8_t *)(shdr + 1), shdr->len);
813	else if (hdr.iface == sc->sc_iface_mt && sc->sc_mt)
814		apldcms_intr(sc->sc_mt, (uint8_t *)(shdr + 1), shdr->len);
815	else {
816		printf("%s: unhandled iface %d\n",
817		    sc->sc_dev.dv_xname, hdr.iface);
818	}
819
820	wakeup(sc);
821	return 1;
822}
823
824void
825apldchidev_cmd(struct apldchidev_softc *sc, uint8_t iface, uint8_t flags,
826    void *data, size_t len)
827{
828	struct mtp_hdr hdr;
829	struct mtp_subhdr shdr;
830	uint32_t checksum = 0xffffffff;
831	uint8_t pad[4];
832
833	KASSERT(sc->sc_busy == 0);
834	sc->sc_busy = 1;
835
836	memset(&hdr, 0, sizeof(hdr));
837	hdr.hdr_len = sizeof(hdr);
838	hdr.chan = MTP_CHAN_CMD;
839	hdr.pkt_len = roundup(len, 4) + sizeof(shdr);
840	if (iface == MTP_IFACE_COMM)
841		hdr.seq = sc->sc_seq_comm++;
842	else if (iface == sc->sc_iface_kbd)
843		hdr.seq = sc->sc_seq_kbd++;
844	else if (iface == sc->sc_iface_mt)
845		hdr.seq = sc->sc_seq_mt++;
846	else if (iface == sc->sc_iface_stm)
847		hdr.seq = sc->sc_seq_stm++;
848	hdr.iface = iface;
849	sc->sc_cmd_iface = hdr.iface;
850	sc->sc_cmd_seq = hdr.seq;
851	memset(&shdr, 0, sizeof(shdr));
852	shdr.flags = flags;
853	shdr.len = len;
854	apldchidev_write(sc, &hdr, sizeof(hdr), &checksum);
855	apldchidev_write(sc, &shdr, sizeof(shdr), &checksum);
856	apldchidev_write(sc, data, len & ~3, &checksum);
857	if (len & 3) {
858		memset(pad, 0, sizeof(pad));
859		memcpy(pad, &data[len & ~3], len & 3);
860		apldchidev_write(sc, pad, sizeof(pad), &checksum);
861	}
862	apldchidev_write(sc, &checksum, sizeof(checksum), NULL);
863}
864
865void
866apldchidev_wait(struct apldchidev_softc *sc)
867{
868	int retry, error;
869
870	if (cold) {
871		for (retry = 10; retry > 0; retry--) {
872			if (sc->sc_busy == 0)
873				break;
874			apldchidev_rx_intr(sc);
875			delay(1000);
876		}
877		return;
878	}
879
880	while (sc->sc_busy) {
881		error = tsleep_nsec(sc, PZERO, "apldcwt", SEC_TO_NSEC(1));
882		if (error == EWOULDBLOCK)
883			return;
884	}
885
886	if (sc->sc_retcode) {
887		printf("%s: command failed with error 0x%04x\n",
888		    sc->sc_dev.dv_xname, sc->sc_retcode);
889	}
890}
891
892void
893apldchidev_enable(struct apldchidev_softc *sc, uint8_t iface)
894{
895	uint8_t cmd[2] = { MTP_CMD_ENABLE_INTERFACE, iface };
896	uint8_t flags;
897
898	flags = MTP_GROUP_CMD << MTP_GROUP_SHIFT;
899	flags |= MTP_REQ_SET_REPORT << MTP_REQ_SHIFT;
900	apldchidev_cmd(sc, MTP_IFACE_COMM, flags, cmd, sizeof(cmd));
901	apldchidev_wait(sc);
902}
903
904void
905apldchidev_reset(struct apldchidev_softc *sc, uint8_t iface, uint8_t state)
906{
907	uint8_t cmd[4] = { MTP_CMD_RESET_INTERFACE, 1, iface, state };
908	uint8_t flags;
909
910	flags = MTP_GROUP_CMD << MTP_GROUP_SHIFT;
911	flags |= MTP_REQ_SET_REPORT << MTP_REQ_SHIFT;
912	apldchidev_cmd(sc, MTP_IFACE_COMM, flags, cmd, sizeof(cmd));
913	apldchidev_wait(sc);
914}
915
916#if NAPLDCMS > 0
917
918int
919apldchidev_send_firmware(struct apldchidev_softc *sc, int iface,
920    void *ucode, size_t ucode_size)
921{
922	bus_dmamap_t map;
923	bus_dma_segment_t seg;
924	uint8_t cmd[16] = {};
925	uint64_t addr;
926	uint32_t size;
927	uint8_t flags;
928	caddr_t buf;
929	int nsegs;
930	int error;
931
932	error = bus_dmamap_create(sc->sc_dmat, ucode_size, 1, ucode_size, 0,
933	    BUS_DMA_WAITOK, &map);
934	if (error)
935		return error;
936
937	error = bus_dmamem_alloc(sc->sc_dmat, ucode_size, 4 * PAGE_SIZE, 0,
938	    &seg, 1, &nsegs, BUS_DMA_WAITOK);
939	if (error) {
940		bus_dmamap_destroy(sc->sc_dmat, map);
941		return error;
942	}
943
944	error = bus_dmamem_map(sc->sc_dmat, &seg, 1, ucode_size, &buf,
945	    BUS_DMA_WAITOK);
946	if (error) {
947		bus_dmamem_free(sc->sc_dmat, &seg, 1);
948		bus_dmamap_destroy(sc->sc_dmat, map);
949		return error;
950	}
951
952	error = bus_dmamap_load_raw(sc->sc_dmat, map, &seg, 1,
953	    ucode_size, BUS_DMA_WAITOK);
954	if (error) {
955		bus_dmamem_unmap(sc->sc_dmat, buf, ucode_size);
956		bus_dmamem_free(sc->sc_dmat, &seg, 1);
957		bus_dmamap_destroy(sc->sc_dmat, map);
958		return error;
959	}
960
961	memcpy(buf, ucode, ucode_size);
962	bus_dmamap_sync(sc->sc_dmat, map, 0, ucode_size, BUS_DMASYNC_PREWRITE);
963
964	cmd[0] = MTP_CMD_SEND_FIRMWARE;
965	cmd[1] = 2;
966	cmd[2] = 0;
967	cmd[3] = iface;
968	addr = map->dm_segs[0].ds_addr;
969	memcpy(&cmd[4], &addr, sizeof(addr));
970	size = map->dm_segs[0].ds_len;
971	memcpy(&cmd[12], &size, sizeof(size));
972
973	flags = MTP_GROUP_CMD << MTP_GROUP_SHIFT;
974	flags |= MTP_REQ_SET_REPORT << MTP_REQ_SHIFT;
975	apldchidev_cmd(sc, MTP_IFACE_COMM, flags, cmd, sizeof(cmd));
976	apldchidev_wait(sc);
977
978	bus_dmamap_unload(sc->sc_dmat, map);
979	bus_dmamem_unmap(sc->sc_dmat, buf, ucode_size);
980	bus_dmamem_free(sc->sc_dmat, &seg, 1);
981	bus_dmamap_destroy(sc->sc_dmat, map);
982
983	return 0;
984}
985
986struct mtp_fwhdr {
987	uint32_t magic;
988#define MTP_FW_MAGIC	0x46444948
989	uint32_t version;
990#define MTP_FW_VERSION	1
991	uint32_t hdr_len;
992	uint32_t data_len;
993	uint32_t iface_off;
994};
995
996int
997apldchidev_load_firmware(struct apldchidev_softc *sc, const char *name)
998{
999	struct mtp_fwhdr *hdr;
1000	uint8_t *ucode;
1001	size_t ucode_size;
1002	uint8_t *data;
1003	size_t size;
1004	int error;
1005
1006	error = loadfirmware(name, &ucode, &ucode_size);
1007	if (error) {
1008		printf("%s: error %d, could not read firmware %s\n",
1009		    sc->sc_dev.dv_xname, error, name);
1010		return error;
1011	}
1012
1013	hdr = (struct mtp_fwhdr *)ucode;
1014	if (sizeof(hdr) > ucode_size ||
1015	    hdr->hdr_len + hdr->data_len > ucode_size) {
1016		printf("%s: loaded firmware is too small\n",
1017		    sc->sc_dev.dv_xname);
1018		return EINVAL;
1019	}
1020	if (hdr->magic != MTP_FW_MAGIC) {
1021		printf("%s: wrong firmware magic number 0x%08x\n",
1022		    sc->sc_dev.dv_xname, hdr->magic);
1023		return EINVAL;
1024	}
1025	if (hdr->version != MTP_FW_VERSION) {
1026		printf("%s: wrong firmware version %d\n",
1027		    sc->sc_dev.dv_xname, hdr->version);
1028		return EINVAL;
1029	}
1030	data = ucode + hdr->hdr_len;
1031	if (hdr->iface_off)
1032		data[hdr->iface_off] = sc->sc_iface_mt;
1033	size = hdr->data_len;
1034
1035	apldchidev_send_firmware(sc, sc->sc_iface_mt, data, size);
1036	apldchidev_reset(sc, sc->sc_iface_mt, 0);
1037	apldchidev_reset(sc, sc->sc_iface_mt, 2);
1038
1039	/* Wait until ready. */
1040	while (sc->sc_mt_ready == 0) {
1041		error = tsleep_nsec(sc, PZERO, "apldcmt", SEC_TO_NSEC(2));
1042		if (error == EWOULDBLOCK)
1043			return error;
1044	}
1045
1046	return 0;
1047}
1048
1049void
1050apldchidev_get_dimensions(struct apldchidev_softc *sc)
1051{
1052	uint8_t cmd[1] = { MTP_CMD_GET_DIMENSIONS };
1053	struct mtp_dim dim;
1054	uint8_t flags;
1055
1056	flags = MTP_GROUP_CMD << MTP_GROUP_SHIFT;
1057	flags |= MTP_REQ_GET_REPORT << MTP_REQ_SHIFT;
1058	apldchidev_cmd(sc, sc->sc_iface_mt, flags, cmd, sizeof(cmd));
1059	apldchidev_wait(sc);
1060
1061	if (sc->sc_retcode == 0 && sc->sc_data_len == sizeof(dim) + 1 &&
1062	    sc->sc_data[0] == MTP_CMD_GET_DIMENSIONS) {
1063		memcpy(&dim, &sc->sc_data[1], sizeof(dim));
1064		sc->sc_x_min = dim.x_min;
1065		sc->sc_x_max = dim.x_max;
1066		sc->sc_y_min = dim.y_min;
1067		sc->sc_y_max = dim.y_max;
1068		sc->sc_h_res = (100 * (dim.x_max - dim.x_min)) / dim.width;
1069		sc->sc_v_res = (100 * (dim.y_max - dim.y_min)) / dim.height;
1070	}
1071}
1072
1073void
1074apldchidev_attachhook(struct device *self)
1075{
1076	struct apldchidev_softc *sc = (struct apldchidev_softc *)self;
1077	struct apldchidev_attach_args aa;
1078	char *firmware_name;
1079	int node, len;
1080	int retry;
1081	int error;
1082
1083	/* Enable interface. */
1084	apldchidev_enable(sc, sc->sc_iface_mt);
1085
1086	node = OF_getnodebyname(sc->sc_node, "multi-touch");
1087	if (node == -1)
1088		return;
1089	len = OF_getproplen(node, "firmware-name");
1090	if (len <= 0)
1091		return;
1092
1093	/* Wait until we have received the multi-touch HID descriptor. */
1094	while (sc->sc_mtdesclen == 0) {
1095		error = tsleep_nsec(sc, PZERO, "apldcmt", SEC_TO_NSEC(1));
1096		if (error == EWOULDBLOCK)
1097			return;
1098	}
1099
1100	firmware_name = malloc(len, M_TEMP, M_WAITOK);
1101	OF_getprop(node, "firmware-name", firmware_name, len);
1102
1103	for (retry = 5; retry > 0; retry--) {
1104		error = apldchidev_load_firmware(sc, firmware_name);
1105		if (error != EWOULDBLOCK)
1106			break;
1107	}
1108	if (error)
1109		goto out;
1110
1111	apldchidev_get_dimensions(sc);
1112
1113	aa.aa_name = "multi-touch";
1114	aa.aa_desc = sc->sc_mtdesc;
1115	aa.aa_desclen = sc->sc_mtdesclen;
1116	sc->sc_mt = config_found(self, &aa, NULL);
1117
1118out:
1119	free(firmware_name, M_TEMP, len);
1120}
1121
1122#endif
1123
1124void
1125apldchidev_set_leds(struct apldchidev_softc *sc, uint8_t leds)
1126{
1127	uint8_t report[2] = { 1, leds };
1128	uint8_t flags;
1129
1130	flags = MTP_GROUP_OUTPUT << MTP_GROUP_SHIFT;
1131	flags |= MTP_REQ_SET_REPORT << MTP_REQ_SHIFT;
1132	apldchidev_cmd(sc, sc->sc_iface_kbd, flags, report, sizeof(report));
1133}
1134
1135/* Keyboard */
1136
1137struct apldckbd_softc {
1138	struct device		sc_dev;
1139	struct apldchidev_softc	*sc_hidev;
1140	struct hidkbd		sc_kbd;
1141	int			sc_spl;
1142};
1143
1144void	apldckbd_cngetc(void *, u_int *, int *);
1145void	apldckbd_cnpollc(void *, int);
1146void	apldckbd_cnbell(void *, u_int, u_int, u_int);
1147
1148const struct wskbd_consops apldckbd_consops = {
1149	apldckbd_cngetc,
1150	apldckbd_cnpollc,
1151	apldckbd_cnbell,
1152};
1153
1154int	apldckbd_enable(void *, int);
1155void	apldckbd_set_leds(void *, int);
1156int	apldckbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
1157
1158const struct wskbd_accessops apldckbd_accessops = {
1159	.enable = apldckbd_enable,
1160	.ioctl = apldckbd_ioctl,
1161	.set_leds = apldckbd_set_leds,
1162};
1163
1164int	 apldckbd_match(struct device *, void *, void *);
1165void	 apldckbd_attach(struct device *, struct device *, void *);
1166
1167const struct cfattach apldckbd_ca = {
1168	sizeof(struct apldckbd_softc), apldckbd_match, apldckbd_attach
1169};
1170
1171struct cfdriver apldckbd_cd = {
1172	NULL, "apldckbd", DV_DULL
1173};
1174
1175int
1176apldckbd_match(struct device *parent, void *match, void *aux)
1177{
1178	struct apldchidev_attach_args *aa = aux;
1179
1180	return strcmp(aa->aa_name, "keyboard") == 0;
1181}
1182
1183void
1184apldckbd_attach(struct device *parent, struct device *self, void *aux)
1185{
1186	struct apldckbd_softc *sc = (struct apldckbd_softc *)self;
1187	struct apldchidev_attach_args *aa = aux;
1188	struct hidkbd *kbd = &sc->sc_kbd;
1189
1190#define APLHIDEV_KBD_DEVICE	1
1191	sc->sc_hidev = (struct apldchidev_softc *)parent;
1192	if (hidkbd_attach(self, kbd, 1, 0, APLHIDEV_KBD_DEVICE,
1193	    aa->aa_desc, aa->aa_desclen))
1194		return;
1195
1196	printf("\n");
1197
1198	if (hid_locate(aa->aa_desc, aa->aa_desclen, HID_USAGE2(HUP_APPLE, HUG_FN_KEY),
1199	    1, hid_input, &kbd->sc_fn, NULL))
1200		kbd->sc_munge = hidkbd_apple_munge;
1201
1202	if (kbd->sc_console_keyboard) {
1203		extern struct wskbd_mapdata ukbd_keymapdata;
1204
1205		ukbd_keymapdata.layout = KB_US | KB_DEFAULT;
1206		wskbd_cnattach(&apldckbd_consops, sc, &ukbd_keymapdata);
1207		apldckbd_enable(sc, 1);
1208	}
1209
1210	hidkbd_attach_wskbd(kbd, KB_US | KB_DEFAULT, &apldckbd_accessops);
1211}
1212
1213void
1214apldckbd_intr(struct device *self, uint8_t *packet, size_t packetlen)
1215{
1216	struct apldckbd_softc *sc = (struct apldckbd_softc *)self;
1217	struct hidkbd *kbd = &sc->sc_kbd;
1218
1219	if (kbd->sc_enabled)
1220		hidkbd_input(kbd, &packet[1], packetlen - 1);
1221}
1222
1223int
1224apldckbd_enable(void *v, int on)
1225{
1226	struct apldckbd_softc *sc = v;
1227	struct hidkbd *kbd = &sc->sc_kbd;
1228
1229	return hidkbd_enable(kbd, on);
1230}
1231
1232int
1233apldckbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
1234{
1235	struct apldckbd_softc *sc = v;
1236	struct hidkbd *kbd = &sc->sc_kbd;
1237
1238	switch (cmd) {
1239	case WSKBDIO_GTYPE:
1240		/* XXX: should we set something else? */
1241		*(u_int *)data = WSKBD_TYPE_USB;
1242		return 0;
1243	case WSKBDIO_SETLEDS:
1244		apldckbd_set_leds(v, *(int *)data);
1245		return 0;
1246	default:
1247		return hidkbd_ioctl(kbd, cmd, data, flag, p);
1248	}
1249}
1250
1251void
1252apldckbd_set_leds(void *v, int leds)
1253{
1254	struct apldckbd_softc *sc = v;
1255	struct hidkbd *kbd = &sc->sc_kbd;
1256	uint8_t res;
1257
1258	if (hidkbd_set_leds(kbd, leds, &res))
1259		apldchidev_set_leds(sc->sc_hidev, res);
1260}
1261
1262/* Console interface. */
1263void
1264apldckbd_cngetc(void *v, u_int *type, int *data)
1265{
1266	struct apldckbd_softc *sc = v;
1267	struct hidkbd *kbd = &sc->sc_kbd;
1268
1269	kbd->sc_polling = 1;
1270	while (kbd->sc_npollchar <= 0) {
1271		apldchidev_rx_intr(sc->sc_dev.dv_parent);
1272		delay(1000);
1273	}
1274	kbd->sc_polling = 0;
1275	hidkbd_cngetc(kbd, type, data);
1276}
1277
1278void
1279apldckbd_cnpollc(void *v, int on)
1280{
1281	struct apldckbd_softc *sc = v;
1282
1283	if (on)
1284		sc->sc_spl = spltty();
1285	else
1286		splx(sc->sc_spl);
1287}
1288
1289void
1290apldckbd_cnbell(void *v, u_int pitch, u_int period, u_int volume)
1291{
1292	hidkbd_bell(pitch, period, volume, 1);
1293}
1294
1295#if NAPLDCMS > 0
1296
1297/* Touchpad */
1298
1299/*
1300 * The contents of the touchpad event packets is identical to those
1301 * used by the ubcmtp(4) driver.  The relevant definitions and the
1302 * code to decode the packets is replicated here.
1303 */
1304
1305struct ubcmtp_finger {
1306	uint16_t	origin;
1307	uint16_t	abs_x;
1308	uint16_t	abs_y;
1309	uint16_t	rel_x;
1310	uint16_t	rel_y;
1311	uint16_t	tool_major;
1312	uint16_t	tool_minor;
1313	uint16_t	orientation;
1314	uint16_t	touch_major;
1315	uint16_t	touch_minor;
1316	uint16_t	unused[2];
1317	uint16_t	pressure;
1318	uint16_t	multi;
1319} __packed __attribute((aligned(2)));
1320
1321#define UBCMTP_MAX_FINGERS	16
1322
1323#define UBCMTP_TYPE4_TPOFF	(20 * sizeof(uint16_t))
1324#define UBCMTP_TYPE4_BTOFF	23
1325#define UBCMTP_TYPE4_FINGERPAD	(1 * sizeof(uint16_t))
1326
1327/* Use a constant, synaptics-compatible pressure value for now. */
1328#define DEFAULT_PRESSURE	40
1329
1330struct apldcms_softc {
1331	struct device		sc_dev;
1332	struct apldchidev_softc	*sc_hidev;
1333	struct device		*sc_wsmousedev;
1334
1335	int			sc_enabled;
1336
1337	int			tp_offset;
1338	int			tp_fingerpad;
1339
1340	struct mtpoint		frame[UBCMTP_MAX_FINGERS];
1341	int			contacts;
1342	int			btn;
1343};
1344
1345int	apldcms_enable(void *);
1346void	apldcms_disable(void *);
1347int	apldcms_ioctl(void *, u_long, caddr_t, int, struct proc *);
1348
1349static struct wsmouse_param apldcms_wsmousecfg[] = {
1350	{ WSMOUSECFG_MTBTN_MAXDIST, 0 }, /* 0: Compute a default value. */
1351};
1352
1353const struct wsmouse_accessops apldcms_accessops = {
1354	.enable = apldcms_enable,
1355	.disable = apldcms_disable,
1356	.ioctl = apldcms_ioctl,
1357};
1358
1359int	 apldcms_match(struct device *, void *, void *);
1360void	 apldcms_attach(struct device *, struct device *, void *);
1361
1362const struct cfattach apldcms_ca = {
1363	sizeof(struct apldcms_softc), apldcms_match, apldcms_attach
1364};
1365
1366struct cfdriver apldcms_cd = {
1367	NULL, "apldcms", DV_DULL
1368};
1369
1370int	apldcms_configure(struct apldcms_softc *);
1371
1372int
1373apldcms_match(struct device *parent, void *match, void *aux)
1374{
1375	struct apldchidev_attach_args *aa = aux;
1376
1377	return strcmp(aa->aa_name, "multi-touch") == 0;
1378}
1379
1380void
1381apldcms_attach(struct device *parent, struct device *self, void *aux)
1382{
1383	struct apldcms_softc *sc = (struct apldcms_softc *)self;
1384	struct wsmousedev_attach_args aa;
1385
1386	sc->sc_hidev = (struct apldchidev_softc *)parent;
1387
1388	printf("\n");
1389
1390	sc->tp_offset = UBCMTP_TYPE4_TPOFF;
1391	sc->tp_fingerpad = UBCMTP_TYPE4_FINGERPAD;
1392
1393	aa.accessops = &apldcms_accessops;
1394	aa.accesscookie = sc;
1395
1396	sc->sc_wsmousedev = config_found(self, &aa, wsmousedevprint);
1397	if (sc->sc_wsmousedev != NULL && apldcms_configure(sc))
1398		apldcms_disable(sc);
1399}
1400
1401int
1402apldcms_configure(struct apldcms_softc *sc)
1403{
1404	struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev);
1405
1406	hw->type = WSMOUSE_TYPE_TOUCHPAD;
1407	hw->hw_type = WSMOUSEHW_CLICKPAD;
1408	hw->x_min = sc->sc_hidev->sc_x_min;
1409	hw->x_max = sc->sc_hidev->sc_x_max;
1410	hw->y_min = sc->sc_hidev->sc_y_min;
1411	hw->y_max = sc->sc_hidev->sc_y_max;
1412	hw->h_res = sc->sc_hidev->sc_h_res;
1413	hw->v_res = sc->sc_hidev->sc_v_res;
1414	hw->mt_slots = UBCMTP_MAX_FINGERS;
1415	hw->flags = WSMOUSEHW_MT_TRACKING;
1416
1417	return wsmouse_configure(sc->sc_wsmousedev, apldcms_wsmousecfg,
1418	    nitems(apldcms_wsmousecfg));
1419}
1420
1421void
1422apldcms_intr(struct device *self, uint8_t *packet, size_t packetlen)
1423{
1424	struct apldcms_softc *sc = (struct apldcms_softc *)self;
1425	struct ubcmtp_finger *finger;
1426	int off, s, btn, contacts;
1427
1428	if (!sc->sc_enabled)
1429		return;
1430
1431	contacts = 0;
1432	for (off = sc->tp_offset; off < packetlen;
1433	    off += (sizeof(struct ubcmtp_finger) + sc->tp_fingerpad)) {
1434		finger = (struct ubcmtp_finger *)(packet + off);
1435
1436		if ((int16_t)letoh16(finger->touch_major) == 0)
1437			continue; /* finger lifted */
1438
1439		sc->frame[contacts].x = (int16_t)letoh16(finger->abs_x);
1440		sc->frame[contacts].y = (int16_t)letoh16(finger->abs_y);
1441		sc->frame[contacts].pressure = DEFAULT_PRESSURE;
1442		contacts++;
1443	}
1444
1445	btn = sc->btn;
1446	sc->btn = !!((int16_t)letoh16(packet[UBCMTP_TYPE4_BTOFF]));
1447
1448	if (contacts || sc->contacts || sc->btn != btn) {
1449		sc->contacts = contacts;
1450		s = spltty();
1451		wsmouse_buttons(sc->sc_wsmousedev, sc->btn);
1452		wsmouse_mtframe(sc->sc_wsmousedev, sc->frame, contacts);
1453		wsmouse_input_sync(sc->sc_wsmousedev);
1454		splx(s);
1455	}
1456}
1457
1458int
1459apldcms_enable(void *v)
1460{
1461	struct apldcms_softc *sc = v;
1462
1463	if (sc->sc_enabled)
1464		return EBUSY;
1465
1466	sc->sc_enabled = 1;
1467	return 0;
1468}
1469
1470void
1471apldcms_disable(void *v)
1472{
1473	struct apldcms_softc *sc = v;
1474
1475	sc->sc_enabled = 0;
1476}
1477
1478int
1479apldcms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
1480{
1481	struct apldcms_softc *sc = v;
1482	struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev);
1483	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
1484	int wsmode;
1485
1486	switch (cmd) {
1487	case WSMOUSEIO_GTYPE:
1488		*(u_int *)data = hw->type;
1489		break;
1490
1491	case WSMOUSEIO_GCALIBCOORDS:
1492		wsmc->minx = hw->x_min;
1493		wsmc->maxx = hw->x_max;
1494		wsmc->miny = hw->y_min;
1495		wsmc->maxy = hw->y_max;
1496		wsmc->swapxy = 0;
1497		wsmc->resx = 0;
1498		wsmc->resy = 0;
1499		break;
1500
1501	case WSMOUSEIO_SETMODE:
1502		wsmode = *(u_int *)data;
1503		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) {
1504			printf("%s: invalid mode %d\n", sc->sc_dev.dv_xname,
1505			    wsmode);
1506			return (EINVAL);
1507		}
1508		wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
1509		break;
1510
1511	default:
1512		return -1;
1513	}
1514
1515	return 0;
1516}
1517
1518#else
1519
1520void
1521apldcms_intr(struct device *self, uint8_t *packet, size_t packetlen)
1522{
1523}
1524
1525#endif
1526