winebus.sys: IOCTL_HID_SET_OUTPUTREPORT for iohid.
[wine.git] / dlls / winebus.sys / bus_iohid.c
blob3b4b0a9f7c9cf8c6e3237a57be07c3f1c0ef1faa
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
20 #include "config.h"
21 #include "wine/port.h"
23 #include <stdarg.h>
25 #if defined(HAVE_IOKIT_HID_IOHIDLIB_H)
26 #define DWORD UInt32
27 #define LPDWORD UInt32*
28 #define LONG SInt32
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>
54 #undef ULONG
55 #undef E_INVALIDARG
56 #undef E_OUTOFMEMORY
57 #undef E_HANDLE
58 #undef E_ACCESSDENIED
59 #undef E_UNEXPECTED
60 #undef E_FAIL
61 #undef E_ABORT
62 #undef E_POINTER
63 #undef E_NOINTERFACE
64 #undef E_NOTIMPL
65 #undef S_FALSE
66 #undef S_OK
67 #undef HRESULT_FACILITY
68 #undef IS_ERROR
69 #undef FAILED
70 #undef SUCCEEDED
71 #undef MAKE_HRESULT
72 #undef HRESULT
73 #undef STDMETHODCALLTYPE
74 #undef DWORD
75 #undef LPDWORD
76 #undef LONG
77 #undef LPLONG
78 #undef E_PENDING
79 #undef PAGE_SHIFT
80 #endif /* HAVE_IOKIT_HID_IOHIDLIB_H */
82 #define NONAMELESSUNION
84 #include "ntstatus.h"
85 #define WIN32_NO_STATUS
86 #include "windef.h"
87 #include "winbase.h"
88 #include "winternl.h"
89 #include "winioctl.h"
90 #include "ddk/wdm.h"
91 #include "ddk/hidtypes.h"
92 #include "wine/debug.h"
94 #include "bus.h"
96 WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
97 #ifdef HAVE_IOHIDMANAGERCREATE
99 static DRIVER_OBJECT *iohid_driver_obj = NULL;
100 static IOHIDManagerRef hid_manager;
102 static const WCHAR busidW[] = {'I','O','H','I','D',0};
104 #include "initguid.h"
105 DEFINE_GUID(GUID_DEVCLASS_IOHID, 0x989D309D,0x0470,0x4E1A,0x89,0x38,0x50,0x1F,0x42,0xBD,0x9A,0xCD);
107 struct platform_private
109 IOHIDDeviceRef device;
110 uint8_t *buffer;
113 static inline struct platform_private *impl_from_DEVICE_OBJECT(DEVICE_OBJECT *device)
115 return (struct platform_private *)get_platform_private(device);
118 static void CFStringToWSTR(CFStringRef cstr, LPWSTR wstr, int length)
120 int len = min(CFStringGetLength(cstr), length-1);
121 CFStringGetCharacters(cstr, CFRangeMake(0, len), (UniChar*)wstr);
122 wstr[len] = 0;
125 static DWORD CFNumberToDWORD(CFNumberRef num)
127 int dwNum = 0;
128 if (num)
129 CFNumberGetValue(num, kCFNumberIntType, &dwNum);
130 return dwNum;
133 static void handle_IOHIDDeviceIOHIDReportCallback(void *context,
134 IOReturn result, void *sender, IOHIDReportType type,
135 uint32_t reportID, uint8_t *report, CFIndex report_length)
137 DEVICE_OBJECT *device = (DEVICE_OBJECT*)context;
138 process_hid_report(device, report, report_length);
141 static int compare_platform_device(DEVICE_OBJECT *device, void *platform_dev)
143 struct platform_private *private = impl_from_DEVICE_OBJECT(device);
144 IOHIDDeviceRef dev2 = (IOHIDDeviceRef)platform_dev;
145 if (private->device != dev2)
146 return 1;
147 else
148 return 0;
151 static NTSTATUS get_reportdescriptor(DEVICE_OBJECT *device, BYTE *buffer, DWORD length, DWORD *out_length)
153 struct platform_private *private = impl_from_DEVICE_OBJECT(device);
154 CFDataRef data = IOHIDDeviceGetProperty(private->device, CFSTR(kIOHIDReportDescriptorKey));
155 int data_length = CFDataGetLength(data);
156 const UInt8 *ptr;
158 *out_length = data_length;
159 if (length < data_length)
160 return STATUS_BUFFER_TOO_SMALL;
162 ptr = CFDataGetBytePtr(data);
163 memcpy(buffer, ptr, data_length);
164 return STATUS_SUCCESS;
167 static NTSTATUS get_string(DEVICE_OBJECT *device, DWORD index, WCHAR *buffer, DWORD length)
169 struct platform_private *private = impl_from_DEVICE_OBJECT(device);
170 CFStringRef str;
171 switch (index)
173 case HID_STRING_ID_IPRODUCT:
174 str = IOHIDDeviceGetProperty(private->device, CFSTR(kIOHIDProductKey));
175 break;
176 case HID_STRING_ID_IMANUFACTURER:
177 str = IOHIDDeviceGetProperty(private->device, CFSTR(kIOHIDManufacturerKey));
178 break;
179 case HID_STRING_ID_ISERIALNUMBER:
180 str = IOHIDDeviceGetProperty(private->device, CFSTR(kIOHIDSerialNumberKey));
181 break;
182 default:
183 ERR("Unknown string index\n");
184 return STATUS_NOT_IMPLEMENTED;
187 if (str)
189 if (length < CFStringGetLength(str) + 1)
190 return STATUS_BUFFER_TOO_SMALL;
191 CFStringToWSTR(str, buffer, length);
193 else
195 if (!length) return STATUS_BUFFER_TOO_SMALL;
196 buffer[0] = 0;
199 return STATUS_SUCCESS;
202 static NTSTATUS begin_report_processing(DEVICE_OBJECT *device)
204 DWORD length;
205 struct platform_private *private = impl_from_DEVICE_OBJECT(device);
206 CFNumberRef num;
208 if (private->buffer)
209 return STATUS_SUCCESS;
211 num = IOHIDDeviceGetProperty(private->device, CFSTR(kIOHIDMaxInputReportSizeKey));
212 length = CFNumberToDWORD(num);
213 private->buffer = HeapAlloc(GetProcessHeap(), 0, length);
215 IOHIDDeviceRegisterInputReportCallback(private->device, private->buffer, length, handle_IOHIDDeviceIOHIDReportCallback, device);
216 return STATUS_SUCCESS;
219 static NTSTATUS set_output_report(DEVICE_OBJECT *device, UCHAR id, BYTE *report, DWORD length, ULONG_PTR *written)
221 IOReturn result;
222 struct platform_private *private = impl_from_DEVICE_OBJECT(device);
223 result = IOHIDDeviceSetReport(private->device, kIOHIDReportTypeOutput, id, report, length);
224 if (result == kIOReturnSuccess)
226 *written = length;
227 return STATUS_SUCCESS;
229 else
231 *written = 0;
232 return STATUS_UNSUCCESSFUL;
236 static NTSTATUS get_feature_report(DEVICE_OBJECT *device, UCHAR id, BYTE *report, DWORD length, ULONG_PTR *read)
238 *read = 0;
239 return STATUS_NOT_IMPLEMENTED;
242 static NTSTATUS set_feature_report(DEVICE_OBJECT *device, UCHAR id, BYTE *report, DWORD length, ULONG_PTR *written)
244 *written = 0;
245 return STATUS_NOT_IMPLEMENTED;
248 static const platform_vtbl iohid_vtbl =
250 compare_platform_device,
251 get_reportdescriptor,
252 get_string,
253 begin_report_processing,
254 set_output_report,
255 get_feature_report,
256 set_feature_report,
259 static void handle_DeviceMatchingCallback(void *context, IOReturn result, void *sender, IOHIDDeviceRef IOHIDDevice)
261 DEVICE_OBJECT *device;
262 DWORD vid, pid, version;
263 CFStringRef str = NULL;
264 WCHAR serial_string[256];
265 BOOL is_gamepad;
267 TRACE("OS/X IOHID Device Added %p\n", IOHIDDevice);
269 vid = CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDVendorIDKey)));
270 pid = CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDProductIDKey)));
271 version = CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDVersionNumberKey)));
272 str = IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDSerialNumberKey));
273 if (str) CFStringToWSTR(str, serial_string, sizeof(serial_string) / sizeof(WCHAR));
275 is_gamepad = (IOHIDDeviceConformsTo(IOHIDDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad) ||
276 IOHIDDeviceConformsTo(IOHIDDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick));
278 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));
279 if (!device)
280 ERR("Failed to create device\n");
281 else
283 struct platform_private *private = impl_from_DEVICE_OBJECT(device);
284 private->device = IOHIDDevice;
285 private->buffer = NULL;
286 IoInvalidateDeviceRelations(device, BusRelations);
290 static void handle_RemovalCallback(void *context, IOReturn result, void *sender, IOHIDDeviceRef IOHIDDevice)
292 DEVICE_OBJECT *device;
293 TRACE("OS/X IOHID Device Removed %p\n", IOHIDDevice);
294 IOHIDDeviceRegisterInputReportCallback(IOHIDDevice, NULL, 0, NULL, NULL);
295 /* Note: Yes, we leak the buffer. But according to research there is no
296 safe way to deallocate that buffer. */
297 device = bus_find_hid_device(&iohid_vtbl, IOHIDDevice);
298 if (device)
300 IoInvalidateDeviceRelations(device, RemovalRelations);
301 bus_remove_hid_device(device);
305 /* This puts the relevant run loop for event handling into a WINE thread */
306 static DWORD CALLBACK runloop_thread(void *args)
308 CFRunLoopRef run_loop = CFRunLoopGetCurrent();
310 IOHIDManagerSetDeviceMatching(hid_manager, NULL);
311 IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, handle_DeviceMatchingCallback, NULL);
312 IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, handle_RemovalCallback, NULL);
313 IOHIDManagerScheduleWithRunLoop(hid_manager, run_loop, kCFRunLoopDefaultMode);
314 if (IOHIDManagerOpen( hid_manager, 0 ) != kIOReturnSuccess)
316 ERR("Couldn't open IOHIDManager.\n");
317 IOHIDManagerUnscheduleFromRunLoop(hid_manager, run_loop, kCFRunLoopDefaultMode);
318 CFRelease(hid_manager);
319 return 0;
322 CFRunLoopRun();
323 TRACE("Run Loop exiting\n");
325 IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, NULL, NULL);
326 IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, NULL, NULL);
327 IOHIDManagerUnscheduleFromRunLoop(hid_manager, run_loop, kCFRunLoopDefaultMode);
328 CFRelease(hid_manager);
329 return 1;
332 NTSTATUS WINAPI iohid_driver_init(DRIVER_OBJECT *driver, UNICODE_STRING *registry_path)
334 HANDLE run_loop_handle;
336 TRACE("(%p, %s)\n", driver, debugstr_w(registry_path->Buffer));
338 iohid_driver_obj = driver;
339 driver->MajorFunction[IRP_MJ_PNP] = common_pnp_dispatch;
340 driver->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = hid_internal_dispatch;
341 hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, 0L);
342 if (!(run_loop_handle = CreateThread(NULL, 0, runloop_thread, NULL, 0, NULL)))
344 ERR("Failed to initialize IOHID Manager thread\n");
345 iohid_driver_obj = NULL;
346 CFRelease(hid_manager);
347 return STATUS_UNSUCCESSFUL;
350 CloseHandle(run_loop_handle);
351 return STATUS_SUCCESS;
354 #else
356 NTSTATUS WINAPI iohid_driver_init(DRIVER_OBJECT *driver, UNICODE_STRING *registry_path)
358 WARN("IOHID Support not compiled into Wine.\n");
359 return STATUS_NOT_IMPLEMENTED;
362 #endif /* HAVE_IOHIDMANAGERCREATE */