hidclass: Properly NULL terminate device_deviceid_fmtW.
[wine.git] / dlls / hidclass.sys / pnp.c
blob30d1045bccb8a1f940e1b56b52e9134be50099ca
1 /*
2 * WINE HID Pseudo-Plug and Play support
4 * Copyright 2015 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 #define NONAMELESSUNION
22 #include <unistd.h>
23 #include <stdarg.h>
24 #include "hid.h"
25 #include "ddk/hidtypes.h"
26 #include "regstr.h"
27 #include "wine/debug.h"
28 #include "wine/unicode.h"
29 #include "wine/list.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(hid);
33 static const WCHAR device_enumeratorW[] = {'H','I','D',0};
34 static const WCHAR separator_W[] = {'\\',0};
35 static const WCHAR device_deviceid_fmtW[] = {'%','s','\\',
36 'v','i','d','_','%','0','4','x','&','p','i','d','_','%', '0','4','x',0};
38 static NTSTATUS WINAPI internalComplete(DEVICE_OBJECT *deviceObject, IRP *irp,
39 void *context)
41 HANDLE event = context;
42 SetEvent(event);
43 return STATUS_MORE_PROCESSING_REQUIRED;
46 static NTSTATUS get_device_id(DEVICE_OBJECT *device, BUS_QUERY_ID_TYPE type, WCHAR **id)
48 NTSTATUS status;
49 IO_STACK_LOCATION *irpsp;
50 IO_STATUS_BLOCK irp_status;
51 HANDLE event;
52 IRP *irp;
54 irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, device, NULL, 0, NULL, NULL, &irp_status);
55 if (irp == NULL)
56 return STATUS_NO_MEMORY;
58 event = CreateEventA(NULL, FALSE, FALSE, NULL);
59 irpsp = IoGetNextIrpStackLocation(irp);
60 irpsp->MinorFunction = IRP_MN_QUERY_ID;
61 irpsp->Parameters.QueryId.IdType = type;
63 IoSetCompletionRoutine(irp, internalComplete, event, TRUE, TRUE, TRUE);
64 status = IoCallDriver(device, irp);
65 if (status == STATUS_PENDING)
66 WaitForSingleObject(event, INFINITE);
68 *id = (WCHAR*)irp->IoStatus.Information;
69 status = irp->IoStatus.u.Status;
70 IoCompleteRequest(irp, IO_NO_INCREMENT );
71 CloseHandle(event);
73 return status;
76 NTSTATUS WINAPI PNP_AddDevice(DRIVER_OBJECT *driver, DEVICE_OBJECT *PDO)
78 DEVICE_OBJECT *device = NULL;
79 NTSTATUS status;
80 minidriver *minidriver;
81 HID_DEVICE_ATTRIBUTES attr;
82 BASE_DEVICE_EXTENSION *ext = NULL;
83 HID_DESCRIPTOR descriptor;
84 BYTE *reportDescriptor;
85 INT i;
86 WCHAR *PDO_id;
87 WCHAR *id_ptr;
89 status = get_device_id(PDO, BusQueryInstanceID, &PDO_id);
90 if (status != STATUS_SUCCESS)
92 ERR("Failed to get PDO id(%x)\n",status);
93 return status;
96 TRACE("PDO add device(%p:%s)\n", PDO, debugstr_w(PDO_id));
97 minidriver = find_minidriver(driver);
99 status = HID_CreateDevice(PDO, &minidriver->minidriver, &device);
100 if (status != STATUS_SUCCESS)
102 ERR("Failed to create HID object (%x)\n",status);
103 HeapFree(GetProcessHeap(), 0, PDO_id);
104 return status;
107 ext = device->DeviceExtension;
108 InitializeListHead(&ext->irp_queue);
110 TRACE("Created device %p\n",device);
111 status = minidriver->AddDevice(minidriver->minidriver.DriverObject, device);
112 if (status != STATUS_SUCCESS)
114 ERR("Minidriver AddDevice failed (%x)\n",status);
115 HeapFree(GetProcessHeap(), 0, PDO_id);
116 HID_DeleteDevice(&minidriver->minidriver, device);
117 return status;
120 status = call_minidriver(IOCTL_HID_GET_DEVICE_ATTRIBUTES, device,
121 NULL, 0, &attr, sizeof(attr));
123 if (status != STATUS_SUCCESS)
125 ERR("Minidriver failed to get Attributes(%x)\n",status);
126 HID_DeleteDevice(&minidriver->minidriver, device);
127 HeapFree(GetProcessHeap(), 0, PDO_id);
128 return status;
131 ext->information.VendorID = attr.VendorID;
132 ext->information.ProductID = attr.ProductID;
133 ext->information.VersionNumber = attr.VersionNumber;
134 ext->information.Polled = minidriver->minidriver.DevicesArePolled;
136 status = call_minidriver(IOCTL_HID_GET_DEVICE_DESCRIPTOR, device, NULL, 0,
137 &descriptor, sizeof(descriptor));
138 if (status != STATUS_SUCCESS)
140 ERR("Cannot get Device Descriptor(%x)\n",status);
141 HID_DeleteDevice(&minidriver->minidriver, device);
142 HeapFree(GetProcessHeap(), 0, PDO_id);
143 return status;
145 for (i = 0; i < descriptor.bNumDescriptors; i++)
146 if (descriptor.DescriptorList[i].bReportType == HID_REPORT_DESCRIPTOR_TYPE)
147 break;
149 if (i >= descriptor.bNumDescriptors)
151 ERR("No Report Descriptor found in reply\n");
152 HID_DeleteDevice(&minidriver->minidriver, device);
153 HeapFree(GetProcessHeap(), 0, PDO_id);
154 return status;
157 reportDescriptor = HeapAlloc(GetProcessHeap(), 0, descriptor.DescriptorList[i].wReportLength);
158 status = call_minidriver(IOCTL_HID_GET_REPORT_DESCRIPTOR, device, NULL, 0,
159 reportDescriptor, descriptor.DescriptorList[i].wReportLength);
160 if (status != STATUS_SUCCESS)
162 ERR("Cannot get Report Descriptor(%x)\n",status);
163 HID_DeleteDevice(&minidriver->minidriver, device);
164 HeapFree(GetProcessHeap(), 0, reportDescriptor);
165 HeapFree(GetProcessHeap(), 0, PDO_id);
166 return status;
169 ext->preparseData = ParseDescriptor(reportDescriptor, descriptor.DescriptorList[0].wReportLength);
171 HeapFree(GetProcessHeap(), 0, reportDescriptor);
172 if (!ext->preparseData)
174 ERR("Cannot parse Report Descriptor\n");
175 HID_DeleteDevice(&minidriver->minidriver, device);
176 HeapFree(GetProcessHeap(), 0, PDO_id);
177 return STATUS_NOT_SUPPORTED;
180 ext->information.DescriptorSize = ext->preparseData->dwSize;
182 lstrcpyW(ext->instance_id, device_enumeratorW);
183 strcatW(ext->instance_id, separator_W);
184 /* Skip the original enumerator */
185 id_ptr = strchrW(PDO_id, '\\');
186 id_ptr++;
187 strcatW(ext->instance_id, id_ptr);
188 HeapFree(GetProcessHeap(), 0, PDO_id);
190 sprintfW(ext->device_id, device_deviceid_fmtW, device_enumeratorW, ext->information.VendorID, ext->information.ProductID);
192 HID_LinkDevice(device);
194 ext->poll_interval = DEFAULT_POLL_INTERVAL;
196 ext->ring_buffer = RingBuffer_Create(sizeof(HID_XFER_PACKET) + ext->preparseData->caps.InputReportByteLength);
198 HID_StartDeviceThread(device);
200 return STATUS_SUCCESS;
203 NTSTATUS WINAPI HID_PNP_Dispatch(DEVICE_OBJECT *device, IRP *irp)
205 NTSTATUS rc = STATUS_NOT_SUPPORTED;
206 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
207 minidriver *minidriver = find_minidriver(device->DriverObject);
209 TRACE("%p, %p\n", device, irp);
211 switch(irpsp->MinorFunction)
213 case IRP_MN_QUERY_ID:
215 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
216 WCHAR *id = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR)*REGSTR_VAL_MAX_HCID_LEN);
217 TRACE("IRP_MN_QUERY_ID[%i]\n", irpsp->Parameters.QueryId.IdType);
218 switch (irpsp->Parameters.QueryId.IdType)
220 case BusQueryHardwareIDs:
221 case BusQueryCompatibleIDs:
223 WCHAR *ptr;
224 ptr = id;
225 /* Instance ID */
226 strcpyW(ptr, ext->instance_id);
227 ptr += lstrlenW(ext->instance_id) + 1;
228 /* Device ID */
229 strcpyW(ptr, ext->device_id);
230 ptr += lstrlenW(ext->device_id) + 1;
231 /* Bus ID */
232 strcpyW(ptr, device_enumeratorW);
233 ptr += lstrlenW(device_enumeratorW) + 1;
234 *ptr = 0;
235 irp->IoStatus.Information = (ULONG_PTR)id;
236 rc = STATUS_SUCCESS;
237 break;
239 case BusQueryDeviceID:
240 strcpyW(id, ext->device_id);
241 irp->IoStatus.Information = (ULONG_PTR)id;
242 rc = STATUS_SUCCESS;
243 break;
244 case BusQueryInstanceID:
245 strcpyW(id, ext->instance_id);
246 irp->IoStatus.Information = (ULONG_PTR)id;
247 rc = STATUS_SUCCESS;
248 break;
249 case BusQueryDeviceSerialNumber:
250 FIXME("BusQueryDeviceSerialNumber not implemented\n");
251 HeapFree(GetProcessHeap(), 0, id);
252 break;
254 break;
256 case IRP_MN_REMOVE_DEVICE:
258 rc = minidriver->PNPDispatch(device, irp);
259 HID_DeleteDevice(&minidriver->minidriver, device);
260 return rc;
262 default:
264 /* Forward IRP to the minidriver */
265 return minidriver->PNPDispatch(device, irp);
269 irp->IoStatus.u.Status = rc;
270 IoCompleteRequest( irp, IO_NO_INCREMENT );
271 return rc;