1 /** \file   joy-unix-usb.c
2  * \brief   NetBSD/FreeBSD USB joystick support
3  *
4  * \author  Dieter Baron <dillo@nih.at>
5  * \author  Marco van den Heuvel <blackystardust68@yahoo.com>
6  */
7 
8 /*
9  * This file is part of VICE, the Versatile Commodore Emulator.
10  * See README for copyright notice.
11  *
12  *  This program is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU General Public License as published by
14  *  the Free Software Foundation; either version 2 of the License, or
15  *  (at your option) any later version.
16  *
17  *  This program is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public License
23  *  along with this program; if not, write to the Free Software
24  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25  *  02111-1307  USA.
26  *
27  */
28 
29 #include "vice.h"
30 
31 #if defined(UNIX_COMPILE) && !defined(MACOSX_SUPPORT)
32 
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <unistd.h>
36 
37 #include "cmdline.h"
38 #include "joy.h"
39 #include "joystick.h"
40 #include "keyboard.h"
41 #include "log.h"
42 #include "resources.h"
43 #include "types.h"
44 
45 #if defined(HAS_JOYSTICK) && defined(HAS_USB_JOYSTICK)
46 
47 #define ITEM_AXIS   0
48 #define ITEM_BUTTON 1
49 #define ITEM_HAT    2
50 
51 int hat_or[] = { 1, 9, 8, 10, 2, 6, 4, 5, };
52 
53 extern log_t joystick_log;
54 
55 #ifdef HAVE_USB_H
56 #include <usb.h>
57 #endif
58 
59 #ifdef __DragonFly__
60 /* sys/param.h contains the __DragonFly_version macro */
61 # include <sys/param.h>
62 # if __DragonFly_version >= 300200
63 /* DragonFly >= 3.2 (USB4BSD stack) */
64 #  include <bus/u4b/usb.h>
65 #  include <bus/u4b/usbhid.h>
66 # else
67 /* DragonFly < 3.2: old USB stack */
68 #  include <bus/usb/usb.h>
69 #  include <bus/usb/usbhid.h>
70 # endif
71 #else
72 # ifdef __FreeBSD__
73 #  include <sys/ioccom.h>
74 # endif
75 # include <dev/usb/usb.h>
76 # include <dev/usb/usbhid.h>
77 #endif
78 
79 #include <errno.h>
80 #include <stdlib.h>
81 #include <string.h>
82 
83 #if defined(HAVE_USBHID_H)
84 #include <usbhid.h>
85 #elif defined(HAVE_LIBUSB_H)
86 #include <libusb.h>
87 #elif defined(HAVE_LIBUSBHID_H)
88 #include <libusbhid.h>
89 #endif
90 
91 #define MAX_DEV 4	/* number of uhid devices to try */
92 
93 struct usb_joy_item {
94     struct hid_item item;
95     struct usb_joy_item *next;
96 
97     int type;
98     int min_or;
99     int min_val;
100     int max_or;
101     int max_val;
102 };
103 
104 static struct usb_joy_item *usb_joy_item[2];
105 
106 static int usb_joy_fd[2] = { -1, -1 };
107 static int usb_joy_size[2];
108 static char *usb_joy_buf[2];
109 
usb_joy_add_item(struct usb_joy_item ** item,struct hid_item * hi,int orval,int type)110 static int usb_joy_add_item(struct usb_joy_item **item, struct hid_item *hi, int orval, int type)
111 {
112     struct usb_joy_item *it;
113     int w;
114 
115     if ((it=malloc(sizeof(*it))) == NULL) {
116         /* XXX */
117         return -1;
118     }
119 
120     it->next = *item;
121     *item = it;
122 
123     memcpy(&it->item, hi, sizeof(*hi));
124     it->type = type;
125     switch (type) {
126         case ITEM_AXIS:
127             w = (hi->logical_maximum - hi->logical_minimum) / 3;
128             it->min_or = orval;
129             it->min_val = hi->logical_minimum + w;
130             it->max_or = orval * 2;
131             it->max_val = hi->logical_maximum - w;
132             break;
133         case ITEM_BUTTON:
134             it->min_or = 0;
135             it->min_val = hi->logical_minimum;
136             it->max_or = orval;
137             it->max_val = hi->logical_maximum - 1;
138             break;
139         case ITEM_HAT:
140             it->min_val = hi->logical_minimum;
141             break;
142     }
143 
144     return 0;
145 }
146 
usb_free_item(struct usb_joy_item ** item)147 static void usb_free_item(struct usb_joy_item **item)
148 {
149     struct usb_joy_item *it, *it2;
150 
151     it=*item;
152     while (it) {
153         it2 = it;
154         it = it->next;
155         free(it2);
156     }
157     *item = NULL;
158 }
159 
usb_joystick_init(void)160 int usb_joystick_init(void)
161 {
162     int i, j, id = 0, fd;
163     report_desc_t report;
164     struct hid_item h;
165     struct hid_data *d;
166     int is_joy, found;
167     char dev[32];
168 
169     for (j=i=0; i<2 && j<MAX_DEV; j++) {
170         sprintf(dev, "/dev/uhid%d", j);
171         fd = open(dev, O_RDONLY | O_NONBLOCK);
172         if (fd < 0) {
173             continue;
174         }
175 
176 #if defined(USB_GET_REPORT_ID) && !defined(__DragonFly__)
177         if (ioctl(fd, USB_GET_REPORT_ID, &id) < 0) {
178             log_warning(joystick_log, "Cannot get report id for joystick device `%s'.", dev);
179             close(fd);
180         }
181 #endif
182 
183         if ((report=hid_get_report_desc(fd)) == NULL) {
184             log_warning(joystick_log, "Cannot report description for joystick device `%s'.", dev);
185             close(fd);
186             continue;
187         }
188         usb_joy_size[i] = hid_report_size(report, hid_input, id);
189 
190         usb_joy_item[i] = NULL;
191 
192         found = 0;
193         is_joy = 0;
194 #if !defined(HAVE_USBHID_H) && !defined(HAVE_LIBUSB_H) && defined(HAVE_LIBUSBHID)
195         for (d = hid_start_parse(report, id);
196 #else
197         for (d = hid_start_parse(report, 1 << hid_input, id);
198 #endif
199         hid_get_item(d, &h);) {
200             if (h.kind == hid_collection && HID_PAGE(h.usage) == HUP_GENERIC_DESKTOP && (HID_USAGE(h.usage) == HUG_JOYSTICK || HID_USAGE(h.usage) == HUG_GAME_PAD)) {
201                 is_joy = 1;
202                 continue;
203             }
204             if (!is_joy) {
205                 continue;
206             }
207 
208             switch (HID_PAGE(h.usage)) {
209                 case HUP_GENERIC_DESKTOP:
210                     switch (HID_USAGE(h.usage)) {
211                         case HUG_X:
212                         case HUG_RX:
213                             if (usb_joy_add_item(usb_joy_item + i, &h, 4, ITEM_AXIS) == 0) {
214                                 found |= 4;
215                             }
216                             break;
217                         case HUG_Y:
218                         case HUG_RY:
219                             if (usb_joy_add_item(usb_joy_item + i, &h, 1, ITEM_AXIS) == 0) {
220                                 found |= 1;
221                             }
222                             break;
223                         case HUG_HAT_SWITCH:
224                             if (usb_joy_add_item(usb_joy_item + i, &h, 0, ITEM_HAT) == 0) {
225                                 found |= 5;
226                             }
227                             break;
228                     }
229                     break;
230                 case HUP_BUTTON:
231                     if (usb_joy_add_item(usb_joy_item + i, &h, 16, ITEM_BUTTON) == 0) {
232                         found |= 16;
233                     }
234                     break;
235             }
236         }
237         hid_end_parse(d);
238 
239         if (found != 21) {
240             close(fd);
241             usb_free_item(usb_joy_item + i);
242             log_message(joystick_log, "Not all axes found in joystick device `%s'.", dev);
243             continue;
244         }
245 
246         if ((usb_joy_buf[i] = malloc(usb_joy_size[i])) == NULL) {
247             log_warning(joystick_log, "Cannot allocate buffer for joystick device `%s'.", dev);
248             close(fd);
249             usb_free_item(usb_joy_item + i);
250             continue;
251         }
252 
253         log_message(joystick_log, "USB joystick found: `%s'.", dev);
254         usb_joy_fd[i] = fd;
255         i++;
256     }
257     return 0;
258 }
259 
usb_joystick_close(void)260 void usb_joystick_close(void)
261 {
262     int i;
263 
264     for (i = 0; i < 2; i++) {
265         if (usb_joy_fd[i] < 0) {
266             continue;
267         }
268 
269         close(usb_joy_fd[i]);
270         usb_joy_fd[i] = -1;
271         usb_free_item(usb_joy_item+i);
272     }
273 }
274 
usb_joystick(void)275 void usb_joystick(void)
276 {
277     int i, jp, val, ret;
278     struct usb_joy_item *it;
279 
280     for (i = 0; i < 4; i++) {
281         jp = joystick_port_map[i];
282         if (jp != JOYDEV_USB_0 && jp != JOYDEV_USB_1) {
283             continue;
284         }
285 
286         jp -= JOYDEV_USB_0;
287 
288         if (usb_joy_fd[jp] < 0) {
289             continue;
290         }
291 
292         val = 0;
293         while ((ret = read(usb_joy_fd[jp], usb_joy_buf[jp], usb_joy_size[jp])) == usb_joy_size[jp]) {
294             val = 1;
295         }
296         if (ret != -1 && errno != EAGAIN) {
297             /* XXX */
298             printf("strange read return: %d/%d\n", ret, errno);
299             continue;
300         }
301         if (!val) {
302             continue;
303         }
304 
305         joystick_set_value_absolute(i + 1, 0);
306 
307         for (it = usb_joy_item[jp]; it; it = it->next) {
308             val = hid_get_data(usb_joy_buf[jp], &it->item);
309             if (it->type == ITEM_HAT) {
310                 val -= it->min_val;
311                 if (val >= 0 && val <= 7) {
312                     joystick_set_value_or(i + 1, hat_or[val]);
313                 }
314             } else {
315                 if (val <= it->min_val) {
316                     joystick_set_value_or(i + 1, it->min_or);
317                 } else if (val > it->max_val) {
318                     joystick_set_value_or(i + 1, it->max_or);
319                 }
320             }
321         }
322     }
323 }
324 
325 #endif /* HAS_JOYSTICK && HAS_USB_JOYSTICK */
326 #endif
327 
328