1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 /* 29 * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf 30 */ 31 32 #include <sys/param.h> 33 #include <sys/stdint.h> 34 #include <sys/stddef.h> 35 #include <sys/queue.h> 36 #include <sys/systm.h> 37 #include <sys/kernel.h> 38 #include <sys/bus.h> 39 #include <sys/linker_set.h> 40 #include <sys/module.h> 41 #include <sys/lock.h> 42 #include <sys/mutex.h> 43 #include <sys/condvar.h> 44 #include <sys/sysctl.h> 45 #include <sys/sx.h> 46 #include <sys/unistd.h> 47 #include <sys/callout.h> 48 #include <sys/malloc.h> 49 #include <sys/priv.h> 50 51 #include <dev/usb/usb.h> 52 #include <dev/usb/usbdi.h> 53 #include <dev/usb/usbdi_util.h> 54 #include <dev/usb/usbhid.h> 55 #include "usb_if.h" 56 57 #define USB_DEBUG_VAR g_mouse_debug 58 #include <dev/usb/usb_debug.h> 59 60 #include <dev/usb/gadget/g_mouse.h> 61 62 static SYSCTL_NODE(_hw_usb, OID_AUTO, g_mouse, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 63 "USB mouse gadget"); 64 65 #ifdef USB_DEBUG 66 static int g_mouse_debug = 0; 67 68 SYSCTL_INT(_hw_usb_g_mouse, OID_AUTO, debug, CTLFLAG_RWTUN, 69 &g_mouse_debug, 0, "Debug level"); 70 #endif 71 72 static int g_mouse_mode = 0; 73 74 SYSCTL_INT(_hw_usb_g_mouse, OID_AUTO, mode, CTLFLAG_RWTUN, 75 &g_mouse_mode, 0, "Mode selection"); 76 77 static int g_mouse_button_press_interval = 0; 78 79 SYSCTL_INT(_hw_usb_g_mouse, OID_AUTO, button_press_interval, CTLFLAG_RWTUN, 80 &g_mouse_button_press_interval, 0, "Mouse button update interval in milliseconds"); 81 82 static int g_mouse_cursor_update_interval = 1023; 83 84 SYSCTL_INT(_hw_usb_g_mouse, OID_AUTO, cursor_update_interval, CTLFLAG_RWTUN, 85 &g_mouse_cursor_update_interval, 0, "Mouse cursor update interval in milliseconds"); 86 87 static int g_mouse_cursor_radius = 128; 88 89 SYSCTL_INT(_hw_usb_g_mouse, OID_AUTO, cursor_radius, CTLFLAG_RWTUN, 90 &g_mouse_cursor_radius, 0, "Mouse cursor radius in pixels"); 91 92 struct g_mouse_data { 93 uint8_t buttons; 94 #define BUT_0 0x01 95 #define BUT_1 0x02 96 #define BUT_2 0x04 97 int8_t dx; 98 int8_t dy; 99 int8_t dz; 100 }; 101 102 enum { 103 G_MOUSE_INTR_DT, 104 G_MOUSE_N_TRANSFER, 105 }; 106 107 struct g_mouse_softc { 108 struct mtx sc_mtx; 109 struct usb_callout sc_button_press_callout; 110 struct usb_callout sc_cursor_update_callout; 111 struct g_mouse_data sc_data; 112 struct usb_xfer *sc_xfer[G_MOUSE_N_TRANSFER]; 113 114 int sc_mode; 115 int sc_radius; 116 int sc_last_x_state; 117 int sc_last_y_state; 118 int sc_curr_x_state; 119 int sc_curr_y_state; 120 int sc_tick; 121 122 uint8_t sc_do_cursor_update; 123 uint8_t sc_do_button_update; 124 }; 125 126 static device_probe_t g_mouse_probe; 127 static device_attach_t g_mouse_attach; 128 static device_detach_t g_mouse_detach; 129 static usb_handle_request_t g_mouse_handle_request; 130 static usb_callback_t g_mouse_intr_callback; 131 132 static device_method_t g_mouse_methods[] = { 133 /* USB interface */ 134 DEVMETHOD(usb_handle_request, g_mouse_handle_request), 135 136 /* Device interface */ 137 DEVMETHOD(device_probe, g_mouse_probe), 138 DEVMETHOD(device_attach, g_mouse_attach), 139 DEVMETHOD(device_detach, g_mouse_detach), 140 141 DEVMETHOD_END 142 }; 143 144 static driver_t g_mouse_driver = { 145 .name = "g_mouse", 146 .methods = g_mouse_methods, 147 .size = sizeof(struct g_mouse_softc), 148 }; 149 150 DRIVER_MODULE(g_mouse, uhub, g_mouse_driver, 0, 0); 151 MODULE_DEPEND(g_mouse, usb, 1, 1, 1); 152 153 static const struct usb_config g_mouse_config[G_MOUSE_N_TRANSFER] = { 154 [G_MOUSE_INTR_DT] = { 155 .type = UE_INTERRUPT, 156 .endpoint = UE_ADDR_ANY, 157 .direction = UE_DIR_IN, 158 .flags = {.ext_buffer = 1,.pipe_bof = 1,}, 159 .bufsize = sizeof(struct g_mouse_data), 160 .callback = &g_mouse_intr_callback, 161 .frames = 1, 162 .usb_mode = USB_MODE_DEVICE, 163 }, 164 }; 165 166 static void g_mouse_button_press_timeout(void *arg); 167 static void g_mouse_cursor_update_timeout(void *arg); 168 169 static void 170 g_mouse_button_press_timeout_reset(struct g_mouse_softc *sc) 171 { 172 int i = g_mouse_button_press_interval; 173 174 if (i <= 0) { 175 sc->sc_data.buttons = 0; 176 sc->sc_do_button_update = 0; 177 } else { 178 sc->sc_do_button_update = 1; 179 } 180 181 if ((i <= 0) || (i > 1023)) 182 i = 1023; 183 184 i = USB_MS_TO_TICKS(i); 185 186 usb_callout_reset(&sc->sc_button_press_callout, i, 187 &g_mouse_button_press_timeout, sc); 188 } 189 190 static void 191 g_mouse_cursor_update_timeout_reset(struct g_mouse_softc *sc) 192 { 193 int i = g_mouse_cursor_update_interval; 194 195 if (i <= 0) { 196 sc->sc_data.dx = 0; 197 sc->sc_data.dy = 0; 198 sc->sc_do_cursor_update = 0; 199 sc->sc_tick = 0; 200 } else { 201 sc->sc_do_cursor_update = 1; 202 } 203 204 if ((i <= 0) || (i > 1023)) 205 i = 1023; 206 207 i = USB_MS_TO_TICKS(i); 208 209 usb_callout_reset(&sc->sc_cursor_update_callout, i, 210 &g_mouse_cursor_update_timeout, sc); 211 } 212 213 static void 214 g_mouse_update_mode_radius(struct g_mouse_softc *sc) 215 { 216 sc->sc_mode = g_mouse_mode; 217 sc->sc_radius = g_mouse_cursor_radius; 218 219 if (sc->sc_radius < 0) 220 sc->sc_radius = 0; 221 else if (sc->sc_radius > 1023) 222 sc->sc_radius = 1023; 223 } 224 225 static void 226 g_mouse_button_press_timeout(void *arg) 227 { 228 struct g_mouse_softc *sc = arg; 229 230 g_mouse_update_mode_radius(sc); 231 232 DPRINTFN(11, "Timeout %p (button press)\n", sc->sc_xfer[G_MOUSE_INTR_DT]); 233 234 g_mouse_button_press_timeout_reset(sc); 235 236 usbd_transfer_start(sc->sc_xfer[G_MOUSE_INTR_DT]); 237 } 238 239 static void 240 g_mouse_cursor_update_timeout(void *arg) 241 { 242 struct g_mouse_softc *sc = arg; 243 244 g_mouse_update_mode_radius(sc); 245 246 DPRINTFN(11, "Timeout %p (cursor update)\n", sc->sc_xfer[G_MOUSE_INTR_DT]); 247 248 g_mouse_cursor_update_timeout_reset(sc); 249 250 usbd_transfer_start(sc->sc_xfer[G_MOUSE_INTR_DT]); 251 } 252 253 static int 254 g_mouse_probe(device_t dev) 255 { 256 struct usb_attach_arg *uaa = device_get_ivars(dev); 257 258 DPRINTFN(11, "\n"); 259 260 if (uaa->usb_mode != USB_MODE_DEVICE) 261 return (ENXIO); 262 263 if ((uaa->info.bInterfaceClass == UICLASS_HID) && 264 (uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) && 265 (uaa->info.bInterfaceProtocol == UIPROTO_MOUSE)) 266 return (0); 267 268 return (ENXIO); 269 } 270 271 static int 272 g_mouse_attach(device_t dev) 273 { 274 struct g_mouse_softc *sc = device_get_softc(dev); 275 struct usb_attach_arg *uaa = device_get_ivars(dev); 276 int error; 277 278 DPRINTFN(11, "\n"); 279 280 device_set_usb_desc(dev); 281 282 mtx_init(&sc->sc_mtx, "g_mouse", NULL, MTX_DEF); 283 284 usb_callout_init_mtx(&sc->sc_button_press_callout, &sc->sc_mtx, 0); 285 usb_callout_init_mtx(&sc->sc_cursor_update_callout, &sc->sc_mtx, 0); 286 287 sc->sc_mode = G_MOUSE_MODE_SILENT; 288 289 error = usbd_transfer_setup(uaa->device, 290 &uaa->info.bIfaceIndex, sc->sc_xfer, g_mouse_config, 291 G_MOUSE_N_TRANSFER, sc, &sc->sc_mtx); 292 293 if (error) { 294 DPRINTF("error=%s\n", usbd_errstr(error)); 295 goto detach; 296 } 297 298 mtx_lock(&sc->sc_mtx); 299 g_mouse_button_press_timeout_reset(sc); 300 g_mouse_cursor_update_timeout_reset(sc); 301 mtx_unlock(&sc->sc_mtx); 302 303 return (0); /* success */ 304 305 detach: 306 g_mouse_detach(dev); 307 308 return (ENXIO); /* error */ 309 } 310 311 static int 312 g_mouse_detach(device_t dev) 313 { 314 struct g_mouse_softc *sc = device_get_softc(dev); 315 316 DPRINTF("\n"); 317 318 mtx_lock(&sc->sc_mtx); 319 usb_callout_stop(&sc->sc_button_press_callout); 320 usb_callout_stop(&sc->sc_cursor_update_callout); 321 mtx_unlock(&sc->sc_mtx); 322 323 usbd_transfer_unsetup(sc->sc_xfer, G_MOUSE_N_TRANSFER); 324 325 usb_callout_drain(&sc->sc_button_press_callout); 326 usb_callout_drain(&sc->sc_cursor_update_callout); 327 328 mtx_destroy(&sc->sc_mtx); 329 330 return (0); 331 } 332 333 static void 334 g_mouse_intr_callback(struct usb_xfer *xfer, usb_error_t error) 335 { 336 struct g_mouse_softc *sc = usbd_xfer_softc(xfer); 337 int actlen; 338 int aframes; 339 int dx; 340 int dy; 341 int radius; 342 343 usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); 344 345 DPRINTF("st=%d aframes=%d actlen=%d bytes\n", 346 USB_GET_STATE(xfer), aframes, actlen); 347 348 switch (USB_GET_STATE(xfer)) { 349 case USB_ST_TRANSFERRED: 350 if (!(sc->sc_do_cursor_update || sc->sc_do_button_update)) 351 break; 352 353 case USB_ST_SETUP: 354 tr_setup: 355 356 if (sc->sc_do_cursor_update) { 357 sc->sc_do_cursor_update = 0; 358 sc->sc_tick += 80; 359 if ((sc->sc_tick < 0) || (sc->sc_tick > 7999)) 360 sc->sc_tick = 0; 361 } 362 363 if (sc->sc_do_button_update) { 364 sc->sc_do_button_update = 0; 365 sc->sc_data.buttons ^= BUT_0; 366 } 367 368 radius = sc->sc_radius; 369 370 switch (sc->sc_mode) { 371 case G_MOUSE_MODE_SILENT: 372 sc->sc_data.buttons = 0; 373 break; 374 case G_MOUSE_MODE_SPIRAL: 375 radius = (radius * (8000-sc->sc_tick)) / 8000; 376 case G_MOUSE_MODE_CIRCLE: 377 /* TODO */ 378 sc->sc_curr_y_state = 0; 379 sc->sc_curr_x_state = 0; 380 break; 381 case G_MOUSE_MODE_BOX: 382 if (sc->sc_tick < 2000) { 383 sc->sc_curr_x_state = (sc->sc_tick * radius) / 2000; 384 sc->sc_curr_y_state = 0; 385 } else if (sc->sc_tick < 4000) { 386 sc->sc_curr_x_state = radius; 387 sc->sc_curr_y_state = -(((sc->sc_tick - 2000) * radius) / 2000); 388 } else if (sc->sc_tick < 6000) { 389 sc->sc_curr_x_state = radius - (((sc->sc_tick - 4000) * radius) / 2000); 390 sc->sc_curr_y_state = -radius; 391 } else { 392 sc->sc_curr_x_state = 0; 393 sc->sc_curr_y_state = -radius + (((sc->sc_tick - 6000) * radius) / 2000); 394 } 395 break; 396 default: 397 break; 398 } 399 400 dx = sc->sc_curr_x_state - sc->sc_last_x_state; 401 dy = sc->sc_curr_y_state - sc->sc_last_y_state; 402 403 if (dx < -63) 404 dx = -63; 405 else if (dx > 63) 406 dx = 63; 407 408 if (dy < -63) 409 dy = -63; 410 else if (dy > 63) 411 dy = 63; 412 413 sc->sc_last_x_state += dx; 414 sc->sc_last_y_state += dy; 415 416 sc->sc_data.dx = dx; 417 sc->sc_data.dy = dy; 418 419 usbd_xfer_set_frame_data(xfer, 0, &sc->sc_data, sizeof(sc->sc_data)); 420 usbd_xfer_set_frames(xfer, 1); 421 usbd_transfer_submit(xfer); 422 break; 423 424 default: /* Error */ 425 DPRINTF("error=%s\n", usbd_errstr(error)); 426 427 if (error != USB_ERR_CANCELLED) { 428 /* try to clear stall first */ 429 usbd_xfer_set_stall(xfer); 430 goto tr_setup; 431 } 432 break; 433 } 434 } 435 436 static int 437 g_mouse_handle_request(device_t dev, 438 const void *preq, void **pptr, uint16_t *plen, 439 uint16_t offset, uint8_t *pstate) 440 { 441 const struct usb_device_request *req = preq; 442 uint8_t is_complete = *pstate; 443 444 if (!is_complete) { 445 if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) && 446 (req->bRequest == UR_SET_PROTOCOL) && 447 (req->wValue[0] == 0x00) && 448 (req->wValue[1] == 0x00)) { 449 *plen = 0; 450 return (0); 451 } 452 } 453 return (ENXIO); /* use builtin handler */ 454 } 455