1 /*++
2
3 Copyright (c) Microsoft Corporation
4
5 Module Name:
6
7 FxIoTargetRemoteUm.cpp
8
9 Abstract:
10
11 Author:
12
13 Environment:
14
15 user mode only
16
17 Revision History:
18
19 --*/
20
21 #include "..\..\FxTargetsShared.hpp"
22
23 extern "C" {
24 #include "FxIoTargetRemoteUm.tmh"
25 }
26
27 #include <initguid.h>
28 #include "wdmguid.h"
29
30 NTSTATUS
InitRemoteModeSpecific(__in FxDeviceBase * Device)31 FxIoTargetRemote::InitRemoteModeSpecific(
32 __in FxDeviceBase* Device
33 )
34 {
35 NTSTATUS status;
36 HRESULT hr;
37 IWudfDeviceStack* devStack;
38
39 devStack = Device->GetDeviceObject()->GetDeviceStackInterface();
40
41 //
42 // Event initialization can fail in UM so initialize it now instead of in
43 // constructor.
44 //
45 status = m_OpenedEvent.Initialize();
46 if (!NT_SUCCESS(status)) {
47 DoTraceLevelMessage(
48 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
49 "Failed to initialize m_OpenedEvent, %!STATUS!", status);
50 return status;
51 }
52
53 //
54 // Create remote dispatcher.
55 // This calls directly into the Host to create.
56 // For most IoTargets the dispatcher is hidden from the Fx, but
57 // for the RemoteTarget, we need to directly dispatch I/O to
58 // a win32 handle, regardless of what dispatch method the device
59 // is set to use in it's INF.
60 //
61 hr = devStack->CreateRemoteDispatcher(&m_pIoDispatcher,
62 &m_pRemoteDispatcher);
63
64 if (FAILED(hr)) {
65 status = FxDevice::NtStatusFromHr(devStack, hr);
66 DoTraceLevelMessage(
67 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
68 "Failed to Create RemoteDispatcher, %!STATUS!", status);
69 return status;
70 }
71
72 return status;
73 }
74
75 VOID
RemoveModeSpecific(VOID)76 FxIoTargetRemote::RemoveModeSpecific(
77 VOID
78 )
79
80 {
81 //
82 // Delete callback object
83 //
84 if (m_NotificationCallback != NULL) {
85 delete m_NotificationCallback;
86 m_NotificationCallback = NULL;
87 }
88
89 SAFE_RELEASE(m_pIoDispatcher);
90 SAFE_RELEASE(m_pRemoteDispatcher);
91 }
92
93 NTSTATUS
OpenTargetHandle(_In_ PWDF_IO_TARGET_OPEN_PARAMS OpenParams,_Inout_ FxIoTargetRemoveOpenParams * pParams)94 FxIoTargetRemote::OpenTargetHandle(
95 _In_ PWDF_IO_TARGET_OPEN_PARAMS OpenParams,
96 _Inout_ FxIoTargetRemoveOpenParams* pParams
97 )
98 {
99 NTSTATUS status;
100 HRESULT hr = S_OK;
101 HANDLE hTarget;
102 ULONG flagsAndAttributes;
103
104 FX_VERIFY_WITH_NAME(INTERNAL,
105 VERIFY(INVALID_HANDLE_VALUE == m_pRemoteDispatcher->GetHandle()),
106 GetDriverGlobals()->Public.DriverName);
107
108 //
109 // UMDF 1.11 allowed following fields to be set by caller.
110 // DWORD dwDesiredAccess
111 // typedef struct _UMDF_IO_TARGET_OPEN_PARAMS
112 // {
113 // DWORD dwShareMode; //
114 // DWORD dwCreationDisposition;
115 // DWORD dwFlagsAndAttributes;
116 // } UMDF_IO_TARGET_OPEN_PARAMS;
117 //
118 //
119 // We always use overlapped I/O
120 //
121 flagsAndAttributes = pParams->FileAttributes | FILE_FLAG_OVERLAPPED;
122
123 hTarget = CreateFile(pParams->TargetDeviceName.Buffer,
124 pParams->DesiredAccess, // dwDesiredAccess
125 pParams->ShareAccess, // dwShareMode
126 NULL, // lpSecurityAttributes
127 pParams->CreateDisposition, // dwCreationDisposition
128 flagsAndAttributes, // dwFlagsAndAttributes
129 NULL);
130
131 if (INVALID_HANDLE_VALUE == hTarget) {
132 hr = HRESULT_FROM_WIN32(GetLastError());
133 status = m_Device->NtStatusFromHr(hr);
134
135 DoTraceLevelMessage(
136 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
137 "CreateFile for WDFIOTARGET %p returned status %!STATUS!",
138 GetObjectHandle(), status);
139
140 FX_VERIFY_WITH_NAME(INTERNAL, VERIFY(FAILED(hr)), GetDriverGlobals()->Public.DriverName);
141 }
142 else {
143 m_TargetHandle = hTarget;
144 status = STATUS_SUCCESS;
145 }
146
147 return status;
148 }
149
150 HANDLE
GetTargetHandle(VOID)151 FxIoTargetRemote::GetTargetHandle(
152 VOID
153 )
154 {
155 HRESULT hrQi;
156 IWudfFile2* pFile;
157 HANDLE handle = m_TargetHandle;
158
159 if (m_OpenParams.OpenType == WdfIoTargetOpenLocalTargetByFile) {
160 if (m_TargetFileObject == NULL) {
161 DoTraceLevelMessage(
162 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
163 "WDFIOTARGET %p has no target file object, could not get handle",
164 GetObjectHandle());
165 }
166 else {
167 ASSERT(m_TargetHandle == NULL);
168
169 hrQi = m_TargetFileObject->QueryInterface(IID_IWudfFile2, (PVOID*)&pFile);
170 FX_VERIFY(INTERNAL, CHECK_QI(hrQi, pFile));
171 pFile->Release();
172
173 handle = pFile->GetWeakRefHandle();
174 }
175 }
176
177 //
178 // Normalize the invalid handle value returned by CreateFile in host
179 // to what's expected by the WdfIoTargetWdmGetTargetFileHandle caller.
180 //
181 if (handle == INVALID_HANDLE_VALUE) {
182 handle = NULL;
183 }
184
185 return handle;
186 }
187
188 NTSTATUS
BindToHandle(VOID)189 FxIoTargetRemote::BindToHandle(
190 VOID
191 )
192 {
193 NTSTATUS status;
194 HRESULT hr;
195
196 //
197 // Tell the RemoteDispatcher to bind to the new handle.
198 //
199 hr = m_pRemoteDispatcher->BindToHandle(m_TargetHandle);
200 if (FAILED(hr)) {
201 status = m_Device->NtStatusFromHr(hr);
202 DoTraceLevelMessage(
203 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
204 "WDFIOTARGET %p could not bind remote dispatcher to new handle, "
205 "%!STATUS!", GetObjectHandle(), status);
206 return status;
207 }
208
209 status = STATUS_SUCCESS;
210 return status;
211 }
212
213 void
UnbindHandle(_In_ FxIoTargetClearedPointers * TargetPointers)214 FxIoTargetRemote::UnbindHandle(
215 _In_ FxIoTargetClearedPointers* TargetPointers
216 )
217 {
218 if (NULL != m_pRemoteDispatcher) {
219 //
220 // Close the handle we gave to the RemoteDispatcher
221 //
222 // NOTE: IWudfRemoteDispatcher::CloseHandle can be safely called even if
223 // we've not previously given it a handle. In this case, it does
224 // nothing.
225 //
226 DoTraceLevelMessage(
227 GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
228 "WDFIOTARGET %p Unbinding RemoteDispatcher %p to handle %p on close",
229 GetObjectHandle(), m_pRemoteDispatcher, TargetPointers->TargetHandle);
230
231 m_pRemoteDispatcher->CloseHandle();
232
233 //
234 // Host closes the handle in CloseHandle call above so set the handle
235 // in TargetPointers to NULL.
236 //
237 TargetPointers->TargetHandle = NULL;
238 }
239 }
240
241 NTSTATUS
GetTargetDeviceRelations(_Inout_ BOOLEAN * Close)242 FxIoTargetRemote::GetTargetDeviceRelations(
243 _Inout_ BOOLEAN* Close
244 )
245 {
246 UNREFERENCED_PARAMETER(Close);
247
248 //
249 // Not needed for UMDF
250 //
251 DO_NOTHING();
252
253 return STATUS_SUCCESS;
254 }
255
256 NTSTATUS
RegisterForPnpNotification(VOID)257 FxIoTargetRemote::RegisterForPnpNotification(
258 VOID
259 )
260 {
261 NTSTATUS status = STATUS_SUCCESS;
262 HRESULT hr;
263 FxIoTargetRemoteNotificationCallback* callback;
264
265 UNREFERENCED_PARAMETER(hr);
266
267 //
268 // Allocate callback object
269 //
270 if (m_NotificationCallback == NULL) {
271 callback = new (GetDriverGlobals())
272 FxIoTargetRemoteNotificationCallback(GetDriverGlobals(), this);
273
274 if (callback == NULL) {
275 status = STATUS_INSUFFICIENT_RESOURCES;
276 DoTraceLevelMessage(
277 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
278 "WDFIOTARGET %p could not allocate resources for "
279 "notification registration, %!STATUS!",
280 GetObjectHandle(), status);
281 return status;
282 }
283
284 m_NotificationCallback = callback;
285 }
286
287 //
288 // Register for Target Device Change notifications
289 // These notifications will arrive asynchronously.
290 //
291 IWudfDeviceStack * pDevStack = m_Device->GetDeviceStack();
292
293 hr = pDevStack->RegisterTargetDeviceNotification(
294 static_cast<IWudfTargetCallbackDeviceChange *> (m_NotificationCallback),
295 m_TargetHandle,
296 &m_TargetNotifyHandle);
297
298 if (FAILED(hr)) {
299 if (m_NotificationCallback != NULL) {
300 delete m_NotificationCallback;
301 m_NotificationCallback = NULL;
302 }
303
304 status = m_Device->NtStatusFromHr(hr);
305 DoTraceLevelMessage(
306 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
307 "WDFIOTARGET %p failed to register for Pnp notification, %!STATUS!",
308 GetObjectHandle(), status);
309 return status;
310 }
311
312 status = STATUS_SUCCESS;
313 DoTraceLevelMessage(
314 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
315 "WDFIOTARGET %p registered for Pnp notification, %!STATUS!",
316 GetObjectHandle(), status);
317
318 return status;
319 }
320
321 VOID
UnregisterForPnpNotification(_In_ MdTargetNotifyHandle NotifyHandle)322 FxIoTargetRemote::UnregisterForPnpNotification(
323 _In_ MdTargetNotifyHandle NotifyHandle
324 )
325 {
326 //
327 // check if we previously registered
328 //
329 if (NotifyHandle == WUDF_TARGET_CONTEXT_INVALID) {
330 return;
331 }
332
333 //
334 // Unregister.
335 //
336 IWudfDeviceStack * pDevStack = m_Device->GetDeviceStack();
337 pDevStack->UnregisterTargetDeviceNotification(NotifyHandle);
338
339 }
340
341 NTSTATUS
OpenLocalTargetByFile(_In_ PWDF_IO_TARGET_OPEN_PARAMS OpenParams)342 FxIoTargetRemote::OpenLocalTargetByFile(
343 _In_ PWDF_IO_TARGET_OPEN_PARAMS OpenParams
344 )
345 {
346 NTSTATUS status;
347
348 //
349 // OpenParams must be non NULL b/c we can't reopen a target with a
350 // previous device object.
351 //
352 ASSERT(OpenParams->Type == WdfIoTargetOpenLocalTargetByFile);
353 m_OpenParams.OpenType = OpenParams->Type;
354
355 //
356 // Create a file object. This is UM-specific feature, where host opens
357 // the reflector control device (optionally supplying the reference string
358 // provided by caller). If there are lower device drivers in the um stack,
359 // host sends them IRP_MJ_CREATE as well. The lower drivers in kernel see
360 // IRP_MJ_CREATE as well as a result of opening the reflector control
361 // object.
362 // Note that m_TargetDevice is already set to next lower device during init.
363 //
364 status = CreateWdfFileObject(&OpenParams->FileName,
365 &m_TargetFileObject);
366
367 if (!NT_SUCCESS(status)) {
368 DoTraceLevelMessage(
369 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
370 "Failed to create WDF File Object, %!STATUS!", status);
371 return status;
372 }
373
374 //
375 // Target handle is not used in this type of open.
376 //
377 m_TargetHandle = NULL;
378
379 //
380 // By taking a manual reference here, we simplify the code in
381 // FxIoTargetRemote::Close where we can assume there is an outstanding
382 // reference on the WDM file object at all times as long as we have a non
383 // NULL pointer.
384 //
385 if (m_TargetFileObject != NULL) {
386 Mx::MxReferenceObject(m_TargetFileObject);
387 }
388
389 return status;
390 }
391
392 NTSTATUS
CreateWdfFileObject(_In_opt_ PUNICODE_STRING FileName,_Out_ MdFileObject * FileObject)393 FxIoTargetRemote::CreateWdfFileObject(
394 _In_opt_ PUNICODE_STRING FileName,
395 _Out_ MdFileObject* FileObject
396 )
397 {
398 HRESULT hr = S_OK;
399 NTSTATUS status;
400 MdFileObject wdmFileObject = NULL;
401
402 FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO), CHECK_NOT_NULL(FileObject),
403 GetDriverGlobals()->Public.DriverName);
404
405 *FileObject = NULL;
406
407 hr = m_Device->GetDeviceStack()->CreateWdfFile(
408 m_Device->GetDeviceObject(),
409 m_Device->GetAttachedDevice(),
410 FileName->Buffer,
411 &wdmFileObject
412 );
413 if (SUCCEEDED(hr)) {
414 *FileObject = wdmFileObject;
415 status = STATUS_SUCCESS;
416 }
417 else {
418 status = m_Device->NtStatusFromHr(hr);
419 }
420
421 return status;
422 }
423
424 VOID
CloseWdfFileObject(_In_ MdFileObject FileObject)425 FxIoTargetRemote::CloseWdfFileObject(
426 _In_ MdFileObject FileObject
427 )
428 {
429 m_Device->GetDeviceStack()->CloseFile(FileObject);
430 SAFE_RELEASE(FileObject);
431 }
432
433 BOOL
434 __stdcall
OnQueryRemove(_In_ WUDF_TARGET_CONTEXT RegistrationID)435 FxIoTargetRemoteNotificationCallback::OnQueryRemove(
436 _In_ WUDF_TARGET_CONTEXT RegistrationID
437 )
438 {
439 PFX_DRIVER_GLOBALS pFxDriverGlobals;
440 FxIoTargetRemote* pThis;
441 NTSTATUS status;
442 BOOLEAN bStatus;
443
444 pThis = m_RemoteTarget;
445
446 //
447 // In one of these callbacks, the driver may decide to delete the target.
448 // If that is the case, we need to be able to return and deref the object until
449 // we are done.
450 //
451 pThis->ADDREF(m_RemoteTarget);
452
453 pFxDriverGlobals = pThis->GetDriverGlobals();
454
455 status = STATUS_SUCCESS;
456 bStatus = TRUE;
457
458 if (GetRegistrationId() != RegistrationID) {
459 //
460 // By design, we can get notification callbacks even after we have
461 // unregistered for notifications. This can happen if there were
462 // callbacks already in flight before we unregistered. In this case, we
463 // simply succeed on query-remove. Since we have already unregistered,
464 // there is no reason for us to fail query-remove.
465 //
466 DoTraceLevelMessage(
467 pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGIOTARGET,
468 "QueryRemove callback was for an old registration, ignoring.");
469
470 bStatus = TRUE;
471 goto exit;
472 }
473
474 DoTraceLevelMessage(
475 pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
476 "WDFIOTARGET %p: query remove notification",
477 m_RemoteTarget->GetObjectHandle());
478
479 //
480 // Device is gracefully being removed. PnP is asking us to close down
481 // the target. If there is a driver callback, there is *no* default
482 // behavior. This is because we don't know what the callback is going
483 // to do. For instance, the driver could reopen the target to a
484 // different device in a multi-path scenario.
485 //
486 if (pThis->m_EvtQueryRemove.m_Method != NULL) {
487 status = pThis->m_EvtQueryRemove.Invoke(
488 pThis->GetHandle());
489 }
490 else {
491 DoTraceLevelMessage(
492 pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
493 "WDFIOTARGET %p: query remove, default action (close for QR)",
494 pThis->GetObjectHandle());
495
496 //
497 // No callback, close it down conditionally.
498 //
499 pThis->Close(FxIoTargetRemoteCloseReasonQueryRemove);
500 }
501
502 if (!NT_SUCCESS(status)) {
503 bStatus = FALSE;
504 }
505
506 exit:
507
508 pThis->RELEASE(m_RemoteTarget);
509
510 return bStatus;
511 }
512
513 VOID
514 __stdcall
OnRemoveComplete(_In_ WUDF_TARGET_CONTEXT RegistrationID)515 FxIoTargetRemoteNotificationCallback::OnRemoveComplete(
516 _In_ WUDF_TARGET_CONTEXT RegistrationID
517 )
518 {
519 PFX_DRIVER_GLOBALS pFxDriverGlobals;
520 FxIoTargetRemote* pThis;
521
522 pThis = m_RemoteTarget;
523
524 //
525 // In one of these callbacks, the driver may decide to delete the target.
526 // If that is the case, we need to be able to return and deref the object until
527 // we are done.
528 //
529 pThis->ADDREF(m_RemoteTarget);
530
531 pFxDriverGlobals = pThis->GetDriverGlobals();
532
533 if (GetRegistrationId() != RegistrationID) {
534 DoTraceLevelMessage(
535 pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGIOTARGET,
536 "RemoveComplete callback was for an old registration, ignoring.");
537
538 goto exit;
539 }
540
541 DoTraceLevelMessage(
542 pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
543 "WDFIOTARGET %p: remove complete notification", pThis->GetObjectHandle());
544
545 //
546 // The device was surprise removed, close it for good if the driver has
547 // no override.
548 //
549 if (pThis->m_EvtRemoveComplete.m_Method != NULL) {
550 pThis->m_EvtRemoveComplete.Invoke(pThis->GetHandle());
551 }
552 else {
553 DoTraceLevelMessage(
554 pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
555 "WDFIOTARGET %p: remove complete, default action (close)",
556 pThis->GetObjectHandle());
557
558 //
559 // The device is now gone for good. Close down the target for good.
560 //
561 pThis->Close(FxIoTargetRemoteCloseReasonPlainClose);
562 }
563
564 exit:
565
566 pThis->RELEASE(m_RemoteTarget);
567 }
568
569 VOID
570 __stdcall
OnRemoveCanceled(_In_ WUDF_TARGET_CONTEXT RegistrationID)571 FxIoTargetRemoteNotificationCallback::OnRemoveCanceled(
572 _In_ WUDF_TARGET_CONTEXT RegistrationID
573 )
574 {
575 PFX_DRIVER_GLOBALS pFxDriverGlobals;
576 FxIoTargetRemote* pThis;
577 NTSTATUS status;
578
579 pThis = m_RemoteTarget;
580
581 //
582 // In one of these callbacks, the driver may decide to delete the target.
583 // If that is the case, we need to be able to return and deref the object until
584 // we are done.
585 //
586 pThis->ADDREF(m_RemoteTarget);
587
588 pFxDriverGlobals = pThis->GetDriverGlobals();
589 status = STATUS_SUCCESS;
590
591 if (GetRegistrationId() != RegistrationID) {
592 DoTraceLevelMessage(
593 pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGIOTARGET,
594 "RemoveCanceled callback was for an old registration, ignoring.");
595
596 goto exit;
597 }
598
599 DoTraceLevelMessage(
600 pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
601 "WDFIOTARGET %p: remove canceled notification", pThis->GetObjectHandle());
602
603 if (pThis->m_EvtRemoveCanceled.m_Method != NULL) {
604 pThis->m_EvtRemoveCanceled.Invoke(pThis->GetHandle());
605 }
606 else {
607 WDF_IO_TARGET_OPEN_PARAMS params;
608
609 DoTraceLevelMessage(
610 pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
611 "WDFIOTARGET %p: remove canceled, default action (reopen)",
612 pThis->GetObjectHandle());
613
614 WDF_IO_TARGET_OPEN_PARAMS_INIT_REOPEN(¶ms);
615
616 //
617 // Attempt to reopen the target with stored settings
618 //
619 status = pThis->Open(¶ms);
620
621
622
623
624
625
626
627 UNREFERENCED_PARAMETER(status);
628 }
629
630 exit:
631
632 pThis->RELEASE(m_RemoteTarget);
633 }
634
635 VOID
636 __stdcall
OnCustomEvent(_In_ WUDF_TARGET_CONTEXT RegistrationID,_In_ REFGUID Event,_In_reads_bytes_ (DataSize)BYTE * Data,_In_ DWORD DataSize,_In_ DWORD NameBufferOffset)637 FxIoTargetRemoteNotificationCallback::OnCustomEvent(
638 _In_ WUDF_TARGET_CONTEXT RegistrationID,
639 _In_ REFGUID Event,
640 _In_reads_bytes_(DataSize) BYTE * Data,
641 _In_ DWORD DataSize,
642 _In_ DWORD NameBufferOffset
643 )
644 {
645 UNREFERENCED_PARAMETER(RegistrationID);
646 UNREFERENCED_PARAMETER(Event);
647 UNREFERENCED_PARAMETER(Data);
648 UNREFERENCED_PARAMETER(DataSize);
649 UNREFERENCED_PARAMETER(NameBufferOffset);
650
651 //
652 // UMDF 2.0 doesn't yet support custom event. Ignore the event.
653 //
654 DO_NOTHING();
655
656 return;
657 }
658
659