1f46bc7f1SMichael Lotz/*
2f46bc7f1SMichael Lotz * Copyright 2011 Michael Lotz <mmlr@mlotz.ch>
3f46bc7f1SMichael Lotz * Distributed under the terms of the MIT license.
4f46bc7f1SMichael Lotz */
5f46bc7f1SMichael Lotz
6f46bc7f1SMichael Lotz
7f46bc7f1SMichael Lotz//!	Driver for USB Human Interface Devices.
8f46bc7f1SMichael Lotz
9f46bc7f1SMichael Lotz
10f46bc7f1SMichael Lotz#include "Driver.h"
11f46bc7f1SMichael Lotz#include "JoystickProtocolHandler.h"
12f46bc7f1SMichael Lotz
13f46bc7f1SMichael Lotz#include "HIDCollection.h"
14f46bc7f1SMichael Lotz#include "HIDDevice.h"
15f46bc7f1SMichael Lotz#include "HIDReport.h"
16f46bc7f1SMichael Lotz#include "HIDReportItem.h"
17f46bc7f1SMichael Lotz
18f46bc7f1SMichael Lotz#include <new>
19f46bc7f1SMichael Lotz#include <string.h>
20f46bc7f1SMichael Lotz#include <usb/USB_hid.h>
21f46bc7f1SMichael Lotz
22428d8bc6SJérôme Duval#include <kernel.h>
23a26a21a5SAugustin Cavalier#include <util/AutoLock.h>
24428d8bc6SJérôme Duval
25f46bc7f1SMichael Lotz
26f46bc7f1SMichael LotzJoystickProtocolHandler::JoystickProtocolHandler(HIDReport &report)
27f46bc7f1SMichael Lotz	:
285052c5b6SMichael Lotz	ProtocolHandler(report.Device(), "joystick/usb/", 0),
295052c5b6SMichael Lotz	fReport(report),
30233d1dc4SMichael Lotz	fAxisCount(0),
31233d1dc4SMichael Lotz	fAxis(NULL),
32233d1dc4SMichael Lotz	fHatCount(0),
33233d1dc4SMichael Lotz	fHats(NULL),
34233d1dc4SMichael Lotz	fButtonCount(0),
35233d1dc4SMichael Lotz	fMaxButton(0),
36233d1dc4SMichael Lotz	fButtons(NULL),
376b9615d0SMichael Lotz	fOpenCount(0),
385052c5b6SMichael Lotz	fUpdateThread(-1)
39f46bc7f1SMichael Lotz{
405052c5b6SMichael Lotz	mutex_init(&fUpdateLock, "joystick update lock");
41233d1dc4SMichael Lotz	memset(&fJoystickModuleInfo, 0, sizeof(joystick_module_info));
42233d1dc4SMichael Lotz	memset(&fCurrentValues, 0, sizeof(variable_joystick));
435052c5b6SMichael Lotz
44f46bc7f1SMichael Lotz	for (uint32 i = 0; i < report.CountItems(); i++) {
45f46bc7f1SMichael Lotz		HIDReportItem *item = report.ItemAt(i);
46f46bc7f1SMichael Lotz		if (!item->HasData())
47f46bc7f1SMichael Lotz			continue;
48f46bc7f1SMichael Lotz
49f46bc7f1SMichael Lotz		switch (item->UsagePage()) {
50f46bc7f1SMichael Lotz			case B_HID_USAGE_PAGE_BUTTON:
51f46bc7f1SMichael Lotz			{
52233d1dc4SMichael Lotz				if (item->UsageID() > INT16_MAX)
53233d1dc4SMichael Lotz					break;
54233d1dc4SMichael Lotz
55233d1dc4SMichael Lotz				HIDReportItem **newButtons = (HIDReportItem **)realloc(fButtons,
56233d1dc4SMichael Lotz					++fButtonCount * sizeof(HIDReportItem *));
57233d1dc4SMichael Lotz				if (newButtons == NULL) {
58233d1dc4SMichael Lotz					fButtonCount--;
59233d1dc4SMichael Lotz					break;
60233d1dc4SMichael Lotz				}
61233d1dc4SMichael Lotz
62233d1dc4SMichael Lotz				fButtons = newButtons;
63233d1dc4SMichael Lotz				fButtons[fButtonCount - 1] = item;
64233d1dc4SMichael Lotz
65233d1dc4SMichael Lotz				if (fMaxButton < item->UsageID())
66233d1dc4SMichael Lotz					fMaxButton = item->UsageID();
67f46bc7f1SMichael Lotz				break;
68f46bc7f1SMichael Lotz			}
69f46bc7f1SMichael Lotz
70f46bc7f1SMichael Lotz			case B_HID_USAGE_PAGE_GENERIC_DESKTOP:
71f46bc7f1SMichael Lotz			{
721b7d2a54SMichael Lotz				if (item->UsageID() == B_HID_UID_GD_HAT_SWITCH) {
731b7d2a54SMichael Lotz					HIDReportItem **newHats = (HIDReportItem **)realloc(fHats,
741b7d2a54SMichael Lotz						++fHatCount * sizeof(HIDReportItem *));
751b7d2a54SMichael Lotz					if (newHats == NULL) {
761b7d2a54SMichael Lotz						fHatCount--;
771b7d2a54SMichael Lotz						break;
781b7d2a54SMichael Lotz					}
791b7d2a54SMichael Lotz
801b7d2a54SMichael Lotz					fHats = newHats;
811b7d2a54SMichael Lotz					fHats[fHatCount - 1] = item;
821b7d2a54SMichael Lotz					break;
831b7d2a54SMichael Lotz				}
841b7d2a54SMichael Lotz
85526e95c5SStephan Aßmus// TODO: "axis" is set but not used!
86526e95c5SStephan Aßmus//				uint16 axis = 0;
87517851a1SMichael Lotz				if (item->UsageID() >= B_HID_UID_GD_X
88517851a1SMichael Lotz					&& item->UsageID() <= B_HID_UID_GD_WHEEL) {
89526e95c5SStephan Aßmus//					axis = item->UsageID() - B_HID_UID_GD_X;
90517851a1SMichael Lotz				} else if (item->UsageID() >= B_HID_UID_GD_VX
91517851a1SMichael Lotz					&& item->UsageID() <= B_HID_UID_GD_VNO) {
92526e95c5SStephan Aßmus//					axis = item->UsageID() - B_HID_UID_GD_VX;
93517851a1SMichael Lotz				} else
94517851a1SMichael Lotz					break;
95517851a1SMichael Lotz
96517851a1SMichael Lotz				HIDReportItem **newAxis = (HIDReportItem **)realloc(fAxis,
97517851a1SMichael Lotz					++fAxisCount * sizeof(HIDReportItem *));
98517851a1SMichael Lotz				if (newAxis == NULL) {
99517851a1SMichael Lotz					fAxisCount--;
100517851a1SMichael Lotz					break;
101f46bc7f1SMichael Lotz				}
102f46bc7f1SMichael Lotz
103517851a1SMichael Lotz				fAxis = newAxis;
104517851a1SMichael Lotz				fAxis[fAxisCount - 1] = item;
105f46bc7f1SMichael Lotz				break;
106f46bc7f1SMichael Lotz			}
107f46bc7f1SMichael Lotz		}
108f46bc7f1SMichael Lotz	}
109f46bc7f1SMichael Lotz
110233d1dc4SMichael Lotz
11145667a94SPhilippe Houdoin	fCurrentValues.initialize(fAxisCount, fHatCount, fMaxButton);
112f46bc7f1SMichael Lotz
11345667a94SPhilippe Houdoin	TRACE("joystick device with %lu buttons, %lu axes and %lu hats\n",
11445667a94SPhilippe Houdoin		fButtonCount, fAxisCount, fHatCount);
115f46bc7f1SMichael Lotz	TRACE("report id: %u\n", report.ID());
116f46bc7f1SMichael Lotz}
117f46bc7f1SMichael Lotz
118f46bc7f1SMichael Lotz
119233d1dc4SMichael LotzJoystickProtocolHandler::~JoystickProtocolHandler()
120233d1dc4SMichael Lotz{
121233d1dc4SMichael Lotz	free(fCurrentValues.data);
122a3954860SMichael Lotz	free(fAxis);
123a3954860SMichael Lotz	free(fHats);
124a3954860SMichael Lotz	free(fButtons);
125233d1dc4SMichael Lotz}
126233d1dc4SMichael Lotz
127233d1dc4SMichael Lotz
128f46bc7f1SMichael Lotzvoid
129f46bc7f1SMichael LotzJoystickProtocolHandler::AddHandlers(HIDDevice &device,
130f46bc7f1SMichael Lotz	HIDCollection &collection, ProtocolHandler *&handlerList)
131f46bc7f1SMichael Lotz{
132f46bc7f1SMichael Lotz	if (collection.UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP
133f46bc7f1SMichael Lotz		|| (collection.UsageID() != B_HID_UID_GD_JOYSTICK
134f46bc7f1SMichael Lotz			&& collection.UsageID() != B_HID_UID_GD_GAMEPAD
135f46bc7f1SMichael Lotz			&& collection.UsageID() != B_HID_UID_GD_MULTIAXIS)) {
136f46bc7f1SMichael Lotz		TRACE("collection not a joystick or gamepad\n");
137f46bc7f1SMichael Lotz		return;
138f46bc7f1SMichael Lotz	}
139f46bc7f1SMichael Lotz
140f46bc7f1SMichael Lotz	HIDParser &parser = device.Parser();
141f46bc7f1SMichael Lotz	uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT);
142f46bc7f1SMichael Lotz	if (maxReportCount == 0)
143f46bc7f1SMichael Lotz		return;
144f46bc7f1SMichael Lotz
145f46bc7f1SMichael Lotz	uint32 inputReportCount = 0;
146f46bc7f1SMichael Lotz	HIDReport *inputReports[maxReportCount];
147f46bc7f1SMichael Lotz	collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports,
148f46bc7f1SMichael Lotz		inputReportCount);
149f46bc7f1SMichael Lotz
150f46bc7f1SMichael Lotz	for (uint32 i = 0; i < inputReportCount; i++) {
151f46bc7f1SMichael Lotz		HIDReport *inputReport = inputReports[i];
152f46bc7f1SMichael Lotz
153f46bc7f1SMichael Lotz		// try to find at least one axis
154f46bc7f1SMichael Lotz		bool foundAxis = false;
155f46bc7f1SMichael Lotz		for (uint32 j = 0; j < inputReport->CountItems(); j++) {
156f46bc7f1SMichael Lotz			HIDReportItem *item = inputReport->ItemAt(j);
157f46bc7f1SMichael Lotz			if (item == NULL || !item->HasData())
158f46bc7f1SMichael Lotz				continue;
159f46bc7f1SMichael Lotz
160f46bc7f1SMichael Lotz			if (item->UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP)
161f46bc7f1SMichael Lotz				continue;
162f46bc7f1SMichael Lotz
163f46bc7f1SMichael Lotz			if (item->UsageID() >= B_HID_UID_GD_X
164f46bc7f1SMichael Lotz				&& item->UsageID() <= B_HID_UID_GD_RZ) {
165f46bc7f1SMichael Lotz				foundAxis = true;
166f46bc7f1SMichael Lotz				break;
167f46bc7f1SMichael Lotz			}
168f46bc7f1SMichael Lotz		}
169f46bc7f1SMichael Lotz
170f46bc7f1SMichael Lotz		if (!foundAxis)
171f46bc7f1SMichael Lotz			continue;
172f46bc7f1SMichael Lotz
173f46bc7f1SMichael Lotz		ProtocolHandler *newHandler
174f46bc7f1SMichael Lotz			= new(std::nothrow) JoystickProtocolHandler(*inputReport);
175f46bc7f1SMichael Lotz		if (newHandler == NULL) {
176f46bc7f1SMichael Lotz			TRACE("failed to allocated joystick protocol handler\n");
177f46bc7f1SMichael Lotz			continue;
178f46bc7f1SMichael Lotz		}
179f46bc7f1SMichael Lotz
180f46bc7f1SMichael Lotz		newHandler->SetNextHandler(handlerList);
181f46bc7f1SMichael Lotz		handlerList = newHandler;
182f46bc7f1SMichael Lotz	}
183f46bc7f1SMichael Lotz}
184f46bc7f1SMichael Lotz
185f46bc7f1SMichael Lotz
1865052c5b6SMichael Lotzstatus_t
1875052c5b6SMichael LotzJoystickProtocolHandler::Open(uint32 flags, uint32 *cookie)
1885052c5b6SMichael Lotz{
189233d1dc4SMichael Lotz	if (fCurrentValues.data == NULL)
190233d1dc4SMichael Lotz		return B_NO_INIT;
191233d1dc4SMichael Lotz
1925052c5b6SMichael Lotz	status_t result = mutex_lock(&fUpdateLock);
1935052c5b6SMichael Lotz	if (result != B_OK)
1945052c5b6SMichael Lotz		return result;
1955052c5b6SMichael Lotz
1965052c5b6SMichael Lotz	if (fUpdateThread < 0) {
1975052c5b6SMichael Lotz		fUpdateThread = spawn_kernel_thread(_UpdateThread, "joystick update",
1985052c5b6SMichael Lotz			B_NORMAL_PRIORITY, (void *)this);
1995052c5b6SMichael Lotz
2005052c5b6SMichael Lotz		if (fUpdateThread < 0)
2015052c5b6SMichael Lotz			result = fUpdateThread;
2025052c5b6SMichael Lotz		else
2035052c5b6SMichael Lotz			resume_thread(fUpdateThread);
2045052c5b6SMichael Lotz	}
2055052c5b6SMichael Lotz
2066b9615d0SMichael Lotz	if (result == B_OK)
2076b9615d0SMichael Lotz		fOpenCount++;
2086b9615d0SMichael Lotz
2095052c5b6SMichael Lotz	mutex_unlock(&fUpdateLock);
2105052c5b6SMichael Lotz	if (result != B_OK)
2115052c5b6SMichael Lotz		return result;
2125052c5b6SMichael Lotz
2135052c5b6SMichael Lotz	return ProtocolHandler::Open(flags, cookie);
2145052c5b6SMichael Lotz}
2155052c5b6SMichael Lotz
2165052c5b6SMichael Lotz
2175052c5b6SMichael Lotzstatus_t
2185052c5b6SMichael LotzJoystickProtocolHandler::Close(uint32 *cookie)
2195052c5b6SMichael Lotz{
2205052c5b6SMichael Lotz	status_t result = mutex_lock(&fUpdateLock);
2215052c5b6SMichael Lotz	if (result == B_OK) {
2226b9615d0SMichael Lotz		if (--fOpenCount == 0)
2236b9615d0SMichael Lotz			fUpdateThread = -1;
2245052c5b6SMichael Lotz		mutex_unlock(&fUpdateLock);
2255052c5b6SMichael Lotz	}
2265052c5b6SMichael Lotz
2275052c5b6SMichael Lotz	return ProtocolHandler::Close(cookie);
2285052c5b6SMichael Lotz}
2295052c5b6SMichael Lotz
2305052c5b6SMichael Lotz
2315052c5b6SMichael Lotz
232f46bc7f1SMichael Lotzstatus_t
233f46bc7f1SMichael LotzJoystickProtocolHandler::Read(uint32 *cookie, off_t position, void *buffer,
234f46bc7f1SMichael Lotz	size_t *numBytes)
235f46bc7f1SMichael Lotz{
236233d1dc4SMichael Lotz	if (*numBytes < fCurrentValues.data_size)
237f46bc7f1SMichael Lotz		return B_BUFFER_OVERFLOW;
238f46bc7f1SMichael Lotz
2395052c5b6SMichael Lotz	// this is a polling interface, we just return the current value
240a26a21a5SAugustin Cavalier	MutexLocker locker(fUpdateLock);
241a26a21a5SAugustin Cavalier	if (!locker.IsLocked()) {
2425052c5b6SMichael Lotz		*numBytes = 0;
243a26a21a5SAugustin Cavalier		return B_ERROR;
244f46bc7f1SMichael Lotz	}
245f46bc7f1SMichael Lotz
246a26a21a5SAugustin Cavalier	if (!IS_USER_ADDRESS(buffer) || user_memcpy(buffer, fCurrentValues.data,
247a26a21a5SAugustin Cavalier			fCurrentValues.data_size) != B_OK)
248a26a21a5SAugustin Cavalier		return B_BAD_ADDRESS;
249f46bc7f1SMichael Lotz
250233d1dc4SMichael Lotz	*numBytes = fCurrentValues.data_size;
251f46bc7f1SMichael Lotz	return B_OK;
252f46bc7f1SMichael Lotz}
253f46bc7f1SMichael Lotz
254f46bc7f1SMichael Lotz
255f46bc7f1SMichael Lotzstatus_t
256f46bc7f1SMichael LotzJoystickProtocolHandler::Write(uint32 *cookie, off_t position,
257f46bc7f1SMichael Lotz	const void *buffer, size_t *numBytes)
258f46bc7f1SMichael Lotz{
259f46bc7f1SMichael Lotz	*numBytes = 0;
260f46bc7f1SMichael Lotz	return B_NOT_SUPPORTED;
261f46bc7f1SMichael Lotz}
262f46bc7f1SMichael Lotz
263f46bc7f1SMichael Lotz
264f46bc7f1SMichael Lotzstatus_t
265f46bc7f1SMichael LotzJoystickProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer,
266f46bc7f1SMichael Lotz	size_t length)
267f46bc7f1SMichael Lotz{
268f46bc7f1SMichael Lotz	switch (op) {
269f46bc7f1SMichael Lotz		case B_JOYSTICK_SET_DEVICE_MODULE:
270f46bc7f1SMichael Lotz		{
271f46bc7f1SMichael Lotz			if (length < sizeof(joystick_module_info))
272f46bc7f1SMichael Lotz				return B_BAD_VALUE;
273f46bc7f1SMichael Lotz
274233d1dc4SMichael Lotz			status_t result = mutex_lock(&fUpdateLock);
275233d1dc4SMichael Lotz			if (result != B_OK)
276233d1dc4SMichael Lotz				return result;
277f46bc7f1SMichael Lotz
278428d8bc6SJérôme Duval			if (!IS_USER_ADDRESS(buffer)
279428d8bc6SJérôme Duval				|| user_memcpy(&fJoystickModuleInfo, buffer,
280428d8bc6SJérôme Duval					sizeof(joystick_module_info)) != B_OK) {
281428d8bc6SJérôme Duval				return B_BAD_ADDRESS;
282428d8bc6SJérôme Duval			}
283f46bc7f1SMichael Lotz
284233d1dc4SMichael Lotz			bool supportsVariable = (fJoystickModuleInfo.flags
285233d1dc4SMichael Lotz				& js_flag_variable_size_reads) != 0;
286233d1dc4SMichael Lotz			if (!supportsVariable) {
287233d1dc4SMichael Lotz				// We revert to a structure that we can support using only
288233d1dc4SMichael Lotz				// the data available in an extended_joystick structure.
289233d1dc4SMichael Lotz				free(fCurrentValues.data);
290233d1dc4SMichael Lotz				fCurrentValues.initialize_to_extended_joystick();
291233d1dc4SMichael Lotz				if (fAxisCount > MAX_AXES)
292233d1dc4SMichael Lotz					fAxisCount = MAX_AXES;
293233d1dc4SMichael Lotz				if (fHatCount > MAX_HATS)
294233d1dc4SMichael Lotz					fHatCount = MAX_HATS;
295233d1dc4SMichael Lotz				if (fMaxButton > MAX_BUTTONS)
296233d1dc4SMichael Lotz					fMaxButton = MAX_BUTTONS;
297233d1dc4SMichael Lotz
298233d1dc4SMichael Lotz				TRACE_ALWAYS("using joystick in extended_joystick mode\n");
299233d1dc4SMichael Lotz			} else {
300233d1dc4SMichael Lotz				TRACE_ALWAYS("using joystick in variable mode\n");
301f46bc7f1SMichael Lotz			}
302f46bc7f1SMichael Lotz
303233d1dc4SMichael Lotz			fJoystickModuleInfo.num_axes = fAxisCount;
304233d1dc4SMichael Lotz			fJoystickModuleInfo.num_buttons = fMaxButton;
305233d1dc4SMichael Lotz			fJoystickModuleInfo.num_hats = fHatCount;
306f46bc7f1SMichael Lotz			fJoystickModuleInfo.num_sticks = 1;
307f46bc7f1SMichael Lotz			fJoystickModuleInfo.config_size = 0;
308233d1dc4SMichael Lotz			mutex_unlock(&fUpdateLock);
309428d8bc6SJérôme Duval			return B_OK;
310f46bc7f1SMichael Lotz		}
311f46bc7f1SMichael Lotz
312f46bc7f1SMichael Lotz		case B_JOYSTICK_GET_DEVICE_MODULE:
313f46bc7f1SMichael Lotz			if (length < sizeof(joystick_module_info))
314f46bc7f1SMichael Lotz				return B_BAD_VALUE;
315f46bc7f1SMichael Lotz
316428d8bc6SJérôme Duval			if (!IS_USER_ADDRESS(buffer)
317428d8bc6SJérôme Duval				|| user_memcpy(buffer, &fJoystickModuleInfo,
318428d8bc6SJérôme Duval					sizeof(joystick_module_info)) != B_OK) {
319428d8bc6SJérôme Duval				return B_BAD_ADDRESS;
320428d8bc6SJérôme Duval			}
321428d8bc6SJérôme Duval			return B_OK;
322f46bc7f1SMichael Lotz	}
323f46bc7f1SMichael Lotz
324f46bc7f1SMichael Lotz	return B_ERROR;
325f46bc7f1SMichael Lotz}
326f46bc7f1SMichael Lotz
327f46bc7f1SMichael Lotz
3285052c5b6SMichael Lotzint32
3295052c5b6SMichael LotzJoystickProtocolHandler::_UpdateThread(void *data)
3305052c5b6SMichael Lotz{
3315052c5b6SMichael Lotz	JoystickProtocolHandler *handler = (JoystickProtocolHandler *)data;
3325052c5b6SMichael Lotz	while (handler->fUpdateThread == find_thread(NULL)) {
3335052c5b6SMichael Lotz		status_t result = handler->_Update();
3345052c5b6SMichael Lotz		if (result != B_OK)
3355052c5b6SMichael Lotz			return result;
3365052c5b6SMichael Lotz	}
3375052c5b6SMichael Lotz
3385052c5b6SMichael Lotz	return B_OK;
3395052c5b6SMichael Lotz}
3405052c5b6SMichael Lotz
3415052c5b6SMichael Lotz
342f46bc7f1SMichael Lotzstatus_t
3435052c5b6SMichael LotzJoystickProtocolHandler::_Update()
344f46bc7f1SMichael Lotz{
345f46bc7f1SMichael Lotz	status_t result = fReport.WaitForReport(B_INFINITE_TIMEOUT);
346f46bc7f1SMichael Lotz	if (result != B_OK) {
347f46bc7f1SMichael Lotz		if (fReport.Device()->IsRemoved()) {
348f46bc7f1SMichael Lotz			TRACE("device has been removed\n");
349f46bc7f1SMichael Lotz			return B_DEV_NOT_READY;
350f46bc7f1SMichael Lotz		}
351f46bc7f1SMichael Lotz
35225f723deSMichael Lotz		if (result == B_CANCELED)
35325f723deSMichael Lotz			return B_CANCELED;
35425f723deSMichael Lotz
355f46bc7f1SMichael Lotz		if (result != B_INTERRUPTED) {
356f46bc7f1SMichael Lotz			// interrupts happen when other reports come in on the same
357f46bc7f1SMichael Lotz			// input as ours
358f46bc7f1SMichael Lotz			TRACE_ALWAYS("error waiting for report: %s\n", strerror(result));
359f46bc7f1SMichael Lotz		}
360f46bc7f1SMichael Lotz
361f46bc7f1SMichael Lotz		// signal that we simply want to try again
362f46bc7f1SMichael Lotz		return B_OK;
363f46bc7f1SMichael Lotz	}
364f46bc7f1SMichael Lotz
3655052c5b6SMichael Lotz	result = mutex_lock(&fUpdateLock);
3665052c5b6SMichael Lotz	if (result != B_OK) {
3675052c5b6SMichael Lotz		fReport.DoneProcessing();
3685052c5b6SMichael Lotz		return result;
3695052c5b6SMichael Lotz	}
3705052c5b6SMichael Lotz
371233d1dc4SMichael Lotz	memset(fCurrentValues.data, 0, fCurrentValues.data_size);
372f46bc7f1SMichael Lotz
373233d1dc4SMichael Lotz	for (uint32 i = 0; i < fAxisCount; i++) {
374f46bc7f1SMichael Lotz		if (fAxis[i] == NULL)
375f46bc7f1SMichael Lotz			continue;
376f46bc7f1SMichael Lotz
377f46bc7f1SMichael Lotz		if (fAxis[i]->Extract() == B_OK && fAxis[i]->Valid())
3785052c5b6SMichael Lotz			fCurrentValues.axes[i] = (int16)fAxis[i]->ScaledData(16, true);
379f46bc7f1SMichael Lotz	}
380f46bc7f1SMichael Lotz
3811b7d2a54SMichael Lotz	for (uint32 i = 0; i < fHatCount; i++) {
3821b7d2a54SMichael Lotz		HIDReportItem *hat = fHats[i];
3831b7d2a54SMichael Lotz		if (hat == NULL)
3841b7d2a54SMichael Lotz			continue;
3851b7d2a54SMichael Lotz
3861b7d2a54SMichael Lotz		if (hat->Extract() != B_OK || !hat->Valid())
3871b7d2a54SMichael Lotz			continue;
3881b7d2a54SMichael Lotz
3891b7d2a54SMichael Lotz		fCurrentValues.hats[i] = hat->ScaledRangeData(1, 8);
3901b7d2a54SMichael Lotz	}
3911b7d2a54SMichael Lotz
392233d1dc4SMichael Lotz	for (uint32 i = 0; i < fButtonCount; i++) {
393f46bc7f1SMichael Lotz		HIDReportItem *button = fButtons[i];
394f46bc7f1SMichael Lotz		if (button == NULL)
395f46bc7f1SMichael Lotz			break;
396f46bc7f1SMichael Lotz
397233d1dc4SMichael Lotz		uint16 index = button->UsageID() - 1;
398233d1dc4SMichael Lotz		if (index >= fMaxButton)
399233d1dc4SMichael Lotz			continue;
400233d1dc4SMichael Lotz
4015052c5b6SMichael Lotz		if (button->Extract() == B_OK && button->Valid()) {
402233d1dc4SMichael Lotz			fCurrentValues.buttons[index / 32]
403233d1dc4SMichael Lotz				|= (button->Data() & 1) << (index % 32);
4045052c5b6SMichael Lotz		}
405f46bc7f1SMichael Lotz	}
406f46bc7f1SMichael Lotz
407f46bc7f1SMichael Lotz	fReport.DoneProcessing();
408f46bc7f1SMichael Lotz	TRACE("got joystick report\n");
409f46bc7f1SMichael Lotz
410233d1dc4SMichael Lotz	*fCurrentValues.timestamp = system_time();
4115052c5b6SMichael Lotz	mutex_unlock(&fUpdateLock);
4125052c5b6SMichael Lotz	return B_OK;
413f46bc7f1SMichael Lotz}