BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / drivers / input / usb_hid / JoystickProtocolHandler.cpp
blob6900951c20e2761df6af1d7af7dc374a1795fcf1
1 /*
2 * Copyright 2011 Michael Lotz <mmlr@mlotz.ch>
3 * Distributed under the terms of the MIT license.
4 */
7 //! Driver for USB Human Interface Devices.
10 #include "Driver.h"
11 #include "JoystickProtocolHandler.h"
13 #include "HIDCollection.h"
14 #include "HIDDevice.h"
15 #include "HIDReport.h"
16 #include "HIDReportItem.h"
18 #include <new>
19 #include <string.h>
20 #include <usb/USB_hid.h>
23 JoystickProtocolHandler::JoystickProtocolHandler(HIDReport &report)
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)
37 mutex_init(&fUpdateLock, "joystick update lock");
38 memset(&fJoystickModuleInfo, 0, sizeof(joystick_module_info));
39 memset(&fCurrentValues, 0, sizeof(variable_joystick));
41 for (uint32 i = 0; i < report.CountItems(); i++) {
42 HIDReportItem *item = report.ItemAt(i);
43 if (!item->HasData())
44 continue;
46 switch (item->UsagePage()) {
47 case B_HID_USAGE_PAGE_BUTTON:
49 if (item->UsageID() > INT16_MAX)
50 break;
52 HIDReportItem **newButtons = (HIDReportItem **)realloc(fButtons,
53 ++fButtonCount * sizeof(HIDReportItem *));
54 if (newButtons == NULL) {
55 fButtonCount--;
56 break;
59 fButtons = newButtons;
60 fButtons[fButtonCount - 1] = item;
62 if (fMaxButton < item->UsageID())
63 fMaxButton = item->UsageID();
64 break;
67 case B_HID_USAGE_PAGE_GENERIC_DESKTOP:
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;
77 fHats = newHats;
78 fHats[fHatCount - 1] = item;
79 break;
82 // TODO: "axis" is set but not used!
83 // uint16 axis = 0;
84 if (item->UsageID() >= B_HID_UID_GD_X
85 && item->UsageID() <= B_HID_UID_GD_WHEEL) {
86 // axis = item->UsageID() - B_HID_UID_GD_X;
87 } else if (item->UsageID() >= B_HID_UID_GD_VX
88 && item->UsageID() <= B_HID_UID_GD_VNO) {
89 // axis = item->UsageID() - B_HID_UID_GD_VX;
90 } else
91 break;
93 HIDReportItem **newAxis = (HIDReportItem **)realloc(fAxis,
94 ++fAxisCount * sizeof(HIDReportItem *));
95 if (newAxis == NULL) {
96 fAxisCount--;
97 break;
100 fAxis = newAxis;
101 fAxis[fAxisCount - 1] = item;
102 break;
108 fCurrentValues.initialize(fAxisCount, fHatCount, fMaxButton);
110 TRACE("joystick device with %lu buttons, %lu axes and %lu hats\n",
111 fButtonCount, fAxisCount, fHatCount);
112 TRACE("report id: %u\n", report.ID());
116 JoystickProtocolHandler::~JoystickProtocolHandler()
118 free(fCurrentValues.data);
119 free(fAxis);
120 free(fHats);
121 free(fButtons);
125 void
126 JoystickProtocolHandler::AddHandlers(HIDDevice &device,
127 HIDCollection &collection, ProtocolHandler *&handlerList)
129 if (collection.UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP
130 || (collection.UsageID() != B_HID_UID_GD_JOYSTICK
131 && collection.UsageID() != B_HID_UID_GD_GAMEPAD
132 && collection.UsageID() != B_HID_UID_GD_MULTIAXIS)) {
133 TRACE("collection not a joystick or gamepad\n");
134 return;
137 HIDParser &parser = device.Parser();
138 uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT);
139 if (maxReportCount == 0)
140 return;
142 uint32 inputReportCount = 0;
143 HIDReport *inputReports[maxReportCount];
144 collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports,
145 inputReportCount);
147 for (uint32 i = 0; i < inputReportCount; i++) {
148 HIDReport *inputReport = inputReports[i];
150 // try to find at least one axis
151 bool foundAxis = false;
152 for (uint32 j = 0; j < inputReport->CountItems(); j++) {
153 HIDReportItem *item = inputReport->ItemAt(j);
154 if (item == NULL || !item->HasData())
155 continue;
157 if (item->UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP)
158 continue;
160 if (item->UsageID() >= B_HID_UID_GD_X
161 && item->UsageID() <= B_HID_UID_GD_RZ) {
162 foundAxis = true;
163 break;
167 if (!foundAxis)
168 continue;
170 ProtocolHandler *newHandler
171 = new(std::nothrow) JoystickProtocolHandler(*inputReport);
172 if (newHandler == NULL) {
173 TRACE("failed to allocated joystick protocol handler\n");
174 continue;
177 newHandler->SetNextHandler(handlerList);
178 handlerList = newHandler;
183 status_t
184 JoystickProtocolHandler::Open(uint32 flags, uint32 *cookie)
186 if (fCurrentValues.data == NULL)
187 return B_NO_INIT;
189 status_t result = mutex_lock(&fUpdateLock);
190 if (result != B_OK)
191 return result;
193 if (fUpdateThread < 0) {
194 fUpdateThread = spawn_kernel_thread(_UpdateThread, "joystick update",
195 B_NORMAL_PRIORITY, (void *)this);
197 if (fUpdateThread < 0)
198 result = fUpdateThread;
199 else
200 resume_thread(fUpdateThread);
203 if (result == B_OK)
204 fOpenCount++;
206 mutex_unlock(&fUpdateLock);
207 if (result != B_OK)
208 return result;
210 return ProtocolHandler::Open(flags, cookie);
214 status_t
215 JoystickProtocolHandler::Close(uint32 *cookie)
217 status_t result = mutex_lock(&fUpdateLock);
218 if (result == B_OK) {
219 if (--fOpenCount == 0)
220 fUpdateThread = -1;
221 mutex_unlock(&fUpdateLock);
224 return ProtocolHandler::Close(cookie);
229 status_t
230 JoystickProtocolHandler::Read(uint32 *cookie, off_t position, void *buffer,
231 size_t *numBytes)
233 if (*numBytes < fCurrentValues.data_size)
234 return B_BUFFER_OVERFLOW;
236 // this is a polling interface, we just return the current value
237 status_t result = mutex_lock(&fUpdateLock);
238 if (result != B_OK) {
239 *numBytes = 0;
240 return result;
243 memcpy(buffer, fCurrentValues.data, fCurrentValues.data_size);
244 mutex_unlock(&fUpdateLock);
246 *numBytes = fCurrentValues.data_size;
247 return B_OK;
251 status_t
252 JoystickProtocolHandler::Write(uint32 *cookie, off_t position,
253 const void *buffer, size_t *numBytes)
255 *numBytes = 0;
256 return B_NOT_SUPPORTED;
260 status_t
261 JoystickProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer,
262 size_t length)
264 switch (op) {
265 case B_JOYSTICK_SET_DEVICE_MODULE:
267 if (length < sizeof(joystick_module_info))
268 return B_BAD_VALUE;
270 status_t result = mutex_lock(&fUpdateLock);
271 if (result != B_OK)
272 return result;
274 fJoystickModuleInfo = *(joystick_module_info *)buffer;
276 bool supportsVariable = (fJoystickModuleInfo.flags
277 & js_flag_variable_size_reads) != 0;
278 if (!supportsVariable) {
279 // We revert to a structure that we can support using only
280 // the data available in an extended_joystick structure.
281 free(fCurrentValues.data);
282 fCurrentValues.initialize_to_extended_joystick();
283 if (fAxisCount > MAX_AXES)
284 fAxisCount = MAX_AXES;
285 if (fHatCount > MAX_HATS)
286 fHatCount = MAX_HATS;
287 if (fMaxButton > MAX_BUTTONS)
288 fMaxButton = MAX_BUTTONS;
290 TRACE_ALWAYS("using joystick in extended_joystick mode\n");
291 } else {
292 TRACE_ALWAYS("using joystick in variable mode\n");
295 fJoystickModuleInfo.num_axes = fAxisCount;
296 fJoystickModuleInfo.num_buttons = fMaxButton;
297 fJoystickModuleInfo.num_hats = fHatCount;
298 fJoystickModuleInfo.num_sticks = 1;
299 fJoystickModuleInfo.config_size = 0;
300 mutex_unlock(&fUpdateLock);
301 break;
304 case B_JOYSTICK_GET_DEVICE_MODULE:
305 if (length < sizeof(joystick_module_info))
306 return B_BAD_VALUE;
308 *(joystick_module_info *)buffer = fJoystickModuleInfo;
309 break;
312 return B_ERROR;
316 int32
317 JoystickProtocolHandler::_UpdateThread(void *data)
319 JoystickProtocolHandler *handler = (JoystickProtocolHandler *)data;
320 while (handler->fUpdateThread == find_thread(NULL)) {
321 status_t result = handler->_Update();
322 if (result != B_OK)
323 return result;
326 return B_OK;
330 status_t
331 JoystickProtocolHandler::_Update()
333 status_t result = fReport.WaitForReport(B_INFINITE_TIMEOUT);
334 if (result != B_OK) {
335 if (fReport.Device()->IsRemoved()) {
336 TRACE("device has been removed\n");
337 return B_DEV_NOT_READY;
340 if (result == B_CANCELED)
341 return B_CANCELED;
343 if (result != B_INTERRUPTED) {
344 // interrupts happen when other reports come in on the same
345 // input as ours
346 TRACE_ALWAYS("error waiting for report: %s\n", strerror(result));
349 // signal that we simply want to try again
350 return B_OK;
353 result = mutex_lock(&fUpdateLock);
354 if (result != B_OK) {
355 fReport.DoneProcessing();
356 return result;
359 memset(fCurrentValues.data, 0, fCurrentValues.data_size);
361 for (uint32 i = 0; i < fAxisCount; i++) {
362 if (fAxis[i] == NULL)
363 continue;
365 if (fAxis[i]->Extract() == B_OK && fAxis[i]->Valid())
366 fCurrentValues.axes[i] = (int16)fAxis[i]->ScaledData(16, true);
369 for (uint32 i = 0; i < fHatCount; i++) {
370 HIDReportItem *hat = fHats[i];
371 if (hat == NULL)
372 continue;
374 if (hat->Extract() != B_OK || !hat->Valid())
375 continue;
377 fCurrentValues.hats[i] = hat->ScaledRangeData(1, 8);
380 for (uint32 i = 0; i < fButtonCount; i++) {
381 HIDReportItem *button = fButtons[i];
382 if (button == NULL)
383 break;
385 uint16 index = button->UsageID() - 1;
386 if (index >= fMaxButton)
387 continue;
389 if (button->Extract() == B_OK && button->Valid()) {
390 fCurrentValues.buttons[index / 32]
391 |= (button->Data() & 1) << (index % 32);
395 fReport.DoneProcessing();
396 TRACE("got joystick report\n");
398 *fCurrentValues.timestamp = system_time();
399 mutex_unlock(&fUpdateLock);
400 return B_OK;