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
69 static const uint8_t uhid_snes_report_descr[] = { UHID_SNES_REPORT_DESCR() };
70 #define SNES_DEV(v,p,i) { USB_VPI(v,p,i) }
71
72 static 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
77 enum {
78 UHID_SNES_INTR_DT_RD,
79 UHID_SNES_STATUS_DT_RD,
80 UHID_SNES_N_TRANSFER
81 };
82
83 struct 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
113 static device_probe_t uhid_snes_probe;
114 static device_attach_t uhid_snes_attach;
115 static device_detach_t uhid_snes_detach;
116
117 static usb_fifo_open_t uhid_snes_open;
118 static usb_fifo_close_t uhid_snes_close;
119 static usb_fifo_ioctl_t uhid_snes_ioctl;
120 static usb_fifo_cmd_t uhid_snes_start_read;
121 static usb_fifo_cmd_t uhid_snes_stop_read;
122
123 static void uhid_snes_reset(struct uhid_snes_softc *);
124 static void uhid_snes_watchdog(void *);
125
126 static usb_callback_t uhid_snes_read_callback;
127 static usb_callback_t uhid_snes_status_callback;
128
129 static 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
138 static 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
158 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)159 uhid_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 }
182 done:
183 if (free_data) {
184 free(kern_data, M_USBDEV);
185 }
186 return (err);
187 }
188
189 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)190 uhid_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 }
210 done:
211 if (free_data) {
212 free(kern_data, M_USBDEV);
213 }
214 return (err);
215 }
216
217 static int
uhid_snes_open(struct usb_fifo * fifo,int fflags)218 uhid_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
243 static void
uhid_snes_reset(struct uhid_snes_softc * sc)244 uhid_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
267 static void
uhid_snes_close(struct usb_fifo * fifo,int fflags)268 uhid_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
276 static int
uhid_snes_ioctl(struct usb_fifo * fifo,u_long cmd,void * data,int fflags)277 uhid_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
413 static void
uhid_snes_watchdog(void * arg)414 uhid_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
426 static void
uhid_snes_start_read(struct usb_fifo * fifo)427 uhid_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
434 static void
uhid_snes_stop_read(struct usb_fifo * fifo)435 uhid_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
442 static void
uhid_snes_read_callback(struct usb_xfer * transfer,usb_error_t error)443 uhid_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
494 static void
uhid_snes_status_callback(struct usb_xfer * transfer,usb_error_t error)495 uhid_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
524 static int
uhid_snes_probe(device_t dev)525 uhid_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
535 static int
uhid_snes_attach(device_t dev)536 uhid_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
574 found:
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
604 detach:
605 uhid_snes_detach(dev);
606 return (ENOMEM);
607 }
608
609 static int
uhid_snes_detach(device_t dev)610 uhid_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
628 static 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
635 static driver_t uhid_snes_driver = {
636 "uhid_snes",
637 uhid_snes_methods,
638 sizeof(struct uhid_snes_softc)
639 };
640
641 DRIVER_MODULE(uhid_snes, uhub, uhid_snes_driver, NULL, NULL);
642 MODULE_DEPEND(uhid_snes, usb, 1, 1, 1);
643 USB_PNP_HOST_INFO(snes_devs);
644