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
23static FILE *sLogFile = NULL;
24
25inline void
26LOG(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
46BJoystick::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	fJoystickData(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->module_info, 0, sizeof(joystick_module_info));
67		fJoystickInfo->calibration_enable = false;
68		fJoystickInfo->max_latency = 0;
69	}
70
71	RescanDevices();
72}
73
74
75BJoystick::~BJoystick()
76{
77	if (fFD >= 0)
78		close(fFD);
79
80	if (fDevices != NULL) {
81		for (int32 i = 0; i < fDevices->CountItems(); i++)
82			delete (BString *)fDevices->ItemAt(i);
83
84		delete fDevices;
85	}
86
87	delete fJoystickInfo;
88
89	if (fJoystickData != NULL) {
90		for (int32 i = 0; i < fJoystickData->CountItems(); i++) {
91			variable_joystick *variableJoystick
92				= (variable_joystick *)fJoystickData->ItemAt(i);
93			if (variableJoystick == NULL)
94				continue;
95
96			free(variableJoystick->data);
97			delete variableJoystick;
98		}
99
100		delete fJoystickData;
101	}
102}
103
104
105status_t
106BJoystick::Open(const char *portName)
107{
108	CALLED();
109	return Open(portName, true);
110}
111
112
113status_t
114BJoystick::Open(const char *portName, bool enhanced)
115{
116	CALLED();
117
118	if (portName == NULL)
119		return B_BAD_VALUE;
120
121	if (fJoystickInfo == NULL || fJoystickData == NULL)
122		return B_NO_INIT;
123
124	fBeBoxMode = !enhanced;
125
126	char nameBuffer[64];
127	if (portName[0] != '/') {
128		snprintf(nameBuffer, sizeof(nameBuffer), DEVICE_BASE_PATH"/%s",
129			portName);
130	} else
131		snprintf(nameBuffer, sizeof(nameBuffer), "%s", portName);
132
133	if (fFD >= 0)
134		close(fFD);
135
136	// TODO: BeOS don't use O_EXCL, and this seems to lead to some issues. I
137	// added this flag having read some comments by Marco Nelissen on the
138	// annotated BeBook. I think BeOS uses O_RDWR | O_NONBLOCK here.
139	fFD = open(nameBuffer, O_RDWR | O_NONBLOCK | O_EXCL);
140	if (fFD < 0)
141		return B_ERROR;
142
143	// read the Joystick Description file for this port/joystick
144	_BJoystickTweaker joystickTweaker(*this);
145	joystickTweaker.GetInfo(fJoystickInfo, portName);
146
147	// signal that we support variable reads
148	fJoystickInfo->module_info.flags |= js_flag_variable_size_reads;
149
150	LOG("ioctl - %d\n", fJoystickInfo->module_info.num_buttons);
151	ioctl(fFD, B_JOYSTICK_SET_DEVICE_MODULE, &fJoystickInfo->module_info,
152		sizeof(joystick_module_info));
153	ioctl(fFD, B_JOYSTICK_GET_DEVICE_MODULE, &fJoystickInfo->module_info,
154		sizeof(joystick_module_info));
155	LOG("ioctl - %d\n", fJoystickInfo->module_info.num_buttons);
156
157	// Allocate the variable_joystick structures to hold the info for each
158	// "stick". Note that the whole num_sticks thing seems a bit bogus, as
159	// all sticks would be required to have exactly the same attributes,
160	// i.e. axis, hat and button counts, since there is only one global
161	// joystick_info for the whole device. What's implemented here is a
162	// "best guess", using the read position in Update() to select the
163	// stick for which data shall be returned.
164	bool supportsVariable
165		= (fJoystickInfo->module_info.flags & js_flag_variable_size_reads) != 0;
166	for (uint16 i = 0; i < fJoystickInfo->module_info.num_sticks; i++) {
167		variable_joystick *variableJoystick
168			= new(std::nothrow) variable_joystick;
169		if (variableJoystick == NULL)
170			return B_NO_MEMORY;
171
172		status_t result;
173		if (supportsVariable) {
174			// The driver supports arbitrary controls.
175			result = variableJoystick->initialize(
176				fJoystickInfo->module_info.num_axes,
177				fJoystickInfo->module_info.num_hats,
178				fJoystickInfo->module_info.num_buttons);
179		} else {
180			// The driver doesn't support our variable requests so we construct
181			// a data structure that is compatible with extended_joystick and
182			// just use that in reads. This allows us to use a single data
183			// format internally but be compatible with both inputs.
184			result = variableJoystick->initialize_to_extended_joystick();
185
186			// Also ensure that we don't read over those boundaries.
187			if (fJoystickInfo->module_info.num_axes > MAX_AXES)
188				fJoystickInfo->module_info.num_axes = MAX_AXES;
189			if (fJoystickInfo->module_info.num_hats > MAX_HATS)
190				fJoystickInfo->module_info.num_hats = MAX_HATS;
191			if (fJoystickInfo->module_info.num_buttons > MAX_BUTTONS)
192				fJoystickInfo->module_info.num_buttons = MAX_BUTTONS;
193		}
194
195		if (result != B_OK) {
196			delete variableJoystick;
197			return result;
198		}
199
200		if (!fJoystickData->AddItem(variableJoystick)) {
201			free(variableJoystick->data);
202			delete variableJoystick;
203			return B_NO_MEMORY;
204		}
205	}
206
207	return fFD;
208}
209
210
211void
212BJoystick::Close(void)
213{
214	CALLED();
215	if (fFD >= 0) {
216		close(fFD);
217		fFD = -1;
218	}
219}
220
221
222status_t
223BJoystick::Update()
224{
225	CALLED();
226	if (fJoystickInfo == NULL || fJoystickData == NULL || fFD < 0)
227		return B_NO_INIT;
228
229	for (uint16 i = 0; i < fJoystickInfo->module_info.num_sticks; i++) {
230		variable_joystick *values
231			= (variable_joystick *)fJoystickData->ItemAt(i);
232		if (values == NULL)
233			return B_NO_INIT;
234
235		ssize_t result = read_pos(fFD, i, values->data,
236			values->data_size);
237		if (result < 0)
238			return result;
239
240		if ((size_t)result != values->data_size)
241			return B_ERROR;
242
243		if (i > 0)
244			continue;
245
246		// fill in the legacy values for the first stick
247		timestamp = *values->timestamp;
248
249		if (values->axis_count >= 1)
250			horizontal = values->axes[0];
251		else
252			horizontal = 0;
253
254		if (values->axis_count >= 2)
255			vertical = values->axes[1];
256		else
257			vertical = 0;
258
259		if (values->button_blocks > 0) {
260			button1 = (*values->buttons & 1) == 0;
261			button2 = (*values->buttons & 2) == 0;
262		} else {
263			button1 = true;
264			button2 = true;
265		}
266	}
267
268	return B_OK;
269}
270
271
272status_t
273BJoystick::SetMaxLatency(bigtime_t maxLatency)
274{
275	CALLED();
276	if (fJoystickInfo == NULL || fFD < 0)
277		return B_NO_INIT;
278
279	status_t result = ioctl(fFD, B_JOYSTICK_SET_MAX_LATENCY, &maxLatency,
280		sizeof(maxLatency));
281	if (result == B_OK)
282		fJoystickInfo->max_latency = maxLatency;
283
284	return result;
285}
286
287
288int32
289BJoystick::CountDevices()
290{
291	CALLED();
292
293	if (fDevices == NULL)
294		return 0;
295
296	int32 count = fDevices->CountItems();
297
298	LOG("Count = %d\n", count);
299	return count;
300}
301
302
303status_t
304BJoystick::GetDeviceName(int32 index, char *name, size_t bufSize)
305{
306	CALLED();
307	if (fDevices == NULL)
308		return B_NO_INIT;
309
310	if (index >= fDevices->CountItems())
311		return B_BAD_INDEX;
312
313	if (name == NULL)
314		return B_BAD_VALUE;
315
316	BString *deviceName = (BString *)fDevices->ItemAt(index);
317	if (deviceName->Length() > (int32)bufSize)
318		return B_NAME_TOO_LONG;
319
320	strlcpy(name, deviceName->String(), bufSize);
321	LOG("Device Name = %s\n", name);
322	return B_OK;
323}
324
325
326status_t
327BJoystick::RescanDevices()
328{
329	CALLED();
330
331	if (fDevices == NULL)
332		return B_NO_INIT;
333
334	ScanDevices(true);
335	return B_OK;
336}
337
338
339bool
340BJoystick::EnterEnhancedMode(const entry_ref *ref)
341{
342	CALLED();
343	fBeBoxMode = false;
344	return !fBeBoxMode;
345}
346
347
348int32
349BJoystick::CountSticks()
350{
351	CALLED();
352	if (fJoystickInfo == NULL)
353		return 0;
354
355	return fJoystickInfo->module_info.num_sticks;
356}
357
358
359int32
360BJoystick::CountAxes()
361{
362	CALLED();
363	if (fJoystickInfo == NULL)
364		return 0;
365
366	return fJoystickInfo->module_info.num_axes;
367}
368
369
370status_t
371BJoystick::GetAxisValues(int16 *outValues, int32 forStick)
372{
373	CALLED();
374
375	if (fJoystickInfo == NULL || fJoystickData == NULL)
376		return B_NO_INIT;
377
378	if (forStick < 0
379		|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
380		return B_BAD_INDEX;
381
382	variable_joystick *variableJoystick
383		= (variable_joystick *)fJoystickData->ItemAt(forStick);
384	if (variableJoystick == NULL)
385		return B_NO_INIT;
386
387	memcpy(outValues, variableJoystick->axes,
388		fJoystickInfo->module_info.num_axes * sizeof(uint16));
389	return B_OK;
390}
391
392
393status_t
394BJoystick::GetAxisNameAt(int32 index, BString *outName)
395{
396	CALLED();
397
398	if (index >= CountAxes())
399		return B_BAD_INDEX;
400
401	if (outName == NULL)
402		return B_BAD_VALUE;
403
404	// TODO: actually retrieve the name from the driver (via a new ioctl)
405	*outName = "Axis ";
406	*outName << index;
407	return B_OK;
408}
409
410
411int32
412BJoystick::CountHats()
413{
414	CALLED();
415	if (fJoystickInfo == NULL)
416		return 0;
417
418	return fJoystickInfo->module_info.num_hats;
419}
420
421
422status_t
423BJoystick::GetHatValues(uint8 *outHats, int32 forStick)
424{
425	CALLED();
426
427	if (fJoystickInfo == NULL || fJoystickData == NULL)
428		return B_NO_INIT;
429
430	if (forStick < 0
431		|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
432		return B_BAD_INDEX;
433
434	variable_joystick *variableJoystick
435		= (variable_joystick *)fJoystickData->ItemAt(forStick);
436	if (variableJoystick == NULL)
437		return B_NO_INIT;
438
439	memcpy(outHats, variableJoystick->hats,
440		fJoystickInfo->module_info.num_hats);
441	return B_OK;
442}
443
444
445status_t
446BJoystick::GetHatNameAt(int32 index, BString *outName)
447{
448	CALLED();
449
450	if (index >= CountHats())
451		return B_BAD_INDEX;
452
453	if (outName == NULL)
454		return B_BAD_VALUE;
455
456	// TODO: actually retrieve the name from the driver (via a new ioctl)
457	*outName = "Hat ";
458	*outName << index;
459	return B_OK;
460}
461
462
463int32
464BJoystick::CountButtons()
465{
466	CALLED();
467	if (fJoystickInfo == NULL)
468		return 0;
469
470	return fJoystickInfo->module_info.num_buttons;
471}
472
473
474uint32
475BJoystick::ButtonValues(int32 forStick)
476{
477	CALLED();
478
479	if (fJoystickInfo == NULL || fJoystickData == NULL)
480		return 0;
481
482	if (forStick < 0
483		|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
484		return 0;
485
486	variable_joystick *variableJoystick
487		= (variable_joystick *)fJoystickData->ItemAt(forStick);
488	if (variableJoystick == NULL || variableJoystick->button_blocks == 0)
489		return 0;
490
491	return *variableJoystick->buttons;
492}
493
494
495status_t
496BJoystick::GetButtonValues(bool *outButtons, int32 forStick)
497{
498	CALLED();
499
500	if (fJoystickInfo == NULL || fJoystickData == NULL)
501		return B_NO_INIT;
502
503	if (forStick < 0
504		|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
505		return B_BAD_INDEX;
506
507	variable_joystick *variableJoystick
508		= (variable_joystick *)fJoystickData->ItemAt(forStick);
509	if (variableJoystick == NULL)
510		return B_NO_INIT;
511
512	int16 buttonCount = fJoystickInfo->module_info.num_buttons;
513	for (int16 i = 0; i < buttonCount; i++) {
514		outButtons[i]
515			= (variableJoystick->buttons[i / 32] & (1 << (i % 32))) != 0;
516	}
517
518	return B_OK;
519}
520
521
522status_t
523BJoystick::GetButtonNameAt(int32 index, BString *outName)
524{
525	CALLED();
526
527	if (index >= CountButtons())
528		return B_BAD_INDEX;
529
530	if (outName == NULL)
531		return B_BAD_VALUE;
532
533	// TODO: actually retrieve the name from the driver (via a new ioctl)
534	*outName = "Button ";
535	*outName << index;
536	return B_OK;
537}
538
539
540status_t
541BJoystick::GetControllerModule(BString *outName)
542{
543	CALLED();
544	if (fJoystickInfo == NULL || fFD < 0)
545		return B_NO_INIT;
546
547	if (outName == NULL)
548		return B_BAD_VALUE;
549
550	outName->SetTo(fJoystickInfo->module_info.module_name);
551	return B_OK;
552}
553
554
555status_t
556BJoystick::GetControllerName(BString *outName)
557{
558	CALLED();
559	if (fJoystickInfo == NULL || fFD < 0)
560		return B_NO_INIT;
561
562	if (outName == NULL)
563		return B_BAD_VALUE;
564
565	outName->SetTo(fJoystickInfo->module_info.device_name);
566	return B_OK;
567}
568
569
570bool
571BJoystick::IsCalibrationEnabled()
572{
573	CALLED();
574	if (fJoystickInfo == NULL)
575		return false;
576
577	return fJoystickInfo->calibration_enable;
578}
579
580
581status_t
582BJoystick::EnableCalibration(bool calibrates)
583{
584	CALLED();
585	if (fJoystickInfo == NULL || fFD < 0)
586		return B_NO_INIT;
587
588	status_t result = ioctl(fFD, B_JOYSTICK_SET_RAW_MODE, &calibrates,
589		sizeof(calibrates));
590	if (result == B_OK)
591		fJoystickInfo->calibration_enable = calibrates;
592
593	return result;
594}
595
596
597void
598BJoystick::Calibrate(struct _extended_joystick *reading)
599{
600	CALLED();
601}
602
603
604void
605BJoystick::ScanDevices(bool useDisabled)
606{
607	CALLED();
608	if (useDisabled) {
609		_BJoystickTweaker joystickTweaker(*this);
610		joystickTweaker.scan_including_disabled();
611	}
612}
613
614
615//	#pragma mark - FBC protection
616
617
618void BJoystick::_ReservedJoystick1() {}
619void BJoystick::_ReservedJoystick2() {}
620void BJoystick::_ReservedJoystick3() {}
621status_t BJoystick::_Reserved_Joystick_4(void*, ...) { return B_ERROR; }
622status_t BJoystick::_Reserved_Joystick_5(void*, ...) { return B_ERROR; }
623status_t BJoystick::_Reserved_Joystick_6(void*, ...) { return B_ERROR; }
624