winebus.sys: Implement IRP_MN_QUERY_ID.
[wine.git] / dlls / winebus.sys / main.c
blob1364eabb7f1274bbe4dc88191bbf529ec5cd58d8
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 "ddk/wdm.h"
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36 #include "wine/list.h"
38 #include "bus.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
42 struct pnp_device
44 struct list entry;
45 DEVICE_OBJECT *device;
48 struct device_extension
50 void *native; /* Must be the first member of the structure */
52 WORD vid, pid;
53 DWORD uid, version, index;
54 BOOL is_gamepad;
55 WCHAR *serial;
56 const WCHAR *busid; /* Expected to be a static constant */
59 static CRITICAL_SECTION device_list_cs;
60 static CRITICAL_SECTION_DEBUG critsect_debug =
62 0, 0, &device_list_cs,
63 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
64 0, 0, { (DWORD_PTR)(__FILE__ ": device_list_cs") }
66 static CRITICAL_SECTION device_list_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
68 static struct list pnp_devset = LIST_INIT(pnp_devset);
70 static const WCHAR zero_serialW[]= {'0','0','0','0',0};
71 static const WCHAR imW[] = {'I','M',0};
72 static const WCHAR igW[] = {'I','G',0};
74 static inline WCHAR *strdupW(const WCHAR *src)
76 WCHAR *dst;
77 if (!src) return NULL;
78 dst = HeapAlloc(GetProcessHeap(), 0, (strlenW(src) + 1)*sizeof(WCHAR));
79 if (dst) strcpyW(dst, src);
80 return dst;
83 static DWORD get_vidpid_index(WORD vid, WORD pid)
85 struct pnp_device *ptr;
86 DWORD index = 1;
88 LIST_FOR_EACH_ENTRY(ptr, &pnp_devset, struct pnp_device, entry)
90 struct device_extension *ext = (struct device_extension *)ptr->device->DeviceExtension;
91 if (ext->vid == vid && ext->pid == pid)
92 index = max(ext->index + 1, index);
95 return index;
98 static WCHAR *get_instance_id(DEVICE_OBJECT *device)
100 static const WCHAR formatW[] = {'%','s','\\','V','i','d','_','%','0','4','x','&', 'P','i','d','_','%','0','4','x','&',
101 '%','s','_','%','i','\\','%','i','&','%','s','&','%','x',0};
102 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
103 const WCHAR *serial = ext->serial ? ext->serial : zero_serialW;
104 DWORD len = strlenW(ext->busid) + strlenW(serial) + 64;
105 WCHAR *dst;
107 if ((dst = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
108 sprintfW(dst, formatW, ext->busid, ext->vid, ext->pid, ext->is_gamepad ? igW : imW,
109 ext->index, ext->version, serial, ext->uid);
111 return dst;
114 static WCHAR *get_device_id(DEVICE_OBJECT *device)
116 static const WCHAR formatW[] = {'%','s','\\','V','i','d','_','%','0','4','x','&','P','i','d','_','%','0','4','x',0};
117 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
118 DWORD len = strlenW(ext->busid) + 19;
119 WCHAR *dst;
121 if ((dst = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
122 sprintfW(dst, formatW, ext->busid, ext->vid, ext->pid);
124 return dst;
127 static WCHAR *get_compatible_ids(DEVICE_OBJECT *device)
129 struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
130 WCHAR *iid, *did, *dst, *ptr;
131 DWORD len;
133 if (!(iid = get_instance_id(device)))
134 return NULL;
136 if (!(did = get_device_id(device)))
138 HeapFree(GetProcessHeap(), 0, iid);
139 return NULL;
142 len = strlenW(iid) + strlenW(did) + strlenW(ext->busid) + 4;
143 if ((dst = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
145 ptr = dst;
146 strcpyW(ptr, iid);
147 ptr += strlenW(iid) + 1;
148 strcpyW(ptr, did);
149 ptr += strlenW(did) + 1;
150 strcpyW(ptr, ext->busid);
151 ptr += strlenW(ext->busid) + 1;
152 *ptr = 0;
155 HeapFree(GetProcessHeap(), 0, iid);
156 HeapFree(GetProcessHeap(), 0, did);
157 return dst;
160 DEVICE_OBJECT *bus_create_hid_device(DRIVER_OBJECT *driver, const WCHAR *busidW, void *native, WORD vid,
161 WORD pid, DWORD version, DWORD uid, const WCHAR *serialW, BOOL is_gamepad,
162 const GUID *class)
164 static const WCHAR device_name_fmtW[] = {'\\','D','e','v','i','c','e','\\','%','s','#','%','p',0};
165 struct device_extension *ext;
166 struct pnp_device *pnp_dev;
167 DEVICE_OBJECT *device;
168 UNICODE_STRING nameW;
169 WCHAR dev_name[256];
170 HDEVINFO devinfo;
171 NTSTATUS status;
173 TRACE("(%p, %s, %p, %04x, %04x, %u, %u, %s, %u, %s)\n", driver, debugstr_w(busidW), native,
174 vid, pid, version, uid, debugstr_w(serialW), is_gamepad, debugstr_guid(class));
176 if (!(pnp_dev = HeapAlloc(GetProcessHeap(), 0, sizeof(*pnp_dev))))
177 return NULL;
179 sprintfW(dev_name, device_name_fmtW, busidW, native);
180 RtlInitUnicodeString(&nameW, dev_name);
181 status = IoCreateDevice(driver, sizeof(*ext), &nameW, 0, 0, FALSE, &device);
182 if (status)
184 FIXME("failed to create device error %x\n", status);
185 HeapFree(GetProcessHeap(), 0, pnp_dev);
186 return NULL;
189 EnterCriticalSection(&device_list_cs);
191 /* fill out device_extension struct */
192 ext = (struct device_extension *)device->DeviceExtension;
193 ext->native = native;
194 ext->vid = vid;
195 ext->pid = pid;
196 ext->uid = uid;
197 ext->version = version;
198 ext->index = get_vidpid_index(vid, pid);
199 ext->is_gamepad = is_gamepad;
200 ext->serial = strdupW(serialW);
201 ext->busid = busidW;
203 /* add to list of pnp devices */
204 pnp_dev->device = device;
205 list_add_tail(&pnp_devset, &pnp_dev->entry);
207 LeaveCriticalSection(&device_list_cs);
209 devinfo = SetupDiGetClassDevsW(class, NULL, NULL, DIGCF_DEVICEINTERFACE);
210 if (devinfo)
212 SP_DEVINFO_DATA data;
213 WCHAR *instance;
215 data.cbSize = sizeof(data);
216 if (!(instance = get_instance_id(device)))
217 ERR("failed to generate instance id\n");
218 else if (!SetupDiCreateDeviceInfoW(devinfo, instance, class, NULL, NULL, DICD_INHERIT_CLASSDRVS, &data))
219 ERR("failed to create device info: %x\n", GetLastError());
220 else if (!SetupDiRegisterDeviceInfo(devinfo, &data, 0, NULL, NULL, NULL))
221 ERR("failed to register device info: %x\n", GetLastError());
223 HeapFree(GetProcessHeap(), 0, instance);
224 SetupDiDestroyDeviceInfoList(devinfo);
226 else
227 ERR("failed to get ClassDevs: %x\n", GetLastError());
229 IoInvalidateDeviceRelations(device, BusRelations);
230 return device;
233 static NTSTATUS handle_IRP_MN_QUERY_ID(DEVICE_OBJECT *device, IRP *irp)
235 NTSTATUS status = irp->IoStatus.u.Status;
236 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
237 BUS_QUERY_ID_TYPE type = irpsp->Parameters.QueryId.IdType;
239 TRACE("(%p, %p)\n", device, irp);
241 switch (type)
243 case BusQueryHardwareIDs:
244 TRACE("BusQueryHardwareIDs\n");
245 irp->IoStatus.Information = (ULONG_PTR)get_compatible_ids(device);
246 break;
247 case BusQueryCompatibleIDs:
248 TRACE("BusQueryCompatibleIDs\n");
249 irp->IoStatus.Information = (ULONG_PTR)get_compatible_ids(device);
250 break;
251 case BusQueryDeviceID:
252 TRACE("BusQueryDeviceID\n");
253 irp->IoStatus.Information = (ULONG_PTR)get_device_id(device);
254 break;
255 case BusQueryInstanceID:
256 TRACE("BusQueryInstanceID\n");
257 irp->IoStatus.Information = (ULONG_PTR)get_instance_id(device);
258 break;
259 default:
260 FIXME("Unhandled type %08x\n", type);
261 return status;
264 status = irp->IoStatus.Information ? STATUS_SUCCESS : STATUS_NO_MEMORY;
265 return status;
268 NTSTATUS WINAPI common_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp)
270 NTSTATUS status = irp->IoStatus.u.Status;
271 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
273 switch (irpsp->MinorFunction)
275 case IRP_MN_QUERY_DEVICE_RELATIONS:
276 TRACE("IRP_MN_QUERY_DEVICE_RELATIONS\n");
277 break;
278 case IRP_MN_QUERY_ID:
279 TRACE("IRP_MN_QUERY_ID\n");
280 status = handle_IRP_MN_QUERY_ID(device, irp);
281 irp->IoStatus.u.Status = status;
282 break;
283 case IRP_MN_QUERY_CAPABILITIES:
284 TRACE("IRP_MN_QUERY_CAPABILITIES\n");
285 break;
286 default:
287 FIXME("Unhandled function %08x\n", irpsp->MinorFunction);
288 break;
291 IoCompleteRequest(irp, IO_NO_INCREMENT);
292 return status;
295 NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
297 static const WCHAR udevW[] = {'\\','D','r','i','v','e','r','\\','U','D','E','V',0};
298 static UNICODE_STRING udev = {sizeof(udevW) - sizeof(WCHAR), sizeof(udevW), (WCHAR *)udevW};
300 TRACE( "(%p, %s)\n", driver, debugstr_w(path->Buffer) );
302 IoCreateDriver(&udev, udev_driver_init);
304 return STATUS_SUCCESS;