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