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