xref: /haiku/src/add-ons/kernel/drivers/input/hid_shared/JoystickProtocolHandler.cpp (revision 9e25244c5e9051f6cd333820d6332397361abd6c)
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 
26 JoystickProtocolHandler::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 
120 JoystickProtocolHandler::~JoystickProtocolHandler()
121 {
122 	free(fCurrentValues.data);
123 	free(fAxis);
124 	free(fHats);
125 	free(fButtons);
126 }
127 
128 
129 void
130 JoystickProtocolHandler::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 
187 status_t
188 JoystickProtocolHandler::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 
218 status_t
219 JoystickProtocolHandler::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 
233 status_t
234 JoystickProtocolHandler::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 
256 status_t
257 JoystickProtocolHandler::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 
265 status_t
266 JoystickProtocolHandler::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 
335 int32
336 JoystickProtocolHandler::_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 
349 status_t
350 JoystickProtocolHandler::_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