This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_sysjoystick.c
598 lines (535 loc) · 14.8 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2006 Sam Lantinga
4
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Lesser General Public
7
License as published by the Free Software Foundation; either
8
version 2.1 of the License, or (at your option) any later version.
9
10
11
12
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
Lesser General Public License for more details.
14
15
16
17
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19
Sam Lantinga
20
slouken@libsdl.org
21
*/
22
#include "SDL_config.h"
23
24
25
#ifdef SDL_JOYSTICK_USBHID
26
27
28
29
30
31
32
/*
* Joystick driver for the uhid(4) interface found in OpenBSD,
* NetBSD and FreeBSD.
*
* Maintainer: <vedge at csoft.org>
*/
33
34
#include <sys/param.h>
35
36
37
38
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
39
40
41
#if defined(HAVE_USB_H)
#include <usb.h>
#endif
42
43
44
45
#ifdef __DragonFly__
#include <bus/usb/usb.h>
#include <bus/usb/usbhid.h>
#else
46
47
#include <dev/usb/usb.h>
#include <dev/usb/usbhid.h>
48
#endif
49
50
51
52
53
54
55
#if defined(HAVE_USBHID_H)
#include <usbhid.h>
#elif defined(HAVE_LIBUSB_H)
#include <libusb.h>
#elif defined(HAVE_LIBUSBHID_H)
#include <libusbhid.h>
56
#endif
57
58
#ifdef __FREEBSD__
59
#ifndef __DragonFly__
60
#include <osreldate.h>
61
#endif
62
63
64
#include <sys/joystick.h>
#endif
65
#if SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
66
#include <machine/joystick.h>
67
68
#endif
69
#include "SDL_joystick.h"
70
71
#include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h"
72
73
74
75
76
#define MAX_UHID_JOYS 4
#define MAX_JOY_JOYS 2
#define MAX_JOYS (MAX_UHID_JOYS + MAX_JOY_JOYS)
77
78
79
80
81
82
83
84
85
86
87
struct report
{
struct usb_ctl_report *buf; /* Buffer */
size_t size; /* Buffer size */
int rid; /* Report ID */
enum
{
SREPORT_UNINIT,
SREPORT_CLEAN,
SREPORT_DIRTY
} status;
88
89
};
90
91
92
93
94
static struct
{
int uhid_report;
hid_kind_t kind;
const char *name;
95
} const repinfo[] = {
96
97
98
{UHID_INPUT_REPORT, hid_input, "input"},
{UHID_OUTPUT_REPORT, hid_output, "output"},
{UHID_FEATURE_REPORT, hid_feature, "feature"}
99
};
100
101
102
103
104
105
enum
{
REPORT_INPUT = 0,
REPORT_OUTPUT = 1,
REPORT_FEATURE = 2
106
107
};
108
109
110
111
112
113
114
115
116
117
118
enum
{
JOYAXE_X,
JOYAXE_Y,
JOYAXE_Z,
JOYAXE_SLIDER,
JOYAXE_WHEEL,
JOYAXE_RX,
JOYAXE_RY,
JOYAXE_RZ,
JOYAXE_count
119
};
120
121
122
123
124
125
126
127
128
129
130
131
132
struct joystick_hwdata
{
int fd;
char *path;
enum
{
BSDJOY_UHID, /* uhid(4) */
BSDJOY_JOY /* joy(4) */
} type;
struct report_desc *repdesc;
struct report inreport;
int axis_map[JOYAXE_count]; /* map present JOYAXE_* to 0,1,.. */
133
134
135
136
137
};
static char *joynames[MAX_JOYS];
static char *joydevnames[MAX_JOYS];
138
139
static int report_alloc(struct report *, struct report_desc *, int);
static void report_free(struct report *);
140
141
142
143
144
145
146
#ifdef USBHID_UCR_DATA
#define REP_BUF_DATA(rep) ((rep)->buf->ucr_data)
#else
#define REP_BUF_DATA(rep) ((rep)->buf->data)
#endif
147
int
148
SDL_SYS_JoystickInit(void)
149
{
150
151
152
153
154
char s[16];
int i, fd;
SDL_numjoysticks = 0;
155
156
SDL_memset(joynames, 0, sizeof(joynames));
SDL_memset(joydevnames, 0, sizeof(joydevnames));
157
158
159
160
for (i = 0; i < MAX_UHID_JOYS; i++) {
SDL_Joystick nj;
161
SDL_snprintf(s, SDL_arraysize(s), "/dev/uhid%d", i);
162
163
nj.index = SDL_numjoysticks;
164
joynames[nj.index] = strdup(s);
165
166
167
if (SDL_SYS_JoystickOpen(&nj) == 0) {
SDL_SYS_JoystickClose(&nj);
168
169
SDL_numjoysticks++;
} else {
170
SDL_free(joynames[nj.index]);
171
172
173
174
joynames[nj.index] = NULL;
}
}
for (i = 0; i < MAX_JOY_JOYS; i++) {
175
176
SDL_snprintf(s, SDL_arraysize(s), "/dev/joy%d", i);
fd = open(s, O_RDONLY);
177
if (fd != -1) {
178
179
joynames[SDL_numjoysticks++] = strdup(s);
close(fd);
180
181
182
183
}
}
/* Read the default USB HID usage table. */
184
hid_init(NULL);
185
186
return (SDL_numjoysticks);
187
188
189
}
const char *
190
SDL_SYS_JoystickName(int index)
191
{
192
193
194
195
if (joydevnames[index] != NULL) {
return (joydevnames[index]);
}
return (joynames[index]);
196
197
}
198
static int
199
usage_to_joyaxe(unsigned usage)
200
201
202
203
{
int joyaxe;
switch (usage) {
case HUG_X:
204
205
joyaxe = JOYAXE_X;
break;
206
case HUG_Y:
207
208
joyaxe = JOYAXE_Y;
break;
209
case HUG_Z:
210
211
joyaxe = JOYAXE_Z;
break;
212
case HUG_SLIDER:
213
214
joyaxe = JOYAXE_SLIDER;
break;
215
case HUG_WHEEL:
216
217
joyaxe = JOYAXE_WHEEL;
break;
218
case HUG_RX:
219
220
joyaxe = JOYAXE_RX;
break;
221
case HUG_RY:
222
223
joyaxe = JOYAXE_RY;
break;
224
case HUG_RZ:
225
226
joyaxe = JOYAXE_RZ;
break;
227
default:
228
joyaxe = -1;
229
}
230
return joyaxe;
231
232
233
}
static unsigned
234
hatval_to_sdl(Sint32 hatval)
235
236
{
static const unsigned hat_dir_map[8] = {
237
238
SDL_HAT_UP, SDL_HAT_RIGHTUP, SDL_HAT_RIGHT, SDL_HAT_RIGHTDOWN,
SDL_HAT_DOWN, SDL_HAT_LEFTDOWN, SDL_HAT_LEFT, SDL_HAT_LEFTUP
239
240
};
unsigned result;
241
242
243
244
if ((hatval & 7) == hatval)
result = hat_dir_map[hatval];
else
result = SDL_HAT_CENTERED;
245
246
247
248
return result;
}
249
int
250
SDL_SYS_JoystickOpen(SDL_Joystick * joy)
251
{
252
253
254
255
256
257
258
259
char *path = joynames[joy->index];
struct joystick_hwdata *hw;
struct hid_item hitem;
struct hid_data *hdata;
struct report *rep;
int fd;
int i;
260
fd = open(path, O_RDONLY);
261
if (fd == -1) {
262
SDL_SetError("%s: %s", path, strerror(errno));
263
264
265
266
return (-1);
}
hw = (struct joystick_hwdata *)
267
SDL_malloc(sizeof(struct joystick_hwdata));
268
if (hw == NULL) {
269
270
SDL_OutOfMemory();
close(fd);
271
272
273
274
return (-1);
}
joy->hwdata = hw;
hw->fd = fd;
275
276
hw->path = strdup(path);
if (!SDL_strncmp(path, "/dev/joy", 8)) {
277
278
279
280
281
hw->type = BSDJOY_JOY;
joy->naxes = 2;
joy->nbuttons = 2;
joy->nhats = 0;
joy->nballs = 0;
282
joydevnames[joy->index] = strdup("Gameport joystick");
283
284
285
286
287
288
289
290
291
292
goto usbend;
} else {
hw->type = BSDJOY_UHID;
}
{
int ax;
for (ax = 0; ax < JOYAXE_count; ax++)
hw->axis_map[ax] = -1;
}
293
hw->repdesc = hid_get_report_desc(fd);
294
if (hw->repdesc == NULL) {
295
296
SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path,
strerror(errno));
297
298
goto usberr;
}
299
300
rep = &hw->inreport;
301
if (ioctl(fd, USB_GET_REPORT_ID, &rep->rid) < 0) {
302
303
rep->rid = -1; /* XXX */
}
304
if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
305
306
307
goto usberr;
}
if (rep->size <= 0) {
308
309
SDL_SetError("%s: Input report descriptor has invalid length",
hw->path);
310
311
goto usberr;
}
312
#if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_version >= 500111)
313
hdata = hid_start_parse(hw->repdesc, 1 << hid_input, rep->rid);
314
#else
315
hdata = hid_start_parse(hw->repdesc, 1 << hid_input);
316
#endif
317
if (hdata == NULL) {
318
SDL_SetError("%s: Cannot start HID parser", hw->path);
319
320
321
322
323
324
325
326
327
goto usberr;
}
joy->naxes = 0;
joy->nbuttons = 0;
joy->nhats = 0;
joy->nballs = 0;
for (i = 0; i < JOYAXE_count; i++)
hw->axis_map[i] = -1;
328
while (hid_get_item(hdata, &hitem) > 0) {
329
330
331
332
333
char *sp;
const char *s;
switch (hitem.kind) {
case hid_collection:
334
switch (HID_PAGE(hitem.usage)) {
335
case HUP_GENERIC_DESKTOP:
336
switch (HID_USAGE(hitem.usage)) {
337
338
case HUG_JOYSTICK:
case HUG_GAME_PAD:
339
340
341
342
s = hid_usage_in_page(hitem.usage);
sp = SDL_malloc(SDL_strlen(s) + 5);
SDL_snprintf(sp, SDL_strlen(s) + 5, "%s (%d)",
s, joy->index);
343
344
345
346
347
joydevnames[joy->index] = sp;
}
}
break;
case hid_input:
348
switch (HID_PAGE(hitem.usage)) {
349
350
case HUP_GENERIC_DESKTOP:
{
351
352
unsigned usage = HID_USAGE(hitem.usage);
int joyaxe = usage_to_joyaxe(usage);
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
if (joyaxe >= 0) {
hw->axis_map[joyaxe] = 1;
} else if (usage == HUG_HAT_SWITCH) {
joy->nhats++;
}
break;
}
case HUP_BUTTON:
joy->nbuttons++;
break;
default:
break;
}
break;
default:
break;
}
}
371
hid_end_parse(hdata);
372
373
374
375
376
377
for (i = 0; i < JOYAXE_count; i++)
if (hw->axis_map[i] > 0)
hw->axis_map[i] = joy->naxes++;
usbend:
/* The poll blocks the event thread. */
378
fcntl(fd, F_SETFL, O_NONBLOCK);
379
380
381
return (0);
usberr:
382
383
384
close(hw->fd);
SDL_free(hw->path);
SDL_free(hw);
385
return (-1);
386
387
388
}
void
389
SDL_SYS_JoystickUpdate(SDL_Joystick * joy)
390
{
391
392
393
394
395
struct hid_item hitem;
struct hid_data *hdata;
struct report *rep;
int nbutton, naxe = -1;
Sint32 v;
396
397
#if defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
398
399
400
401
struct joystick gameport;
static int x, y, xmin = 0xffff, ymin = 0xffff, xmax = 0, ymax = 0;
if (joy->hwdata->type == BSDJOY_JOY) {
402
if (read(joy->hwdata->fd, &gameport, sizeof gameport) !=
403
404
sizeof gameport)
return;
405
if (abs(x - gameport.x) > 8) {
406
407
408
409
410
411
412
413
414
415
416
417
418
419
x = gameport.x;
if (x < xmin) {
xmin = x;
}
if (x > xmax) {
xmax = x;
}
if (xmin == xmax) {
xmin--;
xmax++;
}
v = (Sint32) x;
v -= (xmax + xmin + 1) / 2;
v *= 32768 / ((xmax - xmin + 1) / 2);
420
SDL_PrivateJoystickAxis(joy, 0, v);
421
}
422
if (abs(y - gameport.y) > 8) {
423
424
425
426
427
428
429
430
431
432
433
434
435
436
y = gameport.y;
if (y < ymin) {
ymin = y;
}
if (y > ymax) {
ymax = y;
}
if (ymin == ymax) {
ymin--;
ymax++;
}
v = (Sint32) y;
v -= (ymax + ymin + 1) / 2;
v *= 32768 / ((ymax - ymin + 1) / 2);
437
SDL_PrivateJoystickAxis(joy, 1, v);
438
439
}
if (gameport.b1 != joy->buttons[0]) {
440
SDL_PrivateJoystickButton(joy, 0, gameport.b1);
441
442
}
if (gameport.b2 != joy->buttons[1]) {
443
SDL_PrivateJoystickButton(joy, 1, gameport.b2);
444
445
446
}
return;
}
447
#endif /* defined(__FREEBSD__) || SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H */
448
449
450
rep = &joy->hwdata->inreport;
451
if (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) != rep->size) {
452
453
return;
}
454
#if defined(USBHID_NEW) || (defined(__FREEBSD__) && __FreeBSD_version >= 500111)
455
hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input, rep->rid);
456
#else
457
hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input);
458
#endif
459
if (hdata == NULL) {
460
fprintf(stderr, "%s: Cannot start HID parser\n", joy->hwdata->path);
461
462
463
return;
}
464
for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
465
466
switch (hitem.kind) {
case hid_input:
467
switch (HID_PAGE(hitem.usage)) {
468
469
case HUP_GENERIC_DESKTOP:
{
470
471
unsigned usage = HID_USAGE(hitem.usage);
int joyaxe = usage_to_joyaxe(usage);
472
473
474
if (joyaxe >= 0) {
naxe = joy->hwdata->axis_map[joyaxe];
/* scaleaxe */
475
v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
476
477
478
479
480
481
v -= (hitem.logical_maximum +
hitem.logical_minimum + 1) / 2;
v *= 32768 /
((hitem.logical_maximum -
hitem.logical_minimum + 1) / 2);
if (v != joy->axes[naxe]) {
482
SDL_PrivateJoystickAxis(joy, naxe, v);
483
484
}
} else if (usage == HUG_HAT_SWITCH) {
485
486
487
488
v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
SDL_PrivateJoystickHat(joy, 0,
hatval_to_sdl(v) -
hitem.logical_minimum);
489
490
491
492
}
break;
}
case HUP_BUTTON:
493
v = (Sint32) hid_get_data(REP_BUF_DATA(rep), &hitem);
494
if (joy->buttons[nbutton] != v) {
495
SDL_PrivateJoystickButton(joy, nbutton, v);
496
497
498
499
500
501
502
503
504
505
506
}
nbutton++;
break;
default:
continue;
}
break;
default:
break;
}
}
507
hid_end_parse(hdata);
508
509
return;
510
511
512
513
}
/* Function to close a joystick after use */
void
514
SDL_SYS_JoystickClose(SDL_Joystick * joy)
515
{
516
517
518
if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) {
report_free(&joy->hwdata->inreport);
hid_dispose_report_desc(joy->hwdata->repdesc);
519
}
520
521
522
close(joy->hwdata->fd);
SDL_free(joy->hwdata->path);
SDL_free(joy->hwdata);
523
524
return;
525
526
527
}
void
528
SDL_SYS_JoystickQuit(void)
529
{
530
int i;
531
532
533
for (i = 0; i < MAX_JOYS; i++) {
if (joynames[i] != NULL)
534
SDL_free(joynames[i]);
535
if (joydevnames[i] != NULL)
536
SDL_free(joydevnames[i]);
537
}
538
539
return;
540
541
542
}
static int
543
report_alloc(struct report *r, struct report_desc *rd, int repind)
544
{
545
int len;
546
547
#ifdef __DragonFly__
548
len = hid_report_size(rd, r->rid, repinfo[repind].kind);
549
#elif __FREEBSD__
550
# if (__FreeBSD_version >= 460000)
551
# if (__FreeBSD_version <= 500111)
552
len = hid_report_size(rd, r->rid, repinfo[repind].kind);
553
# else
554
len = hid_report_size(rd, repinfo[repind].kind, r->rid);
555
# endif
556
# else
557
len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
558
# endif
559
#else
560
# ifdef USBHID_NEW
561
len = hid_report_size(rd, repinfo[repind].kind, r->rid);
562
# else
563
len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
564
# endif
565
#endif
566
567
if (len < 0) {
568
SDL_SetError("Negative HID report size");
569
570
571
572
573
return (-1);
}
r->size = len;
if (r->size > 0) {
574
575
r->buf = SDL_malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) +
r->size);
576
if (r->buf == NULL) {
577
SDL_OutOfMemory();
578
579
580
581
582
583
584
585
return (-1);
}
} else {
r->buf = NULL;
}
r->status = SREPORT_CLEAN;
return (0);
586
587
588
}
static void
589
report_free(struct report *r)
590
{
591
if (r->buf != NULL) {
592
SDL_free(r->buf);
593
594
}
r->status = SREPORT_UNINIT;
595
596
}
597
#endif /* SDL_JOYSTICK_USBHID */
598
/* vi: set ts=4 sw=4 expandtab: */