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