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