hidclass.sys: Move IoSetDeviceInterfaceState to PNP_RemoveDevice.
[wine.git] / dlls / hidclass.sys / pnp.c
blob08aae159f22874e147db0772c7fb235edffa5169
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 "ddk/wdm.h"
27 #include "regstr.h"
28 #include "wine/debug.h"
29 #include "wine/unicode.h"
30 #include "wine/list.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(hid);
34 static const WCHAR device_enumeratorW[] = {'H','I','D',0};
35 static const WCHAR separator_W[] = {'\\',0};
36 static const WCHAR device_deviceid_fmtW[] = {'%','s','\\',
37 'v','i','d','_','%','0','4','x','&','p','i','d','_','%', '0','4','x',0};
39 static NTSTATUS WINAPI internalComplete(DEVICE_OBJECT *deviceObject, IRP *irp,
40 void *context)
42 HANDLE event = context;
43 SetEvent(event);
44 return STATUS_MORE_PROCESSING_REQUIRED;
47 static NTSTATUS get_device_id(DEVICE_OBJECT *device, BUS_QUERY_ID_TYPE type, WCHAR **id)
49 NTSTATUS status;
50 IO_STACK_LOCATION *irpsp;
51 IO_STATUS_BLOCK irp_status;
52 HANDLE event;
53 IRP *irp;
55 irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, device, NULL, 0, NULL, NULL, &irp_status);
56 if (irp == NULL)
57 return STATUS_NO_MEMORY;
59 event = CreateEventA(NULL, FALSE, FALSE, NULL);
60 irpsp = IoGetNextIrpStackLocation(irp);
61 irpsp->MinorFunction = IRP_MN_QUERY_ID;
62 irpsp->Parameters.QueryId.IdType = type;
64 IoSetCompletionRoutine(irp, internalComplete, event, TRUE, TRUE, TRUE);
65 status = IoCallDriver(device, irp);
66 if (status == STATUS_PENDING)
67 WaitForSingleObject(event, INFINITE);
69 *id = (WCHAR*)irp->IoStatus.Information;
70 status = irp->IoStatus.u.Status;
71 IoCompleteRequest(irp, IO_NO_INCREMENT );
72 CloseHandle(event);
74 return status;
77 NTSTATUS WINAPI PNP_AddDevice(DRIVER_OBJECT *driver, DEVICE_OBJECT *PDO)
79 hid_device *hiddev;
80 DEVICE_OBJECT *device = NULL;
81 NTSTATUS status;
82 minidriver *minidriver;
83 HID_DEVICE_ATTRIBUTES attr;
84 BASE_DEVICE_EXTENSION *ext = NULL;
85 HID_DESCRIPTOR descriptor;
86 BYTE *reportDescriptor;
87 INT i;
88 WCHAR *PDO_id;
89 WCHAR *id_ptr;
91 status = get_device_id(PDO, BusQueryInstanceID, &PDO_id);
92 if (status != STATUS_SUCCESS)
94 ERR("Failed to get PDO id(%x)\n",status);
95 return status;
98 TRACE("PDO add device(%p:%s)\n", PDO, debugstr_w(PDO_id));
99 minidriver = find_minidriver(driver);
101 hiddev = HeapAlloc(GetProcessHeap(), 0, sizeof(*hiddev));
102 if (!hiddev)
103 return STATUS_NO_MEMORY;
105 status = HID_CreateDevice(PDO, &minidriver->minidriver, &hiddev->device);
106 if (status != STATUS_SUCCESS)
108 ERR("Failed to create HID object (%x)\n",status);
109 HeapFree(GetProcessHeap(), 0, PDO_id);
110 HeapFree(GetProcessHeap(), 0, hiddev);
111 return status;
113 device = hiddev->device;
115 ext = device->DeviceExtension;
116 InitializeListHead(&ext->irp_queue);
118 TRACE("Created device %p\n",device);
119 status = minidriver->AddDevice(minidriver->minidriver.DriverObject, device);
120 if (status != STATUS_SUCCESS)
122 ERR("Minidriver AddDevice failed (%x)\n",status);
123 HeapFree(GetProcessHeap(), 0, PDO_id);
124 HID_DeleteDevice(&minidriver->minidriver, device);
125 return status;
128 status = call_minidriver(IOCTL_HID_GET_DEVICE_ATTRIBUTES, device,
129 NULL, 0, &attr, sizeof(attr));
131 if (status != STATUS_SUCCESS)
133 ERR("Minidriver failed to get Attributes(%x)\n",status);
134 HID_DeleteDevice(&minidriver->minidriver, device);
135 HeapFree(GetProcessHeap(), 0, PDO_id);
136 return status;
139 ext->information.VendorID = attr.VendorID;
140 ext->information.ProductID = attr.ProductID;
141 ext->information.VersionNumber = attr.VersionNumber;
142 ext->information.Polled = minidriver->minidriver.DevicesArePolled;
144 status = call_minidriver(IOCTL_HID_GET_DEVICE_DESCRIPTOR, device, NULL, 0,
145 &descriptor, sizeof(descriptor));
146 if (status != STATUS_SUCCESS)
148 ERR("Cannot get Device Descriptor(%x)\n",status);
149 HID_DeleteDevice(&minidriver->minidriver, device);
150 HeapFree(GetProcessHeap(), 0, PDO_id);
151 return status;
153 for (i = 0; i < descriptor.bNumDescriptors; i++)
154 if (descriptor.DescriptorList[i].bReportType == HID_REPORT_DESCRIPTOR_TYPE)
155 break;
157 if (i >= descriptor.bNumDescriptors)
159 ERR("No Report Descriptor found in reply\n");
160 HID_DeleteDevice(&minidriver->minidriver, device);
161 HeapFree(GetProcessHeap(), 0, PDO_id);
162 return status;
165 reportDescriptor = HeapAlloc(GetProcessHeap(), 0, descriptor.DescriptorList[i].wReportLength);
166 status = call_minidriver(IOCTL_HID_GET_REPORT_DESCRIPTOR, device, NULL, 0,
167 reportDescriptor, descriptor.DescriptorList[i].wReportLength);
168 if (status != STATUS_SUCCESS)
170 ERR("Cannot get Report Descriptor(%x)\n",status);
171 HID_DeleteDevice(&minidriver->minidriver, device);
172 HeapFree(GetProcessHeap(), 0, reportDescriptor);
173 HeapFree(GetProcessHeap(), 0, PDO_id);
174 return status;
177 ext->preparseData = ParseDescriptor(reportDescriptor, descriptor.DescriptorList[0].wReportLength);
179 HeapFree(GetProcessHeap(), 0, reportDescriptor);
180 if (!ext->preparseData)
182 ERR("Cannot parse Report Descriptor\n");
183 HID_DeleteDevice(&minidriver->minidriver, device);
184 HeapFree(GetProcessHeap(), 0, PDO_id);
185 return STATUS_NOT_SUPPORTED;
188 list_add_tail(&(minidriver->device_list), &hiddev->entry);
190 ext->information.DescriptorSize = ext->preparseData->dwSize;
192 lstrcpyW(ext->instance_id, device_enumeratorW);
193 strcatW(ext->instance_id, separator_W);
194 /* Skip the original enumerator */
195 id_ptr = strchrW(PDO_id, '\\');
196 id_ptr++;
197 strcatW(ext->instance_id, id_ptr);
198 HeapFree(GetProcessHeap(), 0, PDO_id);
200 sprintfW(ext->device_id, device_deviceid_fmtW, device_enumeratorW, ext->information.VendorID, ext->information.ProductID);
202 HID_LinkDevice(device);
204 ext->poll_interval = DEFAULT_POLL_INTERVAL;
206 ext->ring_buffer = RingBuffer_Create(sizeof(HID_XFER_PACKET) + ext->preparseData->caps.InputReportByteLength);
208 HID_StartDeviceThread(device);
210 return STATUS_SUCCESS;
213 NTSTATUS PNP_RemoveDevice(minidriver *minidriver, DEVICE_OBJECT *device, IRP *irp)
215 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
216 hid_device *hiddev;
217 NTSTATUS rc = STATUS_NOT_SUPPORTED;
219 rc = IoSetDeviceInterfaceState(&ext->link_name, FALSE);
220 if (rc)
222 FIXME("failed to disable interface %x\n", rc);
223 return rc;
226 if (irp)
227 rc = minidriver->PNPDispatch(device, irp);
228 HID_DeleteDevice(&minidriver->minidriver, device);
229 LIST_FOR_EACH_ENTRY(hiddev, &minidriver->device_list, hid_device, entry)
231 if (hiddev->device == device)
233 list_remove(&hiddev->entry);
234 HeapFree(GetProcessHeap(), 0, hiddev);
235 break;
238 return rc;
241 NTSTATUS WINAPI HID_PNP_Dispatch(DEVICE_OBJECT *device, IRP *irp)
243 NTSTATUS rc = STATUS_NOT_SUPPORTED;
244 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
245 minidriver *minidriver = find_minidriver(device->DriverObject);
247 TRACE("%p, %p\n", device, irp);
249 switch(irpsp->MinorFunction)
251 case IRP_MN_QUERY_ID:
253 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
254 WCHAR *id = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR)*REGSTR_VAL_MAX_HCID_LEN);
255 TRACE("IRP_MN_QUERY_ID[%i]\n", irpsp->Parameters.QueryId.IdType);
256 switch (irpsp->Parameters.QueryId.IdType)
258 case BusQueryHardwareIDs:
259 case BusQueryCompatibleIDs:
261 WCHAR *ptr;
262 ptr = id;
263 /* Instance ID */
264 strcpyW(ptr, ext->instance_id);
265 ptr += lstrlenW(ext->instance_id) + 1;
266 /* Device ID */
267 strcpyW(ptr, ext->device_id);
268 ptr += lstrlenW(ext->device_id) + 1;
269 /* Bus ID */
270 strcpyW(ptr, device_enumeratorW);
271 ptr += lstrlenW(device_enumeratorW) + 1;
272 *ptr = 0;
273 irp->IoStatus.Information = (ULONG_PTR)id;
274 rc = STATUS_SUCCESS;
275 break;
277 case BusQueryDeviceID:
278 strcpyW(id, ext->device_id);
279 irp->IoStatus.Information = (ULONG_PTR)id;
280 rc = STATUS_SUCCESS;
281 break;
282 case BusQueryInstanceID:
283 strcpyW(id, ext->instance_id);
284 irp->IoStatus.Information = (ULONG_PTR)id;
285 rc = STATUS_SUCCESS;
286 break;
287 case BusQueryDeviceSerialNumber:
288 FIXME("BusQueryDeviceSerialNumber not implemented\n");
289 HeapFree(GetProcessHeap(), 0, id);
290 break;
292 break;
294 case IRP_MN_START_DEVICE:
296 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
298 rc = minidriver->PNPDispatch(device, irp);
300 IoSetDeviceInterfaceState(&ext->link_name, TRUE);
301 return rc;
303 case IRP_MN_REMOVE_DEVICE:
305 return PNP_RemoveDevice(minidriver, device, irp);
307 default:
309 /* Forward IRP to the minidriver */
310 return minidriver->PNPDispatch(device, irp);
314 irp->IoStatus.u.Status = rc;
315 IoCompleteRequest( irp, IO_NO_INCREMENT );
316 return rc;