Index: sys/amd64/conf/GENERIC =================================================================== --- sys/amd64/conf/GENERIC +++ sys/amd64/conf/GENERIC @@ -344,6 +344,7 @@ device usb # USB Bus (required) device ukbd # Keyboard device umass # Disks/Mass storage - Requires scbus and da +device vbmouse # VirtualBox mouse support # Sound support device sound # Generic sound driver (required) Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -3445,6 +3445,7 @@ dev/usb/input/usbhid.c optional usbhid dev/usb/input/wmt.c optional wmt dev/usb/input/wsp.c optional wsp +dev/usb/input/vbmouse.c optional vbmouse # # USB quirks # Index: sys/dev/usb/input/vbmouse.c =================================================================== --- sys/dev/usb/input/vbmouse.c +++ sys/dev/usb/input/vbmouse.c @@ -0,0 +1,528 @@ +/*- + * Copyright (c) 2018 - 2022 Soren Schmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$Id$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "usbdevs.h" +#include + +#define VBMOUSE_MAX_BUT 8 + +enum { + VBMOUSE_INTR_DT, + VBMOUSE_N_TRANS, +}; + +struct vbmouse_softc +{ + device_t sc_dev; + struct mtx sc_mtx; + struct usb_xfer *sc_xfer[VBMOUSE_N_TRANS]; + struct hid_location sc_loc_x; + struct hid_location sc_loc_y; + struct hid_location sc_loc_z; + struct hid_location sc_loc_btn[VBMOUSE_MAX_BUT]; + uint8_t sc_iid_x; + uint8_t sc_iid_y; + uint8_t sc_iid_z; + uint8_t sc_iid_btn[VBMOUSE_MAX_BUT]; + uint8_t sc_nbuttons; + uint32_t sc_flags; +#define VBMOUSE_FLAG_X_AXIS 0x0001 +#define VBMOUSE_FLAG_Y_AXIS 0x0002 +#define VBMOUSE_FLAG_Z_AXIS 0x0004 +#define VBMOUSE_FLAG_OPENED 0x0008 + + uint8_t sc_temp[64]; +}; + +static device_probe_t vbmouse_probe; +static device_attach_t vbmouse_attach; +static device_detach_t vbmouse_detach; +static usb_callback_t vbmouse_intr_callback; + +static int vbmouse_hid_test(const void *, uint16_t); +static void vbmouse_hid_parse(struct vbmouse_softc *, const void *, uint16_t); + +static void vbmouse_control(int, int, int, int); +extern struct vt_device *main_vd; + +static MALLOC_DEFINE(M_VBM, "vbmouse", "vbmouse vt buffer"); + +static const struct usb_config vbmouse_config[VBMOUSE_N_TRANS] = { + [VBMOUSE_INTR_DT] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .bufsize = 0, + .callback = &vbmouse_intr_callback, + }, +}; + +static int +vbmouse_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + uint16_t len; + void *ptr; + int err; + + if (uaa->usb_mode != USB_MODE_HOST) + return ENXIO; + + if (uaa->info.bInterfaceClass != UICLASS_HID) + return ENXIO; + + err = usbd_req_get_hid_desc(uaa->device, NULL, &ptr, &len, M_TEMP, + uaa->info.bIfaceIndex); + if (err != USB_ERR_NORMAL_COMPLETION) + return ENXIO; + + if (vbmouse_hid_test(ptr, len)) + err = BUS_PROBE_DEFAULT; + else + err = ENXIO; + + free(ptr, M_TEMP); + return err; +} + +static int +vbmouse_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct vbmouse_softc *sc = device_get_softc(dev); + struct vt_device *vd = main_vd; + void *ptr = NULL; + uint16_t len; + int err; + + device_set_usb_desc(dev); + sc->sc_dev = dev; + + mtx_init(&sc->sc_mtx, "vbmouse lock", NULL, MTX_DEF | MTX_RECURSE); + + err = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, + vbmouse_config, VBMOUSE_N_TRANS, sc, &sc->sc_mtx); + if (err != USB_ERR_NORMAL_COMPLETION) { + usbd_transfer_unsetup(sc->sc_xfer, VBMOUSE_N_TRANS); + mtx_destroy(&sc->sc_mtx); + return ENXIO; + } + + err = usbd_req_get_hid_desc(uaa->device, NULL, &ptr, &len, M_TEMP, + uaa->info.bIfaceIndex); + if (err != USB_ERR_NORMAL_COMPLETION) { + usbd_transfer_unsetup(sc->sc_xfer, VBMOUSE_N_TRANS); + mtx_destroy(&sc->sc_mtx); + return ENXIO; + } + + vbmouse_hid_parse(sc, ptr, len); + free(ptr, M_TEMP); + + /* enable and show mouse */ + vd->vd_flags |= VDF_MOUSECURSOR; + vt_mouse_state(VT_MOUSE_SHOW); + + /* allocate 64kb cut&paste buffer */ + VD_PASTEBUF(vd) = malloc(65536, M_VBM, M_WAITOK | M_ZERO); + VD_PASTEBUFSZ(vd) = 65536; + VD_PASTEBUFLEN(vd) = 0; + + usbd_transfer_start(sc->sc_xfer[VBMOUSE_INTR_DT]); + return 0; +} + +static int +vbmouse_detach(device_t dev) +{ + struct vbmouse_softc *sc = device_get_softc(dev); + struct vt_device *vd = main_vd; + + usbd_transfer_unsetup(sc->sc_xfer, VBMOUSE_N_TRANS); + mtx_destroy(&sc->sc_mtx); + + /* disable and hide mouse */ + vd->vd_flags &= ~VDF_MOUSECURSOR; + vt_mouse_state(VT_MOUSE_HIDE); + + /* free cut&paste buffer */ + free(VD_PASTEBUF(vd), M_VBM); + + return 0; +} + +static void +vbmouse_intr_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct vbmouse_softc *sc = usbd_xfer_softc(xfer); + struct usb_page_cache *pc; + uint8_t *buf = sc->sc_temp; + uint8_t id; + int len, i; + int x = 0, y = 0, z = 0, buttons = 0; + + usbd_xfer_status(xfer, &len, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + break; + + case USB_ST_TRANSFERRED: + if (len > (int)sizeof(sc->sc_temp)) + len = sizeof(sc->sc_temp); + + if (!len) + break; + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, buf, len); + + id = 0; + if (sc->sc_iid_x > 0 || sc->sc_iid_y > 0) { + id = *buf; + len--; + buf++; + } + + x = hid_get_data(buf, len, &sc->sc_loc_x); + y = hid_get_data(buf, len, &sc->sc_loc_y); + z = hid_get_data(buf, len, &sc->sc_loc_z); + + for (i = 0; i < sc->sc_nbuttons; i++) + if (id == sc->sc_iid_btn[i]) { + if (hid_get_data(buf, len, &sc->sc_loc_btn[i])) + buttons |= (1<sc_flags |= VBMOUSE_FLAG_X_AXIS; + sc->sc_loc_x = hi.loc; + sc->sc_iid_x = hi.report_ID; + } + if (hi.usage == HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) && + (hi.flags&(HIO_CONST|HIO_VARIABLE|HIO_RELATIVE))==HIO_VARIABLE){ + sc->sc_flags |= VBMOUSE_FLAG_Y_AXIS; + sc->sc_loc_y = hi.loc; + sc->sc_iid_y = hi.report_ID; + } + break; + default: + break; + } + } + hid_end_parse(hd); + + /* try wheel as the Z activator */ + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), + hid_input, 0, &sc->sc_loc_z, &flags, &sc->sc_iid_z) || + hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), + hid_input, 0, &sc->sc_loc_z, &flags, &sc->sc_iid_z)) { + if (flags & HIO_VARIABLE) + sc->sc_flags |= VBMOUSE_FLAG_Z_AXIS; + } + + /* find number of buttons */ + for (i = 0; i < VBMOUSE_MAX_BUT; i++) { + if (!hid_locate(buf, len, HID_USAGE2(HUP_BUTTON, (i + 1)), + hid_input, 0, &sc->sc_loc_btn[i], NULL, &sc->sc_iid_btn[i])) + break; + } + sc->sc_nbuttons = i; + + if (!sc->sc_flags) + return; + + /* announce ourself */ + device_printf(sc->sc_dev, "%d buttons and [%s%s%s] axes\n", + (sc->sc_nbuttons), + (sc->sc_flags & VBMOUSE_FLAG_X_AXIS) ? "X" : "", + (sc->sc_flags & VBMOUSE_FLAG_Y_AXIS) ? "Y" : "", + (sc->sc_flags & VBMOUSE_FLAG_Z_AXIS) ? "Z" : ""); +} + +static void +vbmouse_control(int x, int y, int z, int buttons) +{ + struct vt_device *vd = main_vd; + struct vt_window *vw = vd->vd_curwindow; + struct vt_font *vf = vw->vw_font; + struct vt_buf *vb = &vw->vw_buf; + struct mouse_info mi; + term_pos_t size; + int changed = 0, len = 0; + static int lx = 0, ly = 0; + + /* convert absolute values to fit screen size */ + vt_termsize(vd, vf, &size); + vd->vd_mx = x / (32768 / (size.tp_col * vf->vf_width)); + vd->vd_my = y / (32768 / (size.tp_row * vf->vf_height)); + + /* send info to sysmouse driver */ + mi.operation = MOUSE_ACTION; + mi.u.data.buttons = buttons; + mi.u.data.x = (vd->vd_mx - lx); lx = vd->vd_mx; + mi.u.data.y = (vd->vd_my - ly); ly = vd->vd_my; + mi.u.data.z = z; + sysmouse_process_event(&mi); + + /* if mousepointer is hidden, do not update framebuffer */ + if (vw->vw_flags & VWF_MOUSE_HIDE) + return; + + /* if !scrollmode && z != 0 -> enter scroll mode */ + if (!(vb->vb_flags & VBF_SCROLL) && z) { + vtbuf_scroll_mode(vb, 1); + vw->vw_flags |= VWF_SCROLL; + return; + } + + /* if scrollmode && z -> scroll up/down */ + if ((vb->vb_flags & VBF_SCROLL) && z) { + vd_drawrect_t *drawrect = vd->vd_driver->vd_drawrect; + + if (vthistory_seek(vb, z * -1, VHS_CUR)) + vd->vd_flags |= VDF_INVALID; + if (drawrect) { + int pos, but, top; + + pos = vb->vb_curroffset - vb->vb_roffset; + if (pos <= 0) + pos += vb->vb_history_size; + pos = ((pos * vd->vd_height) / vb->vb_history_size); + top = pos; + if (top >= vd->vd_height) + top = 0; + else + top = vd->vd_height - (pos + 16); + but = vd->vd_height - pos; + drawrect(vd, vd->vd_width - 10, top, + vd->vd_width - 5, but, 1, TC_WHITE); + } + vt_resume_flush_timer(vw, 0); + return; + } + + /* if scrollmode && leftbutton down -> leave scroll mode */ + if ((vb->vb_flags & VBF_SCROLL) && (buttons & MOUSE_BUTTON1DOWN)) { + vw->vw_flags &= ~VWF_SCROLL; + vtbuf_scroll_mode(vb, 0); + vthistory_seek(vb, 0, VHS_END); + vd->vd_flags |= VDF_INVALID; + vt_resume_flush_timer(vw, 0); + return; + } + + /* update selection while active c&p mode*/ + if (vd->vd_mstate & MOUSE_BUTTON1DOWN) + vtbuf_set_mark(&vw->vw_buf, VTB_MARK_MOVE, + vd->vd_mx / vf->vf_width, vd->vd_my / vf->vf_height); + + /* update screen */ + vt_resume_flush_timer(vw, 0); + + /* if not in scroll mode and buttons pressed */ + if (!(vb->vb_flags & VBF_SCROLL) && (changed = (buttons ^ vd->vd_mstate))) { + + /* start/end of selection */ + if (changed & MOUSE_BUTTON1DOWN) { + if (buttons & MOUSE_BUTTON1DOWN) + vtbuf_set_mark(&vw->vw_buf, VTB_MARK_START, + vd->vd_mx/vf->vf_width, vd->vd_my/vf->vf_height); + else { + if (vtbuf_set_mark(&vw->vw_buf, VTB_MARK_END, + vd->vd_mx/vf->vf_width, + vd->vd_my/vf->vf_height)) { + len = MIN(65535, abs(vtbuf_get_marked_len(&vw->vw_buf))); + if (len) { + VD_PASTEBUFLEN(vd) = len; + vtbuf_extract_marked(&vw->vw_buf, VD_PASTEBUF(vd), + VD_PASTEBUFSZ(vd)); + } + } + } + } + + /* update selection */ + if (changed & MOUSE_BUTTON2DOWN) { + if (buttons & MOUSE_BUTTON2DOWN) + if (vtbuf_set_mark(&vw->vw_buf, VTB_MARK_EXTEND, + (vd->vd_mx / vf->vf_width) + 1, + vd->vd_my / vf->vf_height)) { + len = MIN(65535, abs(vtbuf_get_marked_len(&vw->vw_buf))); + if (len) { + VD_PASTEBUFLEN(vd) = len; + vtbuf_extract_marked(&vw->vw_buf, VD_PASTEBUF(vd), + VD_PASTEBUFSZ(vd)); + } + } + } + + /* paste selection */ + if (changed & MOUSE_BUTTON3DOWN) { + if (buttons & MOUSE_BUTTON3DOWN) { + term_char_t *buf = VD_PASTEBUF(main_vd); + int i; + + len = VD_PASTEBUFLEN(main_vd) / sizeof(term_char_t); + for (i = 0; i < len; i++) { + if (buf[i] == '\0') + continue; + terminal_input_char(main_vd->vd_curwindow->vw_terminal, + buf[i]); + } + } + } + + /* save new state */ + vd->vd_mstate = buttons; + } +} + +static const STRUCT_USB_HOST_ID vbmouse_devs[] = { + {USB_IFACE_CLASS(UICLASS_HID), USB_IFACE_SUBCLASS(0),}, +}; + +static devclass_t vbmouse_devclass; + +static device_method_t vbmouse_methods[] = { + DEVMETHOD(device_probe, vbmouse_probe), + DEVMETHOD(device_attach, vbmouse_attach), + DEVMETHOD(device_detach, vbmouse_detach), + DEVMETHOD_END +}; + +static driver_t vbmouse_driver = { + .name = "vbmouse", + .methods = vbmouse_methods, + .size = sizeof(struct vbmouse_softc), +}; + +DRIVER_MODULE(vbmouse, uhub, vbmouse_driver, vbmouse_devclass, NULL, 0); +MODULE_DEPEND(vbmouse, usb, 1, 1, 1); +MODULE_VERSION(vbmouse, 1); +USB_PNP_HOST_INFO(vbmouse_devs);