xref: /freebsd/sys/dev/usb/input/uhid_snes.c (revision 1323ec57)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright 2013, Michael Terrell <vashisnotatree@gmail.com>
5  * Copyright 2018, Johannes Lundberg <johalun0@gmail.com>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
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.
16  *
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
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31 
32 #include <sys/param.h>
33 #include <sys/module.h>
34 #include <sys/kernel.h>
35 #include <sys/systm.h>
36 
37 #include <sys/conf.h>
38 #include <sys/bus.h>
39 #include <sys/lock.h>
40 #include <sys/mutex.h>
41 #include <sys/syslog.h>
42 #include <sys/fcntl.h>
43 
44 #include <dev/usb/usb.h>
45 #include <dev/usb/usbdi.h>
46 #include <dev/usb/usbdi_util.h>
47 
48 #include <dev/usb/usbhid.h>
49 #include <dev/usb/usb_ioctl.h>
50 
51 #include "usb_rdesc.h"
52 
53 #define	UHID_SNES_IFQ_MAX_LEN 8
54 
55 #define	UREQ_GET_PORT_STATUS 0x01
56 #define	UREQ_SOFT_RESET      0x02
57 
58 #define	UP      0x7f00
59 #define	DOWN    0x7fff
60 #define	LEFT    0x00ff
61 #define	RIGHT   0xff7f
62 #define	X       0x1f
63 #define	Y       0x8f
64 #define	A       0x2f
65 #define	B       0x4f
66 #define	SELECT  0x10
67 #define	START   0x20
68 #define	LEFT_T  0x01
69 #define	RIGHT_T 0x02
70 
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) }
73 
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 */
77 };
78 
79 enum {
80 	UHID_SNES_INTR_DT_RD,
81 	UHID_SNES_STATUS_DT_RD,
82 	UHID_SNES_N_TRANSFER
83 };
84 
85 struct uhid_snes_softc {
86 	device_t sc_dev;
87 	struct usb_device *sc_usb_device;
88 	struct mtx sc_mutex;
89 	struct usb_callout sc_watchdog;
90 	uint8_t sc_iface_num;
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;
94 	int sc_fflags;
95 	struct usb_fifo *sc_fifo_open[2];
96 	uint8_t sc_zero_length_packets;
97 	uint8_t sc_previous_status;
98 	uint8_t sc_iid;
99 	uint8_t sc_oid;
100 	uint8_t sc_fid;
101 	uint8_t sc_iface_index;
102 
103 	uint32_t sc_isize;
104 	uint32_t sc_osize;
105 	uint32_t sc_fsize;
106 
107 	void *sc_repdesc_ptr;
108 
109 	uint16_t sc_repdesc_size;
110 
111 	struct usb_device *sc_udev;
112 #define	UHID_FLAG_IMMED        0x01	/* set if read should be immediate */
113 
114 };
115 
116 static device_probe_t uhid_snes_probe;
117 static device_attach_t uhid_snes_attach;
118 static device_detach_t uhid_snes_detach;
119 
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;
125 
126 static void uhid_snes_reset(struct uhid_snes_softc *);
127 static void uhid_snes_watchdog(void *);
128 
129 static usb_callback_t uhid_snes_read_callback;
130 static usb_callback_t uhid_snes_status_callback;
131 
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"
139 };
140 
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,
148 		.endpoint = 0x81,
149 		.direction = UE_DIR_IN
150 	},
151 	[UHID_SNES_STATUS_DT_RD] = {
152 		.callback = &uhid_snes_status_callback,
153 		.bufsize = sizeof(struct usb_device_request) + 1,
154 		.timeout = 1000,
155 		.type = UE_CONTROL,
156 		.endpoint = 0x00,
157 		.direction = UE_DIR_ANY
158 	}
159 };
160 
161 static int
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)
164 {
165 	int err;
166 	uint8_t free_data = 0;
167 
168 	if (kern_data == NULL) {
169 		kern_data = malloc(len, M_USBDEV, M_WAITOK);
170 		free_data = 1;
171 	}
172 	err = usbd_req_get_report(sc->sc_udev, NULL, kern_data,
173 	    len, sc->sc_iface_index, type, id);
174 	if (err) {
175 		err = ENXIO;
176 		goto done;
177 	}
178 	if (user_data) {
179 		/* dummy buffer */
180 		err = copyout(kern_data, user_data, len);
181 		if (err) {
182 			goto done;
183 		}
184 	}
185 done:
186 	if (free_data) {
187 		free(kern_data, M_USBDEV);
188 	}
189 	return (err);
190 }
191 
192 static int
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)
195 {
196 	int err;
197 	uint8_t free_data = 0;
198 
199 	if (kern_data == NULL) {
200 		kern_data = malloc(len, M_USBDEV, M_WAITOK);
201 		free_data = 1;
202 		err = copyin(user_data, kern_data, len);
203 		if (err) {
204 			goto done;
205 		}
206 	}
207 	err = usbd_req_set_report(sc->sc_udev, NULL, kern_data,
208 	    len, sc->sc_iface_index, type, id);
209 	if (err) {
210 		err = ENXIO;
211 		goto done;
212 	}
213 done:
214 	if (free_data) {
215 		free(kern_data, M_USBDEV);
216 	}
217 	return (err);
218 }
219 
220 static int
221 uhid_snes_open(struct usb_fifo *fifo, int fflags)
222 {
223 	struct uhid_snes_softc *sc = usb_fifo_softc(fifo);
224 	int error;
225 
226 	if (sc->sc_fflags & fflags) {
227 		uhid_snes_reset(sc);
228 		return (EBUSY);
229 	}
230 
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);
234 
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);
238 	if (error)
239 		return (ENOMEM);
240 
241 	sc->sc_fifo_open[USB_FIFO_RX] = fifo;
242 
243 	return (0);
244 }
245 
246 static void
247 uhid_snes_reset(struct uhid_snes_softc *sc)
248 {
249 	struct usb_device_request req;
250 	int error;
251 
252 	req.bRequest = UREQ_SOFT_RESET;
253 	USETW(req.wValue, 0);
254 	USETW(req.wIndex, sc->sc_iface_num);
255 	USETW(req.wLength, 0);
256 
257 	mtx_lock(&sc->sc_mutex);
258 
259 	error = usbd_do_request_flags(sc->sc_usb_device, &sc->sc_mutex,
260 	    &req, NULL, 0, NULL, 2 * USB_MS_HZ);
261 
262 	if (error) {
263 		usbd_do_request_flags(sc->sc_usb_device, &sc->sc_mutex,
264 		    &req, NULL, 0, NULL, 2 * USB_MS_HZ);
265 	}
266 
267 	mtx_unlock(&sc->sc_mutex);
268 }
269 
270 static void
271 uhid_snes_close(struct usb_fifo *fifo, int fflags)
272 {
273 	struct uhid_snes_softc *sc = usb_fifo_softc(fifo);
274 
275 	sc->sc_fflags &= ~(fflags & FREAD);
276 	usb_fifo_free_buffer(fifo);
277 }
278 
279 static int
280 uhid_snes_ioctl(struct usb_fifo *fifo, u_long cmd, void *data, int fflags)
281 {
282 	struct uhid_snes_softc *sc = usb_fifo_softc(fifo);
283 	struct usb_gen_descriptor *ugd;
284 #ifdef COMPAT_FREEBSD32
285 	struct usb_gen_descriptor local_ugd;
286 	struct usb_gen_descriptor32 *ugd32 = NULL;
287 #endif
288 	uint32_t size;
289 	int error = 0;
290 	uint8_t id;
291 
292 	ugd = data;
293 #ifdef COMPAT_FREEBSD32
294 	switch (cmd) {
295 	case USB_GET_REPORT_DESC32:
296 	case USB_GET_REPORT32:
297 	case USB_SET_REPORT32:
298 		ugd32 = data;
299 		ugd = &local_ugd;
300 		usb_gen_descriptor_from32(ugd, ugd32);
301 		cmd = _IOC_NEWTYPE(cmd, struct usb_gen_descriptor);
302 		break;
303 	}
304 #endif
305 
306 	switch (cmd) {
307 	case USB_GET_REPORT_DESC:
308 		if (sc->sc_repdesc_size > ugd->ugd_maxlen) {
309 			size = ugd->ugd_maxlen;
310 		} else {
311 			size = sc->sc_repdesc_size;
312 		}
313 
314 		ugd->ugd_actlen = size;
315 		if (ugd->ugd_data == NULL)
316 			break; /* descriptor length only*/
317 		error = copyout(sc->sc_repdesc_ptr, ugd->ugd_data, size);
318 		break;
319 
320 	case USB_SET_IMMED:
321 		if (!(fflags & FREAD)) {
322 			error = EPERM;
323 			break;
324 		}
325 
326 		if (*(int *)data) {
327 			/* do a test read */
328 			error = uhid_get_report(sc, UHID_INPUT_REPORT,
329 			    sc->sc_iid, NULL, NULL, sc->sc_isize);
330 			if (error) {
331 				break;
332 			}
333 			mtx_lock(&sc->sc_mutex);
334 			sc->sc_fflags |= UHID_FLAG_IMMED;
335 			mtx_unlock(&sc->sc_mutex);
336 		} else {
337 			mtx_lock(&sc->sc_mutex);
338 			sc->sc_fflags &= ~UHID_FLAG_IMMED;
339 			mtx_unlock(&sc->sc_mutex);
340 		}
341 		break;
342 
343 	case USB_GET_REPORT:
344 		if (!(fflags & FREAD)) {
345 			error = EPERM;
346 			break;
347 		}
348 		switch (ugd->ugd_report_type) {
349 		case UHID_INPUT_REPORT:
350 			size = sc->sc_isize;
351 			id = sc->sc_iid;
352 			break;
353 		case UHID_OUTPUT_REPORT:
354 			size = sc->sc_osize;
355 			id = sc->sc_oid;
356 			break;
357 		case UHID_FEATURE_REPORT:
358 			size = sc->sc_fsize;
359 			id = sc->sc_fid;
360 			break;
361 		default:
362 			return (EINVAL);
363 		}
364 		if (id != 0)
365 			copyin(ugd->ugd_data, &id, 1);
366 		error = uhid_get_report(sc, ugd->ugd_report_type, id,
367 		    NULL, ugd->ugd_data, imin(ugd->ugd_maxlen, size));
368 		break;
369 
370 	case USB_SET_REPORT:
371 		if (!(fflags & FWRITE)) {
372 			error = EPERM;
373 			break;
374 		}
375 		switch (ugd->ugd_report_type) {
376 		case UHID_INPUT_REPORT:
377 			size = sc->sc_isize;
378 			id = sc->sc_iid;
379 			break;
380 		case UHID_OUTPUT_REPORT:
381 			size = sc->sc_osize;
382 			id = sc->sc_oid;
383 			break;
384 		case UHID_FEATURE_REPORT:
385 			size = sc->sc_fsize;
386 			id = sc->sc_fid;
387 			break;
388 		default:
389 			return (EINVAL);
390 		}
391 		if (id != 0)
392 			copyin(ugd->ugd_data, &id, 1);
393 		error = uhid_set_report(sc, ugd->ugd_report_type, id,
394 		    NULL, ugd->ugd_data, imin(ugd->ugd_maxlen, size));
395 		break;
396 
397 	case USB_GET_REPORT_ID:
398 		/* XXX: we only support reportid 0? */
399 		*(int *)data = 0;
400 		break;
401 
402 	default:
403 		error = EINVAL;
404 		break;
405 	}
406 
407 #ifdef COMPAT_FREEBSD32
408 	if (ugd32 != NULL)
409 		update_usb_gen_descriptor32(ugd32, ugd);
410 #endif
411 	return (error);
412 }
413 
414 static void
415 uhid_snes_watchdog(void *arg)
416 {
417 	struct uhid_snes_softc *sc = arg;
418 
419 	mtx_assert(&sc->sc_mutex, MA_OWNED);
420 
421 	if (sc->sc_fflags == 0)
422 		usbd_transfer_start(sc->sc_transfer[UHID_SNES_STATUS_DT_RD]);
423 
424 	usb_callout_reset(&sc->sc_watchdog, hz, &uhid_snes_watchdog, sc);
425 }
426 
427 static void
428 uhid_snes_start_read(struct usb_fifo *fifo)
429 {
430 	struct uhid_snes_softc *sc = usb_fifo_softc(fifo);
431 
432 	usbd_transfer_start(sc->sc_transfer[UHID_SNES_INTR_DT_RD]);
433 }
434 
435 static void
436 uhid_snes_stop_read(struct usb_fifo *fifo)
437 {
438 	struct uhid_snes_softc *sc = usb_fifo_softc(fifo);
439 
440 	usbd_transfer_stop(sc->sc_transfer[UHID_SNES_INTR_DT_RD]);
441 }
442 
443 static void
444 uhid_snes_read_callback(struct usb_xfer *transfer, usb_error_t error)
445 {
446 	struct uhid_snes_softc *sc = usbd_xfer_softc(transfer);
447 	struct usb_fifo *fifo = sc->sc_fifo_open[USB_FIFO_RX];
448 	struct usb_page_cache *pc;
449 	int actual, max;
450 
451 	usbd_xfer_status(transfer, &actual, NULL, NULL, NULL);
452 	if (fifo == NULL)
453 		return;
454 
455 	switch (USB_GET_STATE(transfer)) {
456 	case USB_ST_TRANSFERRED:
457 		if (actual == 0) {
458 			if (sc->sc_zero_length_packets == 4)
459 				/* Throttle transfers. */
460 				usbd_xfer_set_interval(transfer, 500);
461 			else
462 				sc->sc_zero_length_packets++;
463 
464 		} else {
465 			/* disable throttling. */
466 			usbd_xfer_set_interval(transfer, 0);
467 			sc->sc_zero_length_packets = 0;
468 		}
469 		pc = usbd_xfer_get_frame(transfer, 0);
470 		usb_fifo_put_data(fifo, pc, 0, actual, 1);
471 		/* Fall through */
472 	setup:
473 	case USB_ST_SETUP:
474 		if (usb_fifo_put_bytes_max(fifo) != 0) {
475 			max = usbd_xfer_max_len(transfer);
476 			usbd_xfer_set_frame_len(transfer, 0, max);
477 			usbd_transfer_submit(transfer);
478 		}
479 		break;
480 
481 	default:
482 		/*disable throttling. */
483 		usbd_xfer_set_interval(transfer, 0);
484 		sc->sc_zero_length_packets = 0;
485 
486 		if (error != USB_ERR_CANCELLED) {
487 			/* Issue a clear-stall request. */
488 			usbd_xfer_set_stall(transfer);
489 			goto setup;
490 		}
491 		break;
492 	}
493 }
494 
495 static void
496 uhid_snes_status_callback(struct usb_xfer *transfer, usb_error_t error)
497 {
498 	struct uhid_snes_softc *sc = usbd_xfer_softc(transfer);
499 	struct usb_device_request req;
500 	struct usb_page_cache *pc;
501 	uint8_t current_status, new_status;
502 
503 	switch (USB_GET_STATE(transfer)) {
504 	case USB_ST_SETUP:
505 		req.bmRequestType = UT_READ_CLASS_INTERFACE;
506 		req.bRequest = UREQ_GET_PORT_STATUS;
507 		USETW(req.wValue, 0);
508 		req.wIndex[0] = sc->sc_iface_num;
509 		req.wIndex[1] = 0;
510 		USETW(req.wLength, 1);
511 
512 		pc = usbd_xfer_get_frame(transfer, 0);
513 		usbd_copy_in(pc, 0, &req, sizeof(req));
514 		usbd_xfer_set_frame_len(transfer, 0, sizeof(req));
515 		usbd_xfer_set_frame_len(transfer, 1, 1);
516 		usbd_xfer_set_frames(transfer, 2);
517 		usbd_transfer_submit(transfer);
518 		break;
519 
520 	case USB_ST_TRANSFERRED:
521 		pc = usbd_xfer_get_frame(transfer, 1);
522 		usbd_copy_out(pc, 0, &current_status, 1);
523 		new_status = current_status & ~sc->sc_previous_status;
524 		sc->sc_previous_status = current_status;
525 		break;
526 
527 	default:
528 		break;
529 	}
530 
531 }
532 
533 static int
534 uhid_snes_probe(device_t dev)
535 {
536 	struct usb_attach_arg *uaa = device_get_ivars(dev);
537 
538 	if (uaa->usb_mode != USB_MODE_HOST)
539 		return (ENXIO);
540 
541 	return (usbd_lookup_id_by_uaa(snes_devs, sizeof(snes_devs), uaa));
542 }
543 
544 static int
545 uhid_snes_attach(device_t dev)
546 {
547 	struct usb_attach_arg *uaa = device_get_ivars(dev);
548 	struct uhid_snes_softc *sc = device_get_softc(dev);
549 	struct usb_interface_descriptor *idesc;
550 	struct usb_config_descriptor *cdesc;
551 	uint8_t alt_index, iface_index = uaa->info.bIfaceIndex;
552 	int error,unit = device_get_unit(dev);
553 
554 	sc->sc_dev = dev;
555 	sc->sc_usb_device = uaa->device;
556 	device_set_usb_desc(dev);
557 	mtx_init(&sc->sc_mutex, "uhid_snes", NULL, MTX_DEF | MTX_RECURSE);
558 	usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mutex, 0);
559 
560 	idesc = usbd_get_interface_descriptor(uaa->iface);
561 	alt_index = -1;
562 	for(;;) {
563 		if (idesc == NULL)
564 			break;
565 
566 		if ((idesc->bDescriptorType == UDESC_INTERFACE) &&
567 		     (idesc->bLength >= sizeof(*idesc))) {
568 			if (idesc->bInterfaceNumber != uaa->info.bIfaceNum) {
569 				break;
570 			} else {
571 				alt_index++;
572 				if (idesc->bInterfaceClass == UICLASS_HID)
573 					goto found;
574 			}
575 		}
576 
577 		cdesc = usbd_get_config_descriptor(uaa->device);
578 		idesc = (void *)usb_desc_foreach(cdesc, (void *)idesc);
579 		goto found;
580 	}
581 	goto detach;
582 
583 found:
584 	if (alt_index) {
585 		error = usbd_set_alt_interface_index(uaa->device, iface_index, alt_index);
586 		if (error)
587 			goto detach;
588 	}
589 
590 	sc->sc_iface_num = idesc->bInterfaceNumber;
591 
592 	error = usbd_transfer_setup(uaa->device, &iface_index,
593 	    sc->sc_transfer, uhid_snes_config, UHID_SNES_N_TRANSFER, sc,
594 	    &sc->sc_mutex);
595 
596 	if (error)
597 		goto detach;
598 
599 	error = usb_fifo_attach(uaa->device, sc, &sc->sc_mutex,
600 	    &uhid_snes_fifo_methods, &sc->sc_fifo, unit, -1,
601 	    iface_index, UID_ROOT, GID_OPERATOR, 0644);
602 	sc->sc_repdesc_size = sizeof(uhid_snes_report_descr);
603 	sc->sc_repdesc_ptr = __DECONST(void*, &uhid_snes_report_descr);
604 
605 	if (error)
606 		goto detach;
607 
608 	mtx_lock(&sc->sc_mutex);
609 	uhid_snes_watchdog(sc);
610 	mtx_unlock(&sc->sc_mutex);
611 	return (0);
612 
613 detach:
614 	uhid_snes_detach(dev);
615 	return (ENOMEM);
616 }
617 
618 static int
619 uhid_snes_detach(device_t dev)
620 {
621 	struct uhid_snes_softc *sc = device_get_softc(dev);
622 
623 	usb_fifo_detach(&sc->sc_fifo);
624 	usb_fifo_detach(&sc->sc_fifo_no_reset);
625 
626 	mtx_lock(&sc->sc_mutex);
627 	usb_callout_stop(&sc->sc_watchdog);
628 	mtx_unlock(&sc->sc_mutex);
629 
630 	usbd_transfer_unsetup(sc->sc_transfer, UHID_SNES_N_TRANSFER);
631 	usb_callout_drain(&sc->sc_watchdog);
632 	mtx_destroy(&sc->sc_mutex);
633 
634 	return (0);
635 }
636 
637 static device_method_t uhid_snes_methods[] = {
638 	DEVMETHOD(device_probe, uhid_snes_probe),
639 	DEVMETHOD(device_attach, uhid_snes_attach),
640 	DEVMETHOD(device_detach, uhid_snes_detach),
641 	DEVMETHOD_END
642 };
643 
644 static driver_t uhid_snes_driver = {
645 	"uhid_snes",
646 	uhid_snes_methods,
647 	sizeof(struct uhid_snes_softc)
648 };
649 
650 static devclass_t uhid_snes_devclass;
651 
652 DRIVER_MODULE(uhid_snes, uhub, uhid_snes_driver, uhid_snes_devclass, NULL, 0);
653 MODULE_DEPEND(uhid_snes, usb, 1, 1, 1);
654 USB_PNP_HOST_INFO(snes_devs);
655