]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/usb/input/uhid_snes.c
MFV r347989:
[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
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
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
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
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
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
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
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
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
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
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
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
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, &current_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
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
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
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);