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
71static const uint8_t uhid_snes_report_descr[] = { UHID_SNES_REPORT_DESCR() };
72#define	SNES_DEV(v,p,i) { USB_VPI(v,p,i) }
73
74static 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
79enum {
80	UHID_SNES_INTR_DT_RD,
81	UHID_SNES_STATUS_DT_RD,
82	UHID_SNES_N_TRANSFER
83};
84
85struct 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
116static device_probe_t uhid_snes_probe;
117static device_attach_t uhid_snes_attach;
118static device_detach_t uhid_snes_detach;
119
120static usb_fifo_open_t uhid_snes_open;
121static usb_fifo_close_t uhid_snes_close;
122static usb_fifo_ioctl_t uhid_snes_ioctl;
123static usb_fifo_cmd_t uhid_snes_start_read;
124static usb_fifo_cmd_t uhid_snes_stop_read;
125
126static void uhid_snes_reset(struct uhid_snes_softc *);
127static void uhid_snes_watchdog(void *);
128
129static usb_callback_t uhid_snes_read_callback;
130static usb_callback_t uhid_snes_status_callback;
131
132static 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
141static 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
161static int
162uhid_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	}
185done:
186	if (free_data) {
187		free(kern_data, M_USBDEV);
188	}
189	return (err);
190}
191
192static int
193uhid_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	}
213done:
214	if (free_data) {
215		free(kern_data, M_USBDEV);
216	}
217	return (err);
218}
219
220static int
221uhid_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
246static void
247uhid_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
270static void
271uhid_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
279static int
280uhid_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
394static void
395uhid_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
407static void
408uhid_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
415static void
416uhid_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
423static void
424uhid_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
475static void
476uhid_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
513static int
514uhid_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
524static int
525uhid_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
563found:
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
593detach:
594	uhid_snes_detach(dev);
595	return (ENOMEM);
596}
597
598static int
599uhid_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
617static 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
624static driver_t uhid_snes_driver = {
625	"uhid_snes",
626	uhid_snes_methods,
627	sizeof(struct uhid_snes_softc)
628};
629
630static devclass_t uhid_snes_devclass;
631
632DRIVER_MODULE(uhid_snes, uhub, uhid_snes_driver, uhid_snes_devclass, NULL, 0);
633MODULE_DEPEND(uhid_snes, usb, 1, 1, 1);
634USB_PNP_HOST_INFO(snes_devs);
635