Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

753 lines
21 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 2000
  3. Module Name:
  4. enum.c
  5. Abstract:
  6. This file contains device enumeration routines
  7. Environment:
  8. kernel mode only
  9. Revision History:
  10. --*/
  11. #include "port.h"
  12. VOID
  13. iSpEnumerateDevicesAsynchronous(
  14. IN PDEVICE_OBJECT DeviceObject,
  15. IN PVOID Context
  16. )
  17. {
  18. PISCSI_FDO_EXTENSION fdoExtension;
  19. PCOMMON_EXTENSION commonExtension;
  20. PISCSI_CONNECTION iScsiConnection;
  21. PIRP Irp;
  22. LARGE_INTEGER Timeout;
  23. NTSTATUS status = STATUS_SUCCESS;
  24. UCHAR oldIrql;
  25. fdoExtension = (PISCSI_FDO_EXTENSION) Context;
  26. commonExtension = DeviceObject->DeviceExtension;
  27. IoFreeWorkItem(fdoExtension->EnumerationWorkItem);
  28. fdoExtension->EnumerationWorkItem = NULL;
  29. //
  30. // Local network nodes should be setup at this point.
  31. // If not, fail the enumeration irp
  32. //
  33. if ((fdoExtension->LocalNodesInitialized) == TRUE) {
  34. PDEVICE_OBJECT pdo;
  35. PISCSI_PDO_EXTENSION pdoExtension;
  36. ULONG inx;
  37. DebugPrint((3, "Number of targets : %d\n",
  38. (fdoExtension->NumberOfTargets)));
  39. fdoExtension->TargetsYetToRespond = fdoExtension->NumberOfTargets;
  40. for (inx = 0; inx < (fdoExtension->NumberOfTargets); inx++) {
  41. pdo = fdoExtension->PDOList[inx];
  42. pdoExtension = (PISCSI_PDO_EXTENSION)(pdo->DeviceExtension);
  43. iScsiConnection = pdoExtension->ClientNodeInfo;
  44. DebugPrint((3, "Will connect to the server\n"));
  45. pdoExtension->LogonTickCount = 0;
  46. //
  47. // Connection timeout is 60 seconds. Is this enough???
  48. //
  49. Timeout.QuadPart = -600000000;
  50. status = iSpTdiConnect(iScsiConnection->ConnectionDeviceObject,
  51. iScsiConnection->ConnectionFileObject,
  52. pdoExtension->TargetIPAddress,
  53. htons(pdoExtension->TargetPortNumber),
  54. Timeout);
  55. if (NT_SUCCESS(status)) {
  56. DebugPrint((3, "Connected to the server\n"));
  57. iScsiConnection->ConnectionState = ConnectionStateConnected;
  58. pdoExtension->CurrentProtocolState = PSLogonInProgress;
  59. DebugPrint((3, "Will send logon packet\n"));
  60. status = iSpSendLoginCommand(pdoExtension);
  61. if (NT_SUCCESS(status)) {
  62. pdoExtension->LogonTickCount = 0;
  63. DebugPrint((3, "Login command sent successfully\n"));
  64. } else {
  65. LARGE_INTEGER disconnectTimeout;
  66. DebugPrint((1,
  67. "Send failed for logon. Status : %x\n",
  68. status));
  69. InterlockedDecrement(&(fdoExtension->TargetsYetToRespond));
  70. pdoExtension->CurrentProtocolState = PSLogonFailed;
  71. disconnectTimeout.QuadPart = -100000000L;
  72. iScsiConnection->ConnectionState = ConnectionStateStopping;
  73. status = iSpTdiDisconnect(iScsiConnection->ConnectionDeviceObject,
  74. iScsiConnection->ConnectionFileObject,
  75. TDI_DISCONNECT_RELEASE,
  76. iSpTdiCompletionRoutine,
  77. iScsiConnection,
  78. disconnectTimeout);
  79. DebugPrint((3, "iSpTdiDisconnect returned : %x\n",
  80. status));
  81. }
  82. } else {
  83. DebugPrint((1, "Could not connect to server. Status : %x\n",
  84. status));
  85. pdoExtension->CurrentProtocolState = PSConnectToServerFailed;
  86. InterlockedDecrement(&(fdoExtension->TargetsYetToRespond));
  87. }
  88. }
  89. KeAcquireSpinLock(&(fdoExtension->EnumerationSpinLock),
  90. &oldIrql);
  91. //
  92. // Launch the enum completion thread if all targets have
  93. // responded or none could be contacted.
  94. //
  95. if (((fdoExtension->TargetsYetToRespond) == 0) &&
  96. ((fdoExtension->EnumerationThreadLaunched) == FALSE)) {
  97. DebugPrint((0,
  98. "All or no targets responded. Will complete QDR\n"));
  99. iSpLaunchEnumerationCompletion(fdoExtension);
  100. }
  101. KeReleaseSpinLock(&(fdoExtension->EnumerationSpinLock),
  102. oldIrql);
  103. } else {
  104. DebugPrint((1, "iSpEnumerateDevices : Client node not setup yet\n"));
  105. status = STATUS_UNSUCCESSFUL;
  106. Irp = fdoExtension->EnumerationIrp;
  107. fdoExtension->EnumerationIrp = NULL;
  108. Irp->IoStatus.Status = status;
  109. Irp->IoStatus.Information = 0L;
  110. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  111. }
  112. return;
  113. }
  114. /*
  115. NTSTATUS
  116. iSpPerformDeviceEnumeration(
  117. IN PDEVICE_OBJECT DeviceObject,
  118. PIRP Irp
  119. )
  120. {
  121. PISCSI_FDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
  122. PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension;
  123. PISCSI_CONNECTION iScsiConnection = fdoExtension->ClientNodeInfo;
  124. PISCSI_LOGIN_COMMAND iscsiLoginCommand;
  125. LARGE_INTEGER disconnectTimeout;
  126. NTSTATUS status;
  127. USHORT connectionID;
  128. ASSERT((iScsiConnection != NULL));
  129. ASSERT((iScsiConnection->Type) == ISCSI_CONNECTION_TYPE);
  130. ASSERT((iScsiConnection->ConnectionState) == ConnectionStateConnected);
  131. if ((fdoExtension->CurrentProtocolState) != PSConnectedToServer) {
  132. DebugPrint((1, "Probably already logged on. CurrentState : %d\n",
  133. (fdoExtension->CurrentProtocolState)));
  134. return STATUS_UNSUCCESSFUL;
  135. }
  136. //
  137. // First send logon packet
  138. //
  139. fdoExtension->CurrentIrp = Irp;
  140. fdoExtension->CurrentProtocolState = PSLogonInProgress;
  141. status = iSpSendLoginCommand(fdoExtension);
  142. if (NT_SUCCESS(status)) {
  143. DebugPrint((3, "Login command sent successfully\n"));
  144. } else {
  145. DebugPrint((1, "Send failed for logon. Status : %x\n", status));
  146. fdoExtension->CurrentIrp = NULL;
  147. fdoExtension->CurrentProtocolState = PSLogonFailed;
  148. disconnectTimeout.QuadPart = -100000000L;
  149. iScsiConnection->ConnectionState = ConnectionStateStopping;
  150. status = iSpTdiDisconnect(iScsiConnection->ConnectionDeviceObject,
  151. iScsiConnection->ConnectionFileObject,
  152. TDI_DISCONNECT_RELEASE,
  153. iSpTdiCompletionRoutine,
  154. iScsiConnection,
  155. disconnectTimeout);
  156. DebugPrint((3, "iSpTdiDisconnect returned : %x\n",
  157. status));
  158. return STATUS_UNSUCCESSFUL;
  159. }
  160. //
  161. // QDR Irp will be completed upon receipt of login response
  162. //
  163. // ISSUE : nramas : 12/24/2000
  164. // Should have a timer here to take care of the case where
  165. // the server fails to send logon response.
  166. //
  167. return STATUS_SUCCESS;
  168. }
  169. */
  170. NTSTATUS
  171. iSpQueryDeviceRelationsCompletion(
  172. IN PDEVICE_OBJECT DeviceObject,
  173. IN PVOID Context
  174. )
  175. {
  176. PISCSI_FDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
  177. PISCSI_PDO_EXTENSION pdoExtension;
  178. PCOMMON_EXTENSION commonExtension;
  179. PISCSI_CONNECTION iScsiConnection;
  180. PISCSI_LOGIN_RESPONSE loginResponse;
  181. PIRP Irp;
  182. PDEVICE_OBJECT pdo;
  183. PDEVICE_RELATIONS deviceRelations;
  184. PACTIVE_REQUESTS activeClientRequests = NULL;
  185. ULONG relationSize;
  186. ULONG maxCmdRN = 0;
  187. ULONG inx;
  188. ULONG targetIndex;
  189. NTSTATUS status = STATUS_SUCCESS;
  190. IoFreeWorkItem((PIO_WORKITEM) Context);
  191. Irp = fdoExtension->EnumerationIrp;
  192. fdoExtension->EnumerationIrp = NULL;
  193. relationSize = sizeof(DEVICE_RELATIONS) +
  194. ((fdoExtension->NumberOfTargets) * sizeof(PDEVICE_OBJECT));
  195. deviceRelations = iSpAllocatePool(PagedPool,
  196. relationSize,
  197. ISCSI_TAG_DEVICE_RELATIONS);
  198. if (deviceRelations == NULL) {
  199. DebugPrint((1, "Failed to allocate memory for device relations\n"));
  200. for (inx = 0; inx < (fdoExtension->NumberOfTargets); inx++) {
  201. pdo = fdoExtension->PDOList[inx];
  202. iSpStopNetwork(pdo);
  203. IoDeleteDevice(pdo);
  204. fdoExtension->PDOList[inx] = NULL;
  205. }
  206. fdoExtension->NumberOfTargets = 0;
  207. fdoExtension->LocalNodesInitialized = FALSE;
  208. Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
  209. Irp->IoStatus.Information = 0L;
  210. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  211. return STATUS_INSUFFICIENT_RESOURCES;
  212. }
  213. deviceRelations->Count = 0;
  214. targetIndex = 0;
  215. for (inx = 0; inx < (fdoExtension->NumberOfTargets); inx++) {
  216. pdo = fdoExtension->PDOList[inx];
  217. pdoExtension = (PISCSI_PDO_EXTENSION)(pdo->DeviceExtension);
  218. iScsiConnection = pdoExtension->ClientNodeInfo;
  219. if ((pdoExtension->CurrentProtocolState) == PSLogonSucceeded) {
  220. loginResponse = (PISCSI_LOGIN_RESPONSE) (iScsiConnection->IScsiPacket);
  221. //
  222. // Allocate memory to keep active requests. Size of this
  223. // array is the value returned in MaxCmdRN field
  224. //
  225. GetUlongFromArray((loginResponse->InitStatRN),
  226. (iScsiConnection->CurrentStatusRefNum));
  227. GetUlongFromArray((loginResponse->ExpCmdRN),
  228. (iScsiConnection->CommandRefNum));
  229. GetUlongFromArray((loginResponse->MaxCmdRN), maxCmdRN);
  230. iScsiConnection->MaxCommandRefNum = maxCmdRN;
  231. DebugPrint((1, "InitStatRN : %d, ExpCmdRN : %d, MaxCmdRN : %d\n",
  232. (iScsiConnection->CurrentStatusRefNum),
  233. (iScsiConnection->CommandRefNum),
  234. maxCmdRN));
  235. ASSERT((maxCmdRN != 0));
  236. ASSERT(((iScsiConnection->CommandRefNum) <= maxCmdRN));
  237. iScsiConnection->NumberOfReqsInProgress = 0;
  238. iScsiConnection->ReceiveState = ReceiveHeader;
  239. iScsiConnection->MaxPendingRequests = maxCmdRN;
  240. activeClientRequests = iSpAllocatePool(
  241. NonPagedPool,
  242. (sizeof(ACTIVE_REQUESTS) * (maxCmdRN + 1)),
  243. ISCSI_TAG_ACTIVE_REQ);
  244. if (activeClientRequests == NULL) {
  245. DebugPrint((1, "Failed to allocate ActiveClientRequests array\n"));
  246. iSpStopNetwork(pdo);
  247. IoDeleteDevice(pdo);
  248. fdoExtension->PDOList[inx] = NULL;
  249. } else {
  250. RtlZeroMemory(activeClientRequests,
  251. (sizeof(ACTIVE_REQUESTS) * (maxCmdRN + 1)));
  252. commonExtension = pdo->DeviceExtension;
  253. pdoExtension = pdo->DeviceExtension;
  254. pdo->StackSize = 1;
  255. pdo->Flags |= (DO_BUS_ENUMERATED_DEVICE | DO_DIRECT_IO);
  256. pdo->AlignmentRequirement = DeviceObject->AlignmentRequirement;
  257. commonExtension->DeviceObject = pdo;
  258. commonExtension->LowerDeviceObject = DeviceObject;
  259. commonExtension->IsPdo = TRUE;
  260. commonExtension->MajorFunction = PdoMajorFunctionTable;
  261. commonExtension->RemoveLock = 0;
  262. commonExtension->CurrentPnpState = 0xff;
  263. commonExtension->PreviousPnpState = 0xff;
  264. iScsiConnection->ActiveClientRequests = activeClientRequests;
  265. //
  266. // Inquiry data will be filled when we get the
  267. // first query id irp
  268. //
  269. pdoExtension->InquiryDataInitialized = FALSE;
  270. pdoExtension->CurrentProtocolState = PSFullFeaturePhase;
  271. pdoExtension->IsEnumerated = TRUE;
  272. //
  273. // Initialize the remove lock event.
  274. //
  275. KeInitializeEvent(
  276. &(commonExtension->RemoveEvent),
  277. SynchronizationEvent,
  278. FALSE);
  279. //
  280. // Initialize the request list for this PDO
  281. //
  282. InitializeListHead(&(iScsiConnection->RequestList));
  283. pdo->Flags &= ~DO_DEVICE_INITIALIZING;
  284. fdoExtension->PDOList[targetIndex] = pdo;
  285. targetIndex++;
  286. ObReferenceObject(pdo);
  287. DebugPrint((1, "PDO %d : 0x%x\n",
  288. (deviceRelations->Count), pdo));
  289. deviceRelations->Objects[deviceRelations->Count] = pdo;
  290. (deviceRelations->Count)++;
  291. }
  292. } else {
  293. iSpStopNetwork(pdo);
  294. IoDeleteDevice(pdo);
  295. fdoExtension->PDOList[inx] = NULL;
  296. }
  297. }
  298. //
  299. // Group all the targets to the beginning of the PDOList
  300. //
  301. for (inx = targetIndex; inx < MAX_TARGETS_SUPPORTED; inx++) {
  302. fdoExtension->PDOList[inx] = NULL;
  303. }
  304. DebugPrint((1, "Number of PDOs reported : %d\n",
  305. (deviceRelations->Count)));
  306. if ((deviceRelations->Count) > 0) {
  307. Irp->IoStatus.Status = STATUS_SUCCESS;
  308. Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
  309. fdoExtension->EnumerationComplete = TRUE;
  310. fdoExtension->NumberOfTargets = deviceRelations->Count;
  311. IoCopyCurrentIrpStackLocationToNext(Irp);
  312. return IoCallDriver((fdoExtension->CommonExtension.LowerDeviceObject),
  313. Irp);
  314. } else {
  315. DebugPrint((1, "No PDOs to report in QDR\n"));
  316. ExFreePool(deviceRelations);
  317. fdoExtension->NumberOfTargets = 0;
  318. fdoExtension->LocalNodesInitialized = FALSE;
  319. Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
  320. Irp->IoStatus.Information = 0L;
  321. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  322. return STATUS_UNSUCCESSFUL;
  323. }
  324. }
  325. NTSTATUS
  326. IssueInquiry(
  327. IN PDEVICE_OBJECT LogicalUnit
  328. )
  329. {
  330. PISCSI_PDO_EXTENSION pdoExtension = LogicalUnit->DeviceExtension;
  331. PIRP irp;
  332. SCSI_REQUEST_BLOCK srb;
  333. PCDB cdb;
  334. PVOID dataBuffer;
  335. PSENSE_DATA senseInfoBuffer;
  336. UCHAR allocationLength;
  337. ULONG bytesReturned;
  338. NTSTATUS status;
  339. PAGED_CODE();
  340. dataBuffer = &(pdoExtension->InquiryData);
  341. senseInfoBuffer = &(pdoExtension->InquirySenseBuffer);
  342. irp = IoAllocateIrp((LogicalUnit->StackSize) + 1, FALSE);
  343. if (irp == NULL) {
  344. DebugPrint((1, "IssueInquiry : Failed to allocate IRP.\n"));
  345. return STATUS_INSUFFICIENT_RESOURCES;
  346. }
  347. IoInitializeIrp(irp,
  348. IoSizeOfIrp((LogicalUnit->StackSize) + 1),
  349. ((LogicalUnit->StackSize) + 1));
  350. //
  351. // Fill in SRB fields.
  352. //
  353. RtlZeroMemory(dataBuffer, sizeof(INQUIRYDATA));
  354. RtlZeroMemory(senseInfoBuffer, SENSE_BUFFER_SIZE);
  355. RtlZeroMemory(&srb, SCSI_REQUEST_BLOCK_SIZE);
  356. srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
  357. srb.Length = SCSI_REQUEST_BLOCK_SIZE;
  358. //
  359. // Set flags to disable synchronous negociation.
  360. //
  361. srb.SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
  362. //
  363. // Set timeout to 4 seconds.
  364. //
  365. srb.TimeOutValue = 4;
  366. srb.CdbLength = 6;
  367. cdb = (PCDB)(srb.Cdb);
  368. //
  369. // Set CDB operation code.
  370. //
  371. cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
  372. //
  373. // Set allocation length to inquiry data buffer size.
  374. //
  375. allocationLength = sizeof(INQUIRYDATA);
  376. cdb->CDB6INQUIRY3.AllocationLength = allocationLength;
  377. cdb->CDB6INQUIRY3.EnableVitalProductData = FALSE;
  378. cdb->CDB6INQUIRY3.PageCode = 0;
  379. status = iSpSendSrbSynchronous(LogicalUnit,
  380. &srb,
  381. irp,
  382. dataBuffer,
  383. allocationLength,
  384. senseInfoBuffer,
  385. SENSE_BUFFER_SIZE,
  386. &bytesReturned
  387. );
  388. ASSERT(bytesReturned <= allocationLength);
  389. //
  390. // Return the inquiry data for the device if the call was successful.
  391. // Otherwise cleanup.
  392. //
  393. if(NT_SUCCESS(status)) {
  394. pdoExtension->InquiryDataInitialized = TRUE;
  395. DebugPrint((3, "Inquiry data obtained successfully\n"));
  396. } else {
  397. DebugPrint((1, "Failed to obtain inquiry data. Status : %x\n",
  398. status));
  399. }
  400. IoFreeIrp(irp);
  401. return status;
  402. }
  403. NTSTATUS
  404. iSpSendSrbSynchronous(
  405. IN PDEVICE_OBJECT LogicalUnit,
  406. IN PSCSI_REQUEST_BLOCK Srb,
  407. IN PIRP Irp,
  408. IN PVOID DataBuffer,
  409. IN ULONG TransferLength,
  410. IN OPTIONAL PVOID SenseInfoBuffer,
  411. IN OPTIONAL UCHAR SenseInfoBufferLength,
  412. OUT PULONG BytesReturned
  413. )
  414. {
  415. KEVENT event;
  416. PIO_STACK_LOCATION irpStack;
  417. PMDL Mdl = NULL;
  418. PSENSE_DATA senseInfo = SenseInfoBuffer;
  419. NTSTATUS status;
  420. PAGED_CODE();
  421. KeInitializeEvent(&event, NotificationEvent, FALSE);
  422. if(ARGUMENT_PRESENT(DataBuffer)) {
  423. ASSERT(TransferLength != 0);
  424. Mdl = IoAllocateMdl(DataBuffer,
  425. TransferLength,
  426. FALSE,
  427. FALSE,
  428. NULL);
  429. if(Mdl == NULL) {
  430. IoFreeIrp(Irp);
  431. return STATUS_INSUFFICIENT_RESOURCES;
  432. }
  433. MmBuildMdlForNonPagedPool(Mdl);
  434. Irp->MdlAddress = Mdl;
  435. } else {
  436. ASSERT(TransferLength == 0);
  437. }
  438. irpStack = IoGetNextIrpStackLocation(Irp);
  439. //
  440. // Mark the minor function to indicate that this is an internal scsiport
  441. // request and that the start state of the device can be ignored.
  442. //
  443. irpStack->MajorFunction = IRP_MJ_SCSI;
  444. irpStack->MinorFunction = 1;
  445. irpStack->Parameters.Scsi.Srb = Srb;
  446. Srb->SrbStatus = Srb->ScsiStatus = 0;
  447. Srb->OriginalRequest = Irp;
  448. //
  449. // Enable auto request sense.
  450. //
  451. if(ARGUMENT_PRESENT(SenseInfoBuffer)) {
  452. Srb->SenseInfoBuffer = SenseInfoBuffer;
  453. Srb->SenseInfoBufferLength = SenseInfoBufferLength;
  454. } else {
  455. Srb->SenseInfoBuffer = NULL;
  456. Srb->SenseInfoBufferLength = 0;
  457. SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_AUTOSENSE);
  458. }
  459. if(ARGUMENT_PRESENT(Mdl)) {
  460. Srb->DataBuffer = MmGetMdlVirtualAddress(Mdl);
  461. Srb->DataTransferLength = TransferLength;
  462. } else {
  463. Srb->DataBuffer = NULL;
  464. Srb->DataTransferLength = 0;
  465. }
  466. IoSetCompletionRoutine(Irp,
  467. iSpSetEvent,
  468. &event,
  469. TRUE,
  470. TRUE,
  471. TRUE);
  472. KeEnterCriticalRegion();
  473. status = IoCallDriver(LogicalUnit, Irp);
  474. KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
  475. status = Irp->IoStatus.Status;
  476. *BytesReturned = (ULONG) Irp->IoStatus.Information;
  477. IoFreeMdl(Mdl);
  478. KeLeaveCriticalRegion();
  479. return status;
  480. }
  481. VOID
  482. iSpTickHandler(
  483. IN PDEVICE_OBJECT DeviceObject,
  484. IN PVOID Context
  485. )
  486. {
  487. PISCSI_FDO_EXTENSION fdoExtension;
  488. PISCSI_PDO_EXTENSION pdoExtension;
  489. PDEVICE_OBJECT pdo;
  490. ULONG inx;
  491. UCHAR oldIrql;
  492. fdoExtension = (PISCSI_FDO_EXTENSION) DeviceObject->DeviceExtension;
  493. KeAcquireSpinLock(&(fdoExtension->EnumerationSpinLock),
  494. &oldIrql);
  495. if ((fdoExtension->TargetsYetToRespond) > 0) {
  496. for (inx = 0; inx < (fdoExtension->NumberOfTargets); inx++) {
  497. pdo = fdoExtension->PDOList[inx];
  498. if (pdo != NULL) {
  499. pdoExtension = (PISCSI_PDO_EXTENSION) (pdo->DeviceExtension);
  500. if ((pdoExtension->CurrentProtocolState) == PSLogonInProgress) {
  501. (pdoExtension->LogonTickCount)++;
  502. }
  503. if ((pdoExtension->LogonTickCount) == MAX_LOGON_WAIT_TIME) {
  504. DebugPrint((0, "Timeout waiting for logon response\n"));
  505. pdoExtension->CurrentProtocolState = PSLogonTimedOut;
  506. (fdoExtension->TargetsYetToRespond)--;
  507. if ((fdoExtension->TargetsYetToRespond) == 0) {
  508. DebugPrint((0,
  509. "TickHandler : All targets responded.\n"));
  510. iSpLaunchEnumerationCompletion(fdoExtension);
  511. }
  512. }
  513. }
  514. }
  515. }
  516. KeReleaseSpinLock(&(fdoExtension->EnumerationSpinLock),
  517. oldIrql);
  518. }
  519. VOID
  520. iSpLaunchEnumerationCompletion(
  521. IN PISCSI_FDO_EXTENSION FdoExtension
  522. )
  523. {
  524. PIO_WORKITEM workItem;
  525. if ((FdoExtension->EnumerationThreadLaunched) == FALSE) {
  526. workItem = IoAllocateWorkItem(FdoExtension->DeviceObject);
  527. if (workItem != NULL) {
  528. IoQueueWorkItem(workItem,
  529. iSpQueryDeviceRelationsCompletion,
  530. DelayedWorkQueue,
  531. workItem);
  532. FdoExtension->EnumerationThreadLaunched = TRUE;
  533. }
  534. }
  535. }