2 * Copyright 2011 Michael Lotz <mmlr@mlotz.ch>
3 * Distributed under the terms of the MIT license.
7 //! Driver for USB Human Interface Devices.
11 #include "JoystickProtocolHandler.h"
13 #include "HIDCollection.h"
14 #include "HIDDevice.h"
15 #include "HIDReport.h"
16 #include "HIDReportItem.h"
20 #include <usb/USB_hid.h>
23 JoystickProtocolHandler::JoystickProtocolHandler(HIDReport
&report
)
25 ProtocolHandler(report
.Device(), "joystick/usb/", 0),
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
);
46 switch (item
->UsagePage()) {
47 case B_HID_USAGE_PAGE_BUTTON
:
49 if (item
->UsageID() > INT16_MAX
)
52 HIDReportItem
**newButtons
= (HIDReportItem
**)realloc(fButtons
,
53 ++fButtonCount
* sizeof(HIDReportItem
*));
54 if (newButtons
== NULL
) {
59 fButtons
= newButtons
;
60 fButtons
[fButtonCount
- 1] = item
;
62 if (fMaxButton
< item
->UsageID())
63 fMaxButton
= item
->UsageID();
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
) {
78 fHats
[fHatCount
- 1] = item
;
82 // TODO: "axis" is set but not used!
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;
93 HIDReportItem
**newAxis
= (HIDReportItem
**)realloc(fAxis
,
94 ++fAxisCount
* sizeof(HIDReportItem
*));
95 if (newAxis
== NULL
) {
101 fAxis
[fAxisCount
- 1] = item
;
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
);
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");
137 HIDParser
&parser
= device
.Parser();
138 uint32 maxReportCount
= parser
.CountReports(HID_REPORT_TYPE_INPUT
);
139 if (maxReportCount
== 0)
142 uint32 inputReportCount
= 0;
143 HIDReport
*inputReports
[maxReportCount
];
144 collection
.BuildReportList(HID_REPORT_TYPE_INPUT
, inputReports
,
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())
157 if (item
->UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP
)
160 if (item
->UsageID() >= B_HID_UID_GD_X
161 && item
->UsageID() <= B_HID_UID_GD_RZ
) {
170 ProtocolHandler
*newHandler
171 = new(std::nothrow
) JoystickProtocolHandler(*inputReport
);
172 if (newHandler
== NULL
) {
173 TRACE("failed to allocated joystick protocol handler\n");
177 newHandler
->SetNextHandler(handlerList
);
178 handlerList
= newHandler
;
184 JoystickProtocolHandler::Open(uint32 flags
, uint32
*cookie
)
186 if (fCurrentValues
.data
== NULL
)
189 status_t result
= mutex_lock(&fUpdateLock
);
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
;
200 resume_thread(fUpdateThread
);
206 mutex_unlock(&fUpdateLock
);
210 return ProtocolHandler::Open(flags
, cookie
);
215 JoystickProtocolHandler::Close(uint32
*cookie
)
217 status_t result
= mutex_lock(&fUpdateLock
);
218 if (result
== B_OK
) {
219 if (--fOpenCount
== 0)
221 mutex_unlock(&fUpdateLock
);
224 return ProtocolHandler::Close(cookie
);
230 JoystickProtocolHandler::Read(uint32
*cookie
, off_t position
, void *buffer
,
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
) {
243 memcpy(buffer
, fCurrentValues
.data
, fCurrentValues
.data_size
);
244 mutex_unlock(&fUpdateLock
);
246 *numBytes
= fCurrentValues
.data_size
;
252 JoystickProtocolHandler::Write(uint32
*cookie
, off_t position
,
253 const void *buffer
, size_t *numBytes
)
256 return B_NOT_SUPPORTED
;
261 JoystickProtocolHandler::Control(uint32
*cookie
, uint32 op
, void *buffer
,
265 case B_JOYSTICK_SET_DEVICE_MODULE
:
267 if (length
< sizeof(joystick_module_info
))
270 status_t result
= mutex_lock(&fUpdateLock
);
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");
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
);
304 case B_JOYSTICK_GET_DEVICE_MODULE
:
305 if (length
< sizeof(joystick_module_info
))
308 *(joystick_module_info
*)buffer
= fJoystickModuleInfo
;
317 JoystickProtocolHandler::_UpdateThread(void *data
)
319 JoystickProtocolHandler
*handler
= (JoystickProtocolHandler
*)data
;
320 while (handler
->fUpdateThread
== find_thread(NULL
)) {
321 status_t result
= handler
->_Update();
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
)
343 if (result
!= B_INTERRUPTED
) {
344 // interrupts happen when other reports come in on the same
346 TRACE_ALWAYS("error waiting for report: %s\n", strerror(result
));
349 // signal that we simply want to try again
353 result
= mutex_lock(&fUpdateLock
);
354 if (result
!= B_OK
) {
355 fReport
.DoneProcessing();
359 memset(fCurrentValues
.data
, 0, fCurrentValues
.data_size
);
361 for (uint32 i
= 0; i
< fAxisCount
; i
++) {
362 if (fAxis
[i
] == NULL
)
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
];
374 if (hat
->Extract() != B_OK
|| !hat
->Valid())
377 fCurrentValues
.hats
[i
] = hat
->ScaledRangeData(1, 8);
380 for (uint32 i
= 0; i
< fButtonCount
; i
++) {
381 HIDReportItem
*button
= fButtons
[i
];
385 uint16 index
= button
->UsageID() - 1;
386 if (index
>= fMaxButton
)
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
);