/* $OpenBSD: aplhidev.c,v 1.13 2024/01/15 13:27:20 kettenis Exp $ */ /* * Copyright (c) 2021 Mark Kettenis * Copyright (c) 2013-2014 joshua stein * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aplhidev.h" #define APLHIDEV_READ_PACKET 0x20 #define APLHIDEV_WRITE_PACKET 0x40 #define APLHIDEV_KBD_DEVICE 1 #define APLHIDEV_TP_DEVICE 2 #define APLHIDEV_INFO_DEVICE 208 #define APLHIDEV_GET_INFO 0x0120 #define APLHIDEV_GET_DESCRIPTOR 0x1020 #define APLHIDEV_DESC_MAX 512 #define APLHIDEV_KBD_REPORT 0x0110 #define APLHIDEV_TP_REPORT 0x0210 #define APLHIDEV_SET_LEDS 0x0151 #define APLHIDEV_SET_MODE 0x0252 #define APLHIDEV_MODE_HID 0x00 #define APLHIDEV_MODE_RAW 0x01 #define APLHIDEV_GET_DIMENSIONS 0xd932 struct aplhidev_dim { uint32_t width; uint32_t height; int16_t x_min; int16_t y_min; int16_t x_max; int16_t y_max; }; struct aplhidev_attach_args { uint8_t aa_reportid; void *aa_desc; size_t aa_desclen; }; struct aplhidev_spi_packet { uint8_t flags; uint8_t device; uint16_t offset; uint16_t remaining; uint16_t len; uint8_t data[246]; uint16_t crc; }; struct aplhidev_spi_status { uint8_t status[4]; }; struct aplhidev_msghdr { uint16_t type; uint8_t device; uint8_t msgid; uint16_t rsplen; uint16_t cmdlen; }; struct aplhidev_info_hdr { uint16_t unknown[2]; uint16_t num_devices; uint16_t vendor; uint16_t product; uint16_t version; uint16_t vendor_str[2]; uint16_t product_str[2]; uint16_t serial_str[2]; }; struct aplhidev_get_desc { struct aplhidev_msghdr hdr; uint16_t crc; }; struct aplhidev_set_leds { struct aplhidev_msghdr hdr; uint8_t reportid; uint8_t leds; uint16_t crc; }; struct aplhidev_set_mode { struct aplhidev_msghdr hdr; uint8_t reportid; uint8_t mode; uint16_t crc; }; struct aplhidev_softc { struct device sc_dev; int sc_node; spi_tag_t sc_spi_tag; struct spi_config sc_spi_conf; uint8_t sc_msgid; uint32_t *sc_gpio; int sc_gpiolen; uint8_t sc_mode; uint16_t sc_vendor; uint16_t sc_product; struct device *sc_kbd; uint8_t sc_kbddesc[APLHIDEV_DESC_MAX]; size_t sc_kbddesclen; struct device *sc_ms; uint8_t sc_tpdesc[APLHIDEV_DESC_MAX]; size_t sc_tpdesclen; uint8_t sc_dimdesc[APLHIDEV_DESC_MAX]; size_t sc_dimdesclen; int sc_x_min; int sc_x_max; int sc_y_min; int sc_y_max; int sc_h_res; int sc_v_res; }; int aplhidev_match(struct device *, void *, void *); void aplhidev_attach(struct device *, struct device *, void *); const struct cfattach aplhidev_ca = { sizeof(struct aplhidev_softc), aplhidev_match, aplhidev_attach }; struct cfdriver aplhidev_cd = { NULL, "aplhidev", DV_DULL }; void aplhidev_get_info(struct aplhidev_softc *); void aplhidev_get_descriptor(struct aplhidev_softc *, uint8_t); void aplhidev_set_leds(struct aplhidev_softc *, uint8_t); void aplhidev_set_mode(struct aplhidev_softc *, uint8_t); void aplhidev_get_dimensions(struct aplhidev_softc *); int aplhidev_intr(void *); void aplkbd_intr(struct device *, uint8_t *, size_t); void aplms_intr(struct device *, uint8_t *, size_t); int aplhidev_match(struct device *parent, void *match, void *aux) { struct spi_attach_args *sa = aux; if (strcmp(sa->sa_name, "apple,spi-hid-transport") == 0) return 1; return 0; } void aplhidev_attach(struct device *parent, struct device *self, void *aux) { struct aplhidev_softc *sc = (struct aplhidev_softc *)self; struct spi_attach_args *sa = aux; struct aplhidev_attach_args aa; struct aplhidev_dim dim; int retry; sc->sc_spi_tag = sa->sa_tag; sc->sc_node = *(int *)sa->sa_cookie; sc->sc_gpiolen = OF_getproplen(sc->sc_node, "spien-gpios"); if (sc->sc_gpiolen > 0) { sc->sc_gpio = malloc(sc->sc_gpiolen, M_TEMP, M_WAITOK); OF_getpropintarray(sc->sc_node, "spien-gpios", sc->sc_gpio, sc->sc_gpiolen); gpio_controller_config_pin(sc->sc_gpio, GPIO_CONFIG_OUTPUT); /* Reset */ gpio_controller_set_pin(sc->sc_gpio, 1); delay(5000); gpio_controller_set_pin(sc->sc_gpio, 0); delay(5000); /* Enable. */ gpio_controller_set_pin(sc->sc_gpio, 1); delay(50000); } sc->sc_spi_conf.sc_bpw = 8; sc->sc_spi_conf.sc_freq = OF_getpropint(sc->sc_node, "spi-max-frequency", 0); sc->sc_spi_conf.sc_cs = OF_getpropint(sc->sc_node, "reg", 0); sc->sc_spi_conf.sc_cs_delay = 100; fdt_intr_establish(sc->sc_node, IPL_TTY, aplhidev_intr, sc, sc->sc_dev.dv_xname); aplhidev_get_info(sc); for (retry = 10; retry > 0; retry--) { aplhidev_intr(sc); delay(1000); if (sc->sc_vendor != 0 && sc->sc_product != 0) break; } aplhidev_get_descriptor(sc, APLHIDEV_KBD_DEVICE); for (retry = 10; retry > 0; retry--) { aplhidev_intr(sc); delay(1000); if (sc->sc_kbddesclen > 0) break; } aplhidev_get_descriptor(sc, APLHIDEV_TP_DEVICE); for (retry = 10; retry > 0; retry--) { aplhidev_intr(sc); delay(1000); if (sc->sc_tpdesclen > 0) break; } sc->sc_mode = APLHIDEV_MODE_HID; aplhidev_set_mode(sc, APLHIDEV_MODE_RAW); for (retry = 10; retry > 0; retry--) { aplhidev_intr(sc); delay(1000); if (sc->sc_mode == APLHIDEV_MODE_RAW) break; } aplhidev_get_dimensions(sc); for (retry = 10; retry > 0; retry--) { aplhidev_intr(sc); delay(1000); if (sc->sc_dimdesclen > 0) break; } printf("\n"); if (sc->sc_dimdesclen == sizeof(dim) + 1) { memcpy(&dim, &sc->sc_dimdesc[1], sizeof(dim)); sc->sc_x_min = dim.x_min; sc->sc_x_max = dim.x_max; sc->sc_y_min = dim.y_min; sc->sc_y_max = dim.y_max; sc->sc_h_res = (100 * (dim.x_max - dim.x_min)) / dim.width; sc->sc_v_res = (100 * (dim.y_max - dim.y_min)) / dim.height; } if (sc->sc_kbddesclen > 0) { aa.aa_reportid = APLHIDEV_KBD_DEVICE; aa.aa_desc = sc->sc_kbddesc; aa.aa_desclen = sc->sc_kbddesclen; sc->sc_kbd = config_found(self, &aa, NULL); } if (sc->sc_tpdesclen > 0) { aa.aa_reportid = APLHIDEV_TP_DEVICE; aa.aa_desc = sc->sc_tpdesc; aa.aa_desclen = sc->sc_tpdesclen; sc->sc_ms = config_found(self, &aa, NULL); } } void aplhidev_get_info(struct aplhidev_softc *sc) { struct aplhidev_spi_packet packet; struct aplhidev_get_desc *msg; struct aplhidev_spi_status status; memset(&packet, 0, sizeof(packet)); packet.flags = APLHIDEV_WRITE_PACKET; packet.device = APLHIDEV_INFO_DEVICE; packet.len = sizeof(*msg); msg = (void *)&packet.data[0]; msg->hdr.type = APLHIDEV_GET_INFO; msg->hdr.device = APLHIDEV_INFO_DEVICE; msg->hdr.msgid = sc->sc_msgid++; msg->hdr.cmdlen = 0; msg->hdr.rsplen = APLHIDEV_DESC_MAX; msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2); packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2); spi_acquire_bus(sc->sc_spi_tag, 0); spi_config(sc->sc_spi_tag, &sc->sc_spi_conf); spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet), SPI_KEEP_CS); delay(100); spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status)); spi_release_bus(sc->sc_spi_tag, 0); delay(1000); } void aplhidev_get_descriptor(struct aplhidev_softc *sc, uint8_t device) { struct aplhidev_spi_packet packet; struct aplhidev_get_desc *msg; struct aplhidev_spi_status status; memset(&packet, 0, sizeof(packet)); packet.flags = APLHIDEV_WRITE_PACKET; packet.device = APLHIDEV_INFO_DEVICE; packet.len = sizeof(*msg); msg = (void *)&packet.data[0]; msg->hdr.type = APLHIDEV_GET_DESCRIPTOR; msg->hdr.device = device; msg->hdr.msgid = sc->sc_msgid++; msg->hdr.cmdlen = 0; msg->hdr.rsplen = APLHIDEV_DESC_MAX; msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2); packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2); spi_acquire_bus(sc->sc_spi_tag, 0); spi_config(sc->sc_spi_tag, &sc->sc_spi_conf); spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet), SPI_KEEP_CS); delay(100); spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status)); spi_release_bus(sc->sc_spi_tag, 0); delay(1000); } void aplhidev_set_leds(struct aplhidev_softc *sc, uint8_t leds) { struct aplhidev_spi_packet packet; struct aplhidev_set_leds *msg; struct aplhidev_spi_status status; memset(&packet, 0, sizeof(packet)); packet.flags = APLHIDEV_WRITE_PACKET; packet.device = APLHIDEV_KBD_DEVICE; packet.len = sizeof(*msg); msg = (void *)&packet.data[0]; msg->hdr.type = APLHIDEV_SET_LEDS; msg->hdr.device = APLHIDEV_KBD_DEVICE; msg->hdr.msgid = sc->sc_msgid++; msg->hdr.cmdlen = sizeof(*msg) - sizeof(struct aplhidev_msghdr) - 2; msg->hdr.rsplen = msg->hdr.cmdlen; msg->reportid = APLHIDEV_KBD_DEVICE; msg->leds = leds; msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2); packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2); /* * XXX Without a delay here, the command will fail. Does the * controller need a bit of time between sending us a keypress * event and accepting a new command from us? */ delay(250); spi_acquire_bus(sc->sc_spi_tag, 0); spi_config(sc->sc_spi_tag, &sc->sc_spi_conf); spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet), SPI_KEEP_CS); delay(100); spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status)); spi_release_bus(sc->sc_spi_tag, 0); } void aplhidev_set_mode(struct aplhidev_softc *sc, uint8_t mode) { struct aplhidev_spi_packet packet; struct aplhidev_set_mode *msg; struct aplhidev_spi_status status; memset(&packet, 0, sizeof(packet)); packet.flags = APLHIDEV_WRITE_PACKET; packet.device = APLHIDEV_TP_DEVICE; packet.len = sizeof(*msg); msg = (void *)&packet.data[0]; msg->hdr.type = APLHIDEV_SET_MODE; msg->hdr.device = APLHIDEV_TP_DEVICE; msg->hdr.msgid = sc->sc_msgid++; msg->hdr.cmdlen = sizeof(*msg) - sizeof(struct aplhidev_msghdr) - 2; msg->hdr.rsplen = msg->hdr.cmdlen; msg->reportid = APLHIDEV_TP_DEVICE; msg->mode = mode; msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2); packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2); spi_acquire_bus(sc->sc_spi_tag, 0); spi_config(sc->sc_spi_tag, &sc->sc_spi_conf); spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet), SPI_KEEP_CS); delay(100); spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status)); spi_release_bus(sc->sc_spi_tag, 0); delay(1000); } void aplhidev_get_dimensions(struct aplhidev_softc *sc) { struct aplhidev_spi_packet packet; struct aplhidev_get_desc *msg; struct aplhidev_spi_status status; memset(&packet, 0, sizeof(packet)); packet.flags = APLHIDEV_WRITE_PACKET; packet.device = APLHIDEV_TP_DEVICE; packet.len = sizeof(*msg); msg = (void *)&packet.data[0]; msg->hdr.type = APLHIDEV_GET_DIMENSIONS; msg->hdr.device = 0; msg->hdr.msgid = sc->sc_msgid++; msg->hdr.cmdlen = 0; msg->hdr.rsplen = APLHIDEV_DESC_MAX; msg->crc = crc16(0, (void *)msg, sizeof(*msg) - 2); packet.crc = crc16(0, (void *)&packet, sizeof(packet) - 2); spi_acquire_bus(sc->sc_spi_tag, 0); spi_config(sc->sc_spi_tag, &sc->sc_spi_conf); spi_transfer(sc->sc_spi_tag, (char *)&packet, NULL, sizeof(packet), SPI_KEEP_CS); delay(100); spi_read(sc->sc_spi_tag, (char *)&status, sizeof(status)); spi_release_bus(sc->sc_spi_tag, 0); delay(1000); } int aplhidev_intr(void *arg) { struct aplhidev_softc *sc = arg; struct aplhidev_spi_packet packet; struct aplhidev_msghdr *hdr = (struct aplhidev_msghdr *)&packet.data[0]; memset(&packet, 0, sizeof(packet)); spi_acquire_bus(sc->sc_spi_tag, 0); spi_config(sc->sc_spi_tag, &sc->sc_spi_conf); spi_read(sc->sc_spi_tag, (char *)&packet, sizeof(packet)); spi_release_bus(sc->sc_spi_tag, 0); /* Treat empty packets as spurious interrupts. */ if (packet.flags == 0 && packet.device == 0 && packet.crc == 0) return 0; if (crc16(0, (uint8_t *)&packet, sizeof(packet))) return 1; /* Keyboard input. */ if (packet.flags == APLHIDEV_READ_PACKET && packet.device == APLHIDEV_KBD_DEVICE && hdr->type == APLHIDEV_KBD_REPORT) { if (sc->sc_kbd) aplkbd_intr(sc->sc_kbd, &packet.data[8], hdr->cmdlen); return 1; } /* Touchpad input. */ if (packet.flags == APLHIDEV_READ_PACKET && packet.device == APLHIDEV_TP_DEVICE && hdr->type == APLHIDEV_TP_REPORT) { if (sc->sc_ms) aplms_intr(sc->sc_ms, &packet.data[8], hdr->cmdlen); return 1; } /* Replies to commands we sent. */ if (packet.flags == APLHIDEV_WRITE_PACKET && packet.device == APLHIDEV_INFO_DEVICE && hdr->type == APLHIDEV_GET_INFO) { struct aplhidev_info_hdr *info = (struct aplhidev_info_hdr *)&packet.data[8]; sc->sc_vendor = info->vendor; sc->sc_product = info->product; return 1; } if (packet.flags == APLHIDEV_WRITE_PACKET && packet.device == APLHIDEV_INFO_DEVICE && hdr->type == APLHIDEV_GET_DESCRIPTOR) { switch (hdr->device) { case APLHIDEV_KBD_DEVICE: memcpy(sc->sc_kbddesc, &packet.data[8], hdr->cmdlen); sc->sc_kbddesclen = hdr->cmdlen; break; case APLHIDEV_TP_DEVICE: memcpy(sc->sc_tpdesc, &packet.data[8], hdr->cmdlen); sc->sc_tpdesclen = hdr->cmdlen; break; } return 1; } if (packet.flags == APLHIDEV_WRITE_PACKET && packet.device == APLHIDEV_TP_DEVICE && hdr->type == APLHIDEV_SET_MODE) { sc->sc_mode = APLHIDEV_MODE_RAW; return 1; } if (packet.flags == APLHIDEV_WRITE_PACKET && packet.device == APLHIDEV_TP_DEVICE && hdr->type == APLHIDEV_GET_DIMENSIONS) { memcpy(sc->sc_dimdesc, &packet.data[8], hdr->cmdlen); sc->sc_dimdesclen = hdr->cmdlen; return 1; } /* Valid, but unrecognized packet; ignore for now. */ return 1; } /* Keyboard */ struct aplkbd_softc { struct device sc_dev; struct aplhidev_softc *sc_hidev; struct hidkbd sc_kbd; int sc_spl; }; void aplkbd_cngetc(void *, u_int *, int *); void aplkbd_cnpollc(void *, int); void aplkbd_cnbell(void *, u_int, u_int, u_int); const struct wskbd_consops aplkbd_consops = { aplkbd_cngetc, aplkbd_cnpollc, aplkbd_cnbell, }; int aplkbd_enable(void *, int); void aplkbd_set_leds(void *, int); int aplkbd_ioctl(void *, u_long, caddr_t, int, struct proc *); const struct wskbd_accessops aplkbd_accessops = { .enable = aplkbd_enable, .ioctl = aplkbd_ioctl, .set_leds = aplkbd_set_leds, }; int aplkbd_match(struct device *, void *, void *); void aplkbd_attach(struct device *, struct device *, void *); const struct cfattach aplkbd_ca = { sizeof(struct aplkbd_softc), aplkbd_match, aplkbd_attach }; struct cfdriver aplkbd_cd = { NULL, "aplkbd", DV_DULL }; int aplkbd_match(struct device *parent, void *match, void *aux) { struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux; return (aa->aa_reportid == APLHIDEV_KBD_DEVICE); } void aplkbd_attach(struct device *parent, struct device *self, void *aux) { struct aplkbd_softc *sc = (struct aplkbd_softc *)self; struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux; struct hidkbd *kbd = &sc->sc_kbd; sc->sc_hidev = (struct aplhidev_softc *)parent; if (hidkbd_attach(self, kbd, 1, 0, APLHIDEV_KBD_DEVICE, aa->aa_desc, aa->aa_desclen)) return; printf("\n"); if (hid_locate(aa->aa_desc, aa->aa_desclen, HID_USAGE2(HUP_APPLE, HUG_FN_KEY), 1, hid_input, &kbd->sc_fn, NULL)) { switch (sc->sc_hidev->sc_product) { case USB_PRODUCT_APPLE_WELLSPRINGM1_J293: kbd->sc_munge = hidkbd_apple_tb_munge; break; default: kbd->sc_munge = hidkbd_apple_munge; break; } } if (kbd->sc_console_keyboard) { extern struct wskbd_mapdata ukbd_keymapdata; ukbd_keymapdata.layout = KB_US | KB_DEFAULT; wskbd_cnattach(&aplkbd_consops, sc, &ukbd_keymapdata); aplkbd_enable(sc, 1); } hidkbd_attach_wskbd(kbd, KB_US | KB_DEFAULT, &aplkbd_accessops); } void aplkbd_intr(struct device *self, uint8_t *packet, size_t packetlen) { struct aplkbd_softc *sc = (struct aplkbd_softc *)self; struct hidkbd *kbd = &sc->sc_kbd; if (kbd->sc_enabled) hidkbd_input(kbd, &packet[1], packetlen - 1); } int aplkbd_enable(void *v, int on) { struct aplkbd_softc *sc = v; struct hidkbd *kbd = &sc->sc_kbd; return hidkbd_enable(kbd, on); } int aplkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) { struct aplkbd_softc *sc = v; struct hidkbd *kbd = &sc->sc_kbd; switch (cmd) { case WSKBDIO_GTYPE: /* XXX: should we set something else? */ *(u_int *)data = WSKBD_TYPE_USB; return 0; case WSKBDIO_SETLEDS: aplkbd_set_leds(v, *(int *)data); return 0; default: return hidkbd_ioctl(kbd, cmd, data, flag, p); } } void aplkbd_set_leds(void *v, int leds) { struct aplkbd_softc *sc = v; struct hidkbd *kbd = &sc->sc_kbd; uint8_t res; if (hidkbd_set_leds(kbd, leds, &res)) aplhidev_set_leds(sc->sc_hidev, res); } /* Console interface. */ void aplkbd_cngetc(void *v, u_int *type, int *data) { struct aplkbd_softc *sc = v; struct hidkbd *kbd = &sc->sc_kbd; kbd->sc_polling = 1; while (kbd->sc_npollchar <= 0) { aplhidev_intr(sc->sc_dev.dv_parent); delay(1000); } kbd->sc_polling = 0; hidkbd_cngetc(kbd, type, data); } void aplkbd_cnpollc(void *v, int on) { struct aplkbd_softc *sc = v; if (on) sc->sc_spl = spltty(); else splx(sc->sc_spl); } void aplkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) { hidkbd_bell(pitch, period, volume, 1); } #if NAPLMS > 0 /* Touchpad */ /* * The contents of the touchpad event packets is identical to those * used by the ubcmtp(4) driver. The relevant definitions and the * code to decode the packets is replicated here. */ struct ubcmtp_finger { uint16_t origin; uint16_t abs_x; uint16_t abs_y; uint16_t rel_x; uint16_t rel_y; uint16_t tool_major; uint16_t tool_minor; uint16_t orientation; uint16_t touch_major; uint16_t touch_minor; uint16_t unused[2]; uint16_t pressure; uint16_t multi; } __packed __attribute((aligned(2))); #define UBCMTP_MAX_FINGERS 16 #define UBCMTP_TYPE4_TPOFF (24 * sizeof(uint16_t)) #define UBCMTP_TYPE4_BTOFF 31 #define UBCMTP_TYPE4_FINGERPAD (1 * sizeof(uint16_t)) /* Use a constant, synaptics-compatible pressure value for now. */ #define DEFAULT_PRESSURE 40 static struct wsmouse_param aplms_wsmousecfg[] = { { WSMOUSECFG_MTBTN_MAXDIST, 0 }, /* 0: Compute a default value. */ }; struct aplms_softc { struct device sc_dev; struct aplhidev_softc *sc_hidev; struct device *sc_wsmousedev; int sc_enabled; int tp_offset; int tp_fingerpad; struct mtpoint frame[UBCMTP_MAX_FINGERS]; int contacts; int btn; }; int aplms_enable(void *); void aplms_disable(void *); int aplms_ioctl(void *, u_long, caddr_t, int, struct proc *); const struct wsmouse_accessops aplms_accessops = { .enable = aplms_enable, .disable = aplms_disable, .ioctl = aplms_ioctl, }; int aplms_match(struct device *, void *, void *); void aplms_attach(struct device *, struct device *, void *); const struct cfattach aplms_ca = { sizeof(struct aplms_softc), aplms_match, aplms_attach }; struct cfdriver aplms_cd = { NULL, "aplms", DV_DULL }; int aplms_configure(struct aplms_softc *); int aplms_match(struct device *parent, void *match, void *aux) { struct aplhidev_attach_args *aa = (struct aplhidev_attach_args *)aux; return (aa->aa_reportid == APLHIDEV_TP_DEVICE); } void aplms_attach(struct device *parent, struct device *self, void *aux) { struct aplms_softc *sc = (struct aplms_softc *)self; struct wsmousedev_attach_args aa; sc->sc_hidev = (struct aplhidev_softc *)parent; printf("\n"); sc->tp_offset = UBCMTP_TYPE4_TPOFF; sc->tp_fingerpad = UBCMTP_TYPE4_FINGERPAD; aa.accessops = &aplms_accessops; aa.accesscookie = sc; sc->sc_wsmousedev = config_found(self, &aa, wsmousedevprint); if (sc->sc_wsmousedev != NULL && aplms_configure(sc)) aplms_disable(sc); } int aplms_configure(struct aplms_softc *sc) { struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev); hw->type = WSMOUSE_TYPE_TOUCHPAD; hw->hw_type = WSMOUSEHW_CLICKPAD; hw->x_min = sc->sc_hidev->sc_x_min; hw->x_max = sc->sc_hidev->sc_x_max; hw->y_min = sc->sc_hidev->sc_y_min; hw->y_max = sc->sc_hidev->sc_y_max; hw->h_res = sc->sc_hidev->sc_h_res; hw->v_res = sc->sc_hidev->sc_v_res; hw->mt_slots = UBCMTP_MAX_FINGERS; hw->flags = WSMOUSEHW_MT_TRACKING; return wsmouse_configure(sc->sc_wsmousedev, aplms_wsmousecfg, nitems(aplms_wsmousecfg)); } void aplms_intr(struct device *self, uint8_t *packet, size_t packetlen) { struct aplms_softc *sc = (struct aplms_softc *)self; struct ubcmtp_finger *finger; int off, s, btn, contacts; if (!sc->sc_enabled) return; contacts = 0; for (off = sc->tp_offset; off < packetlen; off += (sizeof(struct ubcmtp_finger) + sc->tp_fingerpad)) { finger = (struct ubcmtp_finger *)(packet + off); if ((int16_t)letoh16(finger->touch_major) == 0) continue; /* finger lifted */ sc->frame[contacts].x = (int16_t)letoh16(finger->abs_x); sc->frame[contacts].y = (int16_t)letoh16(finger->abs_y); sc->frame[contacts].pressure = DEFAULT_PRESSURE; contacts++; } btn = sc->btn; sc->btn = !!((int16_t)letoh16(packet[UBCMTP_TYPE4_BTOFF])); if (contacts || sc->contacts || sc->btn != btn) { sc->contacts = contacts; s = spltty(); wsmouse_buttons(sc->sc_wsmousedev, sc->btn); wsmouse_mtframe(sc->sc_wsmousedev, sc->frame, contacts); wsmouse_input_sync(sc->sc_wsmousedev); splx(s); } } int aplms_enable(void *v) { struct aplms_softc *sc = v; if (sc->sc_enabled) return EBUSY; sc->sc_enabled = 1; return 0; } void aplms_disable(void *v) { struct aplms_softc *sc = v; sc->sc_enabled = 0; } int aplms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) { struct aplms_softc *sc = v; struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev); struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; int wsmode; switch (cmd) { case WSMOUSEIO_GTYPE: *(u_int *)data = hw->type; break; case WSMOUSEIO_GCALIBCOORDS: wsmc->minx = hw->x_min; wsmc->maxx = hw->x_max; wsmc->miny = hw->y_min; wsmc->maxy = hw->y_max; wsmc->swapxy = 0; wsmc->resx = hw->h_res; wsmc->resy = hw->v_res; break; case WSMOUSEIO_SETMODE: wsmode = *(u_int *)data; if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) { printf("%s: invalid mode %d\n", sc->sc_dev.dv_xname, wsmode); return (EINVAL); } wsmouse_set_mode(sc->sc_wsmousedev, wsmode); break; default: return -1; } return 0; } #else void aplms_intr(struct device *self, uint8_t *packet, size_t packetlen) { } #endif