comparison src/joystick/bsd/SDL_sysjoystick.c @ 278:dcd9f7b50a1c

Added support for joysticks on *BSD (thanks Wilbern!)
author Sam Lantinga <slouken@libsdl.org>
date Thu, 14 Feb 2002 01:24:08 +0000
parents
children f6ffac90895c
comparison
equal deleted inserted replaced
277:255c7ee077cb 278:dcd9f7b50a1c
1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 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 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Sam Lantinga
20 slouken@devolution.com
21 */
22
23 /*
24 * Joystick driver for the uhid(4) interface found in OpenBSD,
25 * NetBSD and FreeBSD.
26 *
27 * Maintainer: <vedge at csoft.org>
28 */
29
30 #ifdef SAVE_RCSID
31 static char rcsid =
32 "@(#) $Id $";
33 #endif
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <string.h>
40 #include <errno.h>
41
42 #include <dev/usb/usb.h>
43 #include <dev/usb/usbhid.h>
44 #include <usbhid.h>
45
46 #include "SDL_error.h"
47 #include "SDL_joystick.h"
48 #include "SDL_sysjoystick.h"
49 #include "SDL_joystick_c.h"
50
51 #define MAX_UHID_JOYS 4
52 #define MAX_JOY_JOYS 2
53 #define MAX_JOYS (MAX_UHID_JOYS + MAX_JOY_JOYS)
54
55 #define SDLAXIS_UINT8(v) \
56 ((v == 127) ? 0 : \
57 (v == 255) ? 32767 : \
58 -32767)
59
60 struct report {
61 struct usb_ctl_report *buf; /* Buffer */
62 size_t size; /* Buffer size */
63 int rid; /* Report ID */
64 enum {
65 SREPORT_UNINIT,
66 SREPORT_CLEAN,
67 SREPORT_DIRTY
68 } status;
69 };
70
71 static struct {
72 int uhid_report;
73 enum hid_kind kind;
74 const char *name;
75 } const repinfo[] = {
76 { UHID_INPUT_REPORT, hid_input, "input" },
77 { UHID_OUTPUT_REPORT, hid_output, "output" },
78 { UHID_FEATURE_REPORT, hid_feature, "feature" }
79 };
80 #define REPORT_INPUT 0
81 #define REPORT_OUTPUT 1
82 #define REPORT_FEATURE 2
83
84 struct joystick_hwdata {
85 int fd;
86 char *path;
87 enum {
88 BSDJOY_UHID, /* uhid(4) */
89 BSDJOY_JOY /* joy(4) */
90 } type;
91 struct report_desc *repdesc;
92 struct report inreport;
93 int axismin[3];
94 int axismax[3];
95 };
96
97 static char *joynames[MAX_JOYS];
98 static char *joydevnames[MAX_JOYS];
99
100 static int report_alloc(struct report *, struct report_desc *, int);
101 static void report_free(struct report *);
102
103 int
104 SDL_SYS_JoystickInit(void)
105 {
106 char s[10];
107 int i, fd;
108
109 SDL_numjoysticks = 0;
110
111 memset(joynames, NULL, sizeof(joynames));
112 memset(joydevnames, NULL, sizeof(joydevnames));
113
114 for (i = 0; i < MAX_UHID_JOYS; i++) {
115 sprintf(s, "/dev/uhid%d", i);
116 fd = open(s, O_RDWR);
117 if (fd > 0) {
118 joynames[SDL_numjoysticks++] = strdup(s);
119 close(fd);
120 }
121 }
122 for (i = 0; i < MAX_JOY_JOYS; i++) {
123 sprintf(s, "/dev/joy%d", i);
124 fd = open(s, O_RDWR);
125 if (fd > 0) {
126 joynames[SDL_numjoysticks++] = strdup(s);
127 close(fd);
128 }
129 }
130
131 /* Read the default USB HID usage table. */
132 hid_init(NULL);
133
134 return (SDL_numjoysticks);
135 }
136
137 const char *
138 SDL_SYS_JoystickName(int index)
139 {
140 if (joydevnames[index] != NULL) {
141 return (joydevnames[index]);
142 }
143 return (joynames[index]);
144 }
145
146 int
147 SDL_SYS_JoystickOpen(SDL_Joystick *joy)
148 {
149 char *path = joynames[joy->index];
150 struct joystick_hwdata *hw;
151 struct hid_item hitem;
152 struct hid_data *hdata;
153 struct report *rep;
154 int fd;
155
156 fd = open(path, O_RDWR);
157 if (fd < 0) {
158 SDL_SetError("%s: %s", path, strerror(errno));
159 return (-1);
160 }
161
162 hw = (struct joystick_hwdata *)malloc(sizeof(struct joystick_hwdata));
163 if (hw == NULL) {
164 SDL_OutOfMemory();
165 close(fd);
166 return (-1);
167 }
168 joy->hwdata = hw;
169 hw->fd = fd;
170 hw->path = strdup(path);
171 hw->type = BSDJOY_UHID;
172 hw->repdesc = hid_get_report_desc(fd);
173 if (hw->repdesc == NULL) {
174 SDL_SetError("%s: USB_GET_REPORT_DESC: %s", hw->path,
175 strerror(errno));
176 goto usberr;
177 }
178
179 rep = &hw->inreport;
180 if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
181 goto usberr;
182 }
183 if (rep->size <= 0) {
184 SDL_SetError("Input report descriptor has invalid length");
185 goto usberr;
186 }
187
188 hdata = hid_start_parse(hw->repdesc, 1 << hid_input);
189 if (hdata == NULL) {
190 SDL_SetError("%s: Cannot start HID parser", hw->path);
191 goto usberr;
192 }
193 joy->naxes = 0;
194 joy->nbuttons = 0;
195 joy->nhats = 0;
196 joy->nballs = 0;
197
198 while (hid_get_item(hdata, &hitem) > 0) {
199 char *s, *sp;
200
201 switch (hitem.kind) {
202 case hid_collection:
203 switch (HID_PAGE(hitem.usage)) {
204 case HUP_GENERIC_DESKTOP:
205 switch (HID_USAGE(hitem.usage)) {
206 case HUG_JOYSTICK:
207 case HUG_GAME_PAD:
208 s = hid_usage_in_page(hitem.usage);
209 sp = malloc(strlen(s) + 5);
210 sprintf(sp, "%s (%d)", s,
211 joy->index);
212 joydevnames[joy->index] = sp;
213 }
214 }
215 break;
216 case hid_input:
217 switch (HID_PAGE(hitem.usage)) {
218 case HUP_UNDEFINED:
219 break;
220 case HUP_GENERIC_DESKTOP:
221 switch (HID_USAGE(hitem.usage)) {
222 case HUG_X:
223 case HUG_Y:
224 case HUG_Z:
225 hw->axismin[joy->naxes] =
226 hitem.logical_minimum;
227 hw->axismax[joy->naxes] =
228 hitem.logical_maximum;
229 joy->naxes++;
230 break;
231 }
232 break;
233 case HUP_BUTTON:
234 joy->nbuttons++;
235 break;
236 }
237 break;
238 default:
239 break;
240 }
241 }
242 hid_end_parse(hdata);
243
244 /* The poll blocks the event thread. */
245 fcntl(fd, F_SETFL, O_NONBLOCK);
246
247 return (0);
248 usberr:
249 close(hw->fd);
250 free(hw->path);
251 free(hw);
252 return (-1);
253 }
254
255 void
256 SDL_SYS_JoystickUpdate(SDL_Joystick *joy)
257 {
258 static struct hid_item hitem;
259 static struct hid_data *hdata;
260 static int nbutton, naxe, v, max, min;
261 static struct report *rep;
262
263 rep = &joy->hwdata->inreport;
264 if (read(joy->hwdata->fd, rep->buf->data, rep->size) != rep->size) {
265 return;
266 }
267 hdata = hid_start_parse(joy->hwdata->repdesc, 1 << hid_input);
268 if (hdata == NULL) {
269 fprintf(stderr, "%s: Cannot start HID parser\n",
270 joy->hwdata->path);
271 return;
272 }
273
274 for (nbutton = 0, naxe = 0; hid_get_item(hdata, &hitem) > 0;) {
275 switch (hitem.kind) {
276 case hid_input:
277 switch (HID_PAGE(hitem.usage)) {
278 case HUP_UNDEFINED:
279 continue;
280 case HUP_GENERIC_DESKTOP:
281 switch (HID_USAGE(hitem.usage)) {
282 case HUG_X:
283 case HUG_Y:
284 case HUG_Z:
285 v = hid_get_data(rep->buf->data,
286 &hitem);
287
288 /*
289 * XXX revisit later. need to test
290 * with more devices.
291 */
292 if (joy->hwdata->axismin[naxe] == 0 &&
293 joy->hwdata->axismax[naxe] == 255) {
294 v = SDLAXIS_UINT8(v);
295 }
296
297 if (v != joy->axes[naxe]) {
298 SDL_PrivateJoystickAxis(joy,
299 naxe, (Sint32)v);
300 }
301 naxe++;
302 break;
303 }
304 break;
305 case HUP_BUTTON:
306 /* XXX assume a 0..1 range */
307 v = hid_get_data(rep->buf->data, &hitem);
308 if (joy->buttons[nbutton] != v) {
309 SDL_PrivateJoystickButton(joy,
310 nbutton, v);
311 }
312 nbutton++;
313 break;
314 }
315 break;
316 default:
317 break;
318 }
319 }
320 hid_end_parse(hdata);
321
322 return;
323 }
324
325 /* Function to close a joystick after use */
326 void
327 SDL_SYS_JoystickClose(SDL_Joystick *joy)
328 {
329 report_free(&joy->hwdata->inreport);
330 hid_dispose_report_desc(joy->hwdata->repdesc);
331 close(joy->hwdata->fd);
332 free(joy->hwdata->path);
333 free(joy->hwdata);
334
335 return;
336 }
337
338 void
339 SDL_SYS_JoystickQuit(void)
340 {
341 int i;
342
343 for (i = 0; i < MAX_JOYS; i++) {
344 if (joynames[i] != NULL)
345 free(joynames[i]);
346 if (joydevnames[i] != NULL)
347 free(joydevnames[i]);
348 }
349
350 return;
351 }
352
353 static int
354 report_alloc(struct report *r, struct report_desc *rd, int repind)
355 {
356 int len;
357
358 len = hid_report_size(rd, repinfo[repind].kind, &r->rid);
359 if (len < 0) {
360 SDL_SetError("Negative HID report size");
361 return (-1);
362 }
363 r->size = len;
364
365 if (r->size > 0) {
366 r->buf = malloc(sizeof(*r->buf) - sizeof(r->buf->data) +
367 r->size);
368 if (r->buf == NULL) {
369 SDL_OutOfMemory();
370 return (-1);
371 }
372 } else {
373 r->buf = NULL;
374 }
375
376 r->status = SREPORT_CLEAN;
377 return (0);
378 }
379
380 static void
381 report_free(struct report *r)
382 {
383 if (r->buf != NULL) {
384 free(r->buf);
385 }
386 r->status = SREPORT_UNINIT;
387 }
388