]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/usb/input/uhid_snes.c
zfs: merge openzfs/zfs@5e2c8338b (master) into main
[FreeBSD/FreeBSD.git] / sys / dev / usb / input / uhid_snes.c
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);