3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: services/dd/blue/blue.c
6 * PURPOSE: Console (blue screen) device driver
7 * PROGRAMMER: Eric Kohl (ekohl@abo.rhein-zeitung.de)
12 /* INCLUDES ******************************************************************/
17 typedef struct _SECURITY_ATTRIBUTES SECURITY_ATTRIBUTES
, *PSECURITY_ATTRIBUTES
;
20 #include <blue/ntddblue.h>
21 #include <ndk/halfuncs.h>
26 /* DEFINITIONS ***************************************************************/
28 #define VIDMEM_BASE 0xb8000
30 #define CRTC_COMMAND ((PUCHAR)0x3d4)
31 #define CRTC_DATA ((PUCHAR)0x3d5)
33 #define CRTC_COLUMNS 0x01
34 #define CRTC_OVERFLOW 0x07
35 #define CRTC_ROWS 0x12
36 #define CRTC_SCANLINES 0x09
37 #define CRTC_CURSORSTART 0x0a
38 #define CRTC_CURSOREND 0x0b
39 #define CRTC_CURSORPOSHI 0x0e
40 #define CRTC_CURSORPOSLO 0x0f
42 #define ATTRC_WRITEREG ((PUCHAR)0x3c0)
43 #define ATTRC_READREG ((PUCHAR)0x3c1)
44 #define ATTRC_INPST1 ((PUCHAR)0x3da)
49 /* NOTES ******************************************************************/
51 * [[character][attribute]][[character][attribute]]....
55 /* TYPEDEFS ***************************************************************/
57 typedef struct _DEVICE_EXTENSION
59 PUCHAR VideoMemory
; /* Pointer to video memory */
64 UCHAR ScanLines
; /* Height of a text line */
65 USHORT Rows
; /* Number of rows */
66 USHORT Columns
; /* Number of columns */
67 } DEVICE_EXTENSION
, *PDEVICE_EXTENSION
;
70 /* FUNCTIONS **************************************************************/
73 DriverEntry (PDRIVER_OBJECT DriverObject
, PUNICODE_STRING RegistryPath
);
75 static NTSTATUS STDCALL
76 ScrCreate(PDEVICE_OBJECT DeviceObject
,
79 PDEVICE_EXTENSION DeviceExtension
;
80 PHYSICAL_ADDRESS BaseAddress
;
85 DeviceExtension
= DeviceObject
->DeviceExtension
;
87 /* disable interrupts */
90 /* get current output position */
91 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORPOSLO
);
92 offset
= READ_PORT_UCHAR (CRTC_DATA
);
93 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORPOSHI
);
94 offset
+= (READ_PORT_UCHAR (CRTC_DATA
) << 8);
96 /* switch blinking characters off */
97 READ_PORT_UCHAR (ATTRC_INPST1
);
98 value
= READ_PORT_UCHAR (ATTRC_WRITEREG
);
99 WRITE_PORT_UCHAR (ATTRC_WRITEREG
, 0x10);
100 data
= READ_PORT_UCHAR (ATTRC_READREG
);
102 WRITE_PORT_UCHAR (ATTRC_WRITEREG
, data
);
103 WRITE_PORT_UCHAR (ATTRC_WRITEREG
, value
);
104 READ_PORT_UCHAR (ATTRC_INPST1
);
106 /* read screen information from crt controller */
107 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_COLUMNS
);
108 DeviceExtension
->Columns
= READ_PORT_UCHAR (CRTC_DATA
) + 1;
109 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_ROWS
);
110 DeviceExtension
->Rows
= READ_PORT_UCHAR (CRTC_DATA
);
111 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_OVERFLOW
);
112 data
= READ_PORT_UCHAR (CRTC_DATA
);
113 DeviceExtension
->Rows
|= (((data
& 0x02) << 7) | ((data
& 0x40) << 3));
114 DeviceExtension
->Rows
++;
115 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_SCANLINES
);
116 DeviceExtension
->ScanLines
= (READ_PORT_UCHAR (CRTC_DATA
) & 0x1F) + 1;
118 /* enable interrupts */
121 /* calculate number of text rows */
122 DeviceExtension
->Rows
=
123 DeviceExtension
->Rows
/ DeviceExtension
->ScanLines
;
125 DeviceExtension
->Rows
= 30;
128 DPRINT ("%d Columns %d Rows %d Scanlines\n",
129 DeviceExtension
->Columns
,
130 DeviceExtension
->Rows
,
131 DeviceExtension
->ScanLines
);
133 /* get pointer to video memory */
134 BaseAddress
.QuadPart
= VIDMEM_BASE
;
135 DeviceExtension
->VideoMemory
=
136 (PUCHAR
)MmMapIoSpace (BaseAddress
, DeviceExtension
->Rows
* DeviceExtension
->Columns
* 2, MmNonCached
);
138 DeviceExtension
->CursorSize
= 5; /* FIXME: value correct?? */
139 DeviceExtension
->CursorVisible
= TRUE
;
141 /* more initialization */
142 DeviceExtension
->CharAttribute
= 0x17; /* light grey on blue */
143 DeviceExtension
->Mode
= ENABLE_PROCESSED_OUTPUT
|
144 ENABLE_WRAP_AT_EOL_OUTPUT
;
146 /* show blinking cursor */
148 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORSTART
);
149 WRITE_PORT_UCHAR (CRTC_DATA
, (DeviceExtension
->ScanLines
- 1) & 0x1F);
150 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSOREND
);
151 data
= READ_PORT_UCHAR (CRTC_DATA
) & 0xE0;
152 WRITE_PORT_UCHAR (CRTC_DATA
,
153 data
| ((DeviceExtension
->ScanLines
- 1) & 0x1F));
156 Status
= STATUS_SUCCESS
;
158 Irp
->IoStatus
.Status
= Status
;
159 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
165 static NTSTATUS STDCALL
166 ScrWrite(PDEVICE_OBJECT DeviceObject
,
169 PIO_STACK_LOCATION stk
= IoGetCurrentIrpStackLocation (Irp
);
170 PDEVICE_EXTENSION DeviceExtension
= DeviceObject
->DeviceExtension
;
172 char *pch
= Irp
->UserBuffer
;
176 int cursorx
, cursory
;
178 int processed
= DeviceExtension
->Mode
& ENABLE_PROCESSED_OUTPUT
;
180 if (HalQueryDisplayOwnership())
182 /* Display is in graphics mode, we're not allowed to touch it */
183 Status
= STATUS_SUCCESS
;
185 Irp
->IoStatus
.Status
= Status
;
186 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
191 vidmem
= DeviceExtension
->VideoMemory
;
192 rows
= DeviceExtension
->Rows
;
193 columns
= DeviceExtension
->Columns
;
196 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORPOSHI
);
197 offset
= READ_PORT_UCHAR (CRTC_DATA
)<<8;
198 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORPOSLO
);
199 offset
+= READ_PORT_UCHAR (CRTC_DATA
);
202 cursory
= offset
/ columns
;
203 cursorx
= offset
% columns
;
206 /* raw output mode */
207 memcpy( &vidmem
[(cursorx
* 2) + (cursory
* columns
* 2)], pch
, stk
->Parameters
.Write
.Length
);
208 offset
+= (stk
->Parameters
.Write
.Length
/ 2);
211 for (i
= 0; i
< stk
->Parameters
.Write
.Length
; i
++, pch
++)
220 else if (cursory
> 0)
222 cursorx
= columns
- 1;
225 vidmem
[(cursorx
* 2) + (cursory
* columns
* 2)] = ' ';
226 vidmem
[(cursorx
* 2) + (cursory
* columns
* 2) + 1] = (char) DeviceExtension
->CharAttribute
;
239 offset
= TAB_WIDTH
- (cursorx
% TAB_WIDTH
);
240 for (j
= 0; j
< offset
; j
++)
242 vidmem
[(cursorx
* 2) + (cursory
* columns
* 2)] = ' ';
245 if (cursorx
>= columns
)
254 vidmem
[(cursorx
* 2) + (cursory
* columns
* 2)] = *pch
;
255 vidmem
[(cursorx
* 2) + (cursory
* columns
* 2) + 1] = (char) DeviceExtension
->CharAttribute
;
257 if (cursorx
>= columns
)
266 unsigned short *LinePtr
;
269 &vidmem
[columns
* 2],
270 columns
* (rows
- 1) * 2);
272 LinePtr
= (unsigned short *) &vidmem
[columns
* (rows
- 1) * 2];
274 for (j
= 0; j
< columns
; j
++)
276 LinePtr
[j
] = DeviceExtension
->CharAttribute
<< 8;
279 for (j
= 0; j
< columns
; j
++)
281 vidmem
[(j
* 2) + (cursory
* columns
* 2)] = ' ';
282 vidmem
[(j
* 2) + (cursory
* columns
* 2) + 1] = (char)DeviceExtension
->CharAttribute
;
287 /* Set the cursor position */
288 offset
= (cursory
* columns
) + cursorx
;
291 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORPOSLO
);
292 WRITE_PORT_UCHAR (CRTC_DATA
, offset
);
293 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORPOSHI
);
295 WRITE_PORT_UCHAR (CRTC_DATA
, offset
);
298 Status
= STATUS_SUCCESS
;
300 Irp
->IoStatus
.Status
= Status
;
301 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
307 static NTSTATUS STDCALL
308 ScrIoControl(PDEVICE_OBJECT DeviceObject
,
311 PIO_STACK_LOCATION stk
= IoGetCurrentIrpStackLocation (Irp
);
312 PDEVICE_EXTENSION DeviceExtension
;
315 DeviceExtension
= DeviceObject
->DeviceExtension
;
316 switch (stk
->Parameters
.DeviceIoControl
.IoControlCode
)
318 case IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO
:
320 PCONSOLE_SCREEN_BUFFER_INFO pcsbi
= (PCONSOLE_SCREEN_BUFFER_INFO
)Irp
->AssociatedIrp
.SystemBuffer
;
321 int rows
= DeviceExtension
->Rows
;
322 int columns
= DeviceExtension
->Columns
;
325 /* read cursor position from crtc */
327 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORPOSLO
);
328 offset
= READ_PORT_UCHAR (CRTC_DATA
);
329 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORPOSHI
);
330 offset
+= (READ_PORT_UCHAR (CRTC_DATA
) << 8);
333 pcsbi
->dwSize
.X
= columns
;
334 pcsbi
->dwSize
.Y
= rows
;
336 pcsbi
->dwCursorPosition
.X
= (SHORT
)(offset
% columns
);
337 pcsbi
->dwCursorPosition
.Y
= (SHORT
)(offset
/ columns
);
339 pcsbi
->wAttributes
= DeviceExtension
->CharAttribute
;
341 pcsbi
->srWindow
.Left
= 0;
342 pcsbi
->srWindow
.Right
= columns
- 1;
343 pcsbi
->srWindow
.Top
= 0;
344 pcsbi
->srWindow
.Bottom
= rows
- 1;
346 pcsbi
->dwMaximumWindowSize
.X
= columns
;
347 pcsbi
->dwMaximumWindowSize
.Y
= rows
;
349 Irp
->IoStatus
.Information
= sizeof (CONSOLE_SCREEN_BUFFER_INFO
);
350 Status
= STATUS_SUCCESS
;
354 case IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO
:
356 PCONSOLE_SCREEN_BUFFER_INFO pcsbi
= (PCONSOLE_SCREEN_BUFFER_INFO
)Irp
->AssociatedIrp
.SystemBuffer
;
359 DeviceExtension
->CharAttribute
= pcsbi
->wAttributes
;
360 offset
= (pcsbi
->dwCursorPosition
.Y
* DeviceExtension
->Columns
) +
361 pcsbi
->dwCursorPosition
.X
;
364 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORPOSLO
);
365 WRITE_PORT_UCHAR (CRTC_DATA
, offset
);
366 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORPOSHI
);
367 WRITE_PORT_UCHAR (CRTC_DATA
, offset
>>8);
370 Irp
->IoStatus
.Information
= 0;
371 Status
= STATUS_SUCCESS
;
375 case IOCTL_CONSOLE_GET_CURSOR_INFO
:
377 PCONSOLE_CURSOR_INFO pcci
= (PCONSOLE_CURSOR_INFO
)Irp
->AssociatedIrp
.SystemBuffer
;
379 pcci
->dwSize
= DeviceExtension
->CursorSize
;
380 pcci
->bVisible
= DeviceExtension
->CursorVisible
;
382 Irp
->IoStatus
.Information
= sizeof (CONSOLE_CURSOR_INFO
);
383 Status
= STATUS_SUCCESS
;
387 case IOCTL_CONSOLE_SET_CURSOR_INFO
:
389 PCONSOLE_CURSOR_INFO pcci
= (PCONSOLE_CURSOR_INFO
)Irp
->AssociatedIrp
.SystemBuffer
;
393 DeviceExtension
->CursorSize
= pcci
->dwSize
;
394 DeviceExtension
->CursorVisible
= pcci
->bVisible
;
395 height
= DeviceExtension
->ScanLines
;
396 data
= (pcci
->bVisible
) ? 0x00 : 0x20;
398 size
= (pcci
->dwSize
* height
) / 100;
404 data
|= (UCHAR
)(height
- size
);
407 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORSTART
);
408 WRITE_PORT_UCHAR (CRTC_DATA
, data
);
409 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSOREND
);
410 value
= READ_PORT_UCHAR (CRTC_DATA
) & 0xE0;
411 WRITE_PORT_UCHAR (CRTC_DATA
, value
| (height
- 1));
415 Irp
->IoStatus
.Information
= 0;
416 Status
= STATUS_SUCCESS
;
420 case IOCTL_CONSOLE_GET_MODE
:
422 PCONSOLE_MODE pcm
= (PCONSOLE_MODE
)Irp
->AssociatedIrp
.SystemBuffer
;
424 pcm
->dwMode
= DeviceExtension
->Mode
;
426 Irp
->IoStatus
.Information
= sizeof(CONSOLE_MODE
);
427 Status
= STATUS_SUCCESS
;
431 case IOCTL_CONSOLE_SET_MODE
:
433 PCONSOLE_MODE pcm
= (PCONSOLE_MODE
)Irp
->AssociatedIrp
.SystemBuffer
;
435 DeviceExtension
->Mode
= pcm
->dwMode
;
437 Irp
->IoStatus
.Information
= 0;
438 Status
= STATUS_SUCCESS
;
442 case IOCTL_CONSOLE_FILL_OUTPUT_ATTRIBUTE
:
444 POUTPUT_ATTRIBUTE Buf
= (POUTPUT_ATTRIBUTE
)Irp
->AssociatedIrp
.SystemBuffer
;
449 vidmem
= DeviceExtension
->VideoMemory
;
450 offset
= (Buf
->dwCoord
.Y
* DeviceExtension
->Columns
* 2) +
451 (Buf
->dwCoord
.X
* 2) + 1;
453 for (dwCount
= 0; dwCount
< Buf
->nLength
; dwCount
++)
455 vidmem
[offset
+ (dwCount
* 2)] = (char) Buf
->wAttribute
;
458 Buf
->dwTransfered
= Buf
->nLength
;
460 Irp
->IoStatus
.Information
= 0;
461 Status
= STATUS_SUCCESS
;
465 case IOCTL_CONSOLE_READ_OUTPUT_ATTRIBUTE
:
467 POUTPUT_ATTRIBUTE Buf
= (POUTPUT_ATTRIBUTE
)Irp
->AssociatedIrp
.SystemBuffer
;
468 PUSHORT pAttr
= (PUSHORT
)MmGetSystemAddressForMdl(Irp
->MdlAddress
);
473 vidmem
= DeviceExtension
->VideoMemory
;
474 offset
= (Buf
->dwCoord
.Y
* DeviceExtension
->Columns
* 2) +
475 (Buf
->dwCoord
.X
* 2) + 1;
477 for (dwCount
= 0; dwCount
< stk
->Parameters
.DeviceIoControl
.OutputBufferLength
; dwCount
++, pAttr
++)
479 *((char *) pAttr
) = vidmem
[offset
+ (dwCount
* 2)];
482 Buf
->dwTransfered
= dwCount
;
484 Irp
->IoStatus
.Information
= sizeof(OUTPUT_ATTRIBUTE
);
485 Status
= STATUS_SUCCESS
;
489 case IOCTL_CONSOLE_WRITE_OUTPUT_ATTRIBUTE
:
491 COORD
*pCoord
= (COORD
*)MmGetSystemAddressForMdl(Irp
->MdlAddress
);
492 CHAR
*pAttr
= (CHAR
*)(pCoord
+ 1);
497 vidmem
= DeviceExtension
->VideoMemory
;
498 offset
= (pCoord
->Y
* DeviceExtension
->Columns
* 2) +
501 for (dwCount
= 0; dwCount
< (stk
->Parameters
.DeviceIoControl
.OutputBufferLength
- sizeof( COORD
)); dwCount
++, pAttr
++)
503 vidmem
[offset
+ (dwCount
* 2)] = *pAttr
;
505 Irp
->IoStatus
.Information
= 0;
506 Status
= STATUS_SUCCESS
;
510 case IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE
:
511 DeviceExtension
->CharAttribute
= (USHORT
)*(PUSHORT
)Irp
->AssociatedIrp
.SystemBuffer
;
512 Irp
->IoStatus
.Information
= 0;
513 Status
= STATUS_SUCCESS
;
516 case IOCTL_CONSOLE_FILL_OUTPUT_CHARACTER
:
518 POUTPUT_CHARACTER Buf
= (POUTPUT_CHARACTER
)Irp
->AssociatedIrp
.SystemBuffer
;
523 vidmem
= DeviceExtension
->VideoMemory
;
524 offset
= (Buf
->dwCoord
.Y
* DeviceExtension
->Columns
* 2) +
525 (Buf
->dwCoord
.X
* 2);
529 for (dwCount
= 0; dwCount
< Buf
->nLength
; dwCount
++)
531 vidmem
[offset
+ (dwCount
* 2)] = (char) Buf
->cCharacter
;
534 Buf
->dwTransfered
= Buf
->nLength
;
536 Irp
->IoStatus
.Information
= 0;
537 Status
= STATUS_SUCCESS
;
541 case IOCTL_CONSOLE_READ_OUTPUT_CHARACTER
:
543 POUTPUT_CHARACTER Buf
= (POUTPUT_CHARACTER
)Irp
->AssociatedIrp
.SystemBuffer
;
544 LPSTR pChar
= (LPSTR
)MmGetSystemAddressForMdl(Irp
->MdlAddress
);
549 vidmem
= DeviceExtension
->VideoMemory
;
550 offset
= (Buf
->dwCoord
.Y
* DeviceExtension
->Columns
* 2) +
551 (Buf
->dwCoord
.X
* 2);
553 for (dwCount
= 0; dwCount
< stk
->Parameters
.DeviceIoControl
.OutputBufferLength
; dwCount
++, pChar
++)
555 *pChar
= vidmem
[offset
+ (dwCount
* 2)];
558 Buf
->dwTransfered
= dwCount
;
560 Irp
->IoStatus
.Information
= sizeof(OUTPUT_ATTRIBUTE
);
561 Status
= STATUS_SUCCESS
;
565 case IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER
:
573 pCoord
= (COORD
*)MmGetSystemAddressForMdl(Irp
->MdlAddress
);
574 pChar
= (CHAR
*)(pCoord
+ 1);
575 vidmem
= DeviceExtension
->VideoMemory
;
576 offset
= (pCoord
->Y
* DeviceExtension
->Columns
* 2) +
579 for (dwCount
= 0; dwCount
< (stk
->Parameters
.DeviceIoControl
.OutputBufferLength
- sizeof( COORD
)); dwCount
++, pChar
++)
581 vidmem
[offset
+ (dwCount
* 2)] = *pChar
;
584 Irp
->IoStatus
.Information
= 0;
585 Status
= STATUS_SUCCESS
;
589 case IOCTL_CONSOLE_DRAW
:
591 PCONSOLE_DRAW ConsoleDraw
;
593 UINT SrcDelta
, DestDelta
, i
, Offset
;
595 ConsoleDraw
= (PCONSOLE_DRAW
) MmGetSystemAddressForMdl(Irp
->MdlAddress
);
596 Src
= (PUCHAR
) (ConsoleDraw
+ 1);
597 SrcDelta
= ConsoleDraw
->SizeX
* 2;
598 Dest
= DeviceExtension
->VideoMemory
+
599 (ConsoleDraw
->Y
* DeviceExtension
->Columns
+ ConsoleDraw
->X
) * 2;
600 DestDelta
= DeviceExtension
->Columns
* 2;
602 for (i
= 0; i
< ConsoleDraw
->SizeY
; i
++)
604 RtlCopyMemory(Dest
, Src
, SrcDelta
);
609 Offset
= (ConsoleDraw
->CursorY
* DeviceExtension
->Columns
) +
610 ConsoleDraw
->CursorX
;
613 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORPOSLO
);
614 WRITE_PORT_UCHAR (CRTC_DATA
, Offset
);
615 WRITE_PORT_UCHAR (CRTC_COMMAND
, CRTC_CURSORPOSHI
);
616 WRITE_PORT_UCHAR (CRTC_DATA
, Offset
>> 8);
619 Irp
->IoStatus
.Information
= 0;
620 Status
= STATUS_SUCCESS
;
625 Status
= STATUS_NOT_IMPLEMENTED
;
628 Irp
->IoStatus
.Status
= Status
;
629 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
635 static NTSTATUS STDCALL
636 ScrDispatch(PDEVICE_OBJECT DeviceObject
,
639 PIO_STACK_LOCATION stk
= IoGetCurrentIrpStackLocation(Irp
);
642 switch (stk
->MajorFunction
)
645 Status
= STATUS_SUCCESS
;
649 Status
= STATUS_NOT_IMPLEMENTED
;
654 Irp
->IoStatus
.Status
= Status
;
655 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
665 DriverEntry (PDRIVER_OBJECT DriverObject
, PUNICODE_STRING RegistryPath
)
667 PDEVICE_OBJECT DeviceObject
;
668 UNICODE_STRING DeviceName
= RTL_CONSTANT_STRING(L
"\\Device\\BlueScreen");
669 UNICODE_STRING SymlinkName
= RTL_CONSTANT_STRING(L
"\\??\\BlueScreen");
671 DPRINT ("Screen Driver 0.0.6\n");
673 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = ScrCreate
;
674 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = ScrDispatch
;
675 DriverObject
->MajorFunction
[IRP_MJ_READ
] = ScrDispatch
;
676 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = ScrWrite
;
677 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = ScrIoControl
;
679 IoCreateDevice (DriverObject
,
680 sizeof(DEVICE_EXTENSION
),
687 IoCreateSymbolicLink (&SymlinkName
, &DeviceName
);
689 return (STATUS_SUCCESS
);