Leaked source code of windows server 2003
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.

1777 lines
42 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1996 - 1999
  3. Module Name:
  4. mcd.c
  5. Abstract:
  6. Environment:
  7. Kernel mode
  8. Revision History :
  9. --*/
  10. #include "stdarg.h"
  11. #include "ntddk.h"
  12. #include "mcd.h"
  13. #include "initguid.h"
  14. #include "ntddstor.h"
  15. typedef struct _MCD_CLASS_DATA {
  16. LONG DeviceOpen;
  17. UNICODE_STRING MediumChangerInterfaceString;
  18. BOOLEAN DosNameCreated;
  19. } MCD_CLASS_DATA, *PMCD_CLASS_DATA;
  20. NTSTATUS
  21. ChangerClassCreateClose (
  22. IN PDEVICE_OBJECT DeviceObject,
  23. IN PIRP Irp
  24. );
  25. NTSTATUS
  26. ChangerClassDeviceControl (
  27. IN PDEVICE_OBJECT DeviceObject,
  28. IN PIRP Irp
  29. );
  30. VOID
  31. ChangerClassError(
  32. PDEVICE_OBJECT DeviceObject,
  33. PSCSI_REQUEST_BLOCK Srb,
  34. NTSTATUS *Status,
  35. BOOLEAN *Retry
  36. );
  37. NTSTATUS
  38. ChangerAddDevice(
  39. IN PDRIVER_OBJECT DriverObject,
  40. IN PDEVICE_OBJECT PhysicalDeviceObject
  41. );
  42. NTSTATUS
  43. ChangerStartDevice(
  44. IN PDEVICE_OBJECT Fdo
  45. );
  46. NTSTATUS
  47. ChangerStopDevice(
  48. IN PDEVICE_OBJECT DeviceObject,
  49. IN UCHAR Type
  50. );
  51. NTSTATUS
  52. ChangerInitDevice(
  53. IN PDEVICE_OBJECT Fdo
  54. );
  55. NTSTATUS
  56. ChangerRemoveDevice(
  57. IN PDEVICE_OBJECT DeviceObject,
  58. IN UCHAR Type
  59. );
  60. NTSTATUS
  61. DriverEntry(
  62. IN PDRIVER_OBJECT DriverObject,
  63. IN PUNICODE_STRING RegistryPath
  64. );
  65. VOID
  66. ChangerUnload(
  67. IN PDRIVER_OBJECT DriverObject
  68. );
  69. NTSTATUS
  70. CreateChangerDeviceObject(
  71. IN PDRIVER_OBJECT DriverObject,
  72. IN PDEVICE_OBJECT PhysicalDeviceObject
  73. );
  74. NTSTATUS
  75. ClasspSendSynchronousCompletion(
  76. IN PDEVICE_OBJECT DeviceObject,
  77. IN PIRP Irp,
  78. IN PVOID Context
  79. );
  80. #ifdef ALLOC_PRAGMA
  81. #pragma alloc_text(INIT, DriverEntry)
  82. #pragma alloc_text(PAGE, ChangerUnload)
  83. #pragma alloc_text(PAGE, CreateChangerDeviceObject)
  84. #pragma alloc_text(PAGE, ChangerClassCreateClose)
  85. #pragma alloc_text(PAGE, ChangerClassDeviceControl)
  86. #pragma alloc_text(PAGE, ChangerAddDevice)
  87. #pragma alloc_text(PAGE, ChangerStartDevice)
  88. #pragma alloc_text(PAGE, ChangerInitDevice)
  89. #pragma alloc_text(PAGE, ChangerRemoveDevice)
  90. #pragma alloc_text(PAGE, ChangerStopDevice)
  91. #pragma alloc_text(PAGE, ChangerReadWriteVerification)
  92. #endif
  93. NTSTATUS
  94. ChangerClassCreateClose (
  95. IN PDEVICE_OBJECT DeviceObject,
  96. IN PIRP Irp
  97. )
  98. /*++
  99. Routine Description:
  100. This routine handles CREATE/CLOSE requests.
  101. As these are exclusive devices, don't allow multiple opens.
  102. Arguments:
  103. DeviceObject
  104. Irp
  105. Return Value:
  106. NT Status
  107. --*/
  108. {
  109. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  110. PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
  111. PMCD_CLASS_DATA mcdClassData;
  112. ULONG miniclassExtSize;
  113. NTSTATUS status = STATUS_SUCCESS;
  114. PAGED_CODE();
  115. mcdClassData = (PMCD_CLASS_DATA)(fdoExtension->CommonExtension.DriverData);
  116. miniclassExtSize = ChangerAdditionalExtensionSize();
  117. //
  118. // The class library's private data is after the miniclass's.
  119. //
  120. (ULONG_PTR)mcdClassData += miniclassExtSize;
  121. if (irpStack->MajorFunction == IRP_MJ_CLOSE) {
  122. DebugPrint((3,
  123. "ChangerClassCreateClose - IRP_MJ_CLOSE\n"));
  124. //
  125. // Indicate that the device is available for others.
  126. //
  127. mcdClassData->DeviceOpen = 0;
  128. status = STATUS_SUCCESS;
  129. } else if (irpStack->MajorFunction == IRP_MJ_CREATE) {
  130. DebugPrint((3,
  131. "ChangerClassCreateClose - IRP_MJ_CREATE\n"));
  132. //
  133. // If already opened, return busy.
  134. //
  135. if (mcdClassData->DeviceOpen) {
  136. DebugPrint((1,
  137. "ChangerClassCreateClose - returning DEVICE_BUSY. DeviceOpen - %x\n",
  138. mcdClassData->DeviceOpen));
  139. status = STATUS_DEVICE_BUSY;
  140. } else {
  141. //
  142. // Indicate that the device is busy.
  143. //
  144. InterlockedIncrement(&mcdClassData->DeviceOpen);
  145. status = STATUS_SUCCESS;
  146. }
  147. }
  148. Irp->IoStatus.Status = status;
  149. ClassReleaseRemoveLock(DeviceObject, Irp);
  150. ClassCompleteRequest(DeviceObject,Irp, IO_NO_INCREMENT);
  151. return status;
  152. } // end ChangerCreate()
  153. NTSTATUS
  154. ChangerClassDeviceControl (
  155. IN PDEVICE_OBJECT DeviceObject,
  156. IN PIRP Irp
  157. )
  158. {
  159. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  160. PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
  161. NTSTATUS status;
  162. PAGED_CODE();
  163. switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
  164. case IOCTL_CHANGER_GET_PARAMETERS:
  165. DebugPrint((3,
  166. "Mcd.ChangerDeviceControl: IOCTL_CHANGER_GET_PARAMETERS\n"));
  167. //
  168. // Validate buffer length.
  169. //
  170. if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
  171. sizeof(GET_CHANGER_PARAMETERS)) {
  172. status = STATUS_INFO_LENGTH_MISMATCH;
  173. } else {
  174. status = ChangerGetParameters(DeviceObject, Irp);
  175. }
  176. break;
  177. case IOCTL_CHANGER_GET_STATUS:
  178. DebugPrint((3,
  179. "Mcd.ChangerDeviceControl: IOCTL_CHANGER_GET_STATUS\n"));
  180. status = ChangerGetStatus(DeviceObject, Irp);
  181. break;
  182. case IOCTL_CHANGER_GET_PRODUCT_DATA:
  183. DebugPrint((3,
  184. "Mcd.ChangerDeviceControl: IOCTL_CHANGER_GET_PRODUCT_DATA\n"));
  185. if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
  186. sizeof(CHANGER_PRODUCT_DATA)) {
  187. status = STATUS_INFO_LENGTH_MISMATCH;
  188. } else {
  189. status = ChangerGetProductData(DeviceObject, Irp);
  190. }
  191. break;
  192. case IOCTL_CHANGER_SET_ACCESS:
  193. DebugPrint((3,
  194. "Mcd.ChangerDeviceControl: IOCTL_CHANGER_SET_ACCESS\n"));
  195. if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
  196. sizeof(CHANGER_SET_ACCESS)) {
  197. status = STATUS_INFO_LENGTH_MISMATCH;
  198. } else {
  199. status = ChangerSetAccess(DeviceObject, Irp);
  200. }
  201. break;
  202. case IOCTL_CHANGER_GET_ELEMENT_STATUS:
  203. DebugPrint((3,
  204. "Mcd.ChangerDeviceControl: IOCTL_CHANGER_GET_ELEMENT_STATUS\n"));
  205. if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
  206. sizeof(CHANGER_READ_ELEMENT_STATUS)) {
  207. status = STATUS_INFO_LENGTH_MISMATCH;
  208. } else {
  209. PCHANGER_READ_ELEMENT_STATUS readElementStatus = Irp->AssociatedIrp.SystemBuffer;
  210. ULONG length = readElementStatus->ElementList.NumberOfElements * sizeof(CHANGER_ELEMENT_STATUS);
  211. //
  212. // Further validate parameters.
  213. //
  214. if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < length) {
  215. status = STATUS_BUFFER_TOO_SMALL;
  216. } else if (length == 0) {
  217. status = STATUS_INVALID_PARAMETER;
  218. } else {
  219. status = ChangerGetElementStatus(DeviceObject, Irp);
  220. }
  221. }
  222. break;
  223. case IOCTL_CHANGER_INITIALIZE_ELEMENT_STATUS:
  224. DebugPrint((3,
  225. "Mcd.ChangerDeviceControl: IOCTL_CHANGER_INITIALIZE_ELEMENT_STATUS\n"));
  226. if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
  227. sizeof(CHANGER_INITIALIZE_ELEMENT_STATUS)) {
  228. status = STATUS_INFO_LENGTH_MISMATCH;
  229. } else {
  230. status = ChangerInitializeElementStatus(DeviceObject, Irp);
  231. }
  232. break;
  233. case IOCTL_CHANGER_SET_POSITION:
  234. DebugPrint((3,
  235. "Mcd.ChangerDeviceControl: IOCTL_CHANGER_SET_POSITION\n"));
  236. if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
  237. sizeof(CHANGER_SET_POSITION)) {
  238. status = STATUS_INFO_LENGTH_MISMATCH;
  239. } else {
  240. status = ChangerSetPosition(DeviceObject, Irp);
  241. }
  242. break;
  243. case IOCTL_CHANGER_EXCHANGE_MEDIUM:
  244. DebugPrint((3,
  245. "Mcd.ChangerDeviceControl: IOCTL_CHANGER_EXCHANGE_MEDIUM\n"));
  246. status = ChangerExchangeMedium(DeviceObject, Irp);
  247. break;
  248. case IOCTL_CHANGER_MOVE_MEDIUM:
  249. DebugPrint((3,
  250. "Mcd.ChangerDeviceControl: IOCTL_CHANGER_MOVE_MEDIUM\n"));
  251. if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
  252. sizeof(CHANGER_MOVE_MEDIUM)) {
  253. status = STATUS_INFO_LENGTH_MISMATCH;
  254. } else {
  255. status = ChangerMoveMedium(DeviceObject, Irp);
  256. }
  257. break;
  258. case IOCTL_CHANGER_REINITIALIZE_TRANSPORT:
  259. DebugPrint((3,
  260. "Mcd.ChangerDeviceControl: IOCTL_CHANGER_REINITIALIZE_TRANSPORT\n"));
  261. if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
  262. sizeof(CHANGER_ELEMENT)) {
  263. status = STATUS_INFO_LENGTH_MISMATCH;
  264. } else {
  265. status = ChangerReinitializeUnit(DeviceObject, Irp);
  266. }
  267. break;
  268. case IOCTL_CHANGER_QUERY_VOLUME_TAGS:
  269. DebugPrint((3,
  270. "Mcd.ChangerDeviceControl: IOCTL_CHANGER_QUERY_VOLUME_TAGS\n"));
  271. if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
  272. sizeof(CHANGER_SEND_VOLUME_TAG_INFORMATION)) {
  273. status = STATUS_INFO_LENGTH_MISMATCH;
  274. } else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
  275. sizeof(READ_ELEMENT_ADDRESS_INFO)) {
  276. status = STATUS_INFO_LENGTH_MISMATCH;
  277. } else {
  278. status = ChangerQueryVolumeTags(DeviceObject, Irp);
  279. }
  280. break;
  281. default:
  282. DebugPrint((1,
  283. "Mcd.ChangerDeviceControl: Unhandled IOCTL\n"));
  284. //
  285. // Pass the request to the common device control routine.
  286. //
  287. return ClassDeviceControl(DeviceObject, Irp);
  288. break;
  289. }
  290. Irp->IoStatus.Status = status;
  291. if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
  292. DebugPrint((1,
  293. "Mcd.ChangerDeviceControl: IOCTL %x, status %x\n",
  294. irpStack->Parameters.DeviceIoControl.IoControlCode,
  295. status));
  296. IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
  297. }
  298. ClassReleaseRemoveLock(DeviceObject, Irp);
  299. ClassCompleteRequest(DeviceObject,Irp, IO_NO_INCREMENT);
  300. return status;
  301. }
  302. VOID
  303. ChangerClassError(
  304. PDEVICE_OBJECT DeviceObject,
  305. PSCSI_REQUEST_BLOCK Srb,
  306. NTSTATUS *Status,
  307. BOOLEAN *Retry
  308. )
  309. /*++
  310. Routine Description:
  311. Arguments:
  312. DeviceObject
  313. Irp
  314. Return Value:
  315. Final Nt status indicating the results of the operation.
  316. Notes:
  317. --*/
  318. {
  319. PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
  320. PIRP irp = Srb->OriginalRequest;
  321. PAGED_CODE();
  322. if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
  323. switch (senseBuffer->SenseKey & 0xf) {
  324. case SCSI_SENSE_ILLEGAL_REQUEST:
  325. switch (senseBuffer->AdditionalSenseCode) {
  326. case SCSI_ADSENSE_ILLEGAL_BLOCK:
  327. if (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_ILLEGAL_ELEMENT_ADDR ) {
  328. DebugPrint((1,
  329. "MediumChanger: An operation was attempted on an invalid element\n"));
  330. //
  331. // Attemped operation to an invalid element.
  332. //
  333. *Retry = FALSE;
  334. *Status = STATUS_ILLEGAL_ELEMENT_ADDRESS;
  335. }
  336. break;
  337. case SCSI_ADSENSE_POSITION_ERROR:
  338. if (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_SOURCE_EMPTY) {
  339. DebugPrint((1,
  340. "MediumChanger: The specified source element has no media\n"));
  341. //
  342. // The indicated source address has no media.
  343. //
  344. *Retry = FALSE;
  345. *Status = STATUS_SOURCE_ELEMENT_EMPTY;
  346. } else if (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_DESTINATION_FULL) {
  347. DebugPrint((1,
  348. "MediumChanger: The specified destination element already has media.\n"));
  349. //
  350. // The indicated destination already contains media.
  351. //
  352. *Retry = FALSE;
  353. *Status = STATUS_DESTINATION_ELEMENT_FULL;
  354. }
  355. break;
  356. default:
  357. break;
  358. }
  359. default:
  360. break;
  361. } // end switch
  362. }
  363. //
  364. // Allow device-specific module to update this.
  365. //
  366. ChangerError(DeviceObject, Srb, Status, Retry);
  367. return;
  368. }
  369. NTSTATUS
  370. ChangerAddDevice(
  371. IN PDRIVER_OBJECT DriverObject,
  372. IN PDEVICE_OBJECT PhysicalDeviceObject
  373. )
  374. /*++
  375. Routine Description:
  376. This routine creates and initializes a new FDO for the corresponding
  377. PDO. It may perform property queries on the FDO but cannot do any
  378. media access operations.
  379. Arguments:
  380. DriverObject - MC class driver object.
  381. Pdo - the physical device object we are being added to
  382. Return Value:
  383. status
  384. --*/
  385. {
  386. PULONG devicesFound = NULL;
  387. NTSTATUS status;
  388. PAGED_CODE();
  389. //
  390. // Get the address of the count of the number of tape devices already initialized.
  391. //
  392. devicesFound = &IoGetConfigurationInformation()->MediumChangerCount;
  393. status = CreateChangerDeviceObject(DriverObject,
  394. PhysicalDeviceObject);
  395. if(NT_SUCCESS(status)) {
  396. (*devicesFound)++;
  397. }
  398. return status;
  399. }
  400. NTSTATUS
  401. ChangerStartDevice(
  402. IN PDEVICE_OBJECT Fdo
  403. )
  404. /*++
  405. Routine Description:
  406. This routine is called after InitDevice, and creates the symbolic link,
  407. and sets up information in the registry.
  408. The routine could be called multiple times, in the event of a StopDevice.
  409. Arguments:
  410. Fdo - a pointer to the functional device object for this device
  411. Return Value:
  412. status
  413. --*/
  414. {
  415. PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
  416. PINQUIRYDATA inquiryData = NULL;
  417. ULONG pageLength;
  418. ULONG inquiryLength;
  419. SCSI_REQUEST_BLOCK srb;
  420. PCDB cdb;
  421. NTSTATUS status;
  422. PMCD_CLASS_DATA mcdClassData = (PMCD_CLASS_DATA)fdoExtension->CommonExtension.DriverData;
  423. ULONG miniClassExtSize = ChangerAdditionalExtensionSize();
  424. PAGED_CODE();
  425. //
  426. // Build and send request to get inquiry data.
  427. //
  428. inquiryData = ExAllocatePool(NonPagedPoolCacheAligned, MAXIMUM_CHANGER_INQUIRY_DATA);
  429. if (!inquiryData) {
  430. //
  431. // The buffer cannot be allocated.
  432. //
  433. return STATUS_INSUFFICIENT_RESOURCES;
  434. }
  435. RtlZeroMemory(&srb, SCSI_REQUEST_BLOCK_SIZE);
  436. //
  437. // Set timeout value.
  438. //
  439. srb.TimeOutValue = 2;
  440. srb.CdbLength = 6;
  441. cdb = (PCDB)srb.Cdb;
  442. //
  443. // Set CDB operation code.
  444. //
  445. cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
  446. //
  447. // Set allocation length to inquiry data buffer size.
  448. //
  449. cdb->CDB6INQUIRY.AllocationLength = MAXIMUM_CHANGER_INQUIRY_DATA;
  450. status = ClassSendSrbSynchronous(Fdo,
  451. &srb,
  452. inquiryData,
  453. MAXIMUM_CHANGER_INQUIRY_DATA,
  454. FALSE);
  455. if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_SUCCESS ||
  456. SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
  457. srb.SrbStatus = SRB_STATUS_SUCCESS;
  458. }
  459. if (srb.SrbStatus == SRB_STATUS_SUCCESS) {
  460. inquiryLength = inquiryData->AdditionalLength + FIELD_OFFSET(INQUIRYDATA, Reserved);
  461. if (inquiryLength > srb.DataTransferLength) {
  462. inquiryLength = srb.DataTransferLength;
  463. }
  464. } else {
  465. //
  466. // The class function will only write inquiryLength of inquiryData
  467. // to the reg. key.
  468. //
  469. inquiryLength = 0;
  470. }
  471. //
  472. // Add changer device info to registry
  473. //
  474. ClassUpdateInformationInRegistry(Fdo,
  475. "Changer",
  476. fdoExtension->DeviceNumber,
  477. inquiryData,
  478. inquiryLength);
  479. ExFreePool(inquiryData);
  480. return STATUS_SUCCESS;
  481. }
  482. NTSTATUS
  483. ChangerStopDevice(
  484. IN PDEVICE_OBJECT DeviceObject,
  485. IN UCHAR Type
  486. )
  487. {
  488. return STATUS_SUCCESS;
  489. }
  490. #define CHANGER_SRB_LIST_SIZE 2
  491. NTSTATUS
  492. ChangerInitDevice(
  493. IN PDEVICE_OBJECT Fdo
  494. )
  495. /*++
  496. Routine Description:
  497. This routine will complete the changer initialization. This includes
  498. allocating sense info buffers and srb s-lists. Additionally, the miniclass
  499. driver's init entry points are called.
  500. This routine will not clean up allocate resources if it fails - that
  501. is left for device stop/removal
  502. Arguments:
  503. Fdo - a pointer to the functional device object for this device
  504. Return Value:
  505. NTSTATUS
  506. --*/
  507. {
  508. PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
  509. PVOID senseData = NULL;
  510. NTSTATUS status;
  511. PVOID minitapeExtension;
  512. STORAGE_PROPERTY_ID propertyId;
  513. UNICODE_STRING interfaceName;
  514. PMCD_CLASS_DATA mcdClassData;
  515. PAGED_CODE();
  516. //
  517. // Allocate request sense buffer.
  518. //
  519. senseData = ExAllocatePool(NonPagedPoolCacheAligned,
  520. SENSE_BUFFER_SIZE);
  521. if (senseData == NULL) {
  522. //
  523. // The buffer cannot be allocated.
  524. //
  525. status = STATUS_INSUFFICIENT_RESOURCES;
  526. goto ChangerInitDeviceExit;
  527. }
  528. //
  529. // Build the lookaside list for srb's for the device. Should only
  530. // need a couple.
  531. //
  532. ClassInitializeSrbLookasideList(&(fdoExtension->CommonExtension), CHANGER_SRB_LIST_SIZE);
  533. //
  534. // Set the sense data pointer in the device extension.
  535. //
  536. fdoExtension->SenseData = senseData;
  537. fdoExtension->TimeOutValue = 600;
  538. //
  539. // Call port driver to get adapter capabilities.
  540. //
  541. propertyId = StorageAdapterProperty;
  542. status = ClassGetDescriptor(fdoExtension->CommonExtension.LowerDeviceObject,
  543. &propertyId,
  544. &(fdoExtension->AdapterDescriptor));
  545. if(!NT_SUCCESS(status)) {
  546. DebugPrint((1,
  547. "ChangerStartDevice: Unable to get adapter descriptor. Status %x\n",
  548. status));
  549. goto ChangerInitDeviceExit;
  550. }
  551. //
  552. // Invoke the device-specific initialization function.
  553. //
  554. status = ChangerInitialize(Fdo);
  555. //
  556. // Register interfaces for this device.
  557. //
  558. RtlInitUnicodeString(&interfaceName, NULL);
  559. status = IoRegisterDeviceInterface(fdoExtension->LowerPdo,
  560. (LPGUID) &MediumChangerClassGuid,
  561. NULL,
  562. &interfaceName);
  563. if(NT_SUCCESS(status)) {
  564. ULONG miniclassExtSize;
  565. mcdClassData = (PMCD_CLASS_DATA)(fdoExtension->CommonExtension.DriverData);
  566. miniclassExtSize = ChangerAdditionalExtensionSize();
  567. //
  568. // The class library's private data is after the miniclass's.
  569. //
  570. (ULONG_PTR)mcdClassData += miniclassExtSize;
  571. mcdClassData->MediumChangerInterfaceString = interfaceName;
  572. status = IoSetDeviceInterfaceState(
  573. &interfaceName,
  574. TRUE);
  575. if(!NT_SUCCESS(status)) {
  576. DebugPrint((1,
  577. "ChangerInitDevice: Unable to register Changer%x interface name - %x.\n",
  578. fdoExtension->DeviceNumber,
  579. status));
  580. status = STATUS_SUCCESS;
  581. }
  582. }
  583. return status;
  584. //
  585. // Fall through and return whatever status the miniclass driver returned.
  586. //
  587. ChangerInitDeviceExit:
  588. if (senseData) {
  589. ExFreePool(senseData);
  590. }
  591. return status;
  592. } // End ChangerStartDevice
  593. NTSTATUS
  594. ChangerRemoveDevice(
  595. IN PDEVICE_OBJECT DeviceObject,
  596. IN UCHAR Type
  597. )
  598. /*++
  599. Routine Description:
  600. This routine is responsible for releasing any resources in use by the
  601. tape driver.
  602. Arguments:
  603. DeviceObject - the device object being removed
  604. Return Value:
  605. none - this routine may not fail
  606. --*/
  607. {
  608. PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
  609. PMCD_CLASS_DATA mcdClassData = (PMCD_CLASS_DATA)fdoExtension->CommonExtension.DriverData;
  610. ULONG miniClassExtSize = ChangerAdditionalExtensionSize();
  611. WCHAR dosNameBuffer[64];
  612. UNICODE_STRING dosUnicodeString;
  613. NTSTATUS status;
  614. PAGED_CODE();
  615. if((Type == IRP_MN_QUERY_REMOVE_DEVICE) ||
  616. (Type == IRP_MN_CANCEL_REMOVE_DEVICE)) {
  617. return STATUS_SUCCESS;
  618. }
  619. //
  620. // Free all allocated memory.
  621. //
  622. if (fdoExtension->DeviceDescriptor) {
  623. ExFreePool(fdoExtension->DeviceDescriptor);
  624. fdoExtension->DeviceDescriptor = NULL;
  625. }
  626. if (fdoExtension->AdapterDescriptor) {
  627. ExFreePool(fdoExtension->AdapterDescriptor);
  628. fdoExtension->AdapterDescriptor = NULL;
  629. }
  630. if (fdoExtension->SenseData) {
  631. ExFreePool(fdoExtension->SenseData);
  632. fdoExtension->SenseData = NULL;
  633. }
  634. //
  635. // Remove the lookaside list.
  636. //
  637. ClassDeleteSrbLookasideList(&fdoExtension->CommonExtension);
  638. (ULONG_PTR)mcdClassData += miniClassExtSize;
  639. if(mcdClassData->MediumChangerInterfaceString.Buffer != NULL) {
  640. IoSetDeviceInterfaceState(&(mcdClassData->MediumChangerInterfaceString),
  641. FALSE);
  642. RtlFreeUnicodeString(&(mcdClassData->MediumChangerInterfaceString));
  643. //
  644. // Clear it.
  645. //
  646. RtlInitUnicodeString(&(mcdClassData->MediumChangerInterfaceString), NULL);
  647. }
  648. //
  649. // Delete the symbolic link "changerN".
  650. //
  651. if(mcdClassData->DosNameCreated) {
  652. swprintf(dosNameBuffer,
  653. L"\\DosDevices\\Changer%d",
  654. fdoExtension->DeviceNumber);
  655. RtlInitUnicodeString(&dosUnicodeString, dosNameBuffer);
  656. IoDeleteSymbolicLink(&dosUnicodeString);
  657. mcdClassData->DosNameCreated = FALSE;
  658. }
  659. //
  660. // Remove registry bits.
  661. //
  662. if(Type == IRP_MN_REMOVE_DEVICE) {
  663. IoGetConfigurationInformation()->MediumChangerCount--;
  664. }
  665. return STATUS_SUCCESS;
  666. }
  667. NTSTATUS
  668. ChangerReadWriteVerification(
  669. IN PDEVICE_OBJECT DeviceObject,
  670. IN PIRP Irp
  671. )
  672. /*++
  673. Routine Description:
  674. This routine is a stub that returns invalid device request.
  675. Arguments:
  676. DeviceObject - Supplies the device object.
  677. Irp - Supplies the I/O request packet.
  678. Return Value:
  679. STATUS_INVALID_DEVICE_REQUEST
  680. --*/
  681. {
  682. return STATUS_INVALID_DEVICE_REQUEST;
  683. }
  684. NTSTATUS
  685. DriverEntry(
  686. IN PDRIVER_OBJECT DriverObject,
  687. IN PUNICODE_STRING RegistryPath
  688. )
  689. /*++
  690. Routine Description:
  691. This routine is called at system initialization time to initialize
  692. this driver.
  693. Arguments:
  694. DriverObject - Supplies the driver object.
  695. RegistryPath - Supplies the registry path for this driver.
  696. Return Value:
  697. STATUS_SUCCESS - We could initialize at least one device.
  698. STATUS_NO_SUCH_DEVICE - We could not initialize even one device.
  699. --*/
  700. {
  701. CLASS_INIT_DATA InitializationData;
  702. //
  703. // Zero InitData
  704. //
  705. RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA));
  706. //
  707. // Set sizes
  708. //
  709. InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
  710. InitializationData.FdoData.DeviceExtensionSize = sizeof(FUNCTIONAL_DEVICE_EXTENSION) + ChangerAdditionalExtensionSize() + sizeof(MCD_CLASS_DATA);
  711. InitializationData.FdoData.DeviceType = FILE_DEVICE_CHANGER;
  712. InitializationData.FdoData.DeviceCharacteristics = 0;
  713. //
  714. // Set entry points
  715. //
  716. InitializationData.FdoData.ClassStartDevice = ChangerStartDevice;
  717. InitializationData.FdoData.ClassInitDevice = ChangerInitDevice;
  718. InitializationData.FdoData.ClassStopDevice = ChangerStopDevice;
  719. InitializationData.FdoData.ClassRemoveDevice = ChangerRemoveDevice;
  720. InitializationData.ClassAddDevice = ChangerAddDevice;
  721. InitializationData.FdoData.ClassReadWriteVerification = NULL;
  722. InitializationData.FdoData.ClassDeviceControl = ChangerClassDeviceControl;
  723. InitializationData.FdoData.ClassError = ChangerClassError;
  724. InitializationData.FdoData.ClassShutdownFlush = NULL;
  725. InitializationData.FdoData.ClassCreateClose = ChangerClassCreateClose;
  726. //
  727. // Stub routine to make the class driver happy.
  728. //
  729. InitializationData.FdoData.ClassReadWriteVerification = ChangerReadWriteVerification;
  730. InitializationData.ClassUnload = ChangerUnload;
  731. //
  732. // Call the class init routine
  733. //
  734. return ClassInitialize( DriverObject, RegistryPath, &InitializationData);
  735. }
  736. VOID
  737. ChangerUnload(
  738. IN PDRIVER_OBJECT DriverObject
  739. )
  740. {
  741. PAGED_CODE();
  742. UNREFERENCED_PARAMETER(DriverObject);
  743. return;
  744. }
  745. NTSTATUS
  746. CreateChangerDeviceObject(
  747. IN PDRIVER_OBJECT DriverObject,
  748. IN PDEVICE_OBJECT PhysicalDeviceObject
  749. )
  750. /*++
  751. Routine Description:
  752. This routine creates an object for the device and then searches
  753. the device for partitions and creates an object for each partition.
  754. Arguments:
  755. DriverObject - Pointer to driver object created by system.
  756. PhysicalDeviceObject - DeviceObject of the attached to device.
  757. Return Value:
  758. NTSTATUS
  759. --*/
  760. {
  761. PDEVICE_OBJECT lowerDevice;
  762. PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
  763. CCHAR deviceNameBuffer[64];
  764. NTSTATUS status;
  765. PDEVICE_OBJECT deviceObject;
  766. ULONG requiredStackSize;
  767. PVOID senseData;
  768. WCHAR dosNameBuffer[64];
  769. WCHAR wideNameBuffer[64];
  770. UNICODE_STRING dosUnicodeString;
  771. UNICODE_STRING deviceUnicodeString;
  772. PMCD_CLASS_DATA mcdClassData;
  773. ULONG mcdCount;
  774. PAGED_CODE();
  775. DebugPrint((3,"CreateChangerDeviceObject: Enter routine\n"));
  776. lowerDevice = IoGetAttachedDeviceReference(PhysicalDeviceObject);
  777. //
  778. // Claim the device. Note that any errors after this
  779. // will goto the generic handler, where the device will
  780. // be released.
  781. //
  782. status = ClassClaimDevice(lowerDevice, FALSE);
  783. if(!NT_SUCCESS(status)) {
  784. //
  785. // Someone already had this device.
  786. //
  787. ObDereferenceObject(lowerDevice);
  788. return status;
  789. }
  790. //
  791. // Create device object for this device.
  792. //
  793. mcdCount = 0;
  794. do {
  795. sprintf(deviceNameBuffer,
  796. "\\Device\\Changer%d",
  797. mcdCount);
  798. status = ClassCreateDeviceObject(DriverObject,
  799. deviceNameBuffer,
  800. PhysicalDeviceObject,
  801. TRUE,
  802. &deviceObject);
  803. mcdCount++;
  804. } while (status == STATUS_OBJECT_NAME_COLLISION);
  805. if (!NT_SUCCESS(status)) {
  806. DebugPrint((1,"CreateChangerDeviceObjects: Can not create device %s\n",
  807. deviceNameBuffer));
  808. goto CreateChangerDeviceObjectExit;
  809. }
  810. //
  811. // Indicate that IRPs should include MDLs.
  812. //
  813. deviceObject->Flags |= DO_DIRECT_IO;
  814. fdoExtension = deviceObject->DeviceExtension;
  815. //
  816. // Back pointer to device object.
  817. //
  818. fdoExtension->CommonExtension.DeviceObject = deviceObject;
  819. //
  820. // This is the physical device.
  821. //
  822. fdoExtension->CommonExtension.PartitionZeroExtension = fdoExtension;
  823. //
  824. // Initialize lock count to zero. The lock count is used to
  825. // disable the ejection mechanism when media is mounted.
  826. //
  827. fdoExtension->LockCount = 0;
  828. //
  829. // Save system tape number
  830. //
  831. fdoExtension->DeviceNumber = mcdCount - 1;
  832. //
  833. // Set the alignment requirements for the device based on the
  834. // host adapter requirements
  835. //
  836. if (lowerDevice->AlignmentRequirement > deviceObject->AlignmentRequirement) {
  837. deviceObject->AlignmentRequirement = lowerDevice->AlignmentRequirement;
  838. }
  839. //
  840. // Save the device descriptors
  841. //
  842. fdoExtension->AdapterDescriptor = NULL;
  843. fdoExtension->DeviceDescriptor = NULL;
  844. //
  845. // Clear the SrbFlags and disable synchronous transfers
  846. //
  847. fdoExtension->SrbFlags = SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
  848. //
  849. // Attach to the PDO
  850. //
  851. fdoExtension->LowerPdo = PhysicalDeviceObject;
  852. fdoExtension->CommonExtension.LowerDeviceObject =
  853. IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
  854. if(fdoExtension->CommonExtension.LowerDeviceObject == NULL) {
  855. //
  856. // The attach failed. Cleanup and return.
  857. //
  858. status = STATUS_UNSUCCESSFUL;
  859. goto CreateChangerDeviceObjectExit;
  860. }
  861. //
  862. // Create the dos port driver name.
  863. //
  864. swprintf(dosNameBuffer,
  865. L"\\DosDevices\\Changer%d",
  866. fdoExtension->DeviceNumber);
  867. RtlInitUnicodeString(&dosUnicodeString, dosNameBuffer);
  868. //
  869. // Recreate the deviceName
  870. //
  871. swprintf(wideNameBuffer,
  872. L"\\Device\\Changer%d",
  873. fdoExtension->DeviceNumber);
  874. RtlInitUnicodeString(&deviceUnicodeString,
  875. wideNameBuffer);
  876. mcdClassData = (PMCD_CLASS_DATA)(fdoExtension->CommonExtension.DriverData);
  877. (ULONG_PTR)mcdClassData += ChangerAdditionalExtensionSize();
  878. if (NT_SUCCESS(IoAssignArcName(&dosUnicodeString, &deviceUnicodeString))) {
  879. mcdClassData->DosNameCreated = TRUE;
  880. } else {
  881. mcdClassData->DosNameCreated = FALSE;
  882. }
  883. //
  884. // The device is initialized properly - mark it as such.
  885. //
  886. deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
  887. ObDereferenceObject(lowerDevice);
  888. return(STATUS_SUCCESS);
  889. CreateChangerDeviceObjectExit:
  890. //
  891. // Release the device since an error occured.
  892. //
  893. // ClassClaimDevice(PortDeviceObject,
  894. // LunInfo,
  895. // TRUE,
  896. // NULL);
  897. ObDereferenceObject(lowerDevice);
  898. if (deviceObject != NULL) {
  899. IoDeleteDevice(deviceObject);
  900. }
  901. return status;
  902. } // end CreateChangerDeviceObject()
  903. NTSTATUS
  904. ChangerClassSendSrbSynchronous(
  905. IN PDEVICE_OBJECT Fdo,
  906. IN PSCSI_REQUEST_BLOCK Srb,
  907. IN PVOID BufferAddress,
  908. IN ULONG BufferLength,
  909. IN BOOLEAN WriteToDevice
  910. )
  911. {
  912. PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
  913. IO_STATUS_BLOCK ioStatus;
  914. ULONG controlType;
  915. PIRP irp;
  916. PIO_STACK_LOCATION irpStack;
  917. KEVENT event;
  918. PUCHAR senseInfoBuffer;
  919. ULONG retryCount = MAXIMUM_RETRIES;
  920. NTSTATUS status;
  921. BOOLEAN retry;
  922. //
  923. // NOTE: While this code may look as though it could be pagable,
  924. // making it pagable creates the possibility of a page
  925. // boundary between IoCallDriver() and ClassReleaseQueue(),
  926. // which could leave the queue frozen as we try to page in
  927. // this code, which is required to unfreeze the queue.
  928. // The result would be a nice case of deadlock.
  929. //
  930. ASSERT(fdoExtension->CommonExtension.IsFdo);
  931. //
  932. // Write length to SRB.
  933. //
  934. Srb->Length = SCSI_REQUEST_BLOCK_SIZE;
  935. //
  936. // Set SCSI bus address.
  937. //
  938. Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
  939. //
  940. // NOTICE: The SCSI-II specification indicates that this field should be
  941. // zero; however, some target controllers ignore the logical unit number
  942. // in the INDENTIFY message and only look at the logical unit number field
  943. // in the CDB.
  944. //
  945. // Srb->Cdb[1] |= deviceExtension->Lun << 5;
  946. //
  947. // Enable auto request sense.
  948. //
  949. Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
  950. //
  951. // Sense buffer is in aligned nonpaged pool.
  952. //
  953. senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
  954. SENSE_BUFFER_SIZE,
  955. '7CcS');
  956. if (senseInfoBuffer == NULL) {
  957. DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense "
  958. "buffer\n"));
  959. return(STATUS_INSUFFICIENT_RESOURCES);
  960. }
  961. Srb->SenseInfoBuffer = senseInfoBuffer;
  962. Srb->DataBuffer = BufferAddress;
  963. if(BufferAddress != NULL) {
  964. if(WriteToDevice) {
  965. Srb->SrbFlags = SRB_FLAGS_DATA_OUT;
  966. } else {
  967. Srb->SrbFlags = SRB_FLAGS_DATA_IN;
  968. }
  969. } else {
  970. Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
  971. }
  972. //
  973. // Start retries here.
  974. //
  975. retry:
  976. //
  977. // Set the event object to the unsignaled state.
  978. // It will be used to signal request completion.
  979. //
  980. KeInitializeEvent(&event, NotificationEvent, FALSE);
  981. //
  982. // Build device I/O control request with METHOD_NEITHER data transfer.
  983. // We'll queue a completion routine to cleanup the MDL's and such ourself.
  984. //
  985. irp = IoAllocateIrp(
  986. (CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
  987. FALSE);
  988. if(irp == NULL) {
  989. ExFreePool(senseInfoBuffer);
  990. DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
  991. return(STATUS_INSUFFICIENT_RESOURCES);
  992. }
  993. //
  994. // Get next stack location.
  995. //
  996. irpStack = IoGetNextIrpStackLocation(irp);
  997. //
  998. // Set up SRB for execute scsi request. Save SRB address in next stack
  999. // for the port driver.
  1000. //
  1001. irpStack->MajorFunction = IRP_MJ_SCSI;
  1002. irpStack->Parameters.Scsi.Srb = Srb;
  1003. IoSetCompletionRoutine(irp,
  1004. ClasspSendSynchronousCompletion,
  1005. Srb,
  1006. TRUE,
  1007. TRUE,
  1008. TRUE);
  1009. irp->UserIosb = &ioStatus;
  1010. irp->UserEvent = &event;
  1011. if(BufferAddress) {
  1012. //
  1013. // Build an MDL for the data buffer and stick it into the irp. The
  1014. // completion routine will unlock the pages and free the MDL.
  1015. //
  1016. irp->MdlAddress = IoAllocateMdl( BufferAddress,
  1017. BufferLength,
  1018. FALSE,
  1019. FALSE,
  1020. irp );
  1021. if (irp->MdlAddress == NULL) {
  1022. ExFreePool(senseInfoBuffer);
  1023. Srb->SenseInfoBuffer = NULL;
  1024. IoFreeIrp( irp );
  1025. DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
  1026. return STATUS_INSUFFICIENT_RESOURCES;
  1027. }
  1028. try {
  1029. MmProbeAndLockPages( irp->MdlAddress,
  1030. KernelMode,
  1031. (WriteToDevice ? IoReadAccess :
  1032. IoWriteAccess));
  1033. } except(EXCEPTION_EXECUTE_HANDLER) {
  1034. status = GetExceptionCode();
  1035. ExFreePool(senseInfoBuffer);
  1036. Srb->SenseInfoBuffer = NULL;
  1037. IoFreeMdl(irp->MdlAddress);
  1038. IoFreeIrp(irp);
  1039. DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx "
  1040. "locking buffer\n", status));
  1041. return status;
  1042. }
  1043. }
  1044. //
  1045. // Disable synchronous transfer for these requests.
  1046. //
  1047. SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
  1048. //
  1049. // Set the transfer length.
  1050. //
  1051. Srb->DataTransferLength = BufferLength;
  1052. //
  1053. // Zero out status.
  1054. //
  1055. Srb->ScsiStatus = Srb->SrbStatus = 0;
  1056. Srb->NextSrb = 0;
  1057. //
  1058. // Set up IRP Address.
  1059. //
  1060. Srb->OriginalRequest = irp;
  1061. //
  1062. // Call the port driver with the request and wait for it to complete.
  1063. //
  1064. status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
  1065. if (status == STATUS_PENDING) {
  1066. KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
  1067. status = ioStatus.Status;
  1068. }
  1069. //
  1070. // Check that request completed without error.
  1071. //
  1072. if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
  1073. ULONG retryInterval;
  1074. //
  1075. // Release the queue if it is frozen.
  1076. //
  1077. if (Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
  1078. ClassReleaseQueue(Fdo);
  1079. }
  1080. //
  1081. // Update status and determine if request should be retried.
  1082. //
  1083. retry = ClassInterpretSenseInfo(Fdo,
  1084. Srb,
  1085. IRP_MJ_SCSI,
  1086. 0,
  1087. MAXIMUM_RETRIES - retryCount,
  1088. &status,
  1089. &retryInterval);
  1090. if (retry) {
  1091. if ((status == STATUS_DEVICE_NOT_READY &&
  1092. ((PSENSE_DATA) senseInfoBuffer)->AdditionalSenseCode ==
  1093. SCSI_ADSENSE_LUN_NOT_READY) ||
  1094. (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
  1095. LARGE_INTEGER delay;
  1096. //
  1097. // Delay for at least 2 seconds.
  1098. //
  1099. if(retryInterval < 2) {
  1100. retryInterval = 2;
  1101. }
  1102. delay.QuadPart = (LONGLONG)( - 10 * 1000 * (LONGLONG)1000 * retryInterval);
  1103. //
  1104. // Stall for a while to let the controller spinup.
  1105. //
  1106. KeDelayExecutionThread(KernelMode, FALSE, &delay);
  1107. }
  1108. //
  1109. // If retries are not exhausted then retry this operation.
  1110. //
  1111. if (retryCount--) {
  1112. goto retry;
  1113. }
  1114. }
  1115. } else {
  1116. status = STATUS_SUCCESS;
  1117. }
  1118. Srb->SenseInfoBuffer = NULL;
  1119. ExFreePool(senseInfoBuffer);
  1120. return status;
  1121. }
  1122. NTSTATUS
  1123. ClasspSendSynchronousCompletion(
  1124. IN PDEVICE_OBJECT DeviceObject,
  1125. IN PIRP Irp,
  1126. IN PVOID Context
  1127. )
  1128. /*++
  1129. Routine Description:
  1130. This completion routine will set the user event in the irp after
  1131. freeing the irp and the associated MDL (if any).
  1132. Arguments:
  1133. DeviceObject - the device object which requested the completion routine
  1134. Irp - the irp being completed
  1135. Context - unused
  1136. Return Value:
  1137. STATUS_MORE_PROCESSING_REQUIRED
  1138. --*/
  1139. {
  1140. DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n",
  1141. DeviceObject, Irp, Context));
  1142. //
  1143. // First set the status and information fields in the io status block
  1144. // provided by the caller.
  1145. //
  1146. *(Irp->UserIosb) = Irp->IoStatus;
  1147. //
  1148. // Unlock the pages for the data buffer.
  1149. //
  1150. if(Irp->MdlAddress) {
  1151. MmUnlockPages(Irp->MdlAddress);
  1152. IoFreeMdl(Irp->MdlAddress);
  1153. }
  1154. //
  1155. // Signal the caller's event.
  1156. //
  1157. KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
  1158. //
  1159. // Free the MDL and the IRP.
  1160. //
  1161. IoFreeIrp(Irp);
  1162. return STATUS_MORE_PROCESSING_REQUIRED;
  1163. }
  1164. PVOID
  1165. ChangerClassAllocatePool(
  1166. IN POOL_TYPE PoolType,
  1167. IN ULONG NumberOfBytes
  1168. )
  1169. {
  1170. return ExAllocatePoolWithTag(PoolType, NumberOfBytes, 'CMcS');
  1171. }
  1172. VOID
  1173. ChangerClassFreePool(
  1174. IN PVOID PoolToFree
  1175. )
  1176. {
  1177. ExFreePool(PoolToFree);
  1178. }
  1179. #if DBG
  1180. ULONG MCDebug = 0;
  1181. UCHAR DebugBuffer[128];
  1182. #endif
  1183. #if DBG
  1184. VOID
  1185. MCDebugPrint(
  1186. ULONG DebugPrintLevel,
  1187. PCCHAR DebugMessage,
  1188. ...
  1189. )
  1190. /*++
  1191. Routine Description:
  1192. Debug print for all medium changer drivers
  1193. Arguments:
  1194. Debug print level between 0 and 3, with 3 being the most verbose.
  1195. Return Value:
  1196. None
  1197. --*/
  1198. {
  1199. va_list ap;
  1200. va_start(ap, DebugMessage);
  1201. if (DebugPrintLevel <= MCDebug) {
  1202. vsprintf(DebugBuffer, DebugMessage, ap);
  1203. DbgPrint(DebugBuffer);
  1204. }
  1205. va_end(ap);
  1206. } // end MCDebugPrint()
  1207. #else
  1208. //
  1209. // DebugPrint stub
  1210. //
  1211. VOID
  1212. MCDebugPrint(
  1213. ULONG DebugPrintLevel,
  1214. PCCHAR DebugMessage,
  1215. ...
  1216. )
  1217. {
  1218. }
  1219. #endif