xref: /freebsd/sys/dev/usb/input/uhid_snes.c (revision a0409676120c1e558d0ade943019934e0f15118d)
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 	uint32_t size;
285 	int error = 0;
286 	uint8_t id;
287 
288 	switch (cmd) {
289 	case USB_GET_REPORT_DESC:
290 		ugd = data;
291 		if (sc->sc_repdesc_size > ugd->ugd_maxlen) {
292 			size = ugd->ugd_maxlen;
293 		} else {
294 			size = sc->sc_repdesc_size;
295 		}
296 
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);
301 		break;
302 
303 	case USB_SET_IMMED:
304 		if (!(fflags & FREAD)) {
305 			error = EPERM;
306 			break;
307 		}
308 
309 		if (*(int *)data) {
310 			/* do a test read */
311 			error = uhid_get_report(sc, UHID_INPUT_REPORT,
312 			    sc->sc_iid, NULL, NULL, sc->sc_isize);
313 			if (error) {
314 				break;
315 			}
316 			mtx_lock(&sc->sc_mutex);
317 			sc->sc_fflags |= UHID_FLAG_IMMED;
318 			mtx_unlock(&sc->sc_mutex);
319 		} else {
320 			mtx_lock(&sc->sc_mutex);
321 			sc->sc_fflags &= ~UHID_FLAG_IMMED;
322 			mtx_unlock(&sc->sc_mutex);
323 		}
324 		break;
325 
326 	case USB_GET_REPORT:
327 		if (!(fflags & FREAD)) {
328 			error = EPERM;
329 			break;
330 		}
331 		ugd = data;
332 		switch (ugd->ugd_report_type) {
333 		case UHID_INPUT_REPORT:
334 			size = sc->sc_isize;
335 			id = sc->sc_iid;
336 			break;
337 		case UHID_OUTPUT_REPORT:
338 			size = sc->sc_osize;
339 			id = sc->sc_oid;
340 			break;
341 		case UHID_FEATURE_REPORT:
342 			size = sc->sc_fsize;
343 			id = sc->sc_fid;
344 			break;
345 		default:
346 			return (EINVAL);
347 		}
348 		if (id != 0)
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));
352 		break;
353 
354 	case USB_SET_REPORT:
355 		if (!(fflags & FWRITE)) {
356 			error = EPERM;
357 			break;
358 		}
359 		ugd = data;
360 		switch (ugd->ugd_report_type) {
361 		case UHID_INPUT_REPORT:
362 			size = sc->sc_isize;
363 			id = sc->sc_iid;
364 			break;
365 		case UHID_OUTPUT_REPORT:
366 			size = sc->sc_osize;
367 			id = sc->sc_oid;
368 			break;
369 		case UHID_FEATURE_REPORT:
370 			size = sc->sc_fsize;
371 			id = sc->sc_fid;
372 			break;
373 		default:
374 			return (EINVAL);
375 		}
376 		if (id != 0)
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));
380 		break;
381 
382 	case USB_GET_REPORT_ID:
383 		/* XXX: we only support reportid 0? */
384 		*(int *)data = 0;
385 		break;
386 
387 	default:
388 		error = EINVAL;
389 		break;
390 	}
391 	return (error);
392 }
393 
394 static void
395 uhid_snes_watchdog(void *arg)
396 {
397 	struct uhid_snes_softc *sc = arg;
398 
399 	mtx_assert(&sc->sc_mutex, MA_OWNED);
400 
401 	if (sc->sc_fflags == 0)
402 		usbd_transfer_start(sc->sc_transfer[UHID_SNES_STATUS_DT_RD]);
403 
404 	usb_callout_reset(&sc->sc_watchdog, hz, &uhid_snes_watchdog, sc);
405 }
406 
407 static void
408 uhid_snes_start_read(struct usb_fifo *fifo)
409 {
410 	struct uhid_snes_softc *sc = usb_fifo_softc(fifo);
411 
412 	usbd_transfer_start(sc->sc_transfer[UHID_SNES_INTR_DT_RD]);
413 }
414 
415 static void
416 uhid_snes_stop_read(struct usb_fifo *fifo)
417 {
418 	struct uhid_snes_softc *sc = usb_fifo_softc(fifo);
419 
420 	usbd_transfer_stop(sc->sc_transfer[UHID_SNES_INTR_DT_RD]);
421 }
422 
423 static void
424 uhid_snes_read_callback(struct usb_xfer *transfer, usb_error_t error)
425 {
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;
429 	int actual, max;
430 
431 	usbd_xfer_status(transfer, &actual, NULL, NULL, NULL);
432 	if (fifo == NULL)
433 		return;
434 
435 	switch (USB_GET_STATE(transfer)) {
436 	case USB_ST_TRANSFERRED:
437 		if (actual == 0) {
438 			if (sc->sc_zero_length_packets == 4)
439 				/* Throttle transfers. */
440 				usbd_xfer_set_interval(transfer, 500);
441 			else
442 				sc->sc_zero_length_packets++;
443 
444 		} else {
445 			/* disable throttling. */
446 			usbd_xfer_set_interval(transfer, 0);
447 			sc->sc_zero_length_packets = 0;
448 		}
449 		pc = usbd_xfer_get_frame(transfer, 0);
450 		usb_fifo_put_data(fifo, pc, 0, actual, 1);
451 		/* Fall through */
452 	setup:
453 	case USB_ST_SETUP:
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);
458 		}
459 		break;
460 
461 	default:
462 		/*disable throttling. */
463 		usbd_xfer_set_interval(transfer, 0);
464 		sc->sc_zero_length_packets = 0;
465 
466 		if (error != USB_ERR_CANCELLED) {
467 			/* Issue a clear-stall request. */
468 			usbd_xfer_set_stall(transfer);
469 			goto setup;
470 		}
471 		break;
472 	}
473 }
474 
475 static void
476 uhid_snes_status_callback(struct usb_xfer *transfer, usb_error_t error)
477 {
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;
482 
483 	switch (USB_GET_STATE(transfer)) {
484 	case USB_ST_SETUP:
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;
489 		req.wIndex[1] = 0;
490 		USETW(req.wLength, 1);
491 
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);
498 		break;
499 
500 	case USB_ST_TRANSFERRED:
501 		pc = usbd_xfer_get_frame(transfer, 1);
502 		usbd_copy_out(pc, 0, &current_status, 1);
503 		new_status = current_status & ~sc->sc_previous_status;
504 		sc->sc_previous_status = current_status;
505 		break;
506 
507 	default:
508 		break;
509 	}
510 
511 }
512 
513 static int
514 uhid_snes_probe(device_t dev)
515 {
516 	struct usb_attach_arg *uaa = device_get_ivars(dev);
517 
518 	if (uaa->usb_mode != USB_MODE_HOST)
519 		return (ENXIO);
520 
521 	return (usbd_lookup_id_by_uaa(snes_devs, sizeof(snes_devs), uaa));
522 }
523 
524 static int
525 uhid_snes_attach(device_t dev)
526 {
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);
533 
534 	sc->sc_dev = 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);
539 
540 	idesc = usbd_get_interface_descriptor(uaa->iface);
541 	alt_index = -1;
542 	for(;;) {
543 		if (idesc == NULL)
544 			break;
545 
546 		if ((idesc->bDescriptorType == UDESC_INTERFACE) &&
547 		     (idesc->bLength >= sizeof(*idesc))) {
548 			if (idesc->bInterfaceNumber != uaa->info.bIfaceNum) {
549 				break;
550 			} else {
551 				alt_index++;
552 				if (idesc->bInterfaceClass == UICLASS_HID)
553 					goto found;
554 			}
555 		}
556 
557 		cdesc = usbd_get_config_descriptor(uaa->device);
558 		idesc = (void *)usb_desc_foreach(cdesc, (void *)idesc);
559 		goto found;
560 	}
561 	goto detach;
562 
563 found:
564 	if (alt_index) {
565 		error = usbd_set_alt_interface_index(uaa->device, iface_index, alt_index);
566 		if (error)
567 			goto detach;
568 	}
569 
570 	sc->sc_iface_num = idesc->bInterfaceNumber;
571 
572 	error = usbd_transfer_setup(uaa->device, &iface_index,
573 	    sc->sc_transfer, uhid_snes_config, UHID_SNES_N_TRANSFER, sc,
574 	    &sc->sc_mutex);
575 
576 	if (error)
577 		goto detach;
578 
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);
584 
585 	if (error)
586 		goto detach;
587 
588 	mtx_lock(&sc->sc_mutex);
589 	uhid_snes_watchdog(sc);
590 	mtx_unlock(&sc->sc_mutex);
591 	return (0);
592 
593 detach:
594 	uhid_snes_detach(dev);
595 	return (ENOMEM);
596 }
597 
598 static int
599 uhid_snes_detach(device_t dev)
600 {
601 	struct uhid_snes_softc *sc = device_get_softc(dev);
602 
603 	usb_fifo_detach(&sc->sc_fifo);
604 	usb_fifo_detach(&sc->sc_fifo_no_reset);
605 
606 	mtx_lock(&sc->sc_mutex);
607 	usb_callout_stop(&sc->sc_watchdog);
608 	mtx_unlock(&sc->sc_mutex);
609 
610 	usbd_transfer_unsetup(sc->sc_transfer, UHID_SNES_N_TRANSFER);
611 	usb_callout_drain(&sc->sc_watchdog);
612 	mtx_destroy(&sc->sc_mutex);
613 
614 	return (0);
615 }
616 
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),
621 	DEVMETHOD_END
622 };
623 
624 static driver_t uhid_snes_driver = {
625 	"uhid_snes",
626 	uhid_snes_methods,
627 	sizeof(struct uhid_snes_softc)
628 };
629 
630 static devclass_t uhid_snes_devclass;
631 
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);
635