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
23 #define NONAMELESSUNION
26 #define WIN32_NO_STATUS
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36 #include "wine/list.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(plugplay
);
45 DEVICE_OBJECT
*device
;
48 struct device_extension
50 void *native
; /* Must be the first member of the structure */
53 DWORD uid
, version
, index
;
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
)
77 if (!src
) return NULL
;
78 dst
= HeapAlloc(GetProcessHeap(), 0, (strlenW(src
) + 1)*sizeof(WCHAR
));
79 if (dst
) strcpyW(dst
, src
);
83 static DWORD
get_vidpid_index(WORD vid
, WORD pid
)
85 struct pnp_device
*ptr
;
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
);
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;
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
);
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;
121 if ((dst
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
))))
122 sprintfW(dst
, formatW
, ext
->busid
, ext
->vid
, ext
->pid
);
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
;
133 if (!(iid
= get_instance_id(device
)))
136 if (!(did
= get_device_id(device
)))
138 HeapFree(GetProcessHeap(), 0, iid
);
142 len
= strlenW(iid
) + strlenW(did
) + strlenW(ext
->busid
) + 4;
143 if ((dst
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
))))
147 ptr
+= strlenW(iid
) + 1;
149 ptr
+= strlenW(did
) + 1;
150 strcpyW(ptr
, ext
->busid
);
151 ptr
+= strlenW(ext
->busid
) + 1;
155 HeapFree(GetProcessHeap(), 0, iid
);
156 HeapFree(GetProcessHeap(), 0, did
);
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
,
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
;
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
))))
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
);
184 FIXME("failed to create device error %x\n", status
);
185 HeapFree(GetProcessHeap(), 0, pnp_dev
);
189 EnterCriticalSection(&device_list_cs
);
191 /* fill out device_extension struct */
192 ext
= (struct device_extension
*)device
->DeviceExtension
;
193 ext
->native
= native
;
197 ext
->version
= version
;
198 ext
->index
= get_vidpid_index(vid
, pid
);
199 ext
->is_gamepad
= is_gamepad
;
200 ext
->serial
= strdupW(serialW
);
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
);
212 SP_DEVINFO_DATA data
;
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
);
227 ERR("failed to get ClassDevs: %x\n", GetLastError());
229 IoInvalidateDeviceRelations(device
, BusRelations
);
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
);
243 case BusQueryHardwareIDs
:
244 TRACE("BusQueryHardwareIDs\n");
245 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
247 case BusQueryCompatibleIDs
:
248 TRACE("BusQueryCompatibleIDs\n");
249 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
251 case BusQueryDeviceID
:
252 TRACE("BusQueryDeviceID\n");
253 irp
->IoStatus
.Information
= (ULONG_PTR
)get_device_id(device
);
255 case BusQueryInstanceID
:
256 TRACE("BusQueryInstanceID\n");
257 irp
->IoStatus
.Information
= (ULONG_PTR
)get_instance_id(device
);
260 FIXME("Unhandled type %08x\n", type
);
264 status
= irp
->IoStatus
.Information
? STATUS_SUCCESS
: STATUS_NO_MEMORY
;
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");
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
;
283 case IRP_MN_QUERY_CAPABILITIES
:
284 TRACE("IRP_MN_QUERY_CAPABILITIES\n");
287 FIXME("Unhandled function %08x\n", irpsp
->MinorFunction
);
291 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
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
;