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