2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright 2013, Michael Terrell <vashisnotatree@gmail.com>
5 * Copyright 2018, Johannes Lundberg <johalun0@gmail.com>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/param.h>
33 #include <sys/module.h>
34 #include <sys/kernel.h>
35 #include <sys/systm.h>
40 #include <sys/mutex.h>
41 #include <sys/syslog.h>
42 #include <sys/fcntl.h>
44 #include <dev/usb/usb.h>
45 #include <dev/usb/usbdi.h>
46 #include <dev/usb/usbdi_util.h>
48 #include <dev/usb/usbhid.h>
49 #include <dev/usb/usb_ioctl.h>
51 #include "usb_rdesc.h"
53 #define UHID_SNES_IFQ_MAX_LEN 8
55 #define UREQ_GET_PORT_STATUS 0x01
56 #define UREQ_SOFT_RESET 0x02
71 static const uint8_t uhid_snes_report_descr[] = { UHID_SNES_REPORT_DESCR() };
72 #define SNES_DEV(v,p,i) { USB_VPI(v,p,i) }
74 static const STRUCT_USB_HOST_ID snes_devs[] = {
75 SNES_DEV(0x0810, 0xe501, 0), /* GeeekPi K-0161 */
76 SNES_DEV(0x0079, 0x0011, 0) /* Dragonrise */
81 UHID_SNES_STATUS_DT_RD,
85 struct uhid_snes_softc {
87 struct usb_device *sc_usb_device;
89 struct usb_callout sc_watchdog;
91 struct usb_xfer *sc_transfer[UHID_SNES_N_TRANSFER];
92 struct usb_fifo_sc sc_fifo;
93 struct usb_fifo_sc sc_fifo_no_reset;
95 struct usb_fifo *sc_fifo_open[2];
96 uint8_t sc_zero_length_packets;
97 uint8_t sc_previous_status;
101 uint8_t sc_iface_index;
107 void *sc_repdesc_ptr;
109 uint16_t sc_repdesc_size;
111 struct usb_device *sc_udev;
112 #define UHID_FLAG_IMMED 0x01 /* set if read should be immediate */
116 static device_probe_t uhid_snes_probe;
117 static device_attach_t uhid_snes_attach;
118 static device_detach_t uhid_snes_detach;
120 static usb_fifo_open_t uhid_snes_open;
121 static usb_fifo_close_t uhid_snes_close;
122 static usb_fifo_ioctl_t uhid_snes_ioctl;
123 static usb_fifo_cmd_t uhid_snes_start_read;
124 static usb_fifo_cmd_t uhid_snes_stop_read;
126 static void uhid_snes_reset(struct uhid_snes_softc *);
127 static void uhid_snes_watchdog(void *);
129 static usb_callback_t uhid_snes_read_callback;
130 static usb_callback_t uhid_snes_status_callback;
132 static struct usb_fifo_methods uhid_snes_fifo_methods = {
133 .f_open = &uhid_snes_open,
134 .f_close = &uhid_snes_close,
135 .f_ioctl = &uhid_snes_ioctl,
136 .f_start_read = &uhid_snes_start_read,
137 .f_stop_read = &uhid_snes_stop_read,
138 .basename[0] = "uhid_snes"
141 static const struct usb_config uhid_snes_config[UHID_SNES_N_TRANSFER] = {
142 [UHID_SNES_INTR_DT_RD] = {
143 .callback = &uhid_snes_read_callback,
144 .bufsize = sizeof(struct usb_device_request) +1,
145 .flags = {.short_xfer_ok = 1, .short_frames_ok = 1,
146 .pipe_bof =1, .proxy_buffer =1},
147 .type = UE_INTERRUPT,
149 .direction = UE_DIR_IN
151 [UHID_SNES_STATUS_DT_RD] = {
152 .callback = &uhid_snes_status_callback,
153 .bufsize = sizeof(struct usb_device_request) + 1,
157 .direction = UE_DIR_ANY
162 uhid_get_report(struct uhid_snes_softc *sc, uint8_t type,
163 uint8_t id, void *kern_data, void *user_data, uint16_t len)
166 uint8_t free_data = 0;
168 if (kern_data == NULL) {
169 kern_data = malloc(len, M_USBDEV, M_WAITOK);
172 err = usbd_req_get_report(sc->sc_udev, NULL, kern_data,
173 len, sc->sc_iface_index, type, id);
180 err = copyout(kern_data, user_data, len);
187 free(kern_data, M_USBDEV);
193 uhid_set_report(struct uhid_snes_softc *sc, uint8_t type,
194 uint8_t id, void *kern_data, void *user_data, uint16_t len)
197 uint8_t free_data = 0;
199 if (kern_data == NULL) {
200 kern_data = malloc(len, M_USBDEV, M_WAITOK);
202 err = copyin(user_data, kern_data, len);
207 err = usbd_req_set_report(sc->sc_udev, NULL, kern_data,
208 len, sc->sc_iface_index, type, id);
215 free(kern_data, M_USBDEV);
221 uhid_snes_open(struct usb_fifo *fifo, int fflags)
223 struct uhid_snes_softc *sc = usb_fifo_softc(fifo);
226 if (sc->sc_fflags & fflags) {
231 mtx_lock(&sc->sc_mutex);
232 usbd_xfer_set_stall(sc->sc_transfer[UHID_SNES_INTR_DT_RD]);
233 mtx_unlock(&sc->sc_mutex);
235 error = usb_fifo_alloc_buffer(fifo,
236 usbd_xfer_max_len(sc->sc_transfer[UHID_SNES_INTR_DT_RD]),
237 UHID_SNES_IFQ_MAX_LEN);
241 sc->sc_fifo_open[USB_FIFO_RX] = fifo;
247 uhid_snes_reset(struct uhid_snes_softc *sc)
249 struct usb_device_request req;
252 req.bRequest = UREQ_SOFT_RESET;
253 USETW(req.wValue, 0);
254 USETW(req.wIndex, sc->sc_iface_num);
255 USETW(req.wLength, 0);
257 mtx_lock(&sc->sc_mutex);
259 error = usbd_do_request_flags(sc->sc_usb_device, &sc->sc_mutex,
260 &req, NULL, 0, NULL, 2 * USB_MS_HZ);
263 usbd_do_request_flags(sc->sc_usb_device, &sc->sc_mutex,
264 &req, NULL, 0, NULL, 2 * USB_MS_HZ);
267 mtx_unlock(&sc->sc_mutex);
271 uhid_snes_close(struct usb_fifo *fifo, int fflags)
273 struct uhid_snes_softc *sc = usb_fifo_softc(fifo);
275 sc->sc_fflags &= ~(fflags & FREAD);
276 usb_fifo_free_buffer(fifo);
280 uhid_snes_ioctl(struct usb_fifo *fifo, u_long cmd, void *data, int fflags)
282 struct uhid_snes_softc *sc = usb_fifo_softc(fifo);
283 struct usb_gen_descriptor *ugd;
289 case USB_GET_REPORT_DESC:
291 if (sc->sc_repdesc_size > ugd->ugd_maxlen) {
292 size = ugd->ugd_maxlen;
294 size = sc->sc_repdesc_size;
297 ugd->ugd_actlen = size;
298 if (ugd->ugd_data == NULL)
299 break; /*desciptor length only*/
300 error = copyout(sc->sc_repdesc_ptr, ugd->ugd_data, size);
304 if (!(fflags & FREAD)) {
311 error = uhid_get_report(sc, UHID_INPUT_REPORT,
312 sc->sc_iid, NULL, NULL, sc->sc_isize);
316 mtx_lock(&sc->sc_mutex);
317 sc->sc_fflags |= UHID_FLAG_IMMED;
318 mtx_unlock(&sc->sc_mutex);
320 mtx_lock(&sc->sc_mutex);
321 sc->sc_fflags &= ~UHID_FLAG_IMMED;
322 mtx_unlock(&sc->sc_mutex);
327 if (!(fflags & FREAD)) {
332 switch (ugd->ugd_report_type) {
333 case UHID_INPUT_REPORT:
337 case UHID_OUTPUT_REPORT:
341 case UHID_FEATURE_REPORT:
349 copyin(ugd->ugd_data, &id, 1);
350 error = uhid_get_report(sc, ugd->ugd_report_type, id,
351 NULL, ugd->ugd_data, imin(ugd->ugd_maxlen, size));
355 if (!(fflags & FWRITE)) {
360 switch (ugd->ugd_report_type) {
361 case UHID_INPUT_REPORT:
365 case UHID_OUTPUT_REPORT:
369 case UHID_FEATURE_REPORT:
377 copyin(ugd->ugd_data, &id, 1);
378 error = uhid_set_report(sc, ugd->ugd_report_type, id,
379 NULL, ugd->ugd_data, imin(ugd->ugd_maxlen, size));
382 case USB_GET_REPORT_ID:
383 /* XXX: we only support reportid 0? */
395 uhid_snes_watchdog(void *arg)
397 struct uhid_snes_softc *sc = arg;
399 mtx_assert(&sc->sc_mutex, MA_OWNED);
401 if (sc->sc_fflags == 0)
402 usbd_transfer_start(sc->sc_transfer[UHID_SNES_STATUS_DT_RD]);
404 usb_callout_reset(&sc->sc_watchdog, hz, &uhid_snes_watchdog, sc);
408 uhid_snes_start_read(struct usb_fifo *fifo)
410 struct uhid_snes_softc *sc = usb_fifo_softc(fifo);
412 usbd_transfer_start(sc->sc_transfer[UHID_SNES_INTR_DT_RD]);
416 uhid_snes_stop_read(struct usb_fifo *fifo)
418 struct uhid_snes_softc *sc = usb_fifo_softc(fifo);
420 usbd_transfer_stop(sc->sc_transfer[UHID_SNES_INTR_DT_RD]);
424 uhid_snes_read_callback(struct usb_xfer *transfer, usb_error_t error)
426 struct uhid_snes_softc *sc = usbd_xfer_softc(transfer);
427 struct usb_fifo *fifo = sc->sc_fifo_open[USB_FIFO_RX];
428 struct usb_page_cache *pc;
431 usbd_xfer_status(transfer, &actual, NULL, NULL, NULL);
435 switch (USB_GET_STATE(transfer)) {
436 case USB_ST_TRANSFERRED:
438 if (sc->sc_zero_length_packets == 4)
439 /* Throttle transfers. */
440 usbd_xfer_set_interval(transfer, 500);
442 sc->sc_zero_length_packets++;
445 /* disable throttling. */
446 usbd_xfer_set_interval(transfer, 0);
447 sc->sc_zero_length_packets = 0;
449 pc = usbd_xfer_get_frame(transfer, 0);
450 usb_fifo_put_data(fifo, pc, 0, actual, 1);
454 if (usb_fifo_put_bytes_max(fifo) != 0) {
455 max = usbd_xfer_max_len(transfer);
456 usbd_xfer_set_frame_len(transfer, 0, max);
457 usbd_transfer_submit(transfer);
462 /*disable throttling. */
463 usbd_xfer_set_interval(transfer, 0);
464 sc->sc_zero_length_packets = 0;
466 if (error != USB_ERR_CANCELLED) {
467 /* Issue a clear-stall request. */
468 usbd_xfer_set_stall(transfer);
476 uhid_snes_status_callback(struct usb_xfer *transfer, usb_error_t error)
478 struct uhid_snes_softc *sc = usbd_xfer_softc(transfer);
479 struct usb_device_request req;
480 struct usb_page_cache *pc;
481 uint8_t current_status, new_status;
483 switch (USB_GET_STATE(transfer)) {
485 req.bmRequestType = UT_READ_CLASS_INTERFACE;
486 req.bRequest = UREQ_GET_PORT_STATUS;
487 USETW(req.wValue, 0);
488 req.wIndex[0] = sc->sc_iface_num;
490 USETW(req.wLength, 1);
492 pc = usbd_xfer_get_frame(transfer, 0);
493 usbd_copy_in(pc, 0, &req, sizeof(req));
494 usbd_xfer_set_frame_len(transfer, 0, sizeof(req));
495 usbd_xfer_set_frame_len(transfer, 1, 1);
496 usbd_xfer_set_frames(transfer, 2);
497 usbd_transfer_submit(transfer);
500 case USB_ST_TRANSFERRED:
501 pc = usbd_xfer_get_frame(transfer, 1);
502 usbd_copy_out(pc, 0, ¤t_status, 1);
503 new_status = current_status & ~sc->sc_previous_status;
504 sc->sc_previous_status = current_status;
514 uhid_snes_probe(device_t dev)
516 struct usb_attach_arg *uaa = device_get_ivars(dev);
518 if (uaa->usb_mode != USB_MODE_HOST)
521 return (usbd_lookup_id_by_uaa(snes_devs, sizeof(snes_devs), uaa));
525 uhid_snes_attach(device_t dev)
527 struct usb_attach_arg *uaa = device_get_ivars(dev);
528 struct uhid_snes_softc *sc = device_get_softc(dev);
529 struct usb_interface_descriptor *idesc;
530 struct usb_config_descriptor *cdesc;
531 uint8_t alt_index, iface_index = uaa->info.bIfaceIndex;
532 int error,unit = device_get_unit(dev);
535 sc->sc_usb_device = uaa->device;
536 device_set_usb_desc(dev);
537 mtx_init(&sc->sc_mutex, "uhid_snes", NULL, MTX_DEF | MTX_RECURSE);
538 usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mutex, 0);
540 idesc = usbd_get_interface_descriptor(uaa->iface);
546 if ((idesc->bDescriptorType == UDESC_INTERFACE) &&
547 (idesc->bLength >= sizeof(*idesc))) {
548 if (idesc->bInterfaceNumber != uaa->info.bIfaceNum) {
552 if (idesc->bInterfaceClass == UICLASS_HID)
557 cdesc = usbd_get_config_descriptor(uaa->device);
558 idesc = (void *)usb_desc_foreach(cdesc, (void *)idesc);
565 error = usbd_set_alt_interface_index(uaa->device, iface_index, alt_index);
570 sc->sc_iface_num = idesc->bInterfaceNumber;
572 error = usbd_transfer_setup(uaa->device, &iface_index,
573 sc->sc_transfer, uhid_snes_config, UHID_SNES_N_TRANSFER, sc,
579 error = usb_fifo_attach(uaa->device, sc, &sc->sc_mutex,
580 &uhid_snes_fifo_methods, &sc->sc_fifo, unit, -1,
581 iface_index, UID_ROOT, GID_OPERATOR, 0644);
582 sc->sc_repdesc_size = sizeof(uhid_snes_report_descr);
583 sc->sc_repdesc_ptr = __DECONST(void*, &uhid_snes_report_descr);
588 mtx_lock(&sc->sc_mutex);
589 uhid_snes_watchdog(sc);
590 mtx_unlock(&sc->sc_mutex);
594 uhid_snes_detach(dev);
599 uhid_snes_detach(device_t dev)
601 struct uhid_snes_softc *sc = device_get_softc(dev);
603 usb_fifo_detach(&sc->sc_fifo);
604 usb_fifo_detach(&sc->sc_fifo_no_reset);
606 mtx_lock(&sc->sc_mutex);
607 usb_callout_stop(&sc->sc_watchdog);
608 mtx_unlock(&sc->sc_mutex);
610 usbd_transfer_unsetup(sc->sc_transfer, UHID_SNES_N_TRANSFER);
611 usb_callout_drain(&sc->sc_watchdog);
612 mtx_destroy(&sc->sc_mutex);
617 static device_method_t uhid_snes_methods[] = {
618 DEVMETHOD(device_probe, uhid_snes_probe),
619 DEVMETHOD(device_attach, uhid_snes_attach),
620 DEVMETHOD(device_detach, uhid_snes_detach),
624 static driver_t uhid_snes_driver = {
627 sizeof(struct uhid_snes_softc)
630 static devclass_t uhid_snes_devclass;
632 DRIVER_MODULE(uhid_snes, uhub, uhid_snes_driver, uhid_snes_devclass, NULL, 0);
633 MODULE_DEPEND(uhid_snes, usb, 1, 1, 1);
634 USB_PNP_HOST_INFO(snes_devs);