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