msdasql: Implement IColumnsRowset GetAvailableColumns.
[wine.git] / dlls / winebus.sys / bus_iohid.c
blobe0527739199abd7e942a3e7271084a2a9fe49bc7
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 #if 0
21 #pragma makedep unix
22 #endif
24 #include "config.h"
26 #include <stdarg.h>
27 #include <sys/types.h>
29 #if defined(HAVE_IOKIT_HID_IOHIDLIB_H)
30 #define DWORD UInt32
31 #define LPDWORD UInt32*
32 #define LONG SInt32
33 #define LPLONG SInt32*
34 #define E_PENDING __carbon_E_PENDING
35 #define ULONG __carbon_ULONG
36 #define E_INVALIDARG __carbon_E_INVALIDARG
37 #define E_OUTOFMEMORY __carbon_E_OUTOFMEMORY
38 #define E_HANDLE __carbon_E_HANDLE
39 #define E_ACCESSDENIED __carbon_E_ACCESSDENIED
40 #define E_UNEXPECTED __carbon_E_UNEXPECTED
41 #define E_FAIL __carbon_E_FAIL
42 #define E_ABORT __carbon_E_ABORT
43 #define E_POINTER __carbon_E_POINTER
44 #define E_NOINTERFACE __carbon_E_NOINTERFACE
45 #define E_NOTIMPL __carbon_E_NOTIMPL
46 #define S_FALSE __carbon_S_FALSE
47 #define S_OK __carbon_S_OK
48 #define HRESULT_FACILITY __carbon_HRESULT_FACILITY
49 #define IS_ERROR __carbon_IS_ERROR
50 #define FAILED __carbon_FAILED
51 #define SUCCEEDED __carbon_SUCCEEDED
52 #define MAKE_HRESULT __carbon_MAKE_HRESULT
53 #define HRESULT __carbon_HRESULT
54 #define STDMETHODCALLTYPE __carbon_STDMETHODCALLTYPE
55 #define PAGE_SHIFT __carbon_PAGE_SHIFT
56 #include <IOKit/IOKitLib.h>
57 #include <IOKit/hid/IOHIDLib.h>
58 #undef ULONG
59 #undef E_INVALIDARG
60 #undef E_OUTOFMEMORY
61 #undef E_HANDLE
62 #undef E_ACCESSDENIED
63 #undef E_UNEXPECTED
64 #undef E_FAIL
65 #undef E_ABORT
66 #undef E_POINTER
67 #undef E_NOINTERFACE
68 #undef E_NOTIMPL
69 #undef S_FALSE
70 #undef S_OK
71 #undef HRESULT_FACILITY
72 #undef IS_ERROR
73 #undef FAILED
74 #undef SUCCEEDED
75 #undef MAKE_HRESULT
76 #undef HRESULT
77 #undef STDMETHODCALLTYPE
78 #undef DWORD
79 #undef LPDWORD
80 #undef LONG
81 #undef LPLONG
82 #undef E_PENDING
83 #undef PAGE_SHIFT
84 #endif /* HAVE_IOKIT_HID_IOHIDLIB_H */
86 #include <pthread.h>
88 #include "ntstatus.h"
89 #define WIN32_NO_STATUS
90 #include "windef.h"
91 #include "winbase.h"
92 #include "winternl.h"
93 #include "winioctl.h"
94 #include "ddk/wdm.h"
95 #include "ddk/hidtypes.h"
96 #include "wine/debug.h"
98 #include "unix_private.h"
100 WINE_DEFAULT_DEBUG_CHANNEL(hid);
101 #ifdef HAVE_IOHIDMANAGERCREATE
103 static pthread_mutex_t iohid_cs = PTHREAD_MUTEX_INITIALIZER;
105 static IOHIDManagerRef hid_manager;
106 static CFRunLoopRef run_loop;
107 static struct list event_queue = LIST_INIT(event_queue);
108 static struct list device_list = LIST_INIT(device_list);
109 static struct iohid_bus_options options;
111 struct iohid_device
113 struct unix_device unix_device;
114 IOHIDDeviceRef device;
115 uint8_t *buffer;
118 static inline struct iohid_device *impl_from_unix_device(struct unix_device *iface)
120 return CONTAINING_RECORD(iface, struct iohid_device, unix_device);
123 static struct iohid_device *find_device_from_iohid(IOHIDDeviceRef IOHIDDevice)
125 struct iohid_device *impl;
127 LIST_FOR_EACH_ENTRY(impl, &device_list, struct iohid_device, unix_device.entry)
128 if (impl->device == IOHIDDevice) return impl;
130 return NULL;
133 static void CFStringToWSTR(CFStringRef cstr, LPWSTR wstr, int length)
135 int len = min(CFStringGetLength(cstr), length - 1);
136 CFStringGetCharacters(cstr, CFRangeMake(0, len), (UniChar*)wstr);
137 wstr[len] = 0;
140 static DWORD CFNumberToDWORD(CFNumberRef num)
142 int dwNum = 0;
143 if (num)
144 CFNumberGetValue(num, kCFNumberIntType, &dwNum);
145 return dwNum;
148 static void handle_IOHIDDeviceIOHIDReportCallback(void *context,
149 IOReturn result, void *sender, IOHIDReportType type,
150 uint32_t reportID, uint8_t *report, CFIndex report_length)
152 struct unix_device *iface = (struct unix_device *)context;
153 bus_event_queue_input_report(&event_queue, iface, report, report_length);
156 static void iohid_device_destroy(struct unix_device *iface)
160 static NTSTATUS iohid_device_start(struct unix_device *iface)
162 DWORD length;
163 struct iohid_device *impl = impl_from_unix_device(iface);
164 CFNumberRef num;
166 num = IOHIDDeviceGetProperty(impl->device, CFSTR(kIOHIDMaxInputReportSizeKey));
167 length = CFNumberToDWORD(num);
168 impl->buffer = malloc(length);
170 IOHIDDeviceRegisterInputReportCallback(impl->device, impl->buffer, length, handle_IOHIDDeviceIOHIDReportCallback, iface);
171 return STATUS_SUCCESS;
174 static void iohid_device_stop(struct unix_device *iface)
176 struct iohid_device *impl = impl_from_unix_device(iface);
178 IOHIDDeviceRegisterInputReportCallback(impl->device, NULL, 0, NULL, NULL);
180 pthread_mutex_lock(&iohid_cs);
181 list_remove(&impl->unix_device.entry);
182 pthread_mutex_unlock(&iohid_cs);
185 static NTSTATUS iohid_device_get_report_descriptor(struct unix_device *iface, BYTE *buffer,
186 UINT length, UINT *out_length)
188 struct iohid_device *impl = impl_from_unix_device(iface);
189 CFDataRef data = IOHIDDeviceGetProperty(impl->device, CFSTR(kIOHIDReportDescriptorKey));
190 int data_length = CFDataGetLength(data);
191 const UInt8 *ptr;
193 *out_length = data_length;
194 if (length < data_length)
195 return STATUS_BUFFER_TOO_SMALL;
197 ptr = CFDataGetBytePtr(data);
198 memcpy(buffer, ptr, data_length);
199 return STATUS_SUCCESS;
202 static void iohid_device_set_output_report(struct unix_device *iface, HID_XFER_PACKET *packet, IO_STATUS_BLOCK *io)
204 IOReturn result;
205 struct iohid_device *impl = impl_from_unix_device(iface);
206 result = IOHIDDeviceSetReport(impl->device, kIOHIDReportTypeOutput, packet->reportId,
207 packet->reportBuffer, packet->reportBufferLen);
208 if (result == kIOReturnSuccess)
210 io->Information = packet->reportBufferLen;
211 io->Status = STATUS_SUCCESS;
213 else
215 io->Information = 0;
216 io->Status = STATUS_UNSUCCESSFUL;
220 static void iohid_device_get_feature_report(struct unix_device *iface, HID_XFER_PACKET *packet, IO_STATUS_BLOCK *io)
222 IOReturn ret;
223 CFIndex report_length = packet->reportBufferLen;
224 struct iohid_device *impl = impl_from_unix_device(iface);
226 ret = IOHIDDeviceGetReport(impl->device, kIOHIDReportTypeFeature, packet->reportId,
227 packet->reportBuffer, &report_length);
228 if (ret == kIOReturnSuccess)
230 io->Information = report_length;
231 io->Status = STATUS_SUCCESS;
233 else
235 io->Information = 0;
236 io->Status = STATUS_UNSUCCESSFUL;
240 static void iohid_device_set_feature_report(struct unix_device *iface, HID_XFER_PACKET *packet, IO_STATUS_BLOCK *io)
242 IOReturn result;
243 struct iohid_device *impl = impl_from_unix_device(iface);
245 result = IOHIDDeviceSetReport(impl->device, kIOHIDReportTypeFeature, packet->reportId,
246 packet->reportBuffer, packet->reportBufferLen);
247 if (result == kIOReturnSuccess)
249 io->Information = packet->reportBufferLen;
250 io->Status = STATUS_SUCCESS;
252 else
254 io->Information = 0;
255 io->Status = STATUS_UNSUCCESSFUL;
259 static const struct raw_device_vtbl iohid_device_vtbl =
261 iohid_device_destroy,
262 iohid_device_start,
263 iohid_device_stop,
264 iohid_device_get_report_descriptor,
265 iohid_device_set_output_report,
266 iohid_device_get_feature_report,
267 iohid_device_set_feature_report,
270 static void handle_DeviceMatchingCallback(void *context, IOReturn result, void *sender, IOHIDDeviceRef IOHIDDevice)
272 struct device_desc desc =
274 .input = -1,
275 .serialnumber = {'0','0','0','0',0},
277 struct iohid_device *impl;
278 CFStringRef str;
280 desc.vid = CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDVendorIDKey)));
281 desc.pid = CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDProductIDKey)));
282 desc.version = CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDVersionNumberKey)));
283 desc.uid = CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDLocationIDKey)));
285 if (IOHIDDeviceOpen(IOHIDDevice, 0) != kIOReturnSuccess)
287 ERR("Failed to open HID device %p (vid %04x, pid %04x)\n", IOHIDDevice, desc.vid, desc.pid);
288 return;
290 IOHIDDeviceScheduleWithRunLoop(IOHIDDevice, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
292 str = IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDManufacturerKey));
293 if (str) CFStringToWSTR(str, desc.manufacturer, ARRAY_SIZE(desc.manufacturer));
294 str = IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDProductKey));
295 if (str) CFStringToWSTR(str, desc.product, ARRAY_SIZE(desc.product));
296 str = IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDSerialNumberKey));
297 if (str) CFStringToWSTR(str, desc.serialnumber, ARRAY_SIZE(desc.serialnumber));
299 if (IOHIDDeviceConformsTo(IOHIDDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad) ||
300 IOHIDDeviceConformsTo(IOHIDDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick))
302 if (is_xbox_gamepad(desc.vid, desc.pid))
303 desc.is_gamepad = TRUE;
304 else
306 int axes=0, buttons=0;
307 CFArrayRef element_array = IOHIDDeviceCopyMatchingElements(
308 IOHIDDevice, NULL, kIOHIDOptionsTypeNone);
310 if (element_array) {
311 CFIndex index;
312 CFIndex count = CFArrayGetCount(element_array);
313 for (index = 0; index < count; index++)
315 IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(element_array, index);
316 if (element)
318 int type = IOHIDElementGetType(element);
319 if (type == kIOHIDElementTypeInput_Button) buttons++;
320 if (type == kIOHIDElementTypeInput_Axis) axes++;
321 if (type == kIOHIDElementTypeInput_Misc)
323 uint32_t usage = IOHIDElementGetUsage(element);
324 switch (usage)
326 case kHIDUsage_GD_X:
327 case kHIDUsage_GD_Y:
328 case kHIDUsage_GD_Z:
329 case kHIDUsage_GD_Rx:
330 case kHIDUsage_GD_Ry:
331 case kHIDUsage_GD_Rz:
332 case kHIDUsage_GD_Slider:
333 axes ++;
338 CFRelease(element_array);
340 desc.is_gamepad = (axes == 6 && buttons >= 14);
344 TRACE("dev %p, desc %s.\n", IOHIDDevice, debugstr_device_desc(&desc));
346 if (!(impl = raw_device_create(&iohid_device_vtbl, sizeof(struct iohid_device)))) return;
347 list_add_tail(&device_list, &impl->unix_device.entry);
348 impl->device = IOHIDDevice;
349 impl->buffer = NULL;
351 bus_event_queue_device_created(&event_queue, &impl->unix_device, &desc);
354 static void handle_RemovalCallback(void *context, IOReturn result, void *sender, IOHIDDeviceRef IOHIDDevice)
356 struct iohid_device *impl;
358 TRACE("OS/X IOHID Device Removed %p\n", IOHIDDevice);
359 IOHIDDeviceRegisterInputReportCallback(IOHIDDevice, NULL, 0, NULL, NULL);
360 /* Note: Yes, we leak the buffer. But according to research there is no
361 safe way to deallocate that buffer. */
362 IOHIDDeviceUnscheduleFromRunLoop(IOHIDDevice, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
363 IOHIDDeviceClose(IOHIDDevice, 0);
365 impl = find_device_from_iohid(IOHIDDevice);
366 if (impl) bus_event_queue_device_removed(&event_queue, &impl->unix_device);
367 else WARN("failed to find device for iohid device %p\n", IOHIDDevice);
370 NTSTATUS iohid_bus_init(void *args)
372 TRACE("args %p\n", args);
374 options = *(struct iohid_bus_options *)args;
376 if (!(hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, 0L)))
378 ERR("IOHID manager creation failed\n");
379 return STATUS_UNSUCCESSFUL;
382 run_loop = CFRunLoopGetCurrent();
384 IOHIDManagerSetDeviceMatching(hid_manager, NULL);
385 IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, handle_DeviceMatchingCallback, NULL);
386 IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, handle_RemovalCallback, NULL);
387 IOHIDManagerScheduleWithRunLoop(hid_manager, run_loop, kCFRunLoopDefaultMode);
388 return STATUS_SUCCESS;
391 NTSTATUS iohid_bus_wait(void *args)
393 struct bus_event *result = args;
394 CFRunLoopRunResult ret;
396 /* cleanup previously returned event */
397 bus_event_cleanup(result);
401 if (bus_event_queue_pop(&event_queue, result)) return STATUS_PENDING;
402 pthread_mutex_lock(&iohid_cs);
403 ret = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, TRUE);
404 pthread_mutex_unlock(&iohid_cs);
405 } while (ret != kCFRunLoopRunStopped);
407 TRACE("IOHID main loop exiting\n");
408 bus_event_queue_destroy(&event_queue);
409 IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, NULL, NULL);
410 IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, NULL, NULL);
411 CFRelease(hid_manager);
412 return STATUS_SUCCESS;
415 NTSTATUS iohid_bus_stop(void *args)
417 if (!run_loop) return STATUS_SUCCESS;
419 IOHIDManagerUnscheduleFromRunLoop(hid_manager, run_loop, kCFRunLoopDefaultMode);
420 CFRunLoopStop(run_loop);
421 return STATUS_SUCCESS;
424 #else
426 NTSTATUS iohid_bus_init(void *args)
428 WARN("IOHID support not compiled in!\n");
429 return STATUS_NOT_IMPLEMENTED;
432 NTSTATUS iohid_bus_wait(void *args)
434 WARN("IOHID support not compiled in!\n");
435 return STATUS_NOT_IMPLEMENTED;
438 NTSTATUS iohid_bus_stop(void *args)
440 WARN("IOHID support not compiled in!\n");
441 return STATUS_NOT_IMPLEMENTED;
444 #endif /* HAVE_IOHIDMANAGERCREATE */