1/*
2 * Copyright 2011 Michael Lotz <mmlr@mlotz.ch>
3 * Distributed under the terms of the MIT license.
4 */
5
6
7//!	Driver for USB Human Interface Devices.
8
9
10#include "Driver.h"
11#include "JoystickProtocolHandler.h"
12
13#include "HIDCollection.h"
14#include "HIDDevice.h"
15#include "HIDReport.h"
16#include "HIDReportItem.h"
17
18#include <new>
19#include <string.h>
20#include <usb/USB_hid.h>
21
22#include <kernel.h>
23#include <util/AutoLock.h>
24
25
26JoystickProtocolHandler::JoystickProtocolHandler(HIDReport &report)
27	:
28	ProtocolHandler(report.Device(), "joystick/" DEVICE_PATH_SUFFIX "/", 0),
29	fReport(report),
30	fAxisCount(0),
31	fAxis(NULL),
32	fHatCount(0),
33	fHats(NULL),
34	fButtonCount(0),
35	fMaxButton(0),
36	fButtons(NULL),
37	fOpenCount(0),
38	fUpdateThread(-1)
39{
40	mutex_init(&fUpdateLock, "joystick update lock");
41	memset(&fJoystickModuleInfo, 0, sizeof(joystick_module_info));
42	memset(&fCurrentValues, 0, sizeof(variable_joystick));
43
44	for (uint32 i = 0; i < report.CountItems(); i++) {
45		HIDReportItem *item = report.ItemAt(i);
46		if (!item->HasData())
47			continue;
48
49		switch (item->UsagePage()) {
50			case B_HID_USAGE_PAGE_BUTTON:
51			{
52				if (item->UsageID() > INT16_MAX)
53					break;
54
55				HIDReportItem **newButtons = (HIDReportItem **)realloc(fButtons,
56					++fButtonCount * sizeof(HIDReportItem *));
57				if (newButtons == NULL) {
58					fButtonCount--;
59					break;
60				}
61
62				fButtons = newButtons;
63				fButtons[fButtonCount - 1] = item;
64
65				if (fMaxButton < item->UsageID())
66					fMaxButton = item->UsageID();
67				break;
68			}
69
70			case B_HID_USAGE_PAGE_GENERIC_DESKTOP:
71			{
72				if (item->UsageID() == B_HID_UID_GD_HAT_SWITCH) {
73					HIDReportItem **newHats = (HIDReportItem **)realloc(fHats,
74						++fHatCount * sizeof(HIDReportItem *));
75					if (newHats == NULL) {
76						fHatCount--;
77						break;
78					}
79
80					fHats = newHats;
81					fHats[fHatCount - 1] = item;
82					break;
83				}
84
85// TODO: "axis" is set but not used!
86//				uint16 axis = 0;
87				if (item->UsageID() >= B_HID_UID_GD_X
88					&& item->UsageID() <= B_HID_UID_GD_WHEEL) {
89//					axis = item->UsageID() - B_HID_UID_GD_X;
90				} else if (item->UsageID() >= B_HID_UID_GD_VX
91					&& item->UsageID() <= B_HID_UID_GD_VNO) {
92//					axis = item->UsageID() - B_HID_UID_GD_VX;
93				} else
94					break;
95
96				HIDReportItem **newAxis = (HIDReportItem **)realloc(fAxis,
97					++fAxisCount * sizeof(HIDReportItem *));
98				if (newAxis == NULL) {
99					fAxisCount--;
100					break;
101				}
102
103				fAxis = newAxis;
104				fAxis[fAxisCount - 1] = item;
105				break;
106			}
107		}
108	}
109
110
111	fCurrentValues.initialize(fAxisCount, fHatCount, fMaxButton);
112
113	TRACE("joystick device with %" B_PRIu32 " buttons, %" B_PRIu32
114		" axes and %" B_PRIu32 " hats\n",
115		fButtonCount, fAxisCount, fHatCount);
116	TRACE("report id: %u\n", report.ID());
117}
118
119
120JoystickProtocolHandler::~JoystickProtocolHandler()
121{
122	free(fCurrentValues.data);
123	free(fAxis);
124	free(fHats);
125	free(fButtons);
126}
127
128
129void
130JoystickProtocolHandler::AddHandlers(HIDDevice &device,
131	HIDCollection &collection, ProtocolHandler *&handlerList)
132{
133	if (collection.UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP
134		|| (collection.UsageID() != B_HID_UID_GD_JOYSTICK
135			&& collection.UsageID() != B_HID_UID_GD_GAMEPAD
136			&& collection.UsageID() != B_HID_UID_GD_MULTIAXIS)) {
137		TRACE("collection not a joystick or gamepad\n");
138		return;
139	}
140
141	HIDParser &parser = device.Parser();
142	uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT);
143	if (maxReportCount == 0)
144		return;
145
146	uint32 inputReportCount = 0;
147	HIDReport *inputReports[maxReportCount];
148	collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports,
149		inputReportCount);
150
151	for (uint32 i = 0; i < inputReportCount; i++) {
152		HIDReport *inputReport = inputReports[i];
153
154		// try to find at least one axis
155		bool foundAxis = false;
156		for (uint32 j = 0; j < inputReport->CountItems(); j++) {
157			HIDReportItem *item = inputReport->ItemAt(j);
158			if (item == NULL || !item->HasData())
159				continue;
160
161			if (item->UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP)
162				continue;
163
164			if (item->UsageID() >= B_HID_UID_GD_X
165				&& item->UsageID() <= B_HID_UID_GD_RZ) {
166				foundAxis = true;
167				break;
168			}
169		}
170
171		if (!foundAxis)
172			continue;
173
174		ProtocolHandler *newHandler
175			= new(std::nothrow) JoystickProtocolHandler(*inputReport);
176		if (newHandler == NULL) {
177			TRACE("failed to allocated joystick protocol handler\n");
178			continue;
179		}
180
181		newHandler->SetNextHandler(handlerList);
182		handlerList = newHandler;
183	}
184}
185
186
187status_t
188JoystickProtocolHandler::Open(uint32 flags, uint32 *cookie)
189{
190	if (fCurrentValues.data == NULL)
191		return B_NO_INIT;
192
193	status_t result = mutex_lock(&fUpdateLock);
194	if (result != B_OK)
195		return result;
196
197	if (fUpdateThread < 0) {
198		fUpdateThread = spawn_kernel_thread(_UpdateThread, "joystick update",
199			B_NORMAL_PRIORITY, (void *)this);
200
201		if (fUpdateThread < 0)
202			result = fUpdateThread;
203		else
204			resume_thread(fUpdateThread);
205	}
206
207	if (result == B_OK)
208		fOpenCount++;
209
210	mutex_unlock(&fUpdateLock);
211	if (result != B_OK)
212		return result;
213
214	return ProtocolHandler::Open(flags, cookie);
215}
216
217
218status_t
219JoystickProtocolHandler::Close(uint32 *cookie)
220{
221	status_t result = mutex_lock(&fUpdateLock);
222	if (result == B_OK) {
223		if (--fOpenCount == 0)
224			fUpdateThread = -1;
225		mutex_unlock(&fUpdateLock);
226	}
227
228	return ProtocolHandler::Close(cookie);
229}
230
231
232
233status_t
234JoystickProtocolHandler::Read(uint32 *cookie, off_t position, void *buffer,
235	size_t *numBytes)
236{
237	if (*numBytes < fCurrentValues.data_size)
238		return B_BUFFER_OVERFLOW;
239
240	// this is a polling interface, we just return the current value
241	MutexLocker locker(fUpdateLock);
242	if (!locker.IsLocked()) {
243		*numBytes = 0;
244		return B_ERROR;
245	}
246
247	if (!IS_USER_ADDRESS(buffer) || user_memcpy(buffer, fCurrentValues.data,
248			fCurrentValues.data_size) != B_OK)
249		return B_BAD_ADDRESS;
250
251	*numBytes = fCurrentValues.data_size;
252	return B_OK;
253}
254
255
256status_t
257JoystickProtocolHandler::Write(uint32 *cookie, off_t position,
258	const void *buffer, size_t *numBytes)
259{
260	*numBytes = 0;
261	return B_NOT_SUPPORTED;
262}
263
264
265status_t
266JoystickProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer,
267	size_t length)
268{
269	switch (op) {
270		case B_GET_DEVICE_NAME:
271		{
272			const char name[] = DEVICE_NAME" Joystick";
273			return IOGetDeviceName(name, buffer, length);
274		}
275
276		case B_JOYSTICK_SET_DEVICE_MODULE:
277		{
278			if (length < sizeof(joystick_module_info))
279				return B_BAD_VALUE;
280
281			status_t result = mutex_lock(&fUpdateLock);
282			if (result != B_OK)
283				return result;
284
285			if (!IS_USER_ADDRESS(buffer)
286				|| user_memcpy(&fJoystickModuleInfo, buffer,
287					sizeof(joystick_module_info)) != B_OK) {
288				return B_BAD_ADDRESS;
289			}
290
291			bool supportsVariable = (fJoystickModuleInfo.flags
292				& js_flag_variable_size_reads) != 0;
293			if (!supportsVariable) {
294				// We revert to a structure that we can support using only
295				// the data available in an extended_joystick structure.
296				free(fCurrentValues.data);
297				fCurrentValues.initialize_to_extended_joystick();
298				if (fAxisCount > MAX_AXES)
299					fAxisCount = MAX_AXES;
300				if (fHatCount > MAX_HATS)
301					fHatCount = MAX_HATS;
302				if (fMaxButton > MAX_BUTTONS)
303					fMaxButton = MAX_BUTTONS;
304
305				TRACE_ALWAYS("using joystick in extended_joystick mode\n");
306			} else {
307				TRACE_ALWAYS("using joystick in variable mode\n");
308			}
309
310			fJoystickModuleInfo.num_axes = fAxisCount;
311			fJoystickModuleInfo.num_buttons = fMaxButton;
312			fJoystickModuleInfo.num_hats = fHatCount;
313			fJoystickModuleInfo.num_sticks = 1;
314			fJoystickModuleInfo.config_size = 0;
315			mutex_unlock(&fUpdateLock);
316			return B_OK;
317		}
318
319		case B_JOYSTICK_GET_DEVICE_MODULE:
320			if (length < sizeof(joystick_module_info))
321				return B_BAD_VALUE;
322
323			if (!IS_USER_ADDRESS(buffer)
324				|| user_memcpy(buffer, &fJoystickModuleInfo,
325					sizeof(joystick_module_info)) != B_OK) {
326				return B_BAD_ADDRESS;
327			}
328			return B_OK;
329	}
330
331	return B_ERROR;
332}
333
334
335int32
336JoystickProtocolHandler::_UpdateThread(void *data)
337{
338	JoystickProtocolHandler *handler = (JoystickProtocolHandler *)data;
339	while (handler->fUpdateThread == find_thread(NULL)) {
340		status_t result = handler->_Update();
341		if (result != B_OK)
342			return result;
343	}
344
345	return B_OK;
346}
347
348
349status_t
350JoystickProtocolHandler::_Update()
351{
352	status_t result = fReport.WaitForReport(B_INFINITE_TIMEOUT);
353	if (result != B_OK) {
354		if (fReport.Device()->IsRemoved()) {
355			TRACE("device has been removed\n");
356			return B_DEV_NOT_READY;
357		}
358
359		if (result == B_CANCELED)
360			return B_CANCELED;
361
362		if (result != B_INTERRUPTED) {
363			// interrupts happen when other reports come in on the same
364			// input as ours
365			TRACE_ALWAYS("error waiting for report: %s\n", strerror(result));
366		}
367
368		// signal that we simply want to try again
369		return B_OK;
370	}
371
372	result = mutex_lock(&fUpdateLock);
373	if (result != B_OK) {
374		fReport.DoneProcessing();
375		return result;
376	}
377
378	memset(fCurrentValues.data, 0, fCurrentValues.data_size);
379
380	for (uint32 i = 0; i < fAxisCount; i++) {
381		if (fAxis[i] == NULL)
382			continue;
383
384		if (fAxis[i]->Extract() == B_OK && fAxis[i]->Valid())
385			fCurrentValues.axes[i] = (int16)fAxis[i]->ScaledData(16, true);
386	}
387
388	for (uint32 i = 0; i < fHatCount; i++) {
389		HIDReportItem *hat = fHats[i];
390		if (hat == NULL)
391			continue;
392
393		if (hat->Extract() != B_OK || !hat->Valid())
394			continue;
395
396		fCurrentValues.hats[i] = hat->ScaledRangeData(1, 8);
397	}
398
399	for (uint32 i = 0; i < fButtonCount; i++) {
400		HIDReportItem *button = fButtons[i];
401		if (button == NULL)
402			break;
403
404		uint16 index = button->UsageID() - 1;
405		if (index >= fMaxButton)
406			continue;
407
408		if (button->Extract() == B_OK && button->Valid()) {
409			fCurrentValues.buttons[index / 32]
410				|= (button->Data() & 1) << (index % 32);
411		}
412	}
413
414	fReport.DoneProcessing();
415	TRACE("got joystick report\n");
416
417	*fCurrentValues.timestamp = system_time();
418	mutex_unlock(&fUpdateLock);
419	return B_OK;
420}
421