xref: /haiku/src/kits/device/Joystick.cpp (revision 4b5bcc668412cfa0cdbaf600fdb5bc082c32e2d6)
1 /*
2  * Copyright 2002-2008, Marcus Overhagen, Stefano Ceccherini, Fredrik Modéen.
3  * All rights reserved.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include <Joystick.h>
9 #include <JoystickTweaker.h>
10 
11 #include <new>
12 #include <stdio.h>
13 #include <sys/ioctl.h>
14 
15 #include <Debug.h>
16 #include <Directory.h>
17 #include <List.h>
18 #include <Path.h>
19 #include <String.h>
20 
21 
22 #if DEBUG
23 static FILE *sLogFile = NULL;
24 
25 inline void
26 LOG(const char *fmt, ...)
27 {
28 	char buf[1024];
29 	va_list ap;
30 	va_start(ap, fmt);
31 	vsprintf(buf, fmt, ap);
32 	va_end(ap);
33 	fputs(buf, sLogFile); fflush(sLogFile);
34 }
35 
36 #	define LOG_ERR(text...) LOG(text)
37 
38 #else
39 #	define LOG(text...)
40 #	define LOG_ERR(text...) fprintf(stderr, text)
41 #endif
42 
43 #define CALLED() LOG("%s\n", __PRETTY_FUNCTION__)
44 
45 
46 BJoystick::BJoystick()
47 	:
48 	// legacy members for standard mode
49 	timestamp(0),
50 	horizontal(0),
51 	vertical(0),
52 	button1(true),
53 	button2(true),
54 
55 	fBeBoxMode(false),
56 	fFD(-1),
57 	fDevices(new(std::nothrow) BList),
58 	fJoystickInfo(new(std::nothrow) joystick_info),
59 	fExtendedJoystick(new(std::nothrow) BList)
60 {
61 #if DEBUG
62 	sLogFile = fopen("/var/log/joystick.log", "a");
63 #endif
64 
65 	if (fJoystickInfo != NULL)
66 		memset(fJoystickInfo, 0, sizeof(joystick_info));
67 
68 	RescanDevices();
69 }
70 
71 
72 BJoystick::~BJoystick()
73 {
74 	if (fFD >= 0)
75 		close(fFD);
76 
77 	if (fDevices != NULL) {
78 		for (int32 i = 0; i < fDevices->CountItems(); i++)
79 			delete (BString *)fDevices->ItemAt(i);
80 
81 		delete fDevices;
82 	}
83 
84 	delete fJoystickInfo;
85 
86 	if (fExtendedJoystick != NULL) {
87 		for (int32 i = 0; i < fExtendedJoystick->CountItems(); i++)
88 			delete (extended_joystick *)fExtendedJoystick->ItemAt(i);
89 
90 		delete fExtendedJoystick;
91 	}
92 }
93 
94 
95 status_t
96 BJoystick::Open(const char *portName)
97 {
98 	CALLED();
99 	return Open(portName, true);
100 }
101 
102 
103 status_t
104 BJoystick::Open(const char *portName, bool enhanced)
105 {
106 	CALLED();
107 
108 	if (portName == NULL)
109 		return B_BAD_VALUE;
110 
111 	if (fJoystickInfo == NULL || fExtendedJoystick == NULL)
112 		return B_NO_INIT;
113 
114 	fBeBoxMode = !enhanced;
115 
116 	char nameBuffer[64];
117 	if (portName[0] != '/') {
118 		snprintf(nameBuffer, sizeof(nameBuffer), DEVICE_BASE_PATH"/%s",
119 			portName);
120 	} else
121 		snprintf(nameBuffer, sizeof(nameBuffer), "%s", portName);
122 
123 	if (fFD >= 0)
124 		close(fFD);
125 
126 	// TODO: BeOS don't use O_EXCL, and this seems to lead to some issues. I
127 	// added this flag having read some comments by Marco Nelissen on the
128 	// annotated BeBook. I think BeOS uses O_RDWR | O_NONBLOCK here.
129 	fFD = open(nameBuffer, O_RDWR | O_NONBLOCK | O_EXCL);
130 	if (fFD < 0)
131 		return B_ERROR;
132 
133 	// read the Joystick Description file for this port/joystick
134 	_BJoystickTweaker joystickTweaker(*this);
135 	joystickTweaker.GetInfo(fJoystickInfo, portName);
136 
137 	LOG("ioctl - %d\n", fJoystickInfo->module_info.num_buttons);
138 	ioctl(fFD, B_JOYSTICK_SET_DEVICE_MODULE, &fJoystickInfo->module_info,
139 		sizeof(joystick_module_info));
140 	ioctl(fFD, B_JOYSTICK_GET_DEVICE_MODULE, &fJoystickInfo->module_info,
141 		sizeof(joystick_module_info));
142 	LOG("ioctl - %d\n", fJoystickInfo->module_info.num_buttons);
143 
144 	// Allocate the extended_joystick structures to hold the info for each
145 	// "stick". Note that the whole num_sticks thing seems a bit bogus, as
146 	// all sticks would be required to have exactly the same attributes,
147 	// i.e. axis, hat and button counts, since there is only one global
148 	// joystick_info for the whole device. What's implemented here is a
149 	// "best guess", using the read position in Update() to select the
150 	// stick for which an extended_joystick structure shall be returned.
151 	for (uint16 i = 0; i < fJoystickInfo->module_info.num_sticks; i++) {
152 		extended_joystick *extendedJoystick
153 			= new(std::nothrow) extended_joystick;
154 		if (extendedJoystick == NULL)
155 			return B_NO_MEMORY;
156 
157 		if (!fExtendedJoystick->AddItem(extendedJoystick)) {
158 			delete extendedJoystick;
159 			return B_NO_MEMORY;
160 		}
161 	}
162 
163 	return fFD;
164 }
165 
166 
167 void
168 BJoystick::Close(void)
169 {
170 	CALLED();
171 	if (fFD >= 0) {
172 		close(fFD);
173 		fFD = -1;
174 	}
175 }
176 
177 
178 status_t
179 BJoystick::Update()
180 {
181 	CALLED();
182 	if (fJoystickInfo == NULL || fExtendedJoystick == NULL || fFD < 0)
183 		return B_NO_INIT;
184 
185 	for (uint16 i = 0; i < fJoystickInfo->module_info.num_sticks; i++) {
186 		extended_joystick *extendedJoystick
187 			= (extended_joystick *)fExtendedJoystick->ItemAt(i);
188 		if (extendedJoystick == NULL)
189 			return B_NO_INIT;
190 
191 		ssize_t result = read_pos(fFD, i, extendedJoystick,
192 			sizeof(extended_joystick));
193 		if (result < 0)
194 			return result;
195 
196 		if (result != sizeof(extended_joystick))
197 			return B_ERROR;
198 
199 		if (i > 0)
200 			continue;
201 
202 		// fill in the legacy values for the first stick
203 		timestamp = extendedJoystick->timestamp;
204 		horizontal = extendedJoystick->axes[0];
205 		vertical = extendedJoystick->axes[1];
206 		button1 = (extendedJoystick->buttons & 1) == 0;
207 		button2 = (extendedJoystick->buttons & 2) == 0;
208 	}
209 
210 	return B_OK;
211 }
212 
213 
214 status_t
215 BJoystick::SetMaxLatency(bigtime_t maxLatency)
216 {
217 	CALLED();
218 	if (fJoystickInfo == NULL || fFD < 0)
219 		return B_NO_INIT;
220 
221 	status_t result = ioctl(fFD, B_JOYSTICK_SET_MAX_LATENCY, &maxLatency,
222 		sizeof(maxLatency));
223 	if (result == B_OK)
224 		fJoystickInfo->max_latency = maxLatency;
225 
226 	return result;
227 }
228 
229 
230 int32
231 BJoystick::CountDevices()
232 {
233 	CALLED();
234 
235 	if (fDevices == NULL)
236 		return 0;
237 
238 	int32 count = fDevices->CountItems();
239 
240 	LOG("Count = %d\n", count);
241 	return count;
242 }
243 
244 
245 status_t
246 BJoystick::GetDeviceName(int32 index, char *name, size_t bufSize)
247 {
248 	CALLED();
249 	if (fDevices == NULL)
250 		return B_NO_INIT;
251 
252 	if (index >= fDevices->CountItems())
253 		return B_BAD_INDEX;
254 
255 	if (name == NULL)
256 		return B_BAD_VALUE;
257 
258 	BString *deviceName = (BString *)fDevices->ItemAt(index);
259 	if (deviceName->Length() > (int32)bufSize)
260 		return B_NAME_TOO_LONG;
261 
262 	strlcpy(name, deviceName->String(), bufSize);
263 	LOG("Device Name = %s\n", name);
264 	return B_OK;
265 }
266 
267 
268 status_t
269 BJoystick::RescanDevices()
270 {
271 	CALLED();
272 
273 	if (fDevices == NULL)
274 		return B_NO_INIT;
275 
276 	ScanDevices(true);
277 	return B_OK;
278 }
279 
280 
281 bool
282 BJoystick::EnterEnhancedMode(const entry_ref *ref)
283 {
284 	CALLED();
285 	fBeBoxMode = false;
286 	return !fBeBoxMode;
287 }
288 
289 
290 int32
291 BJoystick::CountSticks()
292 {
293 	CALLED();
294 	if (fJoystickInfo == NULL)
295 		return 0;
296 
297 	return fJoystickInfo->module_info.num_sticks;
298 }
299 
300 
301 int32
302 BJoystick::CountAxes()
303 {
304 	CALLED();
305 	if (fJoystickInfo == NULL)
306 		return 0;
307 
308 	return fJoystickInfo->module_info.num_axes;
309 }
310 
311 
312 status_t
313 BJoystick::GetAxisValues(int16 *outValues, int32 forStick)
314 {
315 	CALLED();
316 
317 	if (fJoystickInfo == NULL || fExtendedJoystick == NULL)
318 		return B_NO_INIT;
319 
320 	if (forStick < 0
321 		|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
322 		return B_BAD_INDEX;
323 
324 	extended_joystick *extendedJoystick
325 		= (extended_joystick *)fExtendedJoystick->ItemAt(forStick);
326 	if (extendedJoystick == NULL)
327 		return B_NO_INIT;
328 
329 	memcpy(outValues, extendedJoystick->axes,
330 		fJoystickInfo->module_info.num_axes * sizeof(uint16));
331 	return B_OK;
332 }
333 
334 
335 status_t
336 BJoystick::GetAxisNameAt(int32 index, BString *outName)
337 {
338 	CALLED();
339 
340 	if (index >= CountAxes())
341 		return B_BAD_INDEX;
342 
343 	if (outName == NULL)
344 		return B_BAD_VALUE;
345 
346 	// TODO: actually retrieve the name from the driver (via a new ioctl)
347 	*outName = "Axis ";
348 	*outName << index;
349 	return B_OK;
350 }
351 
352 
353 int32
354 BJoystick::CountHats()
355 {
356 	CALLED();
357 	if (fJoystickInfo == NULL)
358 		return 0;
359 
360 	return fJoystickInfo->module_info.num_hats;
361 }
362 
363 
364 status_t
365 BJoystick::GetHatValues(uint8 *outHats, int32 forStick)
366 {
367 	CALLED();
368 
369 	if (fJoystickInfo == NULL || fExtendedJoystick == NULL)
370 		return B_NO_INIT;
371 
372 	if (forStick < 0
373 		|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
374 		return B_BAD_INDEX;
375 
376 	extended_joystick *extendedJoystick
377 		= (extended_joystick *)fExtendedJoystick->ItemAt(forStick);
378 	if (extendedJoystick == NULL)
379 		return B_NO_INIT;
380 
381 	memcpy(outHats, extendedJoystick->hats,
382 		fJoystickInfo->module_info.num_hats);
383 	return B_OK;
384 }
385 
386 
387 status_t
388 BJoystick::GetHatNameAt(int32 index, BString *outName)
389 {
390 	CALLED();
391 
392 	if (index >= CountHats())
393 		return B_BAD_INDEX;
394 
395 	if (outName == NULL)
396 		return B_BAD_VALUE;
397 
398 	// TODO: actually retrieve the name from the driver (via a new ioctl)
399 	*outName = "Hat ";
400 	*outName << index;
401 	return B_OK;
402 }
403 
404 
405 int32
406 BJoystick::CountButtons()
407 {
408 	CALLED();
409 	if (fJoystickInfo == NULL)
410 		return 0;
411 
412 	return fJoystickInfo->module_info.num_buttons;
413 }
414 
415 
416 uint32
417 BJoystick::ButtonValues(int32 forStick)
418 {
419 	CALLED();
420 
421 	if (fJoystickInfo == NULL || fExtendedJoystick == NULL)
422 		return 0;
423 
424 	if (forStick < 0
425 		|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
426 		return 0;
427 
428 	extended_joystick *extendedJoystick
429 		= (extended_joystick *)fExtendedJoystick->ItemAt(forStick);
430 	if (extendedJoystick == NULL)
431 		return 0;
432 
433 	return extendedJoystick->buttons;
434 }
435 
436 
437 status_t
438 BJoystick::GetButtonValues(bool *outButtons, int32 forStick)
439 {
440 	CALLED();
441 
442 	if (fJoystickInfo == NULL || fExtendedJoystick == NULL)
443 		return B_NO_INIT;
444 
445 	if (forStick < 0
446 		|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
447 		return B_BAD_INDEX;
448 
449 	extended_joystick *extendedJoystick
450 		= (extended_joystick *)fExtendedJoystick->ItemAt(forStick);
451 	if (extendedJoystick == NULL)
452 		return B_NO_INIT;
453 
454 	int16 buttonCount = fJoystickInfo->module_info.num_buttons;
455 	for (int16 i = 0; i < buttonCount; i++) {
456 		if (buttonCount >= 32)
457 			outButtons[i] = false;
458 		else
459 			outButtons[i] = (extendedJoystick->buttons & (1 << i)) != 0;
460 	}
461 
462 	return B_OK;
463 }
464 
465 
466 status_t
467 BJoystick::GetButtonNameAt(int32 index, BString *outName)
468 {
469 	CALLED();
470 
471 	if (index >= CountButtons())
472 		return B_BAD_INDEX;
473 
474 	if (outName == NULL)
475 		return B_BAD_VALUE;
476 
477 	// TODO: actually retrieve the name from the driver (via a new ioctl)
478 	*outName = "Button ";
479 	*outName << index;
480 	return B_OK;
481 }
482 
483 
484 status_t
485 BJoystick::GetControllerModule(BString *outName)
486 {
487 	CALLED();
488 	if (fJoystickInfo == NULL || fFD < 0)
489 		return B_NO_INIT;
490 
491 	if (outName == NULL)
492 		return B_BAD_VALUE;
493 
494 	outName->SetTo(fJoystickInfo->module_info.module_name);
495 	return B_OK;
496 }
497 
498 
499 status_t
500 BJoystick::GetControllerName(BString *outName)
501 {
502 	CALLED();
503 	if (fJoystickInfo == NULL || fFD < 0)
504 		return B_NO_INIT;
505 
506 	if (outName == NULL)
507 		return B_BAD_VALUE;
508 
509 	outName->SetTo(fJoystickInfo->module_info.device_name);
510 	return B_OK;
511 }
512 
513 
514 bool
515 BJoystick::IsCalibrationEnabled()
516 {
517 	CALLED();
518 	if (fJoystickInfo == NULL)
519 		return false;
520 
521 	return fJoystickInfo->calibration_enable;
522 }
523 
524 
525 status_t
526 BJoystick::EnableCalibration(bool calibrates)
527 {
528 	CALLED();
529 	if (fJoystickInfo == NULL || fFD < 0)
530 		return B_NO_INIT;
531 
532 	status_t result = ioctl(fFD, B_JOYSTICK_SET_RAW_MODE, &calibrates,
533 		sizeof(calibrates));
534 	if (result == B_OK)
535 		fJoystickInfo->calibration_enable = calibrates;
536 
537 	return result;
538 }
539 
540 
541 void
542 BJoystick::Calibrate(struct _extended_joystick *reading)
543 {
544 	CALLED();
545 }
546 
547 
548 void
549 BJoystick::ScanDevices(bool useDisabled)
550 {
551 	CALLED();
552 	if (useDisabled) {
553 		_BJoystickTweaker joystickTweaker(*this);
554 		joystickTweaker.scan_including_disabled();
555 	}
556 }
557 
558 
559 //	#pragma mark - FBC protection
560 
561 
562 void BJoystick::_ReservedJoystick1() {}
563 void BJoystick::_ReservedJoystick2() {}
564 void BJoystick::_ReservedJoystick3() {}
565 status_t BJoystick::_Reserved_Joystick_4(void*, ...) { return B_ERROR; }
566 status_t BJoystick::_Reserved_Joystick_5(void*, ...) { return B_ERROR; }
567 status_t BJoystick::_Reserved_Joystick_6(void*, ...) { return B_ERROR; }
568