Lines Matching +full:multi +full:- +full:touch

1 /*-
2 * Copyright (c) 2014-2017 Vladimir Kondratyev <wulf@FreeBSD.org>
28 * MS Windows 7/8/10 compatible USB HID Multi-touch Device driver.
29 * https://msdn.microsoft.com/en-us/library/windows/hardware/jj151569(v=vs.85).aspx
30 * https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
62 "USB MSWindows 7/8/10 compatible Multi-touch Device");
109 #define WMT_NO_USAGE -1
214 bool touch; member
289 if (uaa->usb_mode != USB_MODE_HOST) in wmt_probe()
292 if (uaa->info.bInterfaceClass != UICLASS_HID) in wmt_probe()
298 err = usbd_req_get_hid_desc(uaa->device, NULL, in wmt_probe()
299 &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex); in wmt_probe()
304 if (sc->type == WMT_TYPE_UNKNOWN) in wmt_probe()
305 sc->type = wmt_hid_parse(sc, d_ptr, d_len); in wmt_probe()
306 if (sc->type != WMT_TYPE_UNSUPPORTED) in wmt_probe()
312 if (sc->type != WMT_TYPE_UNSUPPORTED && in wmt_probe()
313 (sc->isize <= 0 || sc->isize > WMT_BSIZE)) { in wmt_probe()
314 DPRINTF("Input size invalid or too large: %d\n", sc->isize); in wmt_probe()
333 sc->dev = dev; in wmt_attach()
336 if (sc->cont_max_rlen > 0 && sc->cont_max_rlen <= WMT_BSIZE) { in wmt_attach()
337 err = usbd_req_get_report(uaa->device, NULL, sc->buf, in wmt_attach()
338 sc->cont_max_rlen, uaa->info.bIfaceIndex, in wmt_attach()
339 UHID_FEATURE_REPORT, sc->cont_max_rid); in wmt_attach()
341 cont_count_max = hid_get_udata(sc->buf + 1, in wmt_attach()
342 sc->cont_max_rlen - 1, &sc->cont_max_loc); in wmt_attach()
348 sc->cont_count_max = cont_count_max; in wmt_attach()
354 sc->cont_max_rid, sc->cont_max_rlen); in wmt_attach()
357 if (sc->btn_type_rlen > 1 && sc->btn_type_rlen <= WMT_BSIZE && in wmt_attach()
358 sc->btn_type_rid != sc->cont_max_rid) { in wmt_attach()
359 bzero(sc->buf, sc->btn_type_rlen); in wmt_attach()
360 err = usbd_req_get_report(uaa->device, NULL, sc->buf, in wmt_attach()
361 sc->btn_type_rlen, uaa->info.bIfaceIndex, in wmt_attach()
362 UHID_FEATURE_REPORT, sc->btn_type_rid); in wmt_attach()
364 if (sc->btn_type_rlen > 1) { in wmt_attach()
366 sc->is_clickpad = hid_get_udata(sc->buf + 1, in wmt_attach()
367 sc->btn_type_rlen - 1, &sc->btn_type_loc) == 0; in wmt_attach()
373 if (sc->thqa_cert_rlen > 0 && sc->thqa_cert_rlen <= WMT_BSIZE && in wmt_attach()
374 sc->thqa_cert_rid != sc->cont_max_rid) in wmt_attach()
375 (void)usbd_req_get_report(uaa->device, NULL, sc->buf, in wmt_attach()
376 sc->thqa_cert_rlen, uaa->info.bIfaceIndex, in wmt_attach()
377 UHID_FEATURE_REPORT, sc->thqa_cert_rid); in wmt_attach()
380 if (sc->type == WMT_TYPE_TOUCHPAD) { in wmt_attach()
387 if (sc->cont_count_max > MAX_MT_SLOTS) { in wmt_attach()
389 "supported\n", (int)sc->cont_count_max, MAX_MT_SLOTS); in wmt_attach()
390 sc->cont_count_max = MAX_MT_SLOTS; in wmt_attach()
394 sc->do_timestamps = true; in wmt_attach()
396 mtx_init(&sc->mtx, "wmt lock", NULL, MTX_DEF); in wmt_attach()
398 err = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, in wmt_attach()
399 sc->xfer, wmt_config, WMT_N_TRANSFER, sc, &sc->mtx); in wmt_attach()
405 sc->evdev = evdev_alloc(); in wmt_attach()
406 evdev_set_name(sc->evdev, device_get_desc(dev)); in wmt_attach()
407 evdev_set_phys(sc->evdev, device_get_nameunit(dev)); in wmt_attach()
408 evdev_set_id(sc->evdev, BUS_USB, uaa->info.idVendor, in wmt_attach()
409 uaa->info.idProduct, 0); in wmt_attach()
410 evdev_set_serial(sc->evdev, usb_get_serial(uaa->device)); in wmt_attach()
411 evdev_set_methods(sc->evdev, sc, &wmt_evdev_methods); in wmt_attach()
412 evdev_set_flag(sc->evdev, EVDEV_FLAG_MT_STCOMPAT); in wmt_attach()
413 switch (sc->type) { in wmt_attach()
415 evdev_support_prop(sc->evdev, INPUT_PROP_DIRECT); in wmt_attach()
418 evdev_support_prop(sc->evdev, INPUT_PROP_POINTER); in wmt_attach()
419 if (sc->is_clickpad) in wmt_attach()
420 evdev_support_prop(sc->evdev, INPUT_PROP_BUTTONPAD); in wmt_attach()
423 KASSERT(0, ("wmt_attach: unsupported touch device type")); in wmt_attach()
425 evdev_support_event(sc->evdev, EV_SYN); in wmt_attach()
426 evdev_support_event(sc->evdev, EV_ABS); in wmt_attach()
427 if (sc->do_timestamps) { in wmt_attach()
428 evdev_support_event(sc->evdev, EV_MSC); in wmt_attach()
429 evdev_support_msc(sc->evdev, MSC_TIMESTAMP); in wmt_attach()
432 if (sc->max_button != 0 || sc->has_int_button) { in wmt_attach()
433 evdev_support_event(sc->evdev, EV_KEY); in wmt_attach()
434 if (sc->has_int_button) in wmt_attach()
435 evdev_support_key(sc->evdev, BTN_LEFT); in wmt_attach()
436 for (btn = 0; btn < sc->max_button; ++btn) { in wmt_attach()
437 if (isset(sc->buttons, btn)) { in wmt_attach()
438 evdev_support_key(sc->evdev, BTN_MOUSE + btn); in wmt_attach()
443 evdev_support_abs(sc->evdev, in wmt_attach()
444 ABS_MT_SLOT, 0, sc->cont_count_max - 1, 0, 0, 0); in wmt_attach()
445 WMT_FOREACH_USAGE(sc->caps, i) { in wmt_attach()
447 evdev_support_abs(sc->evdev, ABS_MT_FIRST + i, in wmt_attach()
448 sc->ai[i].min, sc->ai[i].max, 0, 0, sc->ai[i].res); in wmt_attach()
451 err = evdev_register_mtx(sc->evdev, &sc->mtx); in wmt_attach()
455 /* Announce information about the touch device */ in wmt_attach()
456 device_printf(sc->dev, "Multitouch %s with %d external button%s%s\n", in wmt_attach()
457 sc->type == WMT_TYPE_TOUCHSCREEN ? "touchscreen" : "touchpad", in wmt_attach()
459 sc->is_clickpad ? ", click-pad" : ""); in wmt_attach()
460 device_printf(sc->dev, in wmt_attach()
461 "%d contacts and [%s%s%s%s%s]. Report range [%d:%d] - [%d:%d]\n", in wmt_attach()
462 (int)sc->cont_count_max, in wmt_attach()
463 isset(sc->caps, WMT_IN_RANGE) ? "R" : "", in wmt_attach()
464 isset(sc->caps, WMT_CONFIDENCE) ? "C" : "", in wmt_attach()
465 isset(sc->caps, WMT_WIDTH) ? "W" : "", in wmt_attach()
466 isset(sc->caps, WMT_HEIGHT) ? "H" : "", in wmt_attach()
467 isset(sc->caps, WMT_PRESSURE) ? "P" : "", in wmt_attach()
468 (int)sc->ai[WMT_X].min, (int)sc->ai[WMT_Y].min, in wmt_attach()
469 (int)sc->ai[WMT_X].max, (int)sc->ai[WMT_Y].max); in wmt_attach()
483 evdev_free(sc->evdev); in wmt_detach()
484 usbd_transfer_unsetup(sc->xfer, WMT_N_TRANSFER); in wmt_detach()
485 mtx_destroy(&sc->mtx); in wmt_detach()
507 * collection that is embedded in the top-level collection." in wmt_process_report()
510 * report with contactid=0 but contactids are zero-based, find in wmt_process_report()
513 cont_count = hid_get_udata(buf, len, &sc->cont_count_loc); in wmt_process_report()
518 * 4 concurrent physical contacts, can set up its top-level collection in wmt_process_report()
529 sc->nconts_todo = cont_count; in wmt_process_report()
534 WMT_FOREACH_USAGE(sc->caps, usage) { in wmt_process_report()
536 printf(" %-4s", wmt_hid_map[usage].name); in wmt_process_report()
543 cont_count = MIN(sc->nconts_todo, sc->nconts_per_report); in wmt_process_report()
547 slot_data = &sc->slot_data; in wmt_process_report()
548 bzero(slot_data, sizeof(sc->slot_data)); in wmt_process_report()
549 WMT_FOREACH_USAGE(sc->caps, usage) { in wmt_process_report()
550 if (sc->locs[cont][usage].size > 0) in wmt_process_report()
551 slot_data->val[usage] = hid_get_udata( in wmt_process_report()
552 buf, len, &sc->locs[cont][usage]); in wmt_process_report()
555 slot = evdev_mt_id_to_slot(sc->evdev, slot_data->id); in wmt_process_report()
560 WMT_FOREACH_USAGE(sc->caps, usage) { in wmt_process_report()
562 printf("%04x ", slot_data->val[usage]); in wmt_process_report()
568 if (slot == -1) { in wmt_process_report()
570 (unsigned)slot_data->id); in wmt_process_report()
574 if (slot_data->val[WMT_TIP_SWITCH] != 0 && in wmt_process_report()
575 !(isset(sc->caps, WMT_CONFIDENCE) && in wmt_process_report()
576 slot_data->val[WMT_CONFIDENCE] == 0)) { in wmt_process_report()
578 sc->touch = true; in wmt_process_report()
579 slot_data->dist = !slot_data->val[WMT_IN_RANGE]; in wmt_process_report()
580 /* Divided by two to match visual scale of touch */ in wmt_process_report()
581 width = slot_data->val[WMT_WIDTH] >> 1; in wmt_process_report()
582 height = slot_data->val[WMT_HEIGHT] >> 1; in wmt_process_report()
583 slot_data->ori = width > height; in wmt_process_report()
584 slot_data->maj = MAX(width, height); in wmt_process_report()
585 slot_data->min = MIN(width, height); in wmt_process_report()
589 evdev_mt_push_slot(sc->evdev, slot, slot_data); in wmt_process_report()
592 sc->nconts_todo -= cont_count; in wmt_process_report()
593 if (sc->do_timestamps && sc->nconts_todo == 0) { in wmt_process_report()
595 scan_time = hid_get_udata(buf, len, &sc->scan_time_loc); in wmt_process_report()
596 if (sc->prev_touch) { in wmt_process_report()
597 delta = scan_time - sc->scan_time; in wmt_process_report()
599 delta += sc->scan_time_max; in wmt_process_report()
602 sc->scan_time = scan_time; in wmt_process_report()
603 sc->timestamp += delta * 100; in wmt_process_report()
604 evdev_push_msc(sc->evdev, MSC_TIMESTAMP, sc->timestamp); in wmt_process_report()
605 sc->prev_touch = sc->touch; in wmt_process_report()
606 sc->touch = false; in wmt_process_report()
607 if (!sc->prev_touch) in wmt_process_report()
608 sc->timestamp = 0; in wmt_process_report()
610 if (sc->nconts_todo == 0) { in wmt_process_report()
612 if (sc->has_int_button) in wmt_process_report()
613 int_btn = hid_get_data(buf, len, &sc->int_btn_loc); in wmt_process_report()
614 if (isset(sc->buttons, 0)) in wmt_process_report()
615 left_btn = hid_get_data(buf, len, &sc->btn_loc[0]); in wmt_process_report()
616 if (sc->has_int_button || isset(sc->buttons, 0)) in wmt_process_report()
617 evdev_push_key(sc->evdev, BTN_LEFT, in wmt_process_report()
619 for (btn = 1; btn < sc->max_button; ++btn) { in wmt_process_report()
620 if (isset(sc->buttons, btn)) in wmt_process_report()
621 evdev_push_key(sc->evdev, BTN_MOUSE + btn, in wmt_process_report()
624 &sc->btn_loc[btn]) != 0); in wmt_process_report()
626 evdev_sync(sc->evdev); in wmt_process_report()
635 uint8_t *buf = sc->buf; in wmt_intr_callback()
646 if (len >= (int)sc->report_len || in wmt_intr_callback()
647 (len > 0 && sc->report_id != 0)) { in wmt_intr_callback()
649 if (len > (int)sc->report_len) in wmt_intr_callback()
650 len = sc->report_len; in wmt_intr_callback()
655 if (sc->report_id && *buf != sc->report_id) in wmt_intr_callback()
659 if (len < sc->report_len) in wmt_intr_callback()
660 bzero(buf + len, sc->report_len - len); in wmt_intr_callback()
663 if (sc->report_id) { in wmt_intr_callback()
664 len--; in wmt_intr_callback()
676 usbd_xfer_set_frame_len(xfer, 0, sc->isize); in wmt_intr_callback()
694 mtx_assert(&sc->mtx, MA_OWNED); in wmt_ev_close_11()
695 usbd_transfer_stop(sc->xfer[WMT_INTR_DT]); in wmt_ev_close_11()
703 mtx_assert(&sc->mtx, MA_OWNED); in wmt_ev_open_11()
704 usbd_transfer_start(sc->xfer[WMT_INTR_DT]); in wmt_ev_open_11()
775 sc->thqa_cert_rid = hi.report_ID; in wmt_hid_parse()
781 sc->cont_max_rid = hi.report_ID; in wmt_hid_parse()
782 sc->cont_max_loc = hi.loc; in wmt_hid_parse()
787 sc->btn_type_rid = hi.report_ID; in wmt_hid_parse()
788 sc->btn_type_loc = hi.loc; in wmt_hid_parse()
800 if (sc->cont_max_rid == 0) in wmt_hid_parse()
839 sc->int_btn_loc = hi.loc; in wmt_hid_parse()
845 btn = (hi.usage & 0xFFFF) - left_btn; in wmt_hid_parse()
846 setbit(sc->buttons, btn); in wmt_hid_parse()
847 sc->btn_loc[btn] = hi.loc; in wmt_hid_parse()
848 if (btn >= sc->max_button) in wmt_hid_parse()
849 sc->max_button = btn + 1; in wmt_hid_parse()
855 sc->cont_count_loc = hi.loc; in wmt_hid_parse()
862 sc->scan_time_loc = hi.loc; in wmt_hid_parse()
863 sc->scan_time_max = hi.logical_maximum; in wmt_hid_parse()
882 if (sc->locs[cont][i].size) in wmt_hid_parse()
884 sc->locs[cont][i] = hi.loc; in wmt_hid_parse()
892 setbit(sc->caps, i); in wmt_hid_parse()
893 sc->ai[i] = (struct wmt_absinfo) { in wmt_hid_parse()
912 if (wmt_hid_map[i].required && isclr(sc->caps, i)) in wmt_hid_parse()
917 if (type == WMT_TYPE_TOUCHPAD && !sc->max_button && !has_int_button) in wmt_hid_parse()
928 /* Report touch orientation if both width and height are supported */ in wmt_hid_parse()
929 if (isset(sc->caps, WMT_WIDTH) && isset(sc->caps, WMT_HEIGHT)) { in wmt_hid_parse()
930 setbit(sc->caps, WMT_ORIENTATION); in wmt_hid_parse()
931 sc->ai[WMT_ORIENTATION].max = 1; in wmt_hid_parse()
934 sc->isize = hid_report_size_max(d_ptr, d_len, hid_input, NULL); in wmt_hid_parse()
935 sc->report_len = hid_report_size(d_ptr, d_len, hid_input, in wmt_hid_parse()
937 sc->cont_max_rlen = hid_report_size(d_ptr, d_len, hid_feature, in wmt_hid_parse()
938 sc->cont_max_rid); in wmt_hid_parse()
939 if (sc->btn_type_rid > 0) in wmt_hid_parse()
940 sc->btn_type_rlen = hid_report_size(d_ptr, d_len, in wmt_hid_parse()
941 hid_feature, sc->btn_type_rid); in wmt_hid_parse()
942 if (sc->thqa_cert_rid > 0) in wmt_hid_parse()
943 sc->thqa_cert_rlen = hid_report_size(d_ptr, d_len, in wmt_hid_parse()
944 hid_feature, sc->thqa_cert_rid); in wmt_hid_parse()
946 sc->report_id = report_id; in wmt_hid_parse()
947 sc->nconts_per_report = cont; in wmt_hid_parse()
948 sc->has_int_button = has_int_button; in wmt_hid_parse()
949 sc->cont_count_max = cont_count_max; in wmt_hid_parse()
957 struct usb_attach_arg *uaa = device_get_ivars(sc->dev); in wmt_set_input_mode()
960 if (sc->input_mode_rlen < 3 || sc->input_mode_rlen > WMT_BSIZE) { in wmt_set_input_mode()
962 sc->input_mode_rid, sc->input_mode_rlen); in wmt_set_input_mode()
967 err = usbd_req_get_report(uaa->device, NULL, sc->buf, in wmt_set_input_mode()
968 sc->input_mode_rlen, uaa->info.bIfaceIndex, in wmt_set_input_mode()
969 UHID_FEATURE_REPORT, sc->input_mode_rid); in wmt_set_input_mode()
971 bzero(sc->buf + 1, sc->input_mode_rlen - 1); in wmt_set_input_mode()
973 sc->buf[0] = sc->input_mode_rid; in wmt_set_input_mode()
974 hid_put_udata(sc->buf + 1, sc->input_mode_rlen - 1, in wmt_set_input_mode()
975 &sc->input_mode_loc, mode); in wmt_set_input_mode()
976 err = usbd_req_set_report(uaa->device, NULL, sc->buf, in wmt_set_input_mode()
977 sc->input_mode_rlen, uaa->info.bIfaceIndex, in wmt_set_input_mode()
978 UHID_FEATURE_REPORT, sc->input_mode_rid); in wmt_set_input_mode()