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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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