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.

909 lines
24 KiB

  1. #include "mpio.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. NTSTATUS
  5. MPIOQueryDeviceText(
  6. IN PDEVICE_OBJECT DeviceObject,
  7. IN PIRP Irp
  8. )
  9. /*++
  10. Routine Description:
  11. This routine handles the QUERY_DEVICE_TEXT Irp for
  12. Pseudo disks.
  13. Arguments:
  14. DeviceObject
  15. Irp
  16. Return Value:
  17. NTSTATUS
  18. --*/
  19. {
  20. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  21. PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
  22. PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
  23. PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
  24. NTSTATUS status = STATUS_NOT_SUPPORTED;
  25. UCHAR ansiBuffer[256];
  26. ANSI_STRING ansiString;
  27. UNICODE_STRING unicodeString;
  28. PUCHAR index;
  29. PUCHAR inquiryField;
  30. //
  31. // Get the Storage Descriptor from the type extension.
  32. //
  33. deviceDescriptor = diskExtension->DeviceDescriptor;
  34. //
  35. // Set the inquiry data pointer to the front of the descriptor.
  36. //
  37. inquiryField = (PUCHAR)deviceDescriptor;
  38. //
  39. // Zero the string array.
  40. //
  41. RtlZeroMemory(ansiBuffer, sizeof(ansiBuffer));
  42. switch (irpStack->Parameters.QueryDeviceText.DeviceTextType) {
  43. case DeviceTextDescription:
  44. //
  45. // Build <Product><Vendor><"Multi-Path Disk Device">.
  46. //
  47. // Push the inquiry pointer to the VendorId.
  48. //
  49. (ULONG_PTR)inquiryField += deviceDescriptor->VendorIdOffset;
  50. //
  51. // Copy the VendorId to the temp. buffer, and adjust indices.
  52. //
  53. index = ansiBuffer;
  54. RtlCopyMemory(index, inquiryField, 8);
  55. index += 7;
  56. //
  57. // go back and eat all the spaces except for one.
  58. //
  59. while (*index == ' ') {
  60. *index = '\0';
  61. index--;
  62. }
  63. index++;
  64. *index = ' ';
  65. index++;
  66. //
  67. // Handle the ProductID.
  68. //
  69. inquiryField = (PUCHAR)deviceDescriptor;
  70. (ULONG_PTR)inquiryField += deviceDescriptor->ProductIdOffset;
  71. RtlCopyMemory(index, inquiryField, 16);
  72. index += 15;
  73. //
  74. // go back and eat all the spaces except for one.
  75. //
  76. while (*index == ' ') {
  77. *index = '\0';
  78. index--;
  79. }
  80. index++;
  81. *index = ' ';
  82. index++;
  83. //
  84. // Don't use rev or serial number (at least for now)
  85. // TODO: remove or reenable.
  86. //
  87. //inquiryField = (PUCHAR)deviceDescriptor;
  88. //(ULONG_PTR)inquiryField += deviceDescriptor->ProductRevisionOffset;
  89. //RtlCopyMemory(index, inquiryField, 4);
  90. //index += 4;
  91. //
  92. // Append this to the end to distinquish this from the regular scsi drive.
  93. //
  94. sprintf(index, "%s", " Multi-Path Disk Device");
  95. //
  96. // The real wchar buffer is built below.
  97. //
  98. status = STATUS_SUCCESS;
  99. break;
  100. case DeviceTextLocationInformation: {
  101. PSCSI_ADDRESS scsiAddress = NULL;
  102. ULONG i;
  103. PUCHAR index;
  104. UCHAR groupString[25];
  105. //
  106. // Build the group string. Each value in it is the port number of the
  107. // device backing the pseudo disk.
  108. // Adapters(X,Y)
  109. //
  110. RtlZeroMemory(groupString, sizeof(groupString));
  111. sprintf(groupString, "%s", "Port(");
  112. index = groupString;
  113. index += strlen(groupString);
  114. for (i = 0; i < diskExtension->TargetInfoCount; i++) {
  115. //
  116. // Get the SCSI Address for this scsiport PDO
  117. //
  118. status = MPIOGetScsiAddress(diskExtension->TargetInfo[i].PortPdo,
  119. &scsiAddress);
  120. diskExtension->TargetInfo[i].ScsiAddress.PortNumber = scsiAddress->PortNumber;
  121. diskExtension->TargetInfo[i].ScsiAddress.PathId = scsiAddress->PathId;
  122. diskExtension->TargetInfo[i].ScsiAddress.TargetId = scsiAddress->TargetId;
  123. diskExtension->TargetInfo[i].ScsiAddress.Lun = scsiAddress->Lun;
  124. if (status == STATUS_SUCCESS) {
  125. //
  126. // Jam in the PortNumber for this device.
  127. //
  128. sprintf(index, "%d", scsiAddress->PortNumber);
  129. index += strlen(index);
  130. if ((i + 1) == diskExtension->TargetInfoCount) {
  131. //
  132. // Last one, finish off with the closing bracket.
  133. //
  134. sprintf(index,"%s", ")");
  135. } else {
  136. //
  137. // Stick a comma in between each of the port
  138. // numbers.
  139. //
  140. sprintf(index,"%s", ",");
  141. index += strlen(index);
  142. //
  143. // Free the buffer that GetScsiAddress allocated.
  144. //
  145. ExFreePool(scsiAddress);
  146. scsiAddress = NULL;
  147. }
  148. }
  149. }
  150. //
  151. // Free the last one.
  152. //
  153. if (scsiAddress) {
  154. // NOTE: This was above the 'if'. TODO validate and remove this comment.
  155. //
  156. // The last scsiAddress buffer was not freed yet.
  157. // Add in Bus Target Lun based on this Scsi Address.
  158. //
  159. sprintf(ansiBuffer, "%s Bus %d, Target ID %d, LUN %d",
  160. groupString,
  161. scsiAddress->PathId,
  162. scsiAddress->TargetId,
  163. scsiAddress->Lun);
  164. ExFreePool(scsiAddress);
  165. }
  166. break;
  167. }
  168. default:
  169. status = STATUS_NOT_SUPPORTED;
  170. Irp->IoStatus.Information = (ULONG_PTR)NULL;
  171. break;
  172. }
  173. if (status == STATUS_SUCCESS) {
  174. //
  175. // Finally, build the unicode string based on whatever text
  176. // was built above.
  177. //
  178. RtlInitAnsiString(&ansiString, ansiBuffer);
  179. status = RtlAnsiStringToUnicodeString(&unicodeString,
  180. &ansiString,
  181. TRUE);
  182. if (status == STATUS_SUCCESS) {
  183. Irp->IoStatus.Information = (ULONG_PTR)unicodeString.Buffer;
  184. }
  185. }
  186. return status;
  187. }
  188. NTSTATUS
  189. MPIOPdoQdr(
  190. IN PDEVICE_OBJECT DeviceObject,
  191. IN PIRP Irp
  192. )
  193. /*++
  194. Routine Description:
  195. This routine handles QueryDeviceRelations requests sent to the PDO (PseudoDisks).
  196. Arguments:
  197. DeviceObject
  198. Irp
  199. Return Value:
  200. NTSTATUS
  201. --*/
  202. {
  203. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  204. PDEVICE_RELATIONS deviceRelations;
  205. NTSTATUS status = Irp->IoStatus.Status;
  206. if (irpStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation) {
  207. //
  208. // Allocate the return buffer.
  209. //
  210. deviceRelations = ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS));
  211. if (deviceRelations == NULL) {
  212. status = STATUS_INSUFFICIENT_RESOURCES;
  213. } else {
  214. status = STATUS_SUCCESS;
  215. //
  216. // Indicate, of course, there is One, and it's us.
  217. //
  218. deviceRelations->Count = 1;
  219. deviceRelations->Objects[0] = DeviceObject;
  220. //
  221. // Reference our devObj so that it's not removed.
  222. // PnP will deref it when it's finished with the request.
  223. //
  224. ObReferenceObject(DeviceObject);
  225. Irp->IoStatus.Information = (ULONG_PTR)deviceRelations;
  226. }
  227. }
  228. Irp->IoStatus.Status = status;
  229. return status;
  230. }
  231. NTSTATUS
  232. MPIOHardwareIDs(
  233. IN PDEVICE_OBJECT DeviceObject,
  234. IN PIRP Irp,
  235. OUT PUNICODE_STRING UnicodeString
  236. )
  237. /*++
  238. Routine Description:
  239. This routine handles QueryHardwareId requests sent to the PDO (PseudoDisks).
  240. Arguments:
  241. DeviceObject
  242. Irp
  243. Return Value:
  244. NTSTATUS
  245. --*/
  246. {
  247. PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
  248. PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
  249. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  250. PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
  251. NTSTATUS status;
  252. PUCHAR inquiryField;
  253. PUCHAR index;
  254. ULONG bufferLength;
  255. ULONG i;
  256. ANSI_STRING ansiString;
  257. UNICODE_STRING unicodeEntry;
  258. PSTR hwStrings[7];
  259. UCHAR hwId[64];
  260. UCHAR inquiryData[32];
  261. UCHAR savedInquiryData[32];
  262. deviceDescriptor = diskExtension->DeviceDescriptor;
  263. inquiryField = (PUCHAR)deviceDescriptor;
  264. //
  265. // Zero the string array.
  266. //
  267. RtlZeroMemory(hwStrings, sizeof(hwStrings));
  268. RtlZeroMemory(inquiryData, sizeof(inquiryData));
  269. //
  270. // Build the full inquiry data for the device.
  271. // Go through each of the fields in raw data and get
  272. // their offsets. Copy the fields into the buffer.
  273. //
  274. (ULONG_PTR)inquiryField += deviceDescriptor->VendorIdOffset;
  275. index = inquiryData;
  276. RtlCopyMemory(index, inquiryField, 8);
  277. index += 8;
  278. inquiryField = (PUCHAR)deviceDescriptor;
  279. (ULONG_PTR)inquiryField += deviceDescriptor->ProductIdOffset;
  280. RtlCopyMemory(index, inquiryField, 16);
  281. index += 16;
  282. inquiryField = (PUCHAR)deviceDescriptor;
  283. (ULONG_PTR)inquiryField += deviceDescriptor->ProductRevisionOffset;
  284. RtlCopyMemory(index, inquiryField, 4);
  285. //
  286. // Run through and fixup spaces.
  287. //
  288. index = inquiryData;
  289. while (*index != '\0') {
  290. if (*index == ' ') {
  291. *index = '_';
  292. }
  293. index++;
  294. }
  295. //
  296. // Copy this to the saved buffer.
  297. // This is used below while building up each of the HW ID strings.
  298. //
  299. RtlCopyMemory(savedInquiryData, inquiryData, 32);
  300. for (i = 0; i < 6; i++) {
  301. //
  302. // Zero the buffer
  303. //
  304. RtlZeroMemory(hwId, sizeof(hwId));
  305. //
  306. // Each time through the loop, build one of the hardware id strings.
  307. //
  308. // (0) SCSI\DISK\Full Inquiry
  309. // (1) SCSI\Disk\Inquiry - Rev
  310. // (2) SCSI\Disk\Inquiry - Product and rev.
  311. // (3) SCSI\Inquiry with only one char of rev.
  312. // (4) Inquiry with only one char of rev.
  313. // (5) GenDisk
  314. //
  315. switch (i) {
  316. case 0:
  317. //
  318. // Build the header (SCSI\Disk) plus the full inquiry data
  319. //
  320. sprintf(hwId, "%s", "MPIO\\Disk");
  321. index = hwId;
  322. index += strlen(hwId);
  323. RtlMoveMemory(index, inquiryData, strlen(inquiryData));
  324. break;
  325. case 1:
  326. //
  327. // Get rid of the product revision in the inquiryData.
  328. //
  329. index = inquiryData + 24;
  330. *index = '\0';
  331. sprintf(hwId, "%s", "MPIO\\Disk");
  332. index = hwId;
  333. index += strlen(hwId);
  334. RtlMoveMemory(index, inquiryData, strlen(inquiryData));
  335. break;
  336. case 2:
  337. //
  338. // Get rid of the product id.
  339. //
  340. index = inquiryData + 8;
  341. *index = '\0';
  342. sprintf(hwId, "%s", "MPIO\\Disk");
  343. index = hwId;
  344. index += strlen(hwId);
  345. RtlMoveMemory(index, inquiryData, strlen(inquiryData));
  346. break;
  347. case 3:
  348. //
  349. // Remake inquiryData.
  350. //
  351. RtlCopyMemory(inquiryData, savedInquiryData, 32);
  352. //
  353. // Need to strip off all but the first character of revision.
  354. //
  355. inquiryData[25] = '\0';
  356. sprintf(hwId, "%s", "MPIO\\");
  357. index = hwId;
  358. index += strlen(hwId);
  359. RtlMoveMemory(index, inquiryData, strlen(inquiryData));
  360. break;
  361. case 4:
  362. //
  363. // This is like 3, but no SCSI\ in front.
  364. //
  365. sprintf(hwId, "%s", inquiryData);
  366. break;
  367. case 5:
  368. //
  369. // Only the GenDisk
  370. //
  371. sprintf(hwId, "%s", "GenDisk");
  372. break;
  373. default:
  374. break;
  375. }
  376. //
  377. // Allocate and build the hwString entry.
  378. //
  379. hwStrings[i] = ExAllocatePool(PagedPool, strlen(hwId) + sizeof(UCHAR));
  380. if (hwStrings == NULL) {
  381. return STATUS_INSUFFICIENT_RESOURCES;
  382. }
  383. RtlZeroMemory(hwStrings[i], strlen(hwId) + sizeof(UCHAR));
  384. RtlCopyMemory(hwStrings[i], hwId, strlen(hwId));
  385. MPDebugPrint((2,
  386. "MPathHwIds: Hw String[%x] - %s\n",
  387. i,
  388. hwStrings[i]));
  389. }
  390. status = STATUS_SUCCESS;
  391. //
  392. // Convert the hwString entry into the unicode version that the caller wants.
  393. //
  394. for (i = 0, bufferLength = 0; i < 6; i++) {
  395. bufferLength += strlen(hwStrings[i]) * sizeof(WCHAR);
  396. //
  397. // Make room for the NULL after the string.
  398. //
  399. bufferLength += sizeof(UNICODE_NULL);
  400. }
  401. UnicodeString->Length = (USHORT)bufferLength;
  402. //
  403. // Add room for the final terminating NULL
  404. //
  405. bufferLength += sizeof(UNICODE_NULL);
  406. //
  407. // Allocate the buffer.
  408. //
  409. UnicodeString->Buffer = ExAllocatePool(PagedPool, bufferLength);
  410. if (UnicodeString->Buffer) {
  411. RtlZeroMemory(UnicodeString->Buffer, bufferLength);
  412. UnicodeString->MaximumLength = (USHORT)bufferLength;
  413. unicodeEntry = *UnicodeString;
  414. for (i = 0; i < 6; i++) {
  415. //
  416. // Convert ascii to ansi.
  417. //
  418. RtlInitAnsiString(&ansiString, hwStrings[i]);
  419. status = RtlAnsiStringToUnicodeString(&unicodeEntry,
  420. &ansiString,
  421. FALSE);
  422. if (!NT_SUCCESS(status)) {
  423. break;
  424. }
  425. //
  426. // update the unicode fields.
  427. //
  428. ((PSTR)unicodeEntry.Buffer) += unicodeEntry.Length + sizeof(WCHAR);
  429. unicodeEntry.MaximumLength -= unicodeEntry.Length + sizeof(WCHAR);
  430. }
  431. } else {
  432. status = STATUS_INSUFFICIENT_RESOURCES;
  433. }
  434. return status;
  435. }
  436. NTSTATUS
  437. MPIODeviceId(
  438. IN PDEVICE_OBJECT DeviceObject,
  439. IN PIRP Irp,
  440. IN OUT PUNICODE_STRING UnicodeString
  441. )
  442. //
  443. // Need to build up a name like MPATH#<DevType>&VenXXX&ProdXXX&RevXXX
  444. //
  445. {
  446. PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
  447. PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
  448. PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
  449. ANSI_STRING ansiIdString;
  450. CHAR deviceStrings[4][255];
  451. UCHAR deviceId[256];
  452. PUCHAR scsiField;
  453. PUCHAR currentId;
  454. NTSTATUS status;
  455. ULONG i;
  456. ULONG deviceIndex = 0;
  457. RtlZeroMemory(deviceId, 256);
  458. currentId = deviceId;
  459. deviceDescriptor = diskExtension->DeviceDescriptor;
  460. scsiField = (PUCHAR)deviceDescriptor;
  461. //
  462. // Preload the deviceId with MPIO\<deviceType>
  463. //
  464. sprintf(currentId, "%s", "MPIO\\Disk&Ven_");
  465. //
  466. // Push the current pointer past the above field.
  467. //
  468. currentId += strlen(currentId);
  469. //
  470. // Push the scsiField pointer to that of the vendor id.
  471. //
  472. ASSERT(deviceDescriptor->VendorIdOffset != 0);
  473. ASSERT(deviceDescriptor->ProductIdOffset != 0);
  474. ASSERT(deviceDescriptor->ProductRevisionOffset != 0);
  475. (ULONG_PTR)scsiField += deviceDescriptor->VendorIdOffset;
  476. //
  477. // Copy in the Vendor name.
  478. //
  479. for (i = 0; i < VENDOR_ID_LENGTH; i++) {
  480. *currentId = *scsiField;
  481. currentId++;
  482. scsiField++;
  483. }
  484. *currentId = '\0';
  485. currentId++;
  486. //
  487. // Bump the scsiField to that of the product id.
  488. //
  489. scsiField = (PUCHAR)deviceDescriptor;
  490. (ULONG_PTR)scsiField += deviceDescriptor->ProductIdOffset;
  491. //
  492. // Remove any trailing spaces
  493. //
  494. while (*currentId == ' ' || *currentId == '\0') {
  495. currentId--;
  496. }
  497. currentId++;
  498. //
  499. // Jam in &Prod
  500. //
  501. sprintf(currentId, "%s", "&Prod_");
  502. currentId += strlen(currentId);
  503. //
  504. // Copy in the Product Id.
  505. //
  506. for (i = 0; i < PRODUCT_ID_LENGTH; i++) {
  507. *currentId = *scsiField;
  508. currentId++;
  509. scsiField++;
  510. }
  511. *currentId = '\0';
  512. currentId++;
  513. //
  514. // Remove any trailing spaces
  515. //
  516. while (*currentId == ' ' || *currentId == '\0') {
  517. currentId--;
  518. }
  519. currentId++;
  520. //
  521. // Handle the revision.
  522. //
  523. scsiField = (PUCHAR)deviceDescriptor;
  524. (ULONG_PTR)scsiField += deviceDescriptor->ProductRevisionOffset;
  525. sprintf(currentId, "%s", "&Rev_");
  526. currentId += strlen(currentId);
  527. for (i = 0; i < REVISION_LENGTH; i++) {
  528. *currentId = *scsiField;
  529. currentId++;
  530. scsiField++;
  531. }
  532. *currentId = '\0';
  533. currentId++;
  534. //
  535. // Remove any trailing spaces
  536. //
  537. while (*currentId == ' ' || *currentId == '\0') {
  538. currentId--;
  539. }
  540. //
  541. // Run through and fixup spaces.
  542. //
  543. currentId = deviceId;
  544. while (*currentId != '\0') {
  545. if (*currentId == ' ') {
  546. *currentId = '_';
  547. }
  548. currentId++;
  549. }
  550. MPDebugPrint((2,
  551. "MPathDeviceId - %s\n",
  552. deviceId));
  553. //
  554. // Finally make the unicode string that will be returned to PnP.
  555. //
  556. RtlInitAnsiString(&ansiIdString,deviceId);
  557. status = RtlAnsiStringToUnicodeString(UnicodeString,
  558. &ansiIdString,
  559. TRUE);
  560. return status;
  561. }
  562. NTSTATUS
  563. MPIOPdoQueryId(
  564. IN PDEVICE_OBJECT DeviceObject,
  565. IN PIRP Irp
  566. )
  567. /*++
  568. Routine Description:
  569. This routine handles QueryId requests sent to the PDO (PseudoDisks).
  570. Arguments:
  571. DeviceObject
  572. Irp
  573. Return Value:
  574. NTSTATUS
  575. --*/
  576. {
  577. PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
  578. PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
  579. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  580. NTSTATUS status;
  581. UNICODE_STRING unicodeID;
  582. UNICODE_STRING unicodeString;
  583. ANSI_STRING ansiString;
  584. UNICODE_STRING unicodeIndex;
  585. ULONG bufferLength = 0;
  586. UCHAR deviceId[256];
  587. RtlZeroMemory(deviceId, 256);
  588. switch (irpStack->Parameters.QueryId.IdType) {
  589. case BusQueryHardwareIDs: {
  590. status = MPIOHardwareIDs(DeviceObject,
  591. Irp,
  592. &unicodeString);
  593. if (NT_SUCCESS(status)) {
  594. //
  595. // Set the hardware ID buffer.
  596. //
  597. Irp->IoStatus.Information = (ULONG_PTR)unicodeString.Buffer;
  598. }
  599. break;
  600. }
  601. case BusQueryDeviceID: {
  602. status = MPIODeviceId(DeviceObject,
  603. Irp,
  604. &unicodeString);
  605. if (NT_SUCCESS(status)) {
  606. Irp->IoStatus.Information = (ULONG_PTR)unicodeString.Buffer;
  607. }
  608. break;
  609. }
  610. case BusQueryInstanceID: {
  611. PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
  612. ULONG serialNumberLength;
  613. PUCHAR serialNumber;
  614. UCHAR fakeSerialNumber[20];
  615. ANSI_STRING ansiSerialNumber;
  616. deviceDescriptor = diskExtension->DeviceDescriptor;
  617. serialNumber = (PUCHAR)deviceDescriptor;
  618. //
  619. // If the device has no serial number, need to make something up.
  620. // TODO
  621. //
  622. if (deviceDescriptor->SerialNumberOffset == (ULONG)-1) {
  623. RtlZeroMemory(fakeSerialNumber, 20);
  624. sprintf(fakeSerialNumber, "00%d", diskExtension->DeviceOrdinal);
  625. serialNumber = fakeSerialNumber;
  626. } else {
  627. //
  628. // Move serialNumber to the correct position in RawData.
  629. //
  630. (ULONG_PTR)serialNumber += deviceDescriptor->SerialNumberOffset;
  631. }
  632. //
  633. // Get it's length.
  634. //
  635. serialNumberLength = strlen(serialNumber);
  636. //
  637. // Build the ansi string based on the serial number information.
  638. // Then convert to unicode.
  639. //
  640. RtlInitAnsiString(&ansiSerialNumber, serialNumber);
  641. RtlAnsiStringToUnicodeString(&unicodeString, &ansiSerialNumber, TRUE);
  642. status = STATUS_SUCCESS;
  643. Irp->IoStatus.Information = (ULONG_PTR)unicodeString.Buffer;
  644. break;
  645. }
  646. case BusQueryCompatibleIDs: {
  647. UNICODE_STRING unicodeString;
  648. ULONG i;
  649. PSTR deviceStrings[] = {"SCSI\\Disk", "SCSI\\RAW", NULL};
  650. //
  651. // Determine length needed for the unicode buffer.
  652. //
  653. for (i = 0; deviceStrings[i] != NULL; i++) {
  654. bufferLength += strlen(deviceStrings[i]) * sizeof(WCHAR);
  655. //
  656. // Make room for the NULL after the string.
  657. //
  658. bufferLength += sizeof(UNICODE_NULL);
  659. }
  660. unicodeString.Length = (USHORT)bufferLength;
  661. //
  662. // Add room for the final terminating NULL
  663. //
  664. bufferLength += sizeof(UNICODE_NULL);
  665. //
  666. // Allocate the buffer.
  667. //
  668. unicodeString.Buffer = ExAllocatePool(PagedPool, bufferLength);
  669. if (unicodeString.Buffer == NULL) {
  670. status = STATUS_INSUFFICIENT_RESOURCES;
  671. } else {
  672. //
  673. // Finish initing the unicode string.
  674. //
  675. RtlZeroMemory(unicodeString.Buffer, bufferLength);
  676. unicodeString.MaximumLength = (USHORT)bufferLength;
  677. //
  678. // Set the index string to the front of the real unicode string.
  679. // This index will be pushed along so that the creation of the MULTI string
  680. // can be done.
  681. //
  682. unicodeIndex = unicodeString;
  683. for (i = 0, status = STATUS_SUCCESS; deviceStrings[i] != NULL; i++) {
  684. RtlInitAnsiString(&ansiString, deviceStrings[i]);
  685. status = RtlAnsiStringToUnicodeString(&unicodeIndex,
  686. &ansiString,
  687. FALSE);
  688. if (NT_SUCCESS(status)) {
  689. //
  690. // push the index past the last unicode string built.
  691. //
  692. ((PSTR)unicodeIndex.Buffer) += unicodeIndex.Length + sizeof(WCHAR);
  693. //
  694. // Ensure the max length is correct.
  695. //
  696. unicodeIndex.MaximumLength -= unicodeIndex.Length + sizeof(WCHAR);
  697. } else {
  698. //
  699. // TODO: Something.
  700. //
  701. break;
  702. }
  703. }
  704. if (NT_SUCCESS(status)) {
  705. Irp->IoStatus.Information = (ULONG_PTR)unicodeString.Buffer;
  706. }
  707. }
  708. break;
  709. }
  710. default:
  711. status = Irp->IoStatus.Status;
  712. Irp->IoStatus.Information = 0;
  713. break;
  714. }
  715. return status;
  716. }
  717. NTSTATUS
  718. MPIOPdoDeviceUsage(
  719. IN PDEVICE_OBJECT DeviceObject,
  720. IN PIRP Irp
  721. )
  722. {
  723. //
  724. // TODO
  725. //
  726. return STATUS_SUCCESS;
  727. }