Mercurial > sdl-ios-xcode
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 |