Index: head/sys/conf/files =================================================================== --- head/sys/conf/files +++ head/sys/conf/files @@ -3387,6 +3387,7 @@ dev/usb/input/atp.c optional atp dev/usb/input/uep.c optional uep dev/usb/input/uhid.c optional uhid +dev/usb/input/uhid_snes.c optional uhid_snes dev/usb/input/ukbd.c optional ukbd dev/usb/input/ums.c optional ums dev/usb/input/wmt.c optional wmt Index: head/sys/dev/usb/input/uhid_snes.c =================================================================== --- head/sys/dev/usb/input/uhid_snes.c +++ head/sys/dev/usb/input/uhid_snes.c @@ -0,0 +1,644 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2013, Michael Terrell + * Copyright 2018, Johannes Lundberg + * 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. + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "usb_rdesc.h" + +#define UHID_SNES_IFQ_MAX_LEN 8 + +#define UREQ_GET_PORT_STATUS 0x01 +#define UREQ_SOFT_RESET 0x02 + +#define UP 0x7f00 +#define DOWN 0x7fff +#define LEFT 0x00ff +#define RIGHT 0xff7f +#define X 0x1f +#define Y 0x8f +#define A 0x2f +#define B 0x4f +#define SELECT 0x10 +#define START 0x20 +#define LEFT_T 0x01 +#define RIGHT_T 0x02 + +static const uint8_t uhid_snes_report_descr[] = { UHID_SNES_REPORT_DESCR() }; + +#define SNES_DEV(v,p,i) { USB_VPI(v,p,i) } + +static const STRUCT_USB_HOST_ID snes_devs[] = { + SNES_DEV(0x0810, 0xe501, 0), /* GeeekPi K-0161 */ + SNES_DEV(0x0079, 0x0011, 0) /* Dragonrise */ +}; + +enum { + UHID_SNES_INTR_DT_RD, + UHID_SNES_STATUS_DT_RD, + UHID_SNES_N_TRANSFER +}; + +struct uhid_snes_softc { + device_t sc_dev; + struct usb_device *sc_usb_device; + struct mtx sc_mutex; + struct usb_callout sc_watchdog; + uint8_t sc_iface_num; + struct usb_xfer *sc_transfer[UHID_SNES_N_TRANSFER]; + struct usb_fifo_sc sc_fifo; + struct usb_fifo_sc sc_fifo_no_reset; + int sc_fflags; + struct usb_fifo *sc_fifo_open[2]; + uint8_t sc_zero_length_packets; + uint8_t sc_previous_status; + uint8_t sc_iid; + uint8_t sc_oid; + uint8_t sc_fid; + uint8_t sc_iface_index; + + uint32_t sc_isize; + uint32_t sc_osize; + uint32_t sc_fsize; + + void *sc_repdesc_ptr; + + uint16_t sc_repdesc_size; + + struct usb_device *sc_udev; +#define UHID_FLAG_IMMED 0x01 /* set if read should be immediate */ + +}; + +static device_probe_t uhid_snes_probe; +static device_attach_t uhid_snes_attach; +static device_detach_t uhid_snes_detach; + +static usb_fifo_open_t uhid_snes_open; +static usb_fifo_close_t uhid_snes_close; +static usb_fifo_ioctl_t uhid_snes_ioctl; +static usb_fifo_cmd_t uhid_snes_start_read; +static usb_fifo_cmd_t uhid_snes_stop_read; + +static void uhid_snes_reset(struct uhid_snes_softc *); +static void uhid_snes_watchdog(void *); + +static usb_callback_t uhid_snes_read_callback; +static usb_callback_t uhid_snes_status_callback; + +static struct usb_fifo_methods uhid_snes_fifo_methods = { + .f_open = &uhid_snes_open, + .f_close = &uhid_snes_close, + .f_ioctl = &uhid_snes_ioctl, + .f_start_read = &uhid_snes_start_read, + .f_stop_read = &uhid_snes_stop_read, + .basename[0] = "uhid_snes" +}; + +static const struct usb_config uhid_snes_config[UHID_SNES_N_TRANSFER] = { + [UHID_SNES_INTR_DT_RD] = { + .callback = &uhid_snes_read_callback, + .bufsize = sizeof(struct usb_device_request) +1, + .flags = {.short_xfer_ok = 1, .short_frames_ok = 1, + .pipe_bof =1, .proxy_buffer =1}, + .type = UE_INTERRUPT, + .endpoint = 0x81, + .direction = UE_DIR_IN + }, + [UHID_SNES_STATUS_DT_RD] = { + .callback = &uhid_snes_status_callback, + .bufsize = sizeof(struct usb_device_request) + 1, + .timeout = 1000, + .type = UE_CONTROL, + .endpoint = 0x00, + .direction = UE_DIR_ANY + } +}; + +static int +uhid_get_report(struct uhid_snes_softc *sc, uint8_t type, + uint8_t id, void *kern_data, void *user_data, uint16_t len) +{ + int err; + uint8_t free_data = 0; + + if (kern_data == NULL) { + kern_data = malloc(len, M_USBDEV, M_WAITOK); + if (kern_data == NULL) { + err = ENOMEM; + goto done; + } + free_data = 1; + } + err = usbd_req_get_report(sc->sc_udev, NULL, kern_data, + len, sc->sc_iface_index, type, id); + if (err) { + err = ENXIO; + goto done; + } + if (user_data) { + /* dummy buffer */ + err = copyout(kern_data, user_data, len); + if (err) { + goto done; + } + } +done: + if (free_data) { + free(kern_data, M_USBDEV); + } + return (err); +} + +static int +uhid_set_report(struct uhid_snes_softc *sc, uint8_t type, + uint8_t id, void *kern_data, void *user_data, uint16_t len) +{ + int err; + uint8_t free_data = 0; + + if (kern_data == NULL) { + kern_data = malloc(len, M_USBDEV, M_WAITOK); + if (kern_data == NULL) { + err = ENOMEM; + goto done; + } + free_data = 1; + err = copyin(user_data, kern_data, len); + if (err) { + goto done; + } + } + err = usbd_req_set_report(sc->sc_udev, NULL, kern_data, + len, sc->sc_iface_index, type, id); + if (err) { + err = ENXIO; + goto done; + } +done: + if (free_data) { + free(kern_data, M_USBDEV); + } + return (err); +} + +static int +uhid_snes_open(struct usb_fifo *fifo, int fflags) +{ + struct uhid_snes_softc *sc = usb_fifo_softc(fifo); + int error; + + if (sc->sc_fflags & fflags) { + uhid_snes_reset(sc); + return (EBUSY); + } + + mtx_lock(&sc->sc_mutex); + usbd_xfer_set_stall(sc->sc_transfer[UHID_SNES_INTR_DT_RD]); + mtx_unlock(&sc->sc_mutex); + + error = usb_fifo_alloc_buffer(fifo, + usbd_xfer_max_len(sc->sc_transfer[UHID_SNES_INTR_DT_RD]), + UHID_SNES_IFQ_MAX_LEN); + if (error) + return (ENOMEM); + + sc->sc_fifo_open[USB_FIFO_RX] = fifo; + + return (0); +} + +static void +uhid_snes_reset(struct uhid_snes_softc *sc) +{ + struct usb_device_request req; + int error; + + req.bRequest = UREQ_SOFT_RESET; + USETW(req.wValue, 0); + USETW(req.wIndex, sc->sc_iface_num); + USETW(req.wLength, 0); + + mtx_lock(&sc->sc_mutex); + + error = usbd_do_request_flags(sc->sc_usb_device, &sc->sc_mutex, + &req, NULL, 0, NULL, 2 * USB_MS_HZ); + + if (error) { + usbd_do_request_flags(sc->sc_usb_device, &sc->sc_mutex, + &req, NULL, 0, NULL, 2 * USB_MS_HZ); + } + + mtx_unlock(&sc->sc_mutex); +} + +static void +uhid_snes_close(struct usb_fifo *fifo, int fflags) +{ + struct uhid_snes_softc *sc = usb_fifo_softc(fifo); + + sc->sc_fflags &= ~(fflags & FREAD); + usb_fifo_free_buffer(fifo); +} + +static int +uhid_snes_ioctl(struct usb_fifo *fifo, u_long cmd, void *data, int fflags) +{ + struct uhid_snes_softc *sc = usb_fifo_softc(fifo); + struct usb_gen_descriptor *ugd; + uint32_t size; + int error = 0; + uint8_t id; + + switch (cmd) { + case USB_GET_REPORT_DESC: + ugd = data; + if (sc->sc_repdesc_size > ugd->ugd_maxlen) { + size = ugd->ugd_maxlen; + } else { + size = sc->sc_repdesc_size; + } + + ugd->ugd_actlen = size; + if (ugd->ugd_data == NULL) + break; /*desciptor length only*/ + error = copyout(sc->sc_repdesc_ptr, ugd->ugd_data, size); + break; + + case USB_SET_IMMED: + if (!(fflags & FREAD)) { + error = EPERM; + break; + } + + if (*(int *)data) { + /* do a test read */ + error = uhid_get_report(sc, UHID_INPUT_REPORT, + sc->sc_iid, NULL, NULL, sc->sc_isize); + if (error) { + break; + } + mtx_lock(&sc->sc_mutex); + sc->sc_fflags |= UHID_FLAG_IMMED; + mtx_unlock(&sc->sc_mutex); + } else { + mtx_lock(&sc->sc_mutex); + sc->sc_fflags &= ~UHID_FLAG_IMMED; + mtx_unlock(&sc->sc_mutex); + } + break; + + case USB_GET_REPORT: + if (!(fflags & FREAD)) { + error = EPERM; + break; + } + ugd = data; + switch (ugd->ugd_report_type) { + case UHID_INPUT_REPORT: + size = sc->sc_isize; + id = sc->sc_iid; + break; + case UHID_OUTPUT_REPORT: + size = sc->sc_osize; + id = sc->sc_oid; + break; + case UHID_FEATURE_REPORT: + size = sc->sc_fsize; + id = sc->sc_fid; + break; + default: + return (EINVAL); + } + if (id != 0) + copyin(ugd->ugd_data, &id, 1); + error = uhid_get_report(sc, ugd->ugd_report_type, id, + NULL, ugd->ugd_data, imin(ugd->ugd_maxlen, size)); + break; + + case USB_SET_REPORT: + if (!(fflags & FWRITE)) { + error = EPERM; + break; + } + ugd = data; + switch (ugd->ugd_report_type) { + case UHID_INPUT_REPORT: + size = sc->sc_isize; + id = sc->sc_iid; + break; + case UHID_OUTPUT_REPORT: + size = sc->sc_osize; + id = sc->sc_oid; + break; + case UHID_FEATURE_REPORT: + size = sc->sc_fsize; + id = sc->sc_fid; + break; + default: + return (EINVAL); + } + if (id != 0) + copyin(ugd->ugd_data, &id, 1); + error = uhid_set_report(sc, ugd->ugd_report_type, id, + NULL, ugd->ugd_data, imin(ugd->ugd_maxlen, size)); + break; + + case USB_GET_REPORT_ID: + /* XXX: we only support reportid 0? */ + *(int *)data = 0; + break; + + default: + error = EINVAL; + break; + } + return (error); +} + +static void +uhid_snes_watchdog(void *arg) +{ + struct uhid_snes_softc *sc = arg; + + mtx_assert(&sc->sc_mutex, MA_OWNED); + + if (sc->sc_fflags == 0) + usbd_transfer_start(sc->sc_transfer[UHID_SNES_STATUS_DT_RD]); + + usb_callout_reset(&sc->sc_watchdog, hz, &uhid_snes_watchdog, sc); +} + +static void +uhid_snes_start_read(struct usb_fifo *fifo) +{ + struct uhid_snes_softc *sc = usb_fifo_softc(fifo); + + usbd_transfer_start(sc->sc_transfer[UHID_SNES_INTR_DT_RD]); +} + +static void +uhid_snes_stop_read(struct usb_fifo *fifo) +{ + struct uhid_snes_softc *sc = usb_fifo_softc(fifo); + + usbd_transfer_stop(sc->sc_transfer[UHID_SNES_INTR_DT_RD]); +} + +static void +uhid_snes_read_callback(struct usb_xfer *transfer, usb_error_t error) +{ + struct uhid_snes_softc *sc = usbd_xfer_softc(transfer); + struct usb_fifo *fifo = sc->sc_fifo_open[USB_FIFO_RX]; + struct usb_page_cache *pc; + int actual, max; + + usbd_xfer_status(transfer, &actual, NULL, NULL, NULL); + if (fifo == NULL) + return; + + switch (USB_GET_STATE(transfer)) { + case USB_ST_TRANSFERRED: + if (actual == 0) { + if (sc->sc_zero_length_packets == 4) + /* Throttle transfers. */ + usbd_xfer_set_interval(transfer, 500); + else + sc->sc_zero_length_packets++; + + } else { + /* disable throttling. */ + usbd_xfer_set_interval(transfer, 0); + sc->sc_zero_length_packets = 0; + } + pc = usbd_xfer_get_frame(transfer, 0); + usb_fifo_put_data(fifo, pc, 0, actual, 1); + /* Fall through */ + setup: + case USB_ST_SETUP: + if (usb_fifo_put_bytes_max(fifo) != 0) { + max = usbd_xfer_max_len(transfer); + usbd_xfer_set_frame_len(transfer, 0, max); + usbd_transfer_submit(transfer); + } + break; + + default: + /*disable throttling. */ + usbd_xfer_set_interval(transfer, 0); + sc->sc_zero_length_packets = 0; + + if (error != USB_ERR_CANCELLED) { + /* Issue a clear-stall request. */ + usbd_xfer_set_stall(transfer); + goto setup; + } + break; + } +} + +static void +uhid_snes_status_callback(struct usb_xfer *transfer, usb_error_t error) +{ + struct uhid_snes_softc *sc = usbd_xfer_softc(transfer); + struct usb_device_request req; + struct usb_page_cache *pc; + uint8_t current_status, new_status; + + switch (USB_GET_STATE(transfer)) { + case USB_ST_SETUP: + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = UREQ_GET_PORT_STATUS; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_iface_num; + req.wIndex[1] = 0; + USETW(req.wLength, 1); + + pc = usbd_xfer_get_frame(transfer, 0); + usbd_copy_in(pc, 0, &req, sizeof(req)); + usbd_xfer_set_frame_len(transfer, 0, sizeof(req)); + usbd_xfer_set_frame_len(transfer, 1, 1); + usbd_xfer_set_frames(transfer, 2); + usbd_transfer_submit(transfer); + break; + + case USB_ST_TRANSFERRED: + pc = usbd_xfer_get_frame(transfer, 1); + usbd_copy_out(pc, 0, ¤t_status, 1); + new_status = current_status & ~sc->sc_previous_status; + sc->sc_previous_status = current_status; + break; + + default: + break; + } + +} + +static int +uhid_snes_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(snes_devs, sizeof(snes_devs), uaa)); +} + +static int +uhid_snes_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct uhid_snes_softc *sc = device_get_softc(dev); + struct usb_interface_descriptor *idesc; + struct usb_config_descriptor *cdesc; + uint8_t alt_index, iface_index = uaa->info.bIfaceIndex; + int error,unit = device_get_unit(dev); + + sc->sc_dev = dev; + sc->sc_usb_device = uaa->device; + device_set_usb_desc(dev); + mtx_init(&sc->sc_mutex, "uhid_snes", NULL, MTX_DEF | MTX_RECURSE); + usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mutex, 0); + + idesc = usbd_get_interface_descriptor(uaa->iface); + alt_index = -1; + for(;;) { + if (idesc == NULL) + break; + + if ((idesc->bDescriptorType == UDESC_INTERFACE) && + (idesc->bLength >= sizeof(*idesc))) { + if (idesc->bInterfaceNumber != uaa->info.bIfaceNum) { + break; + } else { + alt_index++; + if (idesc->bInterfaceClass == UICLASS_HID) + goto found; + } + } + + cdesc = usbd_get_config_descriptor(uaa->device); + idesc = (void *)usb_desc_foreach(cdesc, (void *)idesc); + goto found; + } + goto detach; + +found: + if (alt_index) { + error = usbd_set_alt_interface_index(uaa->device, iface_index, alt_index); + if (error) + goto detach; + } + + sc->sc_iface_num = idesc->bInterfaceNumber; + + error = usbd_transfer_setup(uaa->device, &iface_index, + sc->sc_transfer, uhid_snes_config, UHID_SNES_N_TRANSFER, sc, + &sc->sc_mutex); + + if (error) + goto detach; + + error = usb_fifo_attach(uaa->device, sc, &sc->sc_mutex, + &uhid_snes_fifo_methods, &sc->sc_fifo, unit, -1, + iface_index, UID_ROOT, GID_OPERATOR, 0644); + sc->sc_repdesc_size = sizeof(uhid_snes_report_descr); + sc->sc_repdesc_ptr = __DECONST(void*, &uhid_snes_report_descr); + + + if (error) + goto detach; + + mtx_lock(&sc->sc_mutex); + uhid_snes_watchdog(sc); + mtx_unlock(&sc->sc_mutex); + return (0); + +detach: + uhid_snes_detach(dev); + return (ENOMEM); +} + +static int +uhid_snes_detach(device_t dev) +{ + struct uhid_snes_softc *sc = device_get_softc(dev); + + usb_fifo_detach(&sc->sc_fifo); + usb_fifo_detach(&sc->sc_fifo_no_reset); + + mtx_lock(&sc->sc_mutex); + usb_callout_stop(&sc->sc_watchdog); + mtx_unlock(&sc->sc_mutex); + + usbd_transfer_unsetup(sc->sc_transfer, UHID_SNES_N_TRANSFER); + usb_callout_drain(&sc->sc_watchdog); + mtx_destroy(&sc->sc_mutex); + + return (0); +} + +static device_method_t uhid_snes_methods[] = { + DEVMETHOD(device_probe, uhid_snes_probe), + DEVMETHOD(device_attach, uhid_snes_attach), + DEVMETHOD(device_detach, uhid_snes_detach), + DEVMETHOD_END +}; + +static driver_t uhid_snes_driver = { + "uhid_snes", + uhid_snes_methods, + sizeof(struct uhid_snes_softc) +}; + +static devclass_t uhid_snes_devclass; + +DRIVER_MODULE(uhid_snes, uhub, uhid_snes_driver, uhid_snes_devclass, NULL, 0); +MODULE_DEPEND(uhid_snes, usb, 1, 1, 1); +USB_PNP_HOST_INFO(snes_devs); Index: head/sys/dev/usb/input/usb_rdesc.h =================================================================== --- head/sys/dev/usb/input/usb_rdesc.h +++ head/sys/dev/usb/input/usb_rdesc.h @@ -276,3 +276,31 @@ 0x81, 0x01, /* INPUT (Constant) */\ 0xc0 /* END COLLECTION */\ +/* Fixed report descriptor for Super Nintendo gamepads */ +#define UHID_SNES_REPORT_DESCR(...) \ + 0x05, 0x01, /* Usage Page (Desktop), */\ + 0x09, 0x04, /* Usage (Joystik), */\ + 0xA1, 0x01, /* Collection (Application), */\ + 0xA1, 0x02, /* Collection (Logical), */\ + 0x14, /* Logical Minimum (0), */\ + 0x75, 0x08, /* Report Size (8), */\ + 0x95, 0x03, /* Report Count (3), */\ + 0x81, 0x01, /* Input (Constant), */\ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */\ + 0x95, 0x02, /* Report Count (2), */\ + 0x09, 0x30, /* Usage (X), */\ + 0x09, 0x31, /* Usage (Y), */\ + 0x81, 0x02, /* Input (Variable), */\ + 0x75, 0x01, /* Report Size (1), */\ + 0x95, 0x04, /* Report Count (4), */\ + 0x81, 0x01, /* Input (Constant), */\ + 0x25, 0x01, /* Logical Maximum (1), */\ + 0x95, 0x0A, /* Report Count (10), */\ + 0x05, 0x09, /* Usage Page (Button), */\ + 0x19, 0x01, /* Usage Minimum (01h), */\ + 0x29, 0x0A, /* Usage Maximum (0Ah), */\ + 0x81, 0x02, /* Input (Variable), */\ + 0x95, 0x0A, /* Report Count (10), */\ + 0x81, 0x01, /* Input (Constant), */\ + 0xC0, /* End Collection, */\ + 0xC0 /* End Collection */\ Index: head/sys/modules/usb/Makefile =================================================================== --- head/sys/modules/usb/Makefile +++ head/sys/modules/usb/Makefile @@ -47,7 +47,7 @@ SUBDIR += ${_dwc_otg} ehci ${_musb} ohci uhci xhci ${_uss820dci} ${_at91dci} \ ${_atmegadci} ${_avr32dci} ${_rsu} ${_rsufw} ${_saf1761otg} SUBDIR += ${_rum} ${_run} ${_runfw} ${_uath} upgt usie ural ${_zyd} ${_urtw} -SUBDIR += atp cfumass uhid ukbd ums udbp ufm uep wmt wsp ugold uled +SUBDIR += atp cfumass uhid uhid_snes ukbd ums udbp ufm uep wmt wsp ugold uled SUBDIR += ucom u3g uark ubsa ubser uchcom ucycom ufoma uftdi ugensa uipaq ulpt \ umct umcs umodem umoscom uplcom uslcom uvisor uvscom SUBDIR += udl Index: head/sys/modules/usb/uhid_snes/Makefile =================================================================== --- head/sys/modules/usb/uhid_snes/Makefile +++ head/sys/modules/usb/uhid_snes/Makefile @@ -0,0 +1,36 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2018 Johannes Lundberg. 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. +# 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +# + +S= ${SRCTOP}/sys + +.PATH: $S/dev/usb/input + +KMOD= uhid_snes +SRCS= opt_bus.h opt_usb.h device_if.h bus_if.h usb_if.h vnode_if.h usbdevs.h \ + uhid_snes.c + +.include