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