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.

957 lines
22 KiB

  1. /*++
  2. Module Name:
  3. mca.c
  4. Abstract:
  5. Sample device driver to register itself with the HAL and log Machine Check
  6. Errors on a Intel Architecture Platform
  7. Author:
  8. Environment:
  9. Kernel mode
  10. Notes:
  11. Revision History:
  12. --*/
  13. #include <stdarg.h>
  14. #include <string.h>
  15. #include <stdio.h>
  16. #include <ntddk.h>
  17. #include "imca.h"
  18. //
  19. // Device names for the MCA driver
  20. //
  21. #define MCA_DEVICE_NAME "\\Device\\imca" // ANSI Name
  22. #define MCA_DEVICE_NAME_U L"\\Device\\imca" // Unicode Name
  23. #define MCA_DEVICE_NAME_DOS "\\DosDevices\\imca" // Device Name for Win32 App
  24. NTSTATUS
  25. DriverEntry(
  26. IN PDRIVER_OBJECT DriverObject,
  27. IN PUNICODE_STRING RegistryPath
  28. );
  29. NTSTATUS
  30. MCAOpen(
  31. IN PDEVICE_OBJECT DeviceObject,
  32. IN PIRP Irp
  33. );
  34. NTSTATUS
  35. MCAClose(
  36. IN PDEVICE_OBJECT DeviceObject,
  37. IN PIRP Irp
  38. );
  39. VOID
  40. MCAStartIo(
  41. IN PDEVICE_OBJECT DeviceObject,
  42. IN PIRP Irp
  43. );
  44. ERROR_SEVERITY
  45. MCADriverExceptionCallback(
  46. IN PDEVICE_OBJECT DeviceObject,
  47. IN PMCA_EXCEPTION InException
  48. );
  49. VOID
  50. MCADriverDpcCallback(
  51. IN PKDPC Dpc,
  52. IN PVOID DeferredContext,
  53. IN PVOID SystemContext1,
  54. IN PVOID SystemContext2
  55. );
  56. NTSTATUS
  57. MCACleanup(
  58. IN PDEVICE_OBJECT DeviceObject,
  59. IN PIRP Irp
  60. );
  61. VOID
  62. McaCancelIrp(
  63. IN PDEVICE_OBJECT DeviceObject,
  64. IN PIRP Irp
  65. );
  66. NTSTATUS
  67. MCADeviceControl(
  68. IN PDEVICE_OBJECT DeviceObject,
  69. IN PIRP Irp
  70. );
  71. VOID
  72. MCAUnload(
  73. IN PDRIVER_OBJECT DriverObject
  74. );
  75. NTSTATUS
  76. MCACreateSymbolicLinkObject(
  77. VOID
  78. );
  79. VOID
  80. MCAProcessWorkItem(
  81. PVOID Context
  82. );
  83. //
  84. // This temporary buffer holds the data between the Machine Check error
  85. // notification from HAL and the asynchronous IOCTL completion to the
  86. // application
  87. //
  88. typedef struct _MCA_DEVICE_EXTENSION {
  89. PDEVICE_OBJECT DeviceObject;
  90. PIRP SavedIrp;
  91. BOOLEAN WorkItemQueued;
  92. WORK_QUEUE_ITEM WorkItem;
  93. // Place to log the exceptions. Whenever the exception callback
  94. // routine is called by the HAL MCA component, record the exception here.
  95. // This can potentially be a link list.
  96. MCA_EXCEPTION McaException;
  97. } MCA_DEVICE_EXTENSION, *PMCA_DEVICE_EXTENSION;
  98. #ifdef ALLOC_PRAGMA
  99. #pragma alloc_text(INIT, DriverEntry)
  100. #pragma alloc_text(INIT, MCACreateSymbolicLinkObject)
  101. #endif // ALLOC_PRAGMA
  102. NTSTATUS
  103. DriverEntry(
  104. IN PDRIVER_OBJECT DriverObject,
  105. IN PUNICODE_STRING RegistryPath
  106. )
  107. /*++
  108. Routine Description:
  109. This routine does the driver specific initialization at entry time
  110. Arguments:
  111. DriverObject: Pointer to the driver object
  112. RegistryPath: Path to driver's registry key
  113. Return Value:
  114. Success or failure
  115. --*/
  116. {
  117. UNICODE_STRING UnicodeString;
  118. NTSTATUS Status = STATUS_SUCCESS;
  119. PMCA_DEVICE_EXTENSION Extension;
  120. PDEVICE_OBJECT McaDeviceObject;
  121. MCA_DRIVER_INFO McaDriverInfo;
  122. //
  123. // Create device object for MCA device.
  124. //
  125. RtlInitUnicodeString(&UnicodeString, MCA_DEVICE_NAME_U);
  126. //
  127. // Device is created as exclusive since only a single thread can send
  128. // I/O requests to this device
  129. //
  130. Status = IoCreateDevice(
  131. DriverObject,
  132. sizeof(MCA_DEVICE_EXTENSION),
  133. &UnicodeString,
  134. FILE_DEVICE_UNKNOWN,
  135. 0,
  136. TRUE,
  137. &McaDeviceObject
  138. );
  139. if (!NT_SUCCESS( Status )) {
  140. DbgPrint("Mca DriverEntry: IoCreateDevice failed\n");
  141. return Status;
  142. }
  143. McaDeviceObject->Flags |= DO_BUFFERED_IO;
  144. Extension = McaDeviceObject->DeviceExtension;
  145. RtlZeroMemory(Extension, sizeof(MCA_DEVICE_EXTENSION));
  146. Extension->DeviceObject = McaDeviceObject;
  147. //
  148. // Make the device visible to Win32 subsystem
  149. //
  150. Status = MCACreateSymbolicLinkObject ();
  151. if (!NT_SUCCESS( Status )) {
  152. DbgPrint("Mca DriverEntry: McaCreateSymbolicLinkObject failed\n");
  153. return Status;
  154. }
  155. //
  156. // Set up the device driver entry points.
  157. //
  158. DriverObject->MajorFunction[IRP_MJ_CREATE] = MCAOpen;
  159. DriverObject->MajorFunction[IRP_MJ_CLOSE] = MCAClose;
  160. DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MCADeviceControl;
  161. DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MCACleanup;
  162. DriverObject->DriverUnload = MCAUnload;
  163. DriverObject->DriverStartIo = MCAStartIo;
  164. //
  165. // Register the driver with the HAL
  166. //
  167. McaDriverInfo.ExceptionCallback = MCADriverExceptionCallback;
  168. McaDriverInfo.DpcCallback = MCADriverDpcCallback;
  169. McaDriverInfo.DeviceContext = McaDeviceObject;
  170. Status = HalSetSystemInformation(
  171. HalMcaRegisterDriver,
  172. sizeof(MCA_DRIVER_INFO),
  173. (PVOID)&McaDriverInfo
  174. );
  175. if (!NT_SUCCESS( Status )) {
  176. DbgPrint("Mca DriverEntry: HalMcaRegisterDriver failed\n");
  177. //
  178. // Clean up whatever we have done so far
  179. //
  180. MCAUnload(DriverObject);
  181. return(STATUS_UNSUCCESSFUL);
  182. }
  183. //
  184. // This is the place where you would check non-volatile area (if any) for
  185. // any Machine Check errros logged and process them.
  186. // ...
  187. //
  188. return STATUS_SUCCESS;
  189. }
  190. NTSTATUS
  191. MCACreateSymbolicLinkObject(
  192. VOID
  193. )
  194. /*++
  195. Routine Description:
  196. Makes MCA device visible to Win32 subsystem
  197. Arguments:
  198. None
  199. Return Value:
  200. Success or failure
  201. --*/
  202. {
  203. NTSTATUS Status;
  204. STRING DosString;
  205. STRING NtString;
  206. UNICODE_STRING DosUnicodeString;
  207. UNICODE_STRING NtUnicodeString;
  208. //
  209. // Create a symbolic link for sharing.
  210. //
  211. RtlInitAnsiString( &DosString, MCA_DEVICE_NAME_DOS );
  212. Status = RtlAnsiStringToUnicodeString(
  213. &DosUnicodeString,
  214. &DosString,
  215. TRUE
  216. );
  217. if ( !NT_SUCCESS( Status )) {
  218. return Status;
  219. }
  220. RtlInitAnsiString( &NtString, MCA_DEVICE_NAME );
  221. Status = RtlAnsiStringToUnicodeString(
  222. &NtUnicodeString,
  223. &NtString,
  224. TRUE
  225. );
  226. if ( !NT_SUCCESS( Status )) {
  227. return Status;
  228. }
  229. Status = IoCreateSymbolicLink(
  230. &DosUnicodeString,
  231. &NtUnicodeString
  232. );
  233. RtlFreeUnicodeString( &DosUnicodeString );
  234. RtlFreeUnicodeString( &NtUnicodeString );
  235. return (Status);
  236. }
  237. //
  238. // Dispatch routine for close requests
  239. //
  240. NTSTATUS
  241. MCAClose(
  242. IN PDEVICE_OBJECT DeviceObject,
  243. IN PIRP Irp
  244. )
  245. /*++
  246. Routine Description:
  247. Close dispatch routine
  248. Arguments:
  249. DeviceObject: Pointer to the device object
  250. Irp: Incoming Irp
  251. Return Value:
  252. Success or failure
  253. --*/
  254. {
  255. NTSTATUS Status = STATUS_SUCCESS;
  256. //
  257. // Complete the request and return status.
  258. //
  259. Irp->IoStatus.Status = Status;
  260. Irp->IoStatus.Information = 0;
  261. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  262. return (Status);
  263. }
  264. NTSTATUS
  265. MCAOpen(
  266. IN PDEVICE_OBJECT DeviceObject,
  267. IN PIRP Irp
  268. )
  269. /*++
  270. Routine Description:
  271. This routine is the dispatch routine for create/open requests.
  272. Arguments:
  273. DeviceObject: Pointer to the device object
  274. Irp: Incoming Irp
  275. Return Value:
  276. Success or failure
  277. --*/
  278. {
  279. //
  280. // Complete the request and return status.
  281. //
  282. Irp->IoStatus.Status = STATUS_SUCCESS;
  283. Irp->IoStatus.Information = FILE_OPENED;
  284. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  285. return (STATUS_SUCCESS);
  286. }
  287. ERROR_SEVERITY
  288. MCADriverExceptionCallback(
  289. IN PDEVICE_OBJECT DeviceObject,
  290. IN PMCA_EXCEPTION InException
  291. )
  292. /*++
  293. Routine Description:
  294. This is the callback routine for MCA exception. It was registered
  295. by this driver at INIT time with the HAL as a callback when a
  296. non-restartable error occurs. This routine simply copies the
  297. information to a platform specific area
  298. NOTE: If the information needs to be saved in NVRAM, this is the place
  299. to do it.
  300. Once you return from this callback, the system is going to bugcheck.
  301. Arguments:
  302. DeviceObject: Pointer to the device object
  303. InException: Exception information record
  304. Return Value:
  305. None
  306. --*/
  307. {
  308. PMCA_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
  309. PCHAR Destination, Source;
  310. UCHAR Bytes;
  311. //
  312. // An exception has occured on a processor.
  313. // Perform any vendor specific action here like saving stuff in NVRAM
  314. // NOTE : No system services of any kind can be used here.
  315. //
  316. //
  317. // Save the exception from HAL. May want to use link list for these
  318. // exceptions.
  319. //
  320. Destination = (PCHAR)&(Extension->McaException); // Put your platform
  321. // specific destination
  322. Source = (PCHAR)InException;
  323. //
  324. // Copy from source to destination here
  325. //
  326. #if defined(_IA64_)
  327. //
  328. // Return information to the generic HAL MCA handler.
  329. //
  330. // Update it accordingly here, the default value being the ERROR_SEVERITY value
  331. // in the MCA exception.
  332. //
  333. return( InException->ErrorSeverity );
  334. #endif // _IA64_
  335. }
  336. //
  337. // DPC routine for IRP completion
  338. //
  339. VOID
  340. MCADriverDpcCallback(
  341. IN PKDPC Dpc,
  342. IN PVOID DeferredContext,
  343. IN PVOID SystemContext1,
  344. IN PVOID SystemContext2
  345. )
  346. /*++
  347. Routine Description:
  348. This is the DPC - callback routine for MCA exception. It was registered
  349. by this driver at INIT time with the HAL as a DPC callback when a
  350. restartable error occurs (which causes Machine Check exception)
  351. Arguments:
  352. Dpc: The DPC Object itself
  353. DefferedContext: Pointer to the device object
  354. SystemContext1: Not used
  355. SystemContext2: Not used
  356. Return Value:
  357. None
  358. --*/
  359. {
  360. PMCA_DEVICE_EXTENSION Extension;
  361. Extension = ((PDEVICE_OBJECT)DeferredContext)->DeviceExtension;
  362. if (Extension->SavedIrp == NULL) {
  363. //
  364. // We got an MCA exception but no app was asking for anything.
  365. //
  366. return;
  367. }
  368. //
  369. // If we have reached this point, it means that the exception was
  370. // restartable. Since we cannot read the log at Dispatch level,
  371. // queue a work item to read the Machine Check log at Passive level
  372. //
  373. if (Extension->WorkItemQueued == FALSE) {
  374. //
  375. // Set a boolean to indicate that we have already queued a work item
  376. //
  377. Extension->WorkItemQueued = TRUE;
  378. //
  379. // Initialize the work item
  380. //
  381. ExInitializeWorkItem(&Extension->WorkItem,
  382. (PWORKER_THREAD_ROUTINE)MCAProcessWorkItem,
  383. (PVOID)DeferredContext
  384. );
  385. //
  386. // Queue the work item for processing at PASSIVE level
  387. //
  388. ExQueueWorkItem(&Extension->WorkItem, CriticalWorkQueue);
  389. }
  390. }
  391. VOID
  392. MCAProcessWorkItem(
  393. PVOID Context
  394. )
  395. /*++
  396. Routine Description:
  397. This routine gets invoked when a work item is queued from the DPC
  398. callback routine for a restartable machine check error.
  399. Its job is to read the machine check registers and copy the log
  400. to complete the asynchronous IRP
  401. Arguments:
  402. Context : Pointer to the device object
  403. Return Value:
  404. None
  405. --*/
  406. {
  407. PMCA_DEVICE_EXTENSION Extension;
  408. KIRQL CancelIrql;
  409. ULONG ReturnedLength;
  410. NTSTATUS Status;
  411. Extension = ((PDEVICE_OBJECT)Context)->DeviceExtension;
  412. //
  413. // Mark this IRP as non-cancellable
  414. //
  415. IoAcquireCancelSpinLock(&CancelIrql);
  416. if (Extension->SavedIrp->Cancel == TRUE) {
  417. IoReleaseCancelSpinLock(CancelIrql);
  418. } else {
  419. IoSetCancelRoutine(Extension->SavedIrp, NULL);
  420. IoReleaseCancelSpinLock(CancelIrql);
  421. //
  422. // Call HalQuerySystemInformation() to obtain MCA log.
  423. //
  424. Status = HalQuerySystemInformation(
  425. HalMcaLogInformation,
  426. sizeof(MCA_EXCEPTION),
  427. Extension->SavedIrp->AssociatedIrp.SystemBuffer,
  428. &ReturnedLength
  429. );
  430. ASSERT(Status != STATUS_NO_SUCH_DEVICE);
  431. ASSERT(Status != STATUS_NOT_FOUND);
  432. IoStartPacket(((PDEVICE_OBJECT)Context), Extension->SavedIrp, 0, NULL);
  433. Extension->SavedIrp = NULL;
  434. Extension->WorkItemQueued = FALSE;
  435. }
  436. }
  437. VOID
  438. MCAStartIo(
  439. IN PDEVICE_OBJECT DeviceObject,
  440. IN PIRP Irp
  441. )
  442. /*++
  443. Routine Description:
  444. This routine completes the async call from the app
  445. Arguments:
  446. DeviceObject: Pointer to the device object
  447. Irp: Incoming Irp
  448. Return Value:
  449. None
  450. --*/
  451. {
  452. //
  453. // The system Buffer has already been setup
  454. //
  455. Irp->IoStatus.Information = sizeof(MCA_EXCEPTION);
  456. Irp->IoStatus.Status = STATUS_SUCCESS;
  457. IoCompleteRequest( Irp, IO_NO_INCREMENT );
  458. IoStartNextPacket(DeviceObject, TRUE);
  459. }
  460. VOID
  461. McaCancelIrp(
  462. IN PDEVICE_OBJECT DeviceObject,
  463. IN PIRP Irp
  464. )
  465. /*++
  466. Routine Description:
  467. This function gets called when the IRP is cancelled. When this routine
  468. is called, we hold the cancel spin lock and we are at DISPATCH level
  469. Arguments:
  470. DeviceObject and the Irp to be cancelled.
  471. Return Value:
  472. None.
  473. --*/
  474. {
  475. ((PMCA_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->SavedIrp = NULL;
  476. Irp->IoStatus.Status = STATUS_CANCELLED;
  477. Irp->IoStatus.Information = 0;
  478. IoReleaseCancelSpinLock(Irp->CancelIrql);
  479. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  480. }
  481. NTSTATUS
  482. MCADeviceControl(
  483. IN PDEVICE_OBJECT DeviceObject,
  484. IN PIRP Irp
  485. )
  486. /*++
  487. Routine Description:
  488. This routine is the dispatch routine for the IOCTL requests to driver.
  489. It accepts an I/O Request Packet, performs the request, and then
  490. returns with the appropriate status.
  491. Arguments:
  492. DeviceObject: Pointer to the device object
  493. Irp: Incoming Irp
  494. Return Value:
  495. Success or failure
  496. --*/
  497. {
  498. NTSTATUS Status;
  499. PIO_STACK_LOCATION IrpSp;
  500. PMCA_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
  501. KIRQL CancelIrql;
  502. ULONG ReturnedLength;
  503. ULONG PhysicalAddress;
  504. KIRQL OldIrql;
  505. //
  506. // Get a pointer to the current stack location in the IRP. This is
  507. // where the function codes and parameters are stored.
  508. //
  509. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  510. //
  511. // The individual IOCTLs will return errors if HAL MCA is not installed
  512. //
  513. //
  514. // Switch on the IOCTL code that is being requested by the user. If the
  515. // operation is a valid one for this device do the needful.
  516. //
  517. switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
  518. case IOCTL_READ_BANKS:
  519. //
  520. // we need a user buffer for this call to complete
  521. // Our user buffer is in SystemBuffer
  522. //
  523. if (Irp->AssociatedIrp.SystemBuffer == NULL) {
  524. Status = STATUS_UNSUCCESSFUL;
  525. break;
  526. }
  527. if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength !=
  528. sizeof(MCA_EXCEPTION)) {
  529. Status = STATUS_UNSUCCESSFUL;
  530. break;
  531. }
  532. //
  533. // Call HalQuerySystemInformation() to obtain MCA log.
  534. // This call can also fail if the processor does not support
  535. // Intel Machine Check Architecture
  536. //
  537. Status = HalQuerySystemInformation(
  538. HalMcaLogInformation,
  539. sizeof(MCA_EXCEPTION),
  540. Irp->AssociatedIrp.SystemBuffer,
  541. &ReturnedLength
  542. );
  543. if (NT_SUCCESS(Status)) {
  544. Irp->IoStatus.Information = ReturnedLength;
  545. } else {
  546. if (Status == STATUS_NO_SUCH_DEVICE) {
  547. //
  548. // MCA support not available\n");
  549. //
  550. NOTHING;
  551. }
  552. if (Status == STATUS_NOT_FOUND) {
  553. //
  554. // No machine check errors present\n");
  555. //
  556. NOTHING;
  557. }
  558. Irp->IoStatus.Information = 0;
  559. }
  560. break;
  561. case IOCTL_READ_BANKS_ASYNC:
  562. if (Irp->AssociatedIrp.SystemBuffer == NULL) {
  563. Status = STATUS_UNSUCCESSFUL;
  564. break;
  565. }
  566. if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength !=
  567. sizeof(MCA_EXCEPTION)) {
  568. Status = STATUS_UNSUCCESSFUL;
  569. break;
  570. }
  571. //
  572. // Implementation Note:
  573. //
  574. // Our Async model is such that the next DeviceIoControl
  575. // does not start from the app until the previous one
  576. // completes (asynchronously). Since there is no inherent
  577. // parallelism that is needed here, we do not have to worry
  578. // about protecting data integrity in the face of more than
  579. // one app level ioctls active at the same time.
  580. //
  581. //
  582. // asynchronous reads provide a mechanism for the
  583. // app to asynchronously get input from the HAL on an
  584. // exception. This request is marked as pending at this time
  585. // but it will be completed when an MCA exception occurs.
  586. //
  587. IoMarkIrpPending(Irp);
  588. //
  589. // Complete the processing in StartIo Dispatch routine
  590. // ASSERT: at any given time there is only 1 async call pending
  591. // So just save the pointer
  592. //
  593. if (Extension->SavedIrp == NULL) {
  594. Extension->SavedIrp = Irp;
  595. } else {
  596. //
  597. // We can have ONLY one outstanding ASYNC request
  598. //
  599. Status = STATUS_DEVICE_BUSY;
  600. break;
  601. }
  602. //
  603. // Set the IRP to cancellable state
  604. //
  605. IoAcquireCancelSpinLock(&CancelIrql);
  606. IoSetCancelRoutine(Irp, McaCancelIrp);
  607. IoReleaseCancelSpinLock(CancelIrql);
  608. return(STATUS_PENDING);
  609. break;
  610. default:
  611. //
  612. // This should not happen
  613. //
  614. DbgPrint("MCA driver: Bad ioctl\n");
  615. Status = STATUS_NOT_IMPLEMENTED;
  616. break;
  617. }
  618. //
  619. // Copy the final status into the return status, complete the request and
  620. // get out of here.
  621. //
  622. if (Status != STATUS_PENDING) {
  623. //
  624. // Complete the Io Request
  625. //
  626. Irp->IoStatus.Status = Status;
  627. IoCompleteRequest( Irp, IO_NO_INCREMENT );
  628. }
  629. return (Status);
  630. }
  631. NTSTATUS
  632. MCACleanup(
  633. IN PDEVICE_OBJECT DeviceObject,
  634. IN PIRP Irp
  635. )
  636. /*++
  637. Routine Description:
  638. This is the dispatch routine for cleanup requests.
  639. All queued IRPs are completed with STATUS_CANCELLED.
  640. Arguments:
  641. DeviceObject: Pointer to the device object
  642. Irp: Incoming Irp
  643. Return Value:
  644. Success or failure
  645. --*/
  646. {
  647. PIRP CurrentIrp;
  648. PMCA_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
  649. //
  650. // Complete all queued requests with STATUS_CANCELLED.
  651. //
  652. if (Extension->SavedIrp != NULL) {
  653. CurrentIrp = Extension->SavedIrp;
  654. //
  655. // Acquire the Cancel Spinlock
  656. //
  657. IoAcquireCancelSpinLock(&CurrentIrp->CancelIrql);
  658. Extension->SavedIrp = NULL;
  659. if (CurrentIrp->Cancel == TRUE) {
  660. //
  661. // Cancel routine got called for this one.
  662. // No need to do anything else
  663. //
  664. IoReleaseCancelSpinLock(CurrentIrp->CancelIrql);
  665. } else {
  666. if (CurrentIrp->CancelRoutine == NULL) {
  667. //
  668. // Release the Cancel Spinlock
  669. //
  670. IoReleaseCancelSpinLock(CurrentIrp->CancelIrql);
  671. } else {
  672. (CurrentIrp->CancelRoutine)(DeviceObject, CurrentIrp );
  673. }
  674. }
  675. }
  676. //
  677. // Complete the Cleanup Dispatch with STATUS_SUCCESS
  678. //
  679. Irp->IoStatus.Status = STATUS_SUCCESS;
  680. Irp->IoStatus.Information = 0;
  681. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  682. return(STATUS_SUCCESS);
  683. }
  684. VOID
  685. MCAUnload(
  686. IN PDRIVER_OBJECT DriverObject
  687. )
  688. /*++
  689. Routine Description:
  690. Dispatch routine for unloads
  691. Arguments:
  692. DeviceObject: Pointer to the device object
  693. Return Value:
  694. None
  695. --*/
  696. {
  697. NTSTATUS Status;
  698. STRING DosString;
  699. UNICODE_STRING DosUnicodeString;
  700. //
  701. // Delete the user visible device name.
  702. //
  703. RtlInitAnsiString( &DosString, MCA_DEVICE_NAME_DOS );
  704. Status = RtlAnsiStringToUnicodeString(
  705. &DosUnicodeString,
  706. &DosString,
  707. TRUE
  708. );
  709. if ( !NT_SUCCESS( Status )) {
  710. DbgPrint("MCAUnload: Error in RtlAnsiStringToUnicodeString\n");
  711. return;
  712. }
  713. Status = IoDeleteSymbolicLink(
  714. &DosUnicodeString
  715. );
  716. RtlFreeUnicodeString( &DosUnicodeString );
  717. //
  718. // Delete the device object
  719. //
  720. IoDeleteDevice(DriverObject->DeviceObject);
  721. return;
  722. }