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