1 /* Bus like function for mac HID devices
3 * Copyright 2016 CodeWeavers, Aric Stewart
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "wine/port.h"
25 #if defined(HAVE_IOKIT_HID_IOHIDLIB_H)
27 #define LPDWORD UInt32*
29 #define LPLONG SInt32*
30 #define E_PENDING __carbon_E_PENDING
31 #define ULONG __carbon_ULONG
32 #define E_INVALIDARG __carbon_E_INVALIDARG
33 #define E_OUTOFMEMORY __carbon_E_OUTOFMEMORY
34 #define E_HANDLE __carbon_E_HANDLE
35 #define E_ACCESSDENIED __carbon_E_ACCESSDENIED
36 #define E_UNEXPECTED __carbon_E_UNEXPECTED
37 #define E_FAIL __carbon_E_FAIL
38 #define E_ABORT __carbon_E_ABORT
39 #define E_POINTER __carbon_E_POINTER
40 #define E_NOINTERFACE __carbon_E_NOINTERFACE
41 #define E_NOTIMPL __carbon_E_NOTIMPL
42 #define S_FALSE __carbon_S_FALSE
43 #define S_OK __carbon_S_OK
44 #define HRESULT_FACILITY __carbon_HRESULT_FACILITY
45 #define IS_ERROR __carbon_IS_ERROR
46 #define FAILED __carbon_FAILED
47 #define SUCCEEDED __carbon_SUCCEEDED
48 #define MAKE_HRESULT __carbon_MAKE_HRESULT
49 #define HRESULT __carbon_HRESULT
50 #define STDMETHODCALLTYPE __carbon_STDMETHODCALLTYPE
51 #define PAGE_SHIFT __carbon_PAGE_SHIFT
52 #include <IOKit/IOKitLib.h>
53 #include <IOKit/hid/IOHIDLib.h>
67 #undef HRESULT_FACILITY
73 #undef STDMETHODCALLTYPE
80 #endif /* HAVE_IOKIT_HID_IOHIDLIB_H */
82 #define NONAMELESSUNION
85 #define WIN32_NO_STATUS
91 #include "ddk/hidtypes.h"
92 #include "wine/debug.h"
96 WINE_DEFAULT_DEBUG_CHANNEL(plugplay
);
97 #ifdef HAVE_IOHIDMANAGERCREATE
99 static DRIVER_OBJECT
*iohid_driver_obj
= NULL
;
100 static IOHIDManagerRef hid_manager
;
101 static CFRunLoopRef run_loop
;
102 static HANDLE run_loop_handle
;
104 static const WCHAR busidW
[] = {'I','O','H','I','D',0};
106 #include "initguid.h"
107 DEFINE_GUID(GUID_DEVCLASS_IOHID
, 0x989D309D,0x0470,0x4E1A,0x89,0x38,0x50,0x1F,0x42,0xBD,0x9A,0xCD);
109 struct platform_private
111 IOHIDDeviceRef device
;
115 static inline struct platform_private
*impl_from_DEVICE_OBJECT(DEVICE_OBJECT
*device
)
117 return (struct platform_private
*)get_platform_private(device
);
120 static void CFStringToWSTR(CFStringRef cstr
, LPWSTR wstr
, int length
)
122 int len
= min(CFStringGetLength(cstr
), length
-1);
123 CFStringGetCharacters(cstr
, CFRangeMake(0, len
), (UniChar
*)wstr
);
127 static DWORD
CFNumberToDWORD(CFNumberRef num
)
131 CFNumberGetValue(num
, kCFNumberIntType
, &dwNum
);
135 static void handle_IOHIDDeviceIOHIDReportCallback(void *context
,
136 IOReturn result
, void *sender
, IOHIDReportType type
,
137 uint32_t reportID
, uint8_t *report
, CFIndex report_length
)
139 DEVICE_OBJECT
*device
= (DEVICE_OBJECT
*)context
;
140 process_hid_report(device
, report
, report_length
);
143 static int compare_platform_device(DEVICE_OBJECT
*device
, void *platform_dev
)
145 struct platform_private
*private = impl_from_DEVICE_OBJECT(device
);
146 IOHIDDeviceRef dev2
= (IOHIDDeviceRef
)platform_dev
;
147 if (private->device
!= dev2
)
153 static NTSTATUS
get_reportdescriptor(DEVICE_OBJECT
*device
, BYTE
*buffer
, DWORD length
, DWORD
*out_length
)
155 struct platform_private
*private = impl_from_DEVICE_OBJECT(device
);
156 CFDataRef data
= IOHIDDeviceGetProperty(private->device
, CFSTR(kIOHIDReportDescriptorKey
));
157 int data_length
= CFDataGetLength(data
);
160 *out_length
= data_length
;
161 if (length
< data_length
)
162 return STATUS_BUFFER_TOO_SMALL
;
164 ptr
= CFDataGetBytePtr(data
);
165 memcpy(buffer
, ptr
, data_length
);
166 return STATUS_SUCCESS
;
169 static NTSTATUS
get_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
171 struct platform_private
*private = impl_from_DEVICE_OBJECT(device
);
175 case HID_STRING_ID_IPRODUCT
:
176 str
= IOHIDDeviceGetProperty(private->device
, CFSTR(kIOHIDProductKey
));
178 case HID_STRING_ID_IMANUFACTURER
:
179 str
= IOHIDDeviceGetProperty(private->device
, CFSTR(kIOHIDManufacturerKey
));
181 case HID_STRING_ID_ISERIALNUMBER
:
182 str
= IOHIDDeviceGetProperty(private->device
, CFSTR(kIOHIDSerialNumberKey
));
185 ERR("Unknown string index\n");
186 return STATUS_NOT_IMPLEMENTED
;
191 if (length
< CFStringGetLength(str
) + 1)
192 return STATUS_BUFFER_TOO_SMALL
;
193 CFStringToWSTR(str
, buffer
, length
);
197 if (!length
) return STATUS_BUFFER_TOO_SMALL
;
201 return STATUS_SUCCESS
;
204 static NTSTATUS
begin_report_processing(DEVICE_OBJECT
*device
)
207 struct platform_private
*private = impl_from_DEVICE_OBJECT(device
);
211 return STATUS_SUCCESS
;
213 num
= IOHIDDeviceGetProperty(private->device
, CFSTR(kIOHIDMaxInputReportSizeKey
));
214 length
= CFNumberToDWORD(num
);
215 private->buffer
= HeapAlloc(GetProcessHeap(), 0, length
);
217 IOHIDDeviceRegisterInputReportCallback(private->device
, private->buffer
, length
, handle_IOHIDDeviceIOHIDReportCallback
, device
);
218 return STATUS_SUCCESS
;
221 static NTSTATUS
set_output_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*written
)
224 struct platform_private
*private = impl_from_DEVICE_OBJECT(device
);
225 result
= IOHIDDeviceSetReport(private->device
, kIOHIDReportTypeOutput
, id
, report
, length
);
226 if (result
== kIOReturnSuccess
)
229 return STATUS_SUCCESS
;
234 return STATUS_UNSUCCESSFUL
;
238 static NTSTATUS
get_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*read
)
241 CFIndex report_length
= length
;
242 struct platform_private
*private = impl_from_DEVICE_OBJECT(device
);
244 ret
= IOHIDDeviceGetReport(private->device
, kIOHIDReportTypeFeature
, id
, report
, &report_length
);
245 if (ret
== kIOReturnSuccess
)
247 *read
= report_length
;
248 return STATUS_SUCCESS
;
253 return STATUS_UNSUCCESSFUL
;
257 static NTSTATUS
set_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*written
)
260 struct platform_private
*private = impl_from_DEVICE_OBJECT(device
);
262 result
= IOHIDDeviceSetReport(private->device
, kIOHIDReportTypeFeature
, id
, report
, length
);
263 if (result
== kIOReturnSuccess
)
266 return STATUS_SUCCESS
;
271 return STATUS_UNSUCCESSFUL
;
275 static const platform_vtbl iohid_vtbl
=
277 compare_platform_device
,
278 get_reportdescriptor
,
280 begin_report_processing
,
286 static void handle_DeviceMatchingCallback(void *context
, IOReturn result
, void *sender
, IOHIDDeviceRef IOHIDDevice
)
288 DEVICE_OBJECT
*device
;
289 DWORD vid
, pid
, version
;
290 CFStringRef str
= NULL
;
291 WCHAR serial_string
[256];
292 BOOL is_gamepad
= FALSE
;
294 TRACE("OS/X IOHID Device Added %p\n", IOHIDDevice
);
296 vid
= CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice
, CFSTR(kIOHIDVendorIDKey
)));
297 pid
= CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice
, CFSTR(kIOHIDProductIDKey
)));
298 version
= CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice
, CFSTR(kIOHIDVersionNumberKey
)));
299 str
= IOHIDDeviceGetProperty(IOHIDDevice
, CFSTR(kIOHIDSerialNumberKey
));
300 if (str
) CFStringToWSTR(str
, serial_string
, ARRAY_SIZE(serial_string
));
302 if (IOHIDDeviceConformsTo(IOHIDDevice
, kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
) ||
303 IOHIDDeviceConformsTo(IOHIDDevice
, kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
))
305 if (is_xbox_gamepad(vid
, pid
))
309 int axes
=0, buttons
=0;
310 CFArrayRef element_array
= IOHIDDeviceCopyMatchingElements(
311 IOHIDDevice
, NULL
, kIOHIDOptionsTypeNone
);
315 CFIndex count
= CFArrayGetCount(element_array
);
316 for (index
= 0; index
< count
; index
++)
318 IOHIDElementRef element
= (IOHIDElementRef
)CFArrayGetValueAtIndex(element_array
, index
);
321 int type
= IOHIDElementGetType(element
);
322 if (type
== kIOHIDElementTypeInput_Button
) buttons
++;
323 if (type
== kIOHIDElementTypeInput_Axis
) axes
++;
324 if (type
== kIOHIDElementTypeInput_Misc
)
326 uint32_t usage
= IOHIDElementGetUsage(element
);
332 case kHIDUsage_GD_Rx
:
333 case kHIDUsage_GD_Ry
:
334 case kHIDUsage_GD_Rz
:
335 case kHIDUsage_GD_Slider
:
341 CFRelease(element_array
);
343 is_gamepad
= (axes
== 6 && buttons
>= 14);
347 device
= bus_create_hid_device(iohid_driver_obj
, busidW
, vid
, pid
, version
, 0, str
?serial_string
:NULL
, is_gamepad
, &GUID_DEVCLASS_IOHID
, &iohid_vtbl
, sizeof(struct platform_private
));
349 ERR("Failed to create device\n");
352 struct platform_private
*private = impl_from_DEVICE_OBJECT(device
);
353 private->device
= IOHIDDevice
;
354 private->buffer
= NULL
;
355 IoInvalidateDeviceRelations(device
, BusRelations
);
359 static void handle_RemovalCallback(void *context
, IOReturn result
, void *sender
, IOHIDDeviceRef IOHIDDevice
)
361 DEVICE_OBJECT
*device
;
362 TRACE("OS/X IOHID Device Removed %p\n", IOHIDDevice
);
363 IOHIDDeviceRegisterInputReportCallback(IOHIDDevice
, NULL
, 0, NULL
, NULL
);
364 /* Note: Yes, we leak the buffer. But according to research there is no
365 safe way to deallocate that buffer. */
366 device
= bus_find_hid_device(&iohid_vtbl
, IOHIDDevice
);
369 IoInvalidateDeviceRelations(device
, RemovalRelations
);
370 bus_remove_hid_device(device
);
374 /* This puts the relevant run loop for event handling into a WINE thread */
375 static DWORD CALLBACK
runloop_thread(void *args
)
377 run_loop
= CFRunLoopGetCurrent();
379 IOHIDManagerSetDeviceMatching(hid_manager
, NULL
);
380 IOHIDManagerRegisterDeviceMatchingCallback(hid_manager
, handle_DeviceMatchingCallback
, NULL
);
381 IOHIDManagerRegisterDeviceRemovalCallback(hid_manager
, handle_RemovalCallback
, NULL
);
382 IOHIDManagerScheduleWithRunLoop(hid_manager
, run_loop
, kCFRunLoopDefaultMode
);
383 if (IOHIDManagerOpen( hid_manager
, 0 ) != kIOReturnSuccess
)
385 ERR("Couldn't open IOHIDManager.\n");
386 IOHIDManagerUnscheduleFromRunLoop(hid_manager
, run_loop
, kCFRunLoopDefaultMode
);
387 CFRelease(hid_manager
);
392 TRACE("Run Loop exiting\n");
397 NTSTATUS WINAPI
iohid_driver_init(DRIVER_OBJECT
*driver
, UNICODE_STRING
*registry_path
)
399 TRACE("(%p, %s)\n", driver
, debugstr_w(registry_path
->Buffer
));
401 iohid_driver_obj
= driver
;
402 driver
->MajorFunction
[IRP_MJ_PNP
] = common_pnp_dispatch
;
403 driver
->MajorFunction
[IRP_MJ_INTERNAL_DEVICE_CONTROL
] = hid_internal_dispatch
;
404 hid_manager
= IOHIDManagerCreate(kCFAllocatorDefault
, 0L);
405 if (!(run_loop_handle
= CreateThread(NULL
, 0, runloop_thread
, NULL
, 0, NULL
)))
407 ERR("Failed to initialize IOHID Manager thread\n");
408 iohid_driver_obj
= NULL
;
409 CFRelease(hid_manager
);
410 return STATUS_UNSUCCESSFUL
;
413 return STATUS_SUCCESS
;
416 void iohid_driver_unload( void )
418 TRACE("Unloading Driver\n");
419 if (iohid_driver_obj
!= NULL
)
421 IOHIDManagerUnscheduleFromRunLoop(hid_manager
, run_loop
, kCFRunLoopDefaultMode
);
422 CFRunLoopStop(run_loop
);
423 WaitForSingleObject(run_loop_handle
, INFINITE
);
424 CloseHandle(run_loop_handle
);
425 IOHIDManagerRegisterDeviceMatchingCallback(hid_manager
, NULL
, NULL
);
426 IOHIDManagerRegisterDeviceRemovalCallback(hid_manager
, NULL
, NULL
);
427 CFRelease(hid_manager
);
429 TRACE("Driver Unloaded\n");
434 NTSTATUS WINAPI
iohid_driver_init(DRIVER_OBJECT
*driver
, UNICODE_STRING
*registry_path
)
436 WARN("IOHID Support not compiled into Wine.\n");
437 return STATUS_NOT_IMPLEMENTED
;
440 void iohid_driver_unload( void )
442 TRACE("Stub: Unload Driver\n");
445 #endif /* HAVE_IOHIDMANAGERCREATE */