winebus.sys: Implement IOCTL_HID_GET_DEVICE_ATTRIBUTES for hid devices.
[wine.git] / dlls / winebus.sys / main.c
blobeaa0b937fe2157aa380b8c892054b65bdcf3f3a9
1 /*
2 * WINE Platform native bus driver
4 * Copyright 2016 Aric Stewart
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define NONAMELESSUNION
25 #include "ntstatus.h"
26 #define WIN32_NO_STATUS
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "winternl.h"
31 #include "winreg.h"
32 #include "setupapi.h"
33 #include "winioctl.h"
34 #include "ddk/wdm.h"
35 #include "ddk/hidport.h"
36 #include "wine/debug.h"
37 #include "wine/unicode.h"
38 #include "wine/list.h"
40 #include "bus.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
44 struct pnp_device
46 struct list entry;
47 DEVICE_OBJECT *device;
50 struct device_extension
52 struct pnp_device *pnp_device;
54 WORD vid, pid;
55 DWORD uid, version, index;
56 BOOL is_gamepad;
57 WCHAR *serial;
58 const WCHAR *busid; /* Expected to be a static constant */
60 const platform_vtbl *vtbl;
61 BYTE platform_private[1];
64 static CRITICAL_SECTION device_list_cs;
65 static CRITICAL_SECTION_DEBUG critsect_debug =
67 0, 0, &device_list_cs,
68 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
69 0, 0, { (DWORD_PTR)(__FILE__ ": device_list_cs") }
71 static CRITICAL_SECTION device_list_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
73 static struct list pnp_devset = LIST_INIT(pnp_devset);
75 static const WCHAR zero_serialW[]= {'0','0','0','0',0};
76 static const WCHAR imW[] = {'I','M',0};
77 static const WCHAR igW[] = {'I','G',0};
79 static inline WCHAR *strdupW(const WCHAR *src)
81 WCHAR *dst;
82 if (!src) return NULL;
83 dst = HeapAlloc(GetProcessHeap(), 0, (strlenW(src) + 1)*sizeof(WCHAR));
84 if (dst) strcpyW(dst, src);
85 return dst;
88 void *get_platform_private(DEVICE_OBJECT *device)
90 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
91 return ext->platform_private;
94 static DWORD get_vidpid_index(WORD vid, WORD pid)
96 struct pnp_device *ptr;
97 DWORD index = 1;
99 LIST_FOR_EACH_ENTRY(ptr, &pnp_devset, struct pnp_device, entry)
101 struct device_extension *ext = (struct device_extension *)ptr->device->DeviceExtension;
102 if (ext->vid == vid && ext->pid == pid)
103 index = max(ext->index + 1, index);
106 return index;
109 static WCHAR *get_instance_id(DEVICE_OBJECT *device)
111 static const WCHAR formatW[] = {'%','s','\\','V','i','d','_','%','0','4','x','&', 'P','i','d','_','%','0','4','x','&',
112 '%','s','_','%','i','\\','%','i','&','%','s','&','%','x',0};
113 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
114 const WCHAR *serial = ext->serial ? ext->serial : zero_serialW;
115 DWORD len = strlenW(ext->busid) + strlenW(serial) + 64;
116 WCHAR *dst;
118 if ((dst = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
119 sprintfW(dst, formatW, ext->busid, ext->vid, ext->pid, ext->is_gamepad ? igW : imW,
120 ext->index, ext->version, serial, ext->uid);
122 return dst;
125 static WCHAR *get_device_id(DEVICE_OBJECT *device)
127 static const WCHAR formatW[] = {'%','s','\\','V','i','d','_','%','0','4','x','&','P','i','d','_','%','0','4','x',0};
128 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
129 DWORD len = strlenW(ext->busid) + 19;
130 WCHAR *dst;
132 if ((dst = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
133 sprintfW(dst, formatW, ext->busid, ext->vid, ext->pid);
135 return dst;
138 static WCHAR *get_compatible_ids(DEVICE_OBJECT *device)
140 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
141 WCHAR *iid, *did, *dst, *ptr;
142 DWORD len;
144 if (!(iid = get_instance_id(device)))
145 return NULL;
147 if (!(did = get_device_id(device)))
149 HeapFree(GetProcessHeap(), 0, iid);
150 return NULL;
153 len = strlenW(iid) + strlenW(did) + strlenW(ext->busid) + 4;
154 if ((dst = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
156 ptr = dst;
157 strcpyW(ptr, iid);
158 ptr += strlenW(iid) + 1;
159 strcpyW(ptr, did);
160 ptr += strlenW(did) + 1;
161 strcpyW(ptr, ext->busid);
162 ptr += strlenW(ext->busid) + 1;
163 *ptr = 0;
166 HeapFree(GetProcessHeap(), 0, iid);
167 HeapFree(GetProcessHeap(), 0, did);
168 return dst;
171 DEVICE_OBJECT *bus_create_hid_device(DRIVER_OBJECT *driver, const WCHAR *busidW, WORD vid, WORD pid,
172 DWORD version, DWORD uid, const WCHAR *serialW, BOOL is_gamepad,
173 const GUID *class, const platform_vtbl *vtbl, DWORD platform_data_size)
175 static const WCHAR device_name_fmtW[] = {'\\','D','e','v','i','c','e','\\','%','s','#','%','p',0};
176 struct device_extension *ext;
177 struct pnp_device *pnp_dev;
178 DEVICE_OBJECT *device;
179 UNICODE_STRING nameW;
180 WCHAR dev_name[256];
181 HDEVINFO devinfo;
182 NTSTATUS status;
183 DWORD length;
185 TRACE("(%p, %s, %04x, %04x, %u, %u, %s, %u, %s, %p, %u)\n", driver, debugstr_w(busidW), vid, pid,
186 version, uid, debugstr_w(serialW), is_gamepad, debugstr_guid(class), vtbl, platform_data_size);
188 if (!(pnp_dev = HeapAlloc(GetProcessHeap(), 0, sizeof(*pnp_dev))))
189 return NULL;
191 sprintfW(dev_name, device_name_fmtW, busidW, pnp_dev);
192 RtlInitUnicodeString(&nameW, dev_name);
193 length = FIELD_OFFSET(struct device_extension, platform_private[platform_data_size]);
194 status = IoCreateDevice(driver, length, &nameW, 0, 0, FALSE, &device);
195 if (status)
197 FIXME("failed to create device error %x\n", status);
198 HeapFree(GetProcessHeap(), 0, pnp_dev);
199 return NULL;
202 EnterCriticalSection(&device_list_cs);
204 /* fill out device_extension struct */
205 ext = (struct device_extension *)device->DeviceExtension;
206 ext->pnp_device = pnp_dev;
207 ext->vid = vid;
208 ext->pid = pid;
209 ext->uid = uid;
210 ext->version = version;
211 ext->index = get_vidpid_index(vid, pid);
212 ext->is_gamepad = is_gamepad;
213 ext->serial = strdupW(serialW);
214 ext->busid = busidW;
215 ext->vtbl = vtbl;
217 /* add to list of pnp devices */
218 pnp_dev->device = device;
219 list_add_tail(&pnp_devset, &pnp_dev->entry);
221 LeaveCriticalSection(&device_list_cs);
223 devinfo = SetupDiGetClassDevsW(class, NULL, NULL, DIGCF_DEVICEINTERFACE);
224 if (devinfo)
226 SP_DEVINFO_DATA data;
227 WCHAR *instance;
229 data.cbSize = sizeof(data);
230 if (!(instance = get_instance_id(device)))
231 ERR("failed to generate instance id\n");
232 else if (!SetupDiCreateDeviceInfoW(devinfo, instance, class, NULL, NULL, DICD_INHERIT_CLASSDRVS, &data))
233 ERR("failed to create device info: %x\n", GetLastError());
234 else if (!SetupDiRegisterDeviceInfo(devinfo, &data, 0, NULL, NULL, NULL))
235 ERR("failed to register device info: %x\n", GetLastError());
237 HeapFree(GetProcessHeap(), 0, instance);
238 SetupDiDestroyDeviceInfoList(devinfo);
240 else
241 ERR("failed to get ClassDevs: %x\n", GetLastError());
243 return device;
246 DEVICE_OBJECT *bus_find_hid_device(const platform_vtbl *vtbl, void *platform_dev)
248 struct pnp_device *dev;
249 DEVICE_OBJECT *ret = NULL;
251 TRACE("(%p, %p)\n", vtbl, platform_dev);
253 EnterCriticalSection(&device_list_cs);
254 LIST_FOR_EACH_ENTRY(dev, &pnp_devset, struct pnp_device, entry)
256 struct device_extension *ext = (struct device_extension *)dev->device->DeviceExtension;
257 if (ext->vtbl != vtbl) continue;
258 if (ext->vtbl->compare_platform_device(dev->device, platform_dev) == 0)
260 ret = dev->device;
261 break;
264 LeaveCriticalSection(&device_list_cs);
266 TRACE("returning %p\n", ret);
267 return ret;
270 void bus_remove_hid_device(DEVICE_OBJECT *device)
272 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
273 struct pnp_device *pnp_device = ext->pnp_device;
275 TRACE("(%p)\n", device);
277 EnterCriticalSection(&device_list_cs);
278 list_remove(&pnp_device->entry);
279 LeaveCriticalSection(&device_list_cs);
281 IoInvalidateDeviceRelations(device, RemovalRelations);
282 HeapFree(GetProcessHeap(), 0, ext->serial);
283 IoDeleteDevice(device);
285 /* pnp_device must be released after the device is gone */
286 HeapFree(GetProcessHeap(), 0, pnp_device);
289 static NTSTATUS handle_IRP_MN_QUERY_ID(DEVICE_OBJECT *device, IRP *irp)
291 NTSTATUS status = irp->IoStatus.u.Status;
292 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
293 BUS_QUERY_ID_TYPE type = irpsp->Parameters.QueryId.IdType;
295 TRACE("(%p, %p)\n", device, irp);
297 switch (type)
299 case BusQueryHardwareIDs:
300 TRACE("BusQueryHardwareIDs\n");
301 irp->IoStatus.Information = (ULONG_PTR)get_compatible_ids(device);
302 break;
303 case BusQueryCompatibleIDs:
304 TRACE("BusQueryCompatibleIDs\n");
305 irp->IoStatus.Information = (ULONG_PTR)get_compatible_ids(device);
306 break;
307 case BusQueryDeviceID:
308 TRACE("BusQueryDeviceID\n");
309 irp->IoStatus.Information = (ULONG_PTR)get_device_id(device);
310 break;
311 case BusQueryInstanceID:
312 TRACE("BusQueryInstanceID\n");
313 irp->IoStatus.Information = (ULONG_PTR)get_instance_id(device);
314 break;
315 default:
316 FIXME("Unhandled type %08x\n", type);
317 return status;
320 status = irp->IoStatus.Information ? STATUS_SUCCESS : STATUS_NO_MEMORY;
321 return status;
324 NTSTATUS WINAPI common_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp)
326 NTSTATUS status = irp->IoStatus.u.Status;
327 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
329 switch (irpsp->MinorFunction)
331 case IRP_MN_QUERY_DEVICE_RELATIONS:
332 TRACE("IRP_MN_QUERY_DEVICE_RELATIONS\n");
333 break;
334 case IRP_MN_QUERY_ID:
335 TRACE("IRP_MN_QUERY_ID\n");
336 status = handle_IRP_MN_QUERY_ID(device, irp);
337 irp->IoStatus.u.Status = status;
338 break;
339 case IRP_MN_QUERY_CAPABILITIES:
340 TRACE("IRP_MN_QUERY_CAPABILITIES\n");
341 break;
342 default:
343 FIXME("Unhandled function %08x\n", irpsp->MinorFunction);
344 break;
347 IoCompleteRequest(irp, IO_NO_INCREMENT);
348 return status;
351 NTSTATUS WINAPI hid_internal_dispatch(DEVICE_OBJECT *device, IRP *irp)
353 NTSTATUS status = irp->IoStatus.u.Status;
354 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
355 struct device_extension *extension = (struct device_extension *)device->DeviceExtension;
357 TRACE("(%p, %p)\n", device, irp);
359 switch (irpsp->Parameters.DeviceIoControl.IoControlCode)
361 case IOCTL_HID_GET_DEVICE_ATTRIBUTES:
363 HID_DEVICE_ATTRIBUTES *attr = (HID_DEVICE_ATTRIBUTES *)irp->UserBuffer;
364 TRACE("IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
366 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(*attr))
368 irp->IoStatus.u.Status = status = STATUS_BUFFER_TOO_SMALL;
369 break;
372 memset(attr, 0, sizeof(*attr));
373 attr->Size = sizeof(HID_DEVICE_ATTRIBUTES);
374 attr->VendorID = extension->vid;
375 attr->ProductID = extension->pid;
376 attr->VersionNumber = extension->version;
377 irp->IoStatus.u.Status = status = STATUS_SUCCESS;
378 irp->IoStatus.Information = sizeof(HID_DEVICE_ATTRIBUTES);
379 break;
381 default:
383 ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode;
384 FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
385 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
386 break;
390 IoCompleteRequest(irp, IO_NO_INCREMENT);
392 return status;
395 NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
397 static const WCHAR udevW[] = {'\\','D','r','i','v','e','r','\\','U','D','E','V',0};
398 static UNICODE_STRING udev = {sizeof(udevW) - sizeof(WCHAR), sizeof(udevW), (WCHAR *)udevW};
400 TRACE( "(%p, %s)\n", driver, debugstr_w(path->Buffer) );
402 IoCreateDriver(&udev, udev_driver_init);
404 return STATUS_SUCCESS;