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