xref: /openbsd-src/sys/arch/arm64/dev/aplhidev.c (revision 3082766e306c41bc22cab340f6eabcdce47f3f30)
1*3082766eSkettenis /*	$OpenBSD: aplhidev.c,v 1.13 2024/01/15 13:27:20 kettenis Exp $	*/
2abd92e8eSkettenis /*
3abd92e8eSkettenis  * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
459cc6b22Skettenis  * Copyright (c) 2013-2014 joshua stein <jcs@openbsd.org>
5abd92e8eSkettenis  *
6abd92e8eSkettenis  * Permission to use, copy, modify, and distribute this software for any
7abd92e8eSkettenis  * purpose with or without fee is hereby granted, provided that the above
8abd92e8eSkettenis  * copyright notice and this permission notice appear in all copies.
9abd92e8eSkettenis  *
10abd92e8eSkettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11abd92e8eSkettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12abd92e8eSkettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13abd92e8eSkettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14abd92e8eSkettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15abd92e8eSkettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16abd92e8eSkettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17abd92e8eSkettenis  */
18abd92e8eSkettenis 
19abd92e8eSkettenis #include <sys/param.h>
20abd92e8eSkettenis #include <sys/systm.h>
21abd92e8eSkettenis #include <sys/kernel.h>
22abd92e8eSkettenis #include <sys/device.h>
23abd92e8eSkettenis #include <sys/malloc.h>
24abd92e8eSkettenis #include <sys/timeout.h>
25abd92e8eSkettenis 
26abd92e8eSkettenis #include <lib/libkern/crc16.h>
27abd92e8eSkettenis 
28abd92e8eSkettenis #include <machine/fdt.h>
29abd92e8eSkettenis 
30abd92e8eSkettenis #include <dev/spi/spivar.h>
31abd92e8eSkettenis 
32abd92e8eSkettenis #include <dev/ofw/openfirm.h>
33abd92e8eSkettenis #include <dev/ofw/ofw_gpio.h>
34abd92e8eSkettenis #include <dev/ofw/ofw_pinctrl.h>
35abd92e8eSkettenis 
369de6d7f5Srobert #include <dev/usb/usbdevs.h>
379de6d7f5Srobert 
38abd92e8eSkettenis #include <dev/wscons/wsconsio.h>
39abd92e8eSkettenis #include <dev/wscons/wskbdvar.h>
40abd92e8eSkettenis #include <dev/wscons/wsksymdef.h>
41abd92e8eSkettenis #include <dev/wscons/wsmousevar.h>
42abd92e8eSkettenis 
43abd92e8eSkettenis #include <dev/hid/hid.h>
44abd92e8eSkettenis #include <dev/hid/hidkbdsc.h>
45abd92e8eSkettenis #include <dev/hid/hidmsvar.h>
46abd92e8eSkettenis 
47abd92e8eSkettenis #include "aplhidev.h"
48abd92e8eSkettenis 
49abd92e8eSkettenis #define APLHIDEV_READ_PACKET	0x20
50abd92e8eSkettenis #define APLHIDEV_WRITE_PACKET	0x40
51abd92e8eSkettenis 
52abd92e8eSkettenis #define APLHIDEV_KBD_DEVICE	1
53abd92e8eSkettenis #define APLHIDEV_TP_DEVICE	2
54abd92e8eSkettenis #define APLHIDEV_INFO_DEVICE	208
55abd92e8eSkettenis 
565db73e29Skettenis #define APLHIDEV_GET_INFO	0x0120
57abd92e8eSkettenis #define APLHIDEV_GET_DESCRIPTOR	0x1020
58abd92e8eSkettenis #define  APLHIDEV_DESC_MAX	512
59abd92e8eSkettenis #define APLHIDEV_KBD_REPORT	0x0110
60abd92e8eSkettenis #define APLHIDEV_TP_REPORT	0x0210
61269321aeSkettenis #define APLHIDEV_SET_LEDS	0x0151
6259cc6b22Skettenis #define APLHIDEV_SET_MODE	0x0252
6359cc6b22Skettenis #define  APLHIDEV_MODE_HID	0x00
6459cc6b22Skettenis #define  APLHIDEV_MODE_RAW	0x01
65*3082766eSkettenis #define APLHIDEV_GET_DIMENSIONS	0xd932
66*3082766eSkettenis 
67*3082766eSkettenis struct aplhidev_dim {
68*3082766eSkettenis 	uint32_t width;
69*3082766eSkettenis 	uint32_t height;
70*3082766eSkettenis 	int16_t x_min;
71*3082766eSkettenis 	int16_t y_min;
72*3082766eSkettenis 	int16_t x_max;
73*3082766eSkettenis 	int16_t y_max;
74*3082766eSkettenis };
75abd92e8eSkettenis 
76abd92e8eSkettenis struct aplhidev_attach_args {
77abd92e8eSkettenis 	uint8_t	aa_reportid;
78abd92e8eSkettenis 	void	*aa_desc;
79abd92e8eSkettenis 	size_t	aa_desclen;
80abd92e8eSkettenis };
81abd92e8eSkettenis 
82abd92e8eSkettenis struct aplhidev_spi_packet {
83abd92e8eSkettenis 	uint8_t		flags;
84abd92e8eSkettenis 	uint8_t		device;
85abd92e8eSkettenis 	uint16_t	offset;
86abd92e8eSkettenis 	uint16_t	remaining;
87abd92e8eSkettenis 	uint16_t	len;
88abd92e8eSkettenis 	uint8_t		data[246];
89abd92e8eSkettenis 	uint16_t	crc;
90abd92e8eSkettenis };
91abd92e8eSkettenis 
92abd92e8eSkettenis struct aplhidev_spi_status {
93abd92e8eSkettenis 	uint8_t		status[4];
94abd92e8eSkettenis };
95abd92e8eSkettenis 
96abd92e8eSkettenis struct aplhidev_msghdr {
97abd92e8eSkettenis 	uint16_t	type;
98abd92e8eSkettenis 	uint8_t		device;
99abd92e8eSkettenis 	uint8_t		msgid;
100abd92e8eSkettenis 	uint16_t	rsplen;
101abd92e8eSkettenis 	uint16_t	cmdlen;
102abd92e8eSkettenis };
103abd92e8eSkettenis 
1045db73e29Skettenis struct aplhidev_info_hdr {
1055db73e29Skettenis 	uint16_t	unknown[2];
1065db73e29Skettenis 	uint16_t	num_devices;
1075db73e29Skettenis 	uint16_t	vendor;
1085db73e29Skettenis 	uint16_t	product;
1095db73e29Skettenis 	uint16_t	version;
1105db73e29Skettenis 	uint16_t	vendor_str[2];
1115db73e29Skettenis 	uint16_t	product_str[2];
1125db73e29Skettenis 	uint16_t	serial_str[2];
1135db73e29Skettenis };
1145db73e29Skettenis 
115abd92e8eSkettenis struct aplhidev_get_desc {
116abd92e8eSkettenis 	struct aplhidev_msghdr	hdr;
117abd92e8eSkettenis 	uint16_t		crc;
118abd92e8eSkettenis };
119abd92e8eSkettenis 
120269321aeSkettenis struct aplhidev_set_leds {
121269321aeSkettenis 	struct aplhidev_msghdr	hdr;
122269321aeSkettenis 	uint8_t			reportid;
123269321aeSkettenis 	uint8_t			leds;
124269321aeSkettenis 	uint16_t		crc;
125269321aeSkettenis };
126269321aeSkettenis 
12759cc6b22Skettenis struct aplhidev_set_mode {
12859cc6b22Skettenis 	struct aplhidev_msghdr	hdr;
12959cc6b22Skettenis 	uint8_t			reportid;
13059cc6b22Skettenis 	uint8_t			mode;
13159cc6b22Skettenis 	uint16_t		crc;
13259cc6b22Skettenis };
13359cc6b22Skettenis 
134abd92e8eSkettenis struct aplhidev_softc {
135abd92e8eSkettenis 	struct device		sc_dev;
136abd92e8eSkettenis 	int			sc_node;
137abd92e8eSkettenis 
138abd92e8eSkettenis 	spi_tag_t		sc_spi_tag;
139abd92e8eSkettenis 	struct spi_config	sc_spi_conf;
140abd92e8eSkettenis 
141abd92e8eSkettenis 	uint8_t			sc_msgid;
142abd92e8eSkettenis 
143abd92e8eSkettenis 	uint32_t		*sc_gpio;
14436defc4bSjsg 	int			sc_gpiolen;
145abd92e8eSkettenis 
1464aaec261Skettenis 	uint8_t			sc_mode;
1475db73e29Skettenis 	uint16_t		sc_vendor;
1485db73e29Skettenis 	uint16_t		sc_product;
1495db73e29Skettenis 
150abd92e8eSkettenis 	struct device 		*sc_kbd;
151abd92e8eSkettenis 	uint8_t			sc_kbddesc[APLHIDEV_DESC_MAX];
152abd92e8eSkettenis 	size_t			sc_kbddesclen;
153abd92e8eSkettenis 
154abd92e8eSkettenis 	struct device		*sc_ms;
155abd92e8eSkettenis 	uint8_t			sc_tpdesc[APLHIDEV_DESC_MAX];
156abd92e8eSkettenis 	size_t			sc_tpdesclen;
157*3082766eSkettenis 	uint8_t			sc_dimdesc[APLHIDEV_DESC_MAX];
158*3082766eSkettenis 	size_t			sc_dimdesclen;
159*3082766eSkettenis 	int			sc_x_min;
160*3082766eSkettenis 	int			sc_x_max;
161*3082766eSkettenis 	int			sc_y_min;
162*3082766eSkettenis 	int			sc_y_max;
163*3082766eSkettenis 	int			sc_h_res;
164*3082766eSkettenis 	int			sc_v_res;
165abd92e8eSkettenis };
166abd92e8eSkettenis 
167abd92e8eSkettenis int	 aplhidev_match(struct device *, void *, void *);
168abd92e8eSkettenis void	 aplhidev_attach(struct device *, struct device *, void *);
169abd92e8eSkettenis 
170471aeecfSnaddy const struct cfattach aplhidev_ca = {
171abd92e8eSkettenis 	sizeof(struct aplhidev_softc), aplhidev_match, aplhidev_attach
172abd92e8eSkettenis };
173abd92e8eSkettenis 
174abd92e8eSkettenis struct cfdriver aplhidev_cd = {
175abd92e8eSkettenis 	NULL, "aplhidev", DV_DULL
176abd92e8eSkettenis };
177abd92e8eSkettenis 
1785db73e29Skettenis void	aplhidev_get_info(struct aplhidev_softc *);
179abd92e8eSkettenis void	aplhidev_get_descriptor(struct aplhidev_softc *, uint8_t);
180269321aeSkettenis void	aplhidev_set_leds(struct aplhidev_softc *, uint8_t);
18159cc6b22Skettenis void	aplhidev_set_mode(struct aplhidev_softc *, uint8_t);
182*3082766eSkettenis void	aplhidev_get_dimensions(struct aplhidev_softc *);
183abd92e8eSkettenis 
184abd92e8eSkettenis int	aplhidev_intr(void *);
18559cc6b22Skettenis void	aplkbd_intr(struct device *, uint8_t *, size_t);
18659cc6b22Skettenis void	aplms_intr(struct device *, uint8_t *, size_t);
187abd92e8eSkettenis 
188abd92e8eSkettenis int
aplhidev_match(struct device * parent,void * match,void * aux)189abd92e8eSkettenis aplhidev_match(struct device *parent, void *match, void *aux)
190abd92e8eSkettenis {
191abd92e8eSkettenis 	struct spi_attach_args *sa = aux;
192abd92e8eSkettenis 
193992fbcd1Skettenis 	if (strcmp(sa->sa_name, "apple,spi-hid-transport") == 0)
194abd92e8eSkettenis 		return 1;
195abd92e8eSkettenis 
196abd92e8eSkettenis 	return 0;
197abd92e8eSkettenis }
198abd92e8eSkettenis 
199abd92e8eSkettenis void
aplhidev_attach(struct device * parent,struct device * self,void * aux)200abd92e8eSkettenis aplhidev_attach(struct device *parent, struct device *self, void *aux)
201abd92e8eSkettenis {
202abd92e8eSkettenis 	struct aplhidev_softc *sc = (struct aplhidev_softc *)self;
203abd92e8eSkettenis 	struct spi_attach_args *sa = aux;
204abd92e8eSkettenis 	struct aplhidev_attach_args aa;
205*3082766eSkettenis 	struct aplhidev_dim dim;
206abd92e8eSkettenis 	int retry;
207abd92e8eSkettenis 
208abd92e8eSkettenis 	sc->sc_spi_tag = sa->sa_tag;
209abd92e8eSkettenis 	sc->sc_node = *(int *)sa->sa_cookie;
210abd92e8eSkettenis 
211abd92e8eSkettenis 	sc->sc_gpiolen = OF_getproplen(sc->sc_node, "spien-gpios");
212abd92e8eSkettenis 	if (sc->sc_gpiolen > 0) {
213abd92e8eSkettenis 		sc->sc_gpio = malloc(sc->sc_gpiolen, M_TEMP, M_WAITOK);
214abd92e8eSkettenis 		OF_getpropintarray(sc->sc_node, "spien-gpios",
215abd92e8eSkettenis 		    sc->sc_gpio, sc->sc_gpiolen);
216abd92e8eSkettenis 		gpio_controller_config_pin(sc->sc_gpio, GPIO_CONFIG_OUTPUT);
217abd92e8eSkettenis 
218abd92e8eSkettenis 		/* Reset */
219abd92e8eSkettenis 		gpio_controller_set_pin(sc->sc_gpio, 1);
220abd92e8eSkettenis 		delay(5000);
221abd92e8eSkettenis 		gpio_controller_set_pin(sc->sc_gpio, 0);
222abd92e8eSkettenis 		delay(5000);
223abd92e8eSkettenis 
224abd92e8eSkettenis 		/* Enable. */
225abd92e8eSkettenis 		gpio_controller_set_pin(sc->sc_gpio, 1);
226abd92e8eSkettenis 		delay(50000);
227abd92e8eSkettenis 	}
228abd92e8eSkettenis 
229abd92e8eSkettenis 	sc->sc_spi_conf.sc_bpw = 8;
230abd92e8eSkettenis 	sc->sc_spi_conf.sc_freq = OF_getpropint(sc->sc_node,
231abd92e8eSkettenis 	    "spi-max-frequency", 0);
232abd92e8eSkettenis 	sc->sc_spi_conf.sc_cs = OF_getpropint(sc->sc_node, "reg", 0);
233abd92e8eSkettenis 	sc->sc_spi_conf.sc_cs_delay = 100;
234abd92e8eSkettenis 
235abd92e8eSkettenis 	fdt_intr_establish(sc->sc_node, IPL_TTY,
236abd92e8eSkettenis 	    aplhidev_intr, sc, sc->sc_dev.dv_xname);
237abd92e8eSkettenis 
2385db73e29Skettenis 	aplhidev_get_info(sc);
2395db73e29Skettenis 	for (retry = 10; retry > 0; retry--) {
2405db73e29Skettenis 		aplhidev_intr(sc);
2415db73e29Skettenis 		delay(1000);
2425db73e29Skettenis 		if (sc->sc_vendor != 0 && sc->sc_product != 0)
2435db73e29Skettenis 			break;
2445db73e29Skettenis 	}
2455db73e29Skettenis 
246abd92e8eSkettenis 	aplhidev_get_descriptor(sc, APLHIDEV_KBD_DEVICE);
247abd92e8eSkettenis 	for (retry = 10; retry > 0; retry--) {
248abd92e8eSkettenis 		aplhidev_intr(sc);
249abd92e8eSkettenis 		delay(1000);
250abd92e8eSkettenis 		if (sc->sc_kbddesclen > 0)
251abd92e8eSkettenis 			break;
252abd92e8eSkettenis 	}
253abd92e8eSkettenis 
254abd92e8eSkettenis 	aplhidev_get_descriptor(sc, APLHIDEV_TP_DEVICE);
255abd92e8eSkettenis 	for (retry = 10; retry > 0; retry--) {
256abd92e8eSkettenis 		aplhidev_intr(sc);
257abd92e8eSkettenis 		delay(1000);
258abd92e8eSkettenis 		if (sc->sc_tpdesclen > 0)
259abd92e8eSkettenis 			break;
260abd92e8eSkettenis 	}
261abd92e8eSkettenis 
2624aaec261Skettenis 	sc->sc_mode = APLHIDEV_MODE_HID;
26359cc6b22Skettenis 	aplhidev_set_mode(sc, APLHIDEV_MODE_RAW);
2644aaec261Skettenis 	for (retry = 10; retry > 0; retry--) {
2654aaec261Skettenis 		aplhidev_intr(sc);
2664aaec261Skettenis 		delay(1000);
2674aaec261Skettenis 		if (sc->sc_mode == APLHIDEV_MODE_RAW)
2684aaec261Skettenis 			break;
2694aaec261Skettenis 	}
27059cc6b22Skettenis 
271*3082766eSkettenis 	aplhidev_get_dimensions(sc);
272*3082766eSkettenis 	for (retry = 10; retry > 0; retry--) {
273*3082766eSkettenis 		aplhidev_intr(sc);
274*3082766eSkettenis 		delay(1000);
275*3082766eSkettenis 		if (sc->sc_dimdesclen > 0)
276*3082766eSkettenis 			break;
277*3082766eSkettenis 	}
278*3082766eSkettenis 
279abd92e8eSkettenis 	printf("\n");
280abd92e8eSkettenis 
281*3082766eSkettenis 	if (sc->sc_dimdesclen == sizeof(dim) + 1) {
282*3082766eSkettenis 		memcpy(&dim, &sc->sc_dimdesc[1], sizeof(dim));
283*3082766eSkettenis 		sc->sc_x_min = dim.x_min;
284*3082766eSkettenis 		sc->sc_x_max = dim.x_max;
285*3082766eSkettenis 		sc->sc_y_min = dim.y_min;
286*3082766eSkettenis 		sc->sc_y_max = dim.y_max;
287*3082766eSkettenis 		sc->sc_h_res = (100 * (dim.x_max - dim.x_min)) / dim.width;
288*3082766eSkettenis 		sc->sc_v_res = (100 * (dim.y_max - dim.y_min)) / dim.height;
289*3082766eSkettenis 	}
290*3082766eSkettenis 
291abd92e8eSkettenis 	if (sc->sc_kbddesclen > 0) {
292abd92e8eSkettenis 		aa.aa_reportid = APLHIDEV_KBD_DEVICE;
293abd92e8eSkettenis 		aa.aa_desc = sc->sc_kbddesc;
294abd92e8eSkettenis 		aa.aa_desclen = sc->sc_kbddesclen;
295abd92e8eSkettenis 		sc->sc_kbd = config_found(self, &aa, NULL);
296abd92e8eSkettenis 	}
297abd92e8eSkettenis 
298abd92e8eSkettenis 	if (sc->sc_tpdesclen > 0) {
299abd92e8eSkettenis 		aa.aa_reportid = APLHIDEV_TP_DEVICE;
300abd92e8eSkettenis 		aa.aa_desc = sc->sc_tpdesc;
301abd92e8eSkettenis 		aa.aa_desclen = sc->sc_tpdesclen;
302abd92e8eSkettenis 		sc->sc_ms = config_found(self, &aa, NULL);
303abd92e8eSkettenis 	}
304abd92e8eSkettenis }
305abd92e8eSkettenis 
306abd92e8eSkettenis void
aplhidev_get_info(struct aplhidev_softc * sc)3075db73e29Skettenis aplhidev_get_info(struct aplhidev_softc *sc)
3085db73e29Skettenis {
3095db73e29Skettenis 	struct aplhidev_spi_packet packet;
3105db73e29Skettenis 	struct aplhidev_get_desc *msg;
3115db73e29Skettenis 	struct aplhidev_spi_status status;
3125db73e29Skettenis 
3135db73e29Skettenis 	memset(&packet, 0, sizeof(packet));
3145db73e29Skettenis 	packet.flags = APLHIDEV_WRITE_PACKET;
3155db73e29Skettenis 	packet.device = APLHIDEV_INFO_DEVICE;
3165db73e29Skettenis 	packet.len = sizeof(*msg);
3175db73e29Skettenis 
3185db73e29Skettenis 	msg = (void *)&packet.data[0];
3195db73e29Skettenis 	msg->hdr.type = APLHIDEV_GET_INFO;
3205db73e29Skettenis 	msg->hdr.device = APLHIDEV_INFO_DEVICE;
3215db73e29Skettenis 	msg->hdr.msgid = sc->sc_msgid++;
3225db73e29Skettenis 	msg->hdr.cmdlen = 0;
3235db73e29Skettenis 	msg->hdr.rsplen = APLHIDEV_DESC_MAX;
3245db73e29Skettenis 	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
3255db73e29Skettenis 
3265db73e29Skettenis 	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
3275db73e29Skettenis 
3285db73e29Skettenis 	spi_acquire_bus(sc->sc_spi_tag, 0);
3295db73e29Skettenis 	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
3305db73e29Skettenis 	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
3315db73e29Skettenis 	    SPI_KEEP_CS);
3325db73e29Skettenis 	delay(100);
3335db73e29Skettenis 	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
3345db73e29Skettenis 	spi_release_bus(sc->sc_spi_tag, 0);
3355db73e29Skettenis 
3365db73e29Skettenis 	delay(1000);
3375db73e29Skettenis }
3385db73e29Skettenis 
3395db73e29Skettenis void
aplhidev_get_descriptor(struct aplhidev_softc * sc,uint8_t device)340abd92e8eSkettenis aplhidev_get_descriptor(struct aplhidev_softc *sc, uint8_t device)
341abd92e8eSkettenis {
342abd92e8eSkettenis 	struct aplhidev_spi_packet packet;
343abd92e8eSkettenis 	struct aplhidev_get_desc *msg;
344abd92e8eSkettenis 	struct aplhidev_spi_status status;
345abd92e8eSkettenis 
346abd92e8eSkettenis 	memset(&packet, 0, sizeof(packet));
347abd92e8eSkettenis 	packet.flags = APLHIDEV_WRITE_PACKET;
348abd92e8eSkettenis 	packet.device = APLHIDEV_INFO_DEVICE;
349abd92e8eSkettenis 	packet.len = sizeof(*msg);
350abd92e8eSkettenis 
351abd92e8eSkettenis 	msg = (void *)&packet.data[0];
352abd92e8eSkettenis 	msg->hdr.type = APLHIDEV_GET_DESCRIPTOR;
353abd92e8eSkettenis 	msg->hdr.device = device;
354abd92e8eSkettenis 	msg->hdr.msgid = sc->sc_msgid++;
355abd92e8eSkettenis 	msg->hdr.cmdlen = 0;
356abd92e8eSkettenis 	msg->hdr.rsplen = APLHIDEV_DESC_MAX;
357abd92e8eSkettenis 	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
358abd92e8eSkettenis 
359abd92e8eSkettenis 	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
360abd92e8eSkettenis 
361abd92e8eSkettenis 	spi_acquire_bus(sc->sc_spi_tag, 0);
362abd92e8eSkettenis 	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
363abd92e8eSkettenis 	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
364abd92e8eSkettenis 	    SPI_KEEP_CS);
365abd92e8eSkettenis 	delay(100);
366abd92e8eSkettenis 	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
367abd92e8eSkettenis 	spi_release_bus(sc->sc_spi_tag, 0);
368abd92e8eSkettenis 
369abd92e8eSkettenis 	delay(1000);
370abd92e8eSkettenis }
371abd92e8eSkettenis 
372269321aeSkettenis void
aplhidev_set_leds(struct aplhidev_softc * sc,uint8_t leds)373269321aeSkettenis aplhidev_set_leds(struct aplhidev_softc *sc, uint8_t leds)
374269321aeSkettenis {
375269321aeSkettenis 	struct aplhidev_spi_packet packet;
376269321aeSkettenis 	struct aplhidev_set_leds *msg;
377269321aeSkettenis 	struct aplhidev_spi_status status;
378269321aeSkettenis 
379269321aeSkettenis 	memset(&packet, 0, sizeof(packet));
380269321aeSkettenis 	packet.flags = APLHIDEV_WRITE_PACKET;
381269321aeSkettenis 	packet.device = APLHIDEV_KBD_DEVICE;
382269321aeSkettenis 	packet.len = sizeof(*msg);
383269321aeSkettenis 
384269321aeSkettenis 	msg = (void *)&packet.data[0];
385269321aeSkettenis 	msg->hdr.type = APLHIDEV_SET_LEDS;
386269321aeSkettenis 	msg->hdr.device = APLHIDEV_KBD_DEVICE;
387269321aeSkettenis 	msg->hdr.msgid = sc->sc_msgid++;
388269321aeSkettenis 	msg->hdr.cmdlen = sizeof(*msg) - sizeof(struct aplhidev_msghdr) - 2;
389269321aeSkettenis 	msg->hdr.rsplen = msg->hdr.cmdlen;
390269321aeSkettenis 	msg->reportid = APLHIDEV_KBD_DEVICE;
391269321aeSkettenis 	msg->leds = leds;
392269321aeSkettenis 	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
393269321aeSkettenis 
394269321aeSkettenis 	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
395269321aeSkettenis 
396269321aeSkettenis 	/*
397269321aeSkettenis 	 * XXX Without a delay here, the command will fail.  Does the
398269321aeSkettenis 	 * controller need a bit of time between sending us a keypress
399269321aeSkettenis 	 * event and accepting a new command from us?
400269321aeSkettenis 	 */
401269321aeSkettenis 	delay(250);
402269321aeSkettenis 
403269321aeSkettenis 	spi_acquire_bus(sc->sc_spi_tag, 0);
404269321aeSkettenis 	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
405269321aeSkettenis 	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
406269321aeSkettenis 	    SPI_KEEP_CS);
407269321aeSkettenis 	delay(100);
408269321aeSkettenis 	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
409269321aeSkettenis 	spi_release_bus(sc->sc_spi_tag, 0);
410269321aeSkettenis }
411269321aeSkettenis 
41259cc6b22Skettenis void
aplhidev_set_mode(struct aplhidev_softc * sc,uint8_t mode)41359cc6b22Skettenis aplhidev_set_mode(struct aplhidev_softc *sc, uint8_t mode)
41459cc6b22Skettenis {
41559cc6b22Skettenis 	struct aplhidev_spi_packet packet;
41659cc6b22Skettenis 	struct aplhidev_set_mode *msg;
41759cc6b22Skettenis 	struct aplhidev_spi_status status;
41859cc6b22Skettenis 
41959cc6b22Skettenis 	memset(&packet, 0, sizeof(packet));
42059cc6b22Skettenis 	packet.flags = APLHIDEV_WRITE_PACKET;
42159cc6b22Skettenis 	packet.device = APLHIDEV_TP_DEVICE;
42259cc6b22Skettenis 	packet.len = sizeof(*msg);
42359cc6b22Skettenis 
42459cc6b22Skettenis 	msg = (void *)&packet.data[0];
42559cc6b22Skettenis 	msg->hdr.type = APLHIDEV_SET_MODE;
42659cc6b22Skettenis 	msg->hdr.device = APLHIDEV_TP_DEVICE;
42759cc6b22Skettenis 	msg->hdr.msgid = sc->sc_msgid++;
42859cc6b22Skettenis 	msg->hdr.cmdlen = sizeof(*msg) - sizeof(struct aplhidev_msghdr) - 2;
42959cc6b22Skettenis 	msg->hdr.rsplen = msg->hdr.cmdlen;
43059cc6b22Skettenis 	msg->reportid = APLHIDEV_TP_DEVICE;
43159cc6b22Skettenis 	msg->mode = mode;
43259cc6b22Skettenis 	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
43359cc6b22Skettenis 
43459cc6b22Skettenis 	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
43559cc6b22Skettenis 
43659cc6b22Skettenis 	spi_acquire_bus(sc->sc_spi_tag, 0);
43759cc6b22Skettenis 	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
43859cc6b22Skettenis 	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
43959cc6b22Skettenis 	    SPI_KEEP_CS);
44059cc6b22Skettenis 	delay(100);
44159cc6b22Skettenis 	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
44259cc6b22Skettenis 	spi_release_bus(sc->sc_spi_tag, 0);
4434aaec261Skettenis 
4444aaec261Skettenis 	delay(1000);
44559cc6b22Skettenis }
44659cc6b22Skettenis 
447*3082766eSkettenis void
aplhidev_get_dimensions(struct aplhidev_softc * sc)448*3082766eSkettenis aplhidev_get_dimensions(struct aplhidev_softc *sc)
449*3082766eSkettenis {
450*3082766eSkettenis 	struct aplhidev_spi_packet packet;
451*3082766eSkettenis 	struct aplhidev_get_desc *msg;
452*3082766eSkettenis 	struct aplhidev_spi_status status;
453*3082766eSkettenis 
454*3082766eSkettenis 	memset(&packet, 0, sizeof(packet));
455*3082766eSkettenis 	packet.flags = APLHIDEV_WRITE_PACKET;
456*3082766eSkettenis 	packet.device = APLHIDEV_TP_DEVICE;
457*3082766eSkettenis 	packet.len = sizeof(*msg);
458*3082766eSkettenis 
459*3082766eSkettenis 	msg = (void *)&packet.data[0];
460*3082766eSkettenis 	msg->hdr.type = APLHIDEV_GET_DIMENSIONS;
461*3082766eSkettenis 	msg->hdr.device = 0;
462*3082766eSkettenis 	msg->hdr.msgid = sc->sc_msgid++;
463*3082766eSkettenis 	msg->hdr.cmdlen = 0;
464*3082766eSkettenis 	msg->hdr.rsplen = APLHIDEV_DESC_MAX;
465*3082766eSkettenis 	msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2);
466*3082766eSkettenis 
467*3082766eSkettenis 	packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2);
468*3082766eSkettenis 
469*3082766eSkettenis 	spi_acquire_bus(sc->sc_spi_tag, 0);
470*3082766eSkettenis 	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
471*3082766eSkettenis 	spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet),
472*3082766eSkettenis 	    SPI_KEEP_CS);
473*3082766eSkettenis 	delay(100);
474*3082766eSkettenis 	spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status));
475*3082766eSkettenis 	spi_release_bus(sc->sc_spi_tag, 0);
476*3082766eSkettenis 
477*3082766eSkettenis 	delay(1000);
478*3082766eSkettenis }
479*3082766eSkettenis 
480abd92e8eSkettenis int
aplhidev_intr(void * arg)481abd92e8eSkettenis aplhidev_intr(void *arg)
482abd92e8eSkettenis {
483abd92e8eSkettenis 	struct aplhidev_softc *sc = arg;
484abd92e8eSkettenis 	struct aplhidev_spi_packet packet;
485abd92e8eSkettenis 	struct aplhidev_msghdr *hdr = (struct aplhidev_msghdr *)&packet.data[0];
486abd92e8eSkettenis 
487abd92e8eSkettenis 	memset(&packet, 0, sizeof(packet));
488abd92e8eSkettenis 	spi_acquire_bus(sc->sc_spi_tag, 0);
489abd92e8eSkettenis 	spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
490abd92e8eSkettenis 	spi_read(sc->sc_spi_tag, (char *)&packet, sizeof(packet));
491abd92e8eSkettenis 	spi_release_bus(sc->sc_spi_tag, 0);
492abd92e8eSkettenis 
493abd92e8eSkettenis 	/* Treat empty packets as spurious interrupts. */
494abd92e8eSkettenis 	if (packet.flags == 0 && packet.device == 0 && packet.crc == 0)
495abd92e8eSkettenis 		return 0;
496abd92e8eSkettenis 
497abd92e8eSkettenis 	if (crc16(0, (uint8_t *)&packet, sizeof(packet)))
498abd92e8eSkettenis 		return 1;
499abd92e8eSkettenis 
500abd92e8eSkettenis 	/* Keyboard input. */
501abd92e8eSkettenis 	if (packet.flags == APLHIDEV_READ_PACKET &&
502abd92e8eSkettenis 	    packet.device == APLHIDEV_KBD_DEVICE &&
503abd92e8eSkettenis 	    hdr->type == APLHIDEV_KBD_REPORT) {
504abd92e8eSkettenis 		if (sc->sc_kbd)
50559cc6b22Skettenis 			aplkbd_intr(sc->sc_kbd, &packet.data[8], hdr->cmdlen);
506abd92e8eSkettenis 		return 1;
507abd92e8eSkettenis 	}
508abd92e8eSkettenis 
509abd92e8eSkettenis 	/* Touchpad input. */
510abd92e8eSkettenis 	if (packet.flags == APLHIDEV_READ_PACKET &&
511abd92e8eSkettenis 	    packet.device == APLHIDEV_TP_DEVICE &&
512abd92e8eSkettenis 	    hdr->type == APLHIDEV_TP_REPORT) {
513abd92e8eSkettenis 		if (sc->sc_ms)
51459cc6b22Skettenis 			aplms_intr(sc->sc_ms, &packet.data[8], hdr->cmdlen);
515abd92e8eSkettenis 		return 1;
516abd92e8eSkettenis 	}
517abd92e8eSkettenis 
518abd92e8eSkettenis 	/* Replies to commands we sent. */
519abd92e8eSkettenis 	if (packet.flags == APLHIDEV_WRITE_PACKET &&
520abd92e8eSkettenis 	    packet.device == APLHIDEV_INFO_DEVICE &&
5215db73e29Skettenis 	    hdr->type == APLHIDEV_GET_INFO) {
5225db73e29Skettenis 		struct aplhidev_info_hdr *info =
5235db73e29Skettenis 		    (struct aplhidev_info_hdr *)&packet.data[8];
5245db73e29Skettenis 		sc->sc_vendor = info->vendor;
5255db73e29Skettenis 		sc->sc_product = info->product;
5265db73e29Skettenis 		return 1;
5275db73e29Skettenis 	}
5285db73e29Skettenis 	if (packet.flags == APLHIDEV_WRITE_PACKET &&
5295db73e29Skettenis 	    packet.device == APLHIDEV_INFO_DEVICE &&
530abd92e8eSkettenis 	    hdr->type == APLHIDEV_GET_DESCRIPTOR) {
531abd92e8eSkettenis 		switch (hdr->device) {
532abd92e8eSkettenis 		case APLHIDEV_KBD_DEVICE:
533abd92e8eSkettenis 			memcpy(sc->sc_kbddesc, &packet.data[8], hdr->cmdlen);
534abd92e8eSkettenis 			sc->sc_kbddesclen = hdr->cmdlen;
535abd92e8eSkettenis 			break;
536abd92e8eSkettenis 		case APLHIDEV_TP_DEVICE:
537abd92e8eSkettenis 			memcpy(sc->sc_tpdesc, &packet.data[8], hdr->cmdlen);
538abd92e8eSkettenis 			sc->sc_tpdesclen = hdr->cmdlen;
539abd92e8eSkettenis 			break;
540abd92e8eSkettenis 		}
541abd92e8eSkettenis 
542abd92e8eSkettenis 		return 1;
543abd92e8eSkettenis 	}
5444aaec261Skettenis 	if (packet.flags == APLHIDEV_WRITE_PACKET &&
5454aaec261Skettenis 	    packet.device == APLHIDEV_TP_DEVICE &&
5464aaec261Skettenis 	    hdr->type == APLHIDEV_SET_MODE) {
5474aaec261Skettenis 		sc->sc_mode = APLHIDEV_MODE_RAW;
5484aaec261Skettenis 		return 1;
5494aaec261Skettenis 	}
550*3082766eSkettenis 	if (packet.flags == APLHIDEV_WRITE_PACKET &&
551*3082766eSkettenis 	    packet.device == APLHIDEV_TP_DEVICE &&
552*3082766eSkettenis 	    hdr->type == APLHIDEV_GET_DIMENSIONS) {
553*3082766eSkettenis 		memcpy(sc->sc_dimdesc, &packet.data[8], hdr->cmdlen);
554*3082766eSkettenis 		sc->sc_dimdesclen = hdr->cmdlen;
555*3082766eSkettenis 		return 1;
556*3082766eSkettenis 	}
557abd92e8eSkettenis 
558abd92e8eSkettenis 	/* Valid, but unrecognized packet; ignore for now. */
559abd92e8eSkettenis 	return 1;
560abd92e8eSkettenis }
561abd92e8eSkettenis 
562abd92e8eSkettenis /* Keyboard */
563abd92e8eSkettenis 
564abd92e8eSkettenis struct aplkbd_softc {
565abd92e8eSkettenis 	struct device		sc_dev;
566269321aeSkettenis 	struct aplhidev_softc	*sc_hidev;
567abd92e8eSkettenis 	struct hidkbd		sc_kbd;
568abd92e8eSkettenis 	int			sc_spl;
569abd92e8eSkettenis };
570abd92e8eSkettenis 
571abd92e8eSkettenis void	aplkbd_cngetc(void *, u_int *, int *);
572abd92e8eSkettenis void	aplkbd_cnpollc(void *, int);
573abd92e8eSkettenis void	aplkbd_cnbell(void *, u_int, u_int, u_int);
574abd92e8eSkettenis 
575abd92e8eSkettenis const struct wskbd_consops aplkbd_consops = {
576abd92e8eSkettenis 	aplkbd_cngetc,
577abd92e8eSkettenis 	aplkbd_cnpollc,
578abd92e8eSkettenis 	aplkbd_cnbell,
579abd92e8eSkettenis };
580abd92e8eSkettenis 
581abd92e8eSkettenis int	aplkbd_enable(void *, int);
582abd92e8eSkettenis void	aplkbd_set_leds(void *, int);
583abd92e8eSkettenis int	aplkbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
584abd92e8eSkettenis 
585abd92e8eSkettenis const struct wskbd_accessops aplkbd_accessops = {
586abd92e8eSkettenis 	.enable = aplkbd_enable,
587abd92e8eSkettenis 	.ioctl = aplkbd_ioctl,
588abd92e8eSkettenis 	.set_leds = aplkbd_set_leds,
589abd92e8eSkettenis };
590abd92e8eSkettenis 
591abd92e8eSkettenis int	 aplkbd_match(struct device *, void *, void *);
592abd92e8eSkettenis void	 aplkbd_attach(struct device *, struct device *, void *);
593abd92e8eSkettenis 
594471aeecfSnaddy const struct cfattach aplkbd_ca = {
595abd92e8eSkettenis 	sizeof(struct aplkbd_softc), aplkbd_match, aplkbd_attach
596abd92e8eSkettenis };
597abd92e8eSkettenis 
598abd92e8eSkettenis struct cfdriver aplkbd_cd = {
599abd92e8eSkettenis 	NULL, "aplkbd", DV_DULL
600abd92e8eSkettenis };
601abd92e8eSkettenis 
602abd92e8eSkettenis int
aplkbd_match(struct device * parent,void * match,void * aux)603abd92e8eSkettenis aplkbd_match(struct device *parent, void *match, void *aux)
604abd92e8eSkettenis {
605abd92e8eSkettenis 	struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux;
606abd92e8eSkettenis 
607abd92e8eSkettenis 	return (aa->aa_reportid == APLHIDEV_KBD_DEVICE);
608abd92e8eSkettenis }
609abd92e8eSkettenis 
610abd92e8eSkettenis void
aplkbd_attach(struct device * parent,struct device * self,void * aux)611abd92e8eSkettenis aplkbd_attach(struct device *parent, struct device *self, void *aux)
612abd92e8eSkettenis {
613abd92e8eSkettenis 	struct aplkbd_softc *sc = (struct aplkbd_softc *)self;
614abd92e8eSkettenis 	struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux;
615abd92e8eSkettenis 	struct hidkbd *kbd = &sc->sc_kbd;
616abd92e8eSkettenis 
617269321aeSkettenis 	sc->sc_hidev = (struct aplhidev_softc *)parent;
618abd92e8eSkettenis 	if (hidkbd_attach(self, kbd, 1, 0, APLHIDEV_KBD_DEVICE,
619abd92e8eSkettenis 	    aa->aa_desc, aa->aa_desclen))
620abd92e8eSkettenis 		return;
621abd92e8eSkettenis 
622abd92e8eSkettenis 	printf("\n");
623abd92e8eSkettenis 
6246bd2bf21Srobert 	if (hid_locate(aa->aa_desc, aa->aa_desclen, HID_USAGE2(HUP_APPLE, HUG_FN_KEY),
6259de6d7f5Srobert 	    1, hid_input, &kbd->sc_fn, NULL)) {
6269de6d7f5Srobert 		switch (sc->sc_hidev->sc_product) {
6279de6d7f5Srobert 		case USB_PRODUCT_APPLE_WELLSPRINGM1_J293:
6289de6d7f5Srobert 			kbd->sc_munge = hidkbd_apple_tb_munge;
6299de6d7f5Srobert 			break;
6309de6d7f5Srobert 		default:
6316bd2bf21Srobert 			kbd->sc_munge = hidkbd_apple_munge;
6329de6d7f5Srobert 			break;
6339de6d7f5Srobert 		}
6349de6d7f5Srobert 	}
6356bd2bf21Srobert 
636abd92e8eSkettenis 	if (kbd->sc_console_keyboard) {
637abd92e8eSkettenis 		extern struct wskbd_mapdata ukbd_keymapdata;
638abd92e8eSkettenis 
639abd92e8eSkettenis 		ukbd_keymapdata.layout = KB_US | KB_DEFAULT;
640abd92e8eSkettenis 		wskbd_cnattach(&aplkbd_consops, sc, &ukbd_keymapdata);
641abd92e8eSkettenis 		aplkbd_enable(sc, 1);
642abd92e8eSkettenis 	}
643abd92e8eSkettenis 
644abd92e8eSkettenis 	hidkbd_attach_wskbd(kbd, KB_US | KB_DEFAULT, &aplkbd_accessops);
645abd92e8eSkettenis }
646abd92e8eSkettenis 
647abd92e8eSkettenis void
aplkbd_intr(struct device * self,uint8_t * packet,size_t packetlen)64859cc6b22Skettenis aplkbd_intr(struct device *self, uint8_t *packet, size_t packetlen)
649abd92e8eSkettenis {
650abd92e8eSkettenis 	struct aplkbd_softc *sc = (struct aplkbd_softc *)self;
651abd92e8eSkettenis 	struct hidkbd *kbd = &sc->sc_kbd;
652abd92e8eSkettenis 
653abd92e8eSkettenis 	if (kbd->sc_enabled)
65459cc6b22Skettenis 		hidkbd_input(kbd, &packet[1], packetlen - 1);
655abd92e8eSkettenis }
656abd92e8eSkettenis 
657abd92e8eSkettenis int
aplkbd_enable(void * v,int on)658abd92e8eSkettenis aplkbd_enable(void *v, int on)
659abd92e8eSkettenis {
660abd92e8eSkettenis 	struct aplkbd_softc *sc = v;
661abd92e8eSkettenis 	struct hidkbd *kbd = &sc->sc_kbd;
662abd92e8eSkettenis 
663abd92e8eSkettenis 	return hidkbd_enable(kbd, on);
664abd92e8eSkettenis }
665abd92e8eSkettenis 
666abd92e8eSkettenis int
aplkbd_ioctl(void * v,u_long cmd,caddr_t data,int flag,struct proc * p)667abd92e8eSkettenis aplkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
668abd92e8eSkettenis {
669abd92e8eSkettenis 	struct aplkbd_softc *sc = v;
670abd92e8eSkettenis 	struct hidkbd *kbd = &sc->sc_kbd;
671abd92e8eSkettenis 
672abd92e8eSkettenis 	switch (cmd) {
673abd92e8eSkettenis 	case WSKBDIO_GTYPE:
674abd92e8eSkettenis 		/* XXX: should we set something else? */
675abd92e8eSkettenis 		*(u_int *)data = WSKBD_TYPE_USB;
676abd92e8eSkettenis 		return 0;
6779ff4421eStobhe 	case WSKBDIO_SETLEDS:
6789ff4421eStobhe 		aplkbd_set_leds(v, *(int *)data);
6799ff4421eStobhe 		return 0;
680abd92e8eSkettenis 	default:
681abd92e8eSkettenis 		return hidkbd_ioctl(kbd, cmd, data, flag, p);
682abd92e8eSkettenis 	}
683abd92e8eSkettenis }
684abd92e8eSkettenis 
685abd92e8eSkettenis void
aplkbd_set_leds(void * v,int leds)686abd92e8eSkettenis aplkbd_set_leds(void *v, int leds)
687abd92e8eSkettenis {
688269321aeSkettenis 	struct aplkbd_softc *sc = v;
689269321aeSkettenis 	struct hidkbd *kbd = &sc->sc_kbd;
690269321aeSkettenis 	uint8_t res;
691269321aeSkettenis 
692269321aeSkettenis 	if (hidkbd_set_leds(kbd, leds, &res))
693269321aeSkettenis 		aplhidev_set_leds(sc->sc_hidev, res);
694abd92e8eSkettenis }
695abd92e8eSkettenis 
696abd92e8eSkettenis /* Console interface. */
697abd92e8eSkettenis void
aplkbd_cngetc(void * v,u_int * type,int * data)698abd92e8eSkettenis aplkbd_cngetc(void *v, u_int *type, int *data)
699abd92e8eSkettenis {
700abd92e8eSkettenis 	struct aplkbd_softc *sc = v;
701abd92e8eSkettenis 	struct hidkbd *kbd = &sc->sc_kbd;
702abd92e8eSkettenis 
703abd92e8eSkettenis 	kbd->sc_polling = 1;
704abd92e8eSkettenis 	while (kbd->sc_npollchar <= 0) {
705abd92e8eSkettenis 		aplhidev_intr(sc->sc_dev.dv_parent);
706abd92e8eSkettenis 		delay(1000);
707abd92e8eSkettenis 	}
708abd92e8eSkettenis 	kbd->sc_polling = 0;
709abd92e8eSkettenis 	hidkbd_cngetc(kbd, type, data);
710abd92e8eSkettenis }
711abd92e8eSkettenis 
712abd92e8eSkettenis void
aplkbd_cnpollc(void * v,int on)713abd92e8eSkettenis aplkbd_cnpollc(void *v, int on)
714abd92e8eSkettenis {
715abd92e8eSkettenis 	struct aplkbd_softc *sc = v;
716abd92e8eSkettenis 
717abd92e8eSkettenis 	if (on)
718abd92e8eSkettenis 		sc->sc_spl = spltty();
719abd92e8eSkettenis 	else
720abd92e8eSkettenis 		splx(sc->sc_spl);
721abd92e8eSkettenis }
722abd92e8eSkettenis 
723abd92e8eSkettenis void
aplkbd_cnbell(void * v,u_int pitch,u_int period,u_int volume)724abd92e8eSkettenis aplkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume)
725abd92e8eSkettenis {
726abd92e8eSkettenis 	hidkbd_bell(pitch, period, volume, 1);
727abd92e8eSkettenis }
728abd92e8eSkettenis 
729abd92e8eSkettenis #if NAPLMS > 0
730abd92e8eSkettenis 
731abd92e8eSkettenis /* Touchpad */
732abd92e8eSkettenis 
73359cc6b22Skettenis /*
73459cc6b22Skettenis  * The contents of the touchpad event packets is identical to those
73559cc6b22Skettenis  * used by the ubcmtp(4) driver.  The relevant definitions and the
73659cc6b22Skettenis  * code to decode the packets is replicated here.
73759cc6b22Skettenis  */
73859cc6b22Skettenis 
73959cc6b22Skettenis struct ubcmtp_finger {
74059cc6b22Skettenis 	uint16_t	origin;
74159cc6b22Skettenis 	uint16_t	abs_x;
74259cc6b22Skettenis 	uint16_t	abs_y;
74359cc6b22Skettenis 	uint16_t	rel_x;
74459cc6b22Skettenis 	uint16_t	rel_y;
74559cc6b22Skettenis 	uint16_t	tool_major;
74659cc6b22Skettenis 	uint16_t	tool_minor;
74759cc6b22Skettenis 	uint16_t	orientation;
74859cc6b22Skettenis 	uint16_t	touch_major;
74959cc6b22Skettenis 	uint16_t	touch_minor;
75059cc6b22Skettenis 	uint16_t	unused[2];
75159cc6b22Skettenis 	uint16_t	pressure;
75259cc6b22Skettenis 	uint16_t	multi;
75359cc6b22Skettenis } __packed __attribute((aligned(2)));
75459cc6b22Skettenis 
75559cc6b22Skettenis #define UBCMTP_MAX_FINGERS	16
75659cc6b22Skettenis 
75759cc6b22Skettenis #define UBCMTP_TYPE4_TPOFF	(24 * sizeof(uint16_t))
75859cc6b22Skettenis #define UBCMTP_TYPE4_BTOFF	31
75959cc6b22Skettenis #define UBCMTP_TYPE4_FINGERPAD	(1 * sizeof(uint16_t))
76059cc6b22Skettenis 
76159cc6b22Skettenis /* Use a constant, synaptics-compatible pressure value for now. */
76259cc6b22Skettenis #define DEFAULT_PRESSURE	40
76359cc6b22Skettenis 
7649310c18aSbru static struct wsmouse_param aplms_wsmousecfg[] = {
7659310c18aSbru 	{ WSMOUSECFG_MTBTN_MAXDIST, 0 }, /* 0: Compute a default value. */
7669310c18aSbru };
7679310c18aSbru 
768abd92e8eSkettenis struct aplms_softc {
769abd92e8eSkettenis 	struct device		sc_dev;
770*3082766eSkettenis 	struct aplhidev_softc	*sc_hidev;
77159cc6b22Skettenis 	struct device		*sc_wsmousedev;
77259cc6b22Skettenis 
77359cc6b22Skettenis 	int			sc_enabled;
77459cc6b22Skettenis 
77559cc6b22Skettenis 	int			tp_offset;
77659cc6b22Skettenis 	int			tp_fingerpad;
77759cc6b22Skettenis 
77859cc6b22Skettenis 	struct mtpoint		frame[UBCMTP_MAX_FINGERS];
77959cc6b22Skettenis 	int			contacts;
78059cc6b22Skettenis 	int			btn;
781abd92e8eSkettenis };
782abd92e8eSkettenis 
783abd92e8eSkettenis int	aplms_enable(void *);
784abd92e8eSkettenis void	aplms_disable(void *);
785abd92e8eSkettenis int	aplms_ioctl(void *, u_long, caddr_t, int, struct proc *);
786abd92e8eSkettenis 
787abd92e8eSkettenis const struct wsmouse_accessops aplms_accessops = {
788abd92e8eSkettenis 	.enable = aplms_enable,
789abd92e8eSkettenis 	.disable = aplms_disable,
790abd92e8eSkettenis 	.ioctl = aplms_ioctl,
791abd92e8eSkettenis };
792abd92e8eSkettenis 
793abd92e8eSkettenis int	 aplms_match(struct device *, void *, void *);
794abd92e8eSkettenis void	 aplms_attach(struct device *, struct device *, void *);
795abd92e8eSkettenis 
796471aeecfSnaddy const struct cfattach aplms_ca = {
797abd92e8eSkettenis 	sizeof(struct aplms_softc), aplms_match, aplms_attach
798abd92e8eSkettenis };
799abd92e8eSkettenis 
800abd92e8eSkettenis struct cfdriver aplms_cd = {
801abd92e8eSkettenis 	NULL, "aplms", DV_DULL
802abd92e8eSkettenis };
803abd92e8eSkettenis 
80459cc6b22Skettenis int	aplms_configure(struct aplms_softc *);
80559cc6b22Skettenis 
806abd92e8eSkettenis int
aplms_match(struct device * parent,void * match,void * aux)807abd92e8eSkettenis aplms_match(struct device *parent, void *match, void *aux)
808abd92e8eSkettenis {
809abd92e8eSkettenis 	struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux;
810abd92e8eSkettenis 
811abd92e8eSkettenis 	return (aa->aa_reportid == APLHIDEV_TP_DEVICE);
812abd92e8eSkettenis }
813abd92e8eSkettenis 
814abd92e8eSkettenis void
aplms_attach(struct device * parent,struct device * self,void * aux)815abd92e8eSkettenis aplms_attach(struct device *parent, struct device *self, void *aux)
816abd92e8eSkettenis {
817abd92e8eSkettenis 	struct aplms_softc *sc = (struct aplms_softc *)self;
81859cc6b22Skettenis 	struct wsmousedev_attach_args aa;
819abd92e8eSkettenis 
820*3082766eSkettenis 	sc->sc_hidev = (struct aplhidev_softc *)parent;
821*3082766eSkettenis 
82259cc6b22Skettenis 	printf("\n");
823abd92e8eSkettenis 
82459cc6b22Skettenis 	sc->tp_offset = UBCMTP_TYPE4_TPOFF;
82559cc6b22Skettenis 	sc->tp_fingerpad = UBCMTP_TYPE4_FINGERPAD;
82659cc6b22Skettenis 
82759cc6b22Skettenis 	aa.accessops = &aplms_accessops;
82859cc6b22Skettenis 	aa.accesscookie = sc;
82959cc6b22Skettenis 
83059cc6b22Skettenis 	sc->sc_wsmousedev = config_found(self, &aa, wsmousedevprint);
83159cc6b22Skettenis 	if (sc->sc_wsmousedev != NULL && aplms_configure(sc))
83259cc6b22Skettenis 		aplms_disable(sc);
83359cc6b22Skettenis }
83459cc6b22Skettenis 
83559cc6b22Skettenis int
aplms_configure(struct aplms_softc * sc)83659cc6b22Skettenis aplms_configure(struct aplms_softc *sc)
83759cc6b22Skettenis {
83859cc6b22Skettenis 	struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev);
83959cc6b22Skettenis 
84059cc6b22Skettenis 	hw->type = WSMOUSE_TYPE_TOUCHPAD;
84159cc6b22Skettenis 	hw->hw_type = WSMOUSEHW_CLICKPAD;
842*3082766eSkettenis 	hw->x_min = sc->sc_hidev->sc_x_min;
843*3082766eSkettenis 	hw->x_max = sc->sc_hidev->sc_x_max;
844*3082766eSkettenis 	hw->y_min = sc->sc_hidev->sc_y_min;
845*3082766eSkettenis 	hw->y_max = sc->sc_hidev->sc_y_max;
846*3082766eSkettenis 	hw->h_res = sc->sc_hidev->sc_h_res;
847*3082766eSkettenis 	hw->v_res = sc->sc_hidev->sc_v_res;
84859cc6b22Skettenis 	hw->mt_slots = UBCMTP_MAX_FINGERS;
84959cc6b22Skettenis 	hw->flags = WSMOUSEHW_MT_TRACKING;
85059cc6b22Skettenis 
8519310c18aSbru 	return wsmouse_configure(sc->sc_wsmousedev,
8529310c18aSbru 	    aplms_wsmousecfg, nitems(aplms_wsmousecfg));
853abd92e8eSkettenis }
854abd92e8eSkettenis 
855abd92e8eSkettenis void
aplms_intr(struct device * self,uint8_t * packet,size_t packetlen)85659cc6b22Skettenis aplms_intr(struct device *self, uint8_t *packet, size_t packetlen)
857abd92e8eSkettenis {
858abd92e8eSkettenis 	struct aplms_softc *sc = (struct aplms_softc *)self;
85959cc6b22Skettenis 	struct ubcmtp_finger *finger;
86059cc6b22Skettenis 	int off, s, btn, contacts;
861abd92e8eSkettenis 
86259cc6b22Skettenis 	if (!sc->sc_enabled)
86359cc6b22Skettenis 		return;
86459cc6b22Skettenis 
86559cc6b22Skettenis 	contacts = 0;
86659cc6b22Skettenis 	for (off = sc->tp_offset; off < packetlen;
86759cc6b22Skettenis 	    off += (sizeof(struct ubcmtp_finger) + sc->tp_fingerpad)) {
86859cc6b22Skettenis 		finger = (struct ubcmtp_finger *)(packet + off);
86959cc6b22Skettenis 
87059cc6b22Skettenis 		if ((int16_t)letoh16(finger->touch_major) == 0)
87159cc6b22Skettenis 			continue; /* finger lifted */
87259cc6b22Skettenis 
87359cc6b22Skettenis 		sc->frame[contacts].x = (int16_t)letoh16(finger->abs_x);
87459cc6b22Skettenis 		sc->frame[contacts].y = (int16_t)letoh16(finger->abs_y);
87559cc6b22Skettenis 		sc->frame[contacts].pressure = DEFAULT_PRESSURE;
87659cc6b22Skettenis 		contacts++;
87759cc6b22Skettenis 	}
87859cc6b22Skettenis 
87959cc6b22Skettenis 	btn = sc->btn;
88059cc6b22Skettenis 	sc->btn = !!((int16_t)letoh16(packet[UBCMTP_TYPE4_BTOFF]));
88159cc6b22Skettenis 
88259cc6b22Skettenis 	if (contacts || sc->contacts || sc->btn != btn) {
88359cc6b22Skettenis 		sc->contacts = contacts;
88459cc6b22Skettenis 		s = spltty();
88559cc6b22Skettenis 		wsmouse_buttons(sc->sc_wsmousedev, sc->btn);
88659cc6b22Skettenis 		wsmouse_mtframe(sc->sc_wsmousedev, sc->frame, contacts);
88759cc6b22Skettenis 		wsmouse_input_sync(sc->sc_wsmousedev);
88859cc6b22Skettenis 		splx(s);
88959cc6b22Skettenis 	}
890abd92e8eSkettenis }
891abd92e8eSkettenis 
892abd92e8eSkettenis int
aplms_enable(void * v)893abd92e8eSkettenis aplms_enable(void *v)
894abd92e8eSkettenis {
895abd92e8eSkettenis 	struct aplms_softc *sc = v;
896abd92e8eSkettenis 
89759cc6b22Skettenis 	if (sc->sc_enabled)
89859cc6b22Skettenis 		return EBUSY;
89959cc6b22Skettenis 
90059cc6b22Skettenis 	sc->sc_enabled = 1;
90159cc6b22Skettenis 	return 0;
902abd92e8eSkettenis }
903abd92e8eSkettenis 
904abd92e8eSkettenis void
aplms_disable(void * v)905abd92e8eSkettenis aplms_disable(void *v)
906abd92e8eSkettenis {
907abd92e8eSkettenis 	struct aplms_softc *sc = v;
908abd92e8eSkettenis 
90959cc6b22Skettenis 	sc->sc_enabled = 0;
910abd92e8eSkettenis }
911abd92e8eSkettenis 
912abd92e8eSkettenis int
aplms_ioctl(void * v,u_long cmd,caddr_t data,int flag,struct proc * p)913abd92e8eSkettenis aplms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
914abd92e8eSkettenis {
915abd92e8eSkettenis 	struct aplms_softc *sc = v;
91659cc6b22Skettenis 	struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev);
91759cc6b22Skettenis 	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
91859cc6b22Skettenis 	int wsmode;
919abd92e8eSkettenis 
920abd92e8eSkettenis 	switch (cmd) {
921abd92e8eSkettenis 	case WSMOUSEIO_GTYPE:
92259cc6b22Skettenis 		*(u_int *)data = hw->type;
92359cc6b22Skettenis 		break;
92459cc6b22Skettenis 
92559cc6b22Skettenis 	case WSMOUSEIO_GCALIBCOORDS:
92659cc6b22Skettenis 		wsmc->minx = hw->x_min;
92759cc6b22Skettenis 		wsmc->maxx = hw->x_max;
92859cc6b22Skettenis 		wsmc->miny = hw->y_min;
92959cc6b22Skettenis 		wsmc->maxy = hw->y_max;
93059cc6b22Skettenis 		wsmc->swapxy = 0;
931*3082766eSkettenis 		wsmc->resx = hw->h_res;
932*3082766eSkettenis 		wsmc->resy = hw->v_res;
93359cc6b22Skettenis 		break;
93459cc6b22Skettenis 
93559cc6b22Skettenis 	case WSMOUSEIO_SETMODE:
93659cc6b22Skettenis 		wsmode = *(u_int *)data;
93759cc6b22Skettenis 		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) {
93859cc6b22Skettenis 			printf("%s: invalid mode %d\n", sc->sc_dev.dv_xname,
93959cc6b22Skettenis 			    wsmode);
94059cc6b22Skettenis 			return (EINVAL);
941abd92e8eSkettenis 		}
94259cc6b22Skettenis 		wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
94359cc6b22Skettenis 		break;
94459cc6b22Skettenis 
94559cc6b22Skettenis 	default:
94659cc6b22Skettenis 		return -1;
94759cc6b22Skettenis 	}
94859cc6b22Skettenis 
94959cc6b22Skettenis 	return 0;
950abd92e8eSkettenis }
951abd92e8eSkettenis 
952abd92e8eSkettenis #else
953abd92e8eSkettenis 
954abd92e8eSkettenis void
aplms_intr(struct device * self,uint8_t * packet,size_t packetlen)95559cc6b22Skettenis aplms_intr(struct device *self, uint8_t *packet, size_t packetlen)
956abd92e8eSkettenis {
957abd92e8eSkettenis }
958abd92e8eSkettenis 
959abd92e8eSkettenis #endif
960