\r processing.
[reactos.git] / reactos / drivers / dd / blue / blue.c
1 /* $Id: blue.c,v 1.30 2001/07/30 11:49:38 ea Exp $
2 *
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)
8 * UPDATE HISTORY:
9 * ??? Created
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <ddk/ntddblue.h>
16 #include <string.h>
17 #include <defines.h>
18
19 #define NDEBUG
20 #include <debug.h>
21
22
23 /* DEFINITIONS ***************************************************************/
24
25 #define VIDMEM_BASE 0xb8000
26 #define VIDMEM_SIZE 0x2000
27
28 #define CRTC_COMMAND ((PUCHAR)0x3d4)
29 #define CRTC_DATA ((PUCHAR)0x3d5)
30
31 #define CRTC_COLUMNS 0x01
32 #define CRTC_OVERFLOW 0x07
33 #define CRTC_ROWS 0x12
34 #define CRTC_SCANLINES 0x09
35 #define CRTC_CURSORSTART 0x0a
36 #define CRTC_CURSOREND 0x0b
37 #define CRTC_CURSORPOSHI 0x0e
38 #define CRTC_CURSORPOSLO 0x0f
39
40 #define ATTRC_WRITEREG ((PUCHAR)0x3c0)
41 #define ATTRC_READREG ((PUCHAR)0x3c1)
42 #define ATTRC_INPST1 ((PUCHAR)0x3da)
43
44 #define TAB_WIDTH 8
45
46
47 /* NOTES ******************************************************************/
48 /*
49 * [[character][attribute]][[character][attribute]]....
50 */
51
52
53 /* TYPEDEFS ***************************************************************/
54
55 typedef struct _DEVICE_EXTENSION
56 {
57 PBYTE VideoMemory; /* Pointer to video memory */
58 DWORD CursorSize;
59 BOOL CursorVisible;
60 WORD CharAttribute;
61 DWORD Mode;
62 BYTE ScanLines; /* Height of a text line */
63 WORD Rows; /* Number of rows */
64 WORD Columns; /* Number of columns */
65 } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
66
67
68 /* FUNCTIONS **************************************************************/
69
70 NTSTATUS
71 STDCALL ScrCreate (PDEVICE_OBJECT DeviceObject, PIRP Irp)
72 {
73 PDEVICE_EXTENSION DeviceExtension;
74 PHYSICAL_ADDRESS BaseAddress;
75 NTSTATUS Status;
76 unsigned int offset;
77 BYTE data, value;
78
79 DeviceExtension = DeviceObject->DeviceExtension;
80
81 /* get pointer to video memory */
82 BaseAddress.QuadPart = VIDMEM_BASE;
83 DeviceExtension->VideoMemory =
84 (PBYTE)MmMapIoSpace (BaseAddress, VIDMEM_SIZE, FALSE);
85
86 /* disable interrupts */
87 __asm__("cli\n\t");
88
89 /* get current output position */
90 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSORPOSLO);
91 offset = READ_PORT_UCHAR (CRTC_DATA);
92 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSORPOSHI);
93 offset += (READ_PORT_UCHAR (CRTC_DATA) << 8);
94
95 /* switch blinking characters off */
96 READ_PORT_UCHAR (ATTRC_INPST1);
97 value = READ_PORT_UCHAR (ATTRC_WRITEREG);
98 WRITE_PORT_UCHAR (ATTRC_WRITEREG, 0x10);
99 data = READ_PORT_UCHAR (ATTRC_READREG);
100 data = data & ~0x08;
101 WRITE_PORT_UCHAR (ATTRC_WRITEREG, data);
102 WRITE_PORT_UCHAR (ATTRC_WRITEREG, value);
103 READ_PORT_UCHAR (ATTRC_INPST1);
104
105 /* read screen information from crt controller */
106 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_COLUMNS);
107 DeviceExtension->Columns = READ_PORT_UCHAR (CRTC_DATA) + 1;
108 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_ROWS);
109 DeviceExtension->Rows = READ_PORT_UCHAR (CRTC_DATA);
110 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_OVERFLOW);
111 data = READ_PORT_UCHAR (CRTC_DATA);
112 DeviceExtension->Rows |= (((data & 0x02) << 7) | ((data & 0x40) << 3));
113 DeviceExtension->Rows++;
114 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_SCANLINES);
115 DeviceExtension->ScanLines = (READ_PORT_UCHAR (CRTC_DATA) & 0x1F) + 1;
116
117 /* enable interrupts */
118 __asm__("sti\n\t");
119
120 /* calculate number of text rows */
121 DeviceExtension->Rows =
122 DeviceExtension->Rows / DeviceExtension->ScanLines;
123 #ifdef BOCHS_30ROWS
124 DeviceExtension->Rows = 30;
125 #endif
126
127 DPRINT ("%d Columns %d Rows %d Scanlines\n",
128 DeviceExtension->Columns,
129 DeviceExtension->Rows,
130 DeviceExtension->ScanLines);
131
132 DeviceExtension->CursorSize = 5; /* FIXME: value correct?? */
133 DeviceExtension->CursorVisible = TRUE;
134
135 /* more initialization */
136 DeviceExtension->CharAttribute = 0x17; /* light grey on blue */
137 DeviceExtension->Mode = ENABLE_PROCESSED_OUTPUT |
138 ENABLE_WRAP_AT_EOL_OUTPUT;
139
140 /* show blinking cursor */
141 __asm__("cli\n\t");
142 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSORSTART);
143 WRITE_PORT_UCHAR (CRTC_DATA, (DeviceExtension->ScanLines - 1) & 0x1F);
144 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSOREND);
145 data = READ_PORT_UCHAR (CRTC_DATA) & 0xE0;
146 WRITE_PORT_UCHAR (CRTC_DATA,
147 data | ((DeviceExtension->ScanLines - 1) & 0x1F));
148 __asm__("sti\n\t");
149
150 Status = STATUS_SUCCESS;
151
152 Irp->IoStatus.Status = Status;
153 IoCompleteRequest (Irp, IO_NO_INCREMENT);
154
155 return (Status);
156 }
157
158
159 NTSTATUS
160 STDCALL ScrWrite (PDEVICE_OBJECT DeviceObject, PIRP Irp)
161 {
162 PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation (Irp);
163 PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
164 NTSTATUS Status;
165 char *pch = Irp->UserBuffer;
166 char *vidmem;
167 int i, j, offset;
168 int cursorx, cursory;
169 int rows, columns;
170 int processed = DeviceExtension->Mode & ENABLE_PROCESSED_OUTPUT;
171
172 vidmem = DeviceExtension->VideoMemory;
173 rows = DeviceExtension->Rows;
174 columns = DeviceExtension->Columns;
175
176 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSORPOSHI);
177 offset = READ_PORT_UCHAR (CRTC_DATA)<<8;
178 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSORPOSLO);
179 offset += READ_PORT_UCHAR (CRTC_DATA);
180
181 cursory = offset / columns;
182 cursorx = offset % columns;
183 if( processed == 0 )
184 {
185 /* raw output mode */
186 memcpy( &vidmem[(cursorx * 2) + (cursory * columns * 2)], pch, stk->Parameters.Write.Length );
187 offset += (stk->Parameters.Write.Length / 2);
188 }
189 else {
190 for (i = 0; i < stk->Parameters.Write.Length; i++, pch++)
191 {
192 switch (*pch)
193 {
194 case '\b':
195 if (cursorx > 0)
196 {
197 cursorx--;
198 }
199 else if (cursory > 0)
200 {
201 cursorx = columns - 1;
202 cursory--;
203 }
204 vidmem[(cursorx * 2) + (cursory * columns * 2)] = ' ';
205 vidmem[(cursorx * 2) + (cursory * columns * 2) + 1] = (char) DeviceExtension->CharAttribute;
206 break;
207
208 case '\n':
209 cursory++;
210 cursorx = 0;
211 break;
212
213 case '\r':
214 cursorx = 0;
215 break;
216
217 case '\t':
218 offset = TAB_WIDTH - (cursorx % TAB_WIDTH);
219 for (j = 0; j < offset; j++)
220 {
221 vidmem[(cursorx * 2) + (cursory * columns * 2)] = ' ';
222 cursorx++;
223
224 if (cursorx >= columns)
225 {
226 cursory++;
227 cursorx = 0;
228 }
229 }
230 break;
231
232 default:
233 vidmem[(cursorx * 2) + (cursory * columns * 2)] = *pch;
234 vidmem[(cursorx * 2) + (cursory * columns * 2) + 1] = (char) DeviceExtension->CharAttribute;
235 cursorx++;
236 if (cursorx >= columns)
237 {
238 cursory++;
239 cursorx = 0;
240 }
241 break;
242 }
243 if (cursory >= rows)
244 {
245 unsigned short *LinePtr;
246
247 memcpy (vidmem,
248 &vidmem[columns * 2],
249 columns * (rows - 1) * 2);
250
251 LinePtr = (unsigned short *) &vidmem[columns * (rows - 1) * 2];
252
253 for (j = 0; j < columns; j++)
254 {
255 LinePtr[j] = DeviceExtension->CharAttribute << 8;
256 }
257 cursory = rows - 1;
258 for (j = 0; j < columns; j++)
259 {
260 vidmem[(j * 2) + (cursory * columns * 2)] = ' ';
261 vidmem[(j * 2) + (cursory * columns * 2) + 1] = (char)DeviceExtension->CharAttribute;
262 }
263 }
264 }
265
266 /* Set the cursor position */
267 offset = (cursory * columns) + cursorx;
268 }
269 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSORPOSLO);
270 WRITE_PORT_UCHAR (CRTC_DATA, offset);
271 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSORPOSHI);
272 offset >>= 8;
273 WRITE_PORT_UCHAR (CRTC_DATA, offset);
274
275 Status = STATUS_SUCCESS;
276
277 Irp->IoStatus.Status = Status;
278 IoCompleteRequest (Irp, IO_NO_INCREMENT);
279
280 return (Status);
281 }
282
283
284 NTSTATUS
285 STDCALL ScrIoControl (PDEVICE_OBJECT DeviceObject, PIRP Irp)
286 {
287 PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation (Irp);
288 PDEVICE_EXTENSION DeviceExtension;
289 NTSTATUS Status;
290 DeviceExtension = DeviceObject->DeviceExtension;
291 switch (stk->Parameters.DeviceIoControl.IoControlCode)
292 {
293 case IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO:
294 {
295 PCONSOLE_SCREEN_BUFFER_INFO pcsbi = (PCONSOLE_SCREEN_BUFFER_INFO)Irp->AssociatedIrp.SystemBuffer;
296 int rows = DeviceExtension->Rows;
297 int columns = DeviceExtension->Columns;
298 unsigned int offset;
299
300 /* read cursor position from crtc */
301 __asm__("cli\n\t");
302 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSORPOSLO);
303 offset = READ_PORT_UCHAR (CRTC_DATA);
304 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSORPOSHI);
305 offset += (READ_PORT_UCHAR (CRTC_DATA) << 8);
306 __asm__("sti\n\t");
307
308 pcsbi->dwSize.X = columns;
309 pcsbi->dwSize.Y = rows;
310
311 pcsbi->dwCursorPosition.X = (SHORT)(offset % columns);
312 pcsbi->dwCursorPosition.Y = (SHORT)(offset / columns);
313
314 pcsbi->wAttributes = DeviceExtension->CharAttribute;
315
316 pcsbi->srWindow.Left = 0;
317 pcsbi->srWindow.Right = columns - 1;
318 pcsbi->srWindow.Top = 0;
319 pcsbi->srWindow.Bottom = rows - 1;
320
321 pcsbi->dwMaximumWindowSize.X = columns;
322 pcsbi->dwMaximumWindowSize.Y = rows;
323
324 Irp->IoStatus.Information = sizeof (CONSOLE_SCREEN_BUFFER_INFO);
325 Status = STATUS_SUCCESS;
326 }
327 break;
328
329 case IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO:
330 {
331 PCONSOLE_SCREEN_BUFFER_INFO pcsbi = (PCONSOLE_SCREEN_BUFFER_INFO)Irp->AssociatedIrp.SystemBuffer;
332 unsigned int offset;
333
334 DeviceExtension->CharAttribute = pcsbi->wAttributes;
335 offset = (pcsbi->dwCursorPosition.Y * DeviceExtension->Columns) +
336 pcsbi->dwCursorPosition.X;
337
338 __asm__("cli\n\t");
339 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSORPOSLO);
340 WRITE_PORT_UCHAR (CRTC_DATA, offset);
341 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSORPOSHI);
342 WRITE_PORT_UCHAR (CRTC_DATA, offset>>8);
343 __asm__("sti\n\t");
344
345 Irp->IoStatus.Information = 0;
346 Status = STATUS_SUCCESS;
347 }
348 break;
349
350 case IOCTL_CONSOLE_GET_CURSOR_INFO:
351 {
352 PCONSOLE_CURSOR_INFO pcci = (PCONSOLE_CURSOR_INFO)Irp->AssociatedIrp.SystemBuffer;
353
354 pcci->dwSize = DeviceExtension->CursorSize;
355 pcci->bVisible = DeviceExtension->CursorVisible;
356
357 Irp->IoStatus.Information = sizeof (CONSOLE_CURSOR_INFO);
358 Status = STATUS_SUCCESS;
359 }
360 break;
361
362 case IOCTL_CONSOLE_SET_CURSOR_INFO:
363 {
364 PCONSOLE_CURSOR_INFO pcci = (PCONSOLE_CURSOR_INFO)Irp->AssociatedIrp.SystemBuffer;
365 BYTE data, value;
366 DWORD size, height;
367
368 DeviceExtension->CursorSize = pcci->dwSize;
369 DeviceExtension->CursorVisible = pcci->bVisible;
370 height = DeviceExtension->ScanLines;
371 data = (pcci->bVisible) ? 0x40 : 0x20;
372
373 size = (pcci->dwSize * height) / 100;
374 if (size < 1)
375 size = 1;
376
377 data |= (BYTE)(height - size);
378
379 __asm__("cli\n\t");
380 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSORSTART);
381 WRITE_PORT_UCHAR (CRTC_DATA, data);
382 WRITE_PORT_UCHAR (CRTC_COMMAND, CRTC_CURSOREND);
383 value = READ_PORT_UCHAR (CRTC_DATA) & 0xE0;
384 WRITE_PORT_UCHAR (CRTC_DATA, value | (height - 1));
385
386 __asm__("sti\n\t");
387
388 Irp->IoStatus.Information = 0;
389 Status = STATUS_SUCCESS;
390 }
391 break;
392
393 case IOCTL_CONSOLE_GET_MODE:
394 {
395 PCONSOLE_MODE pcm = (PCONSOLE_MODE)Irp->AssociatedIrp.SystemBuffer;
396
397 pcm->dwMode = DeviceExtension->Mode;
398
399 Irp->IoStatus.Information = sizeof(CONSOLE_MODE);
400 Status = STATUS_SUCCESS;
401 }
402 break;
403
404 case IOCTL_CONSOLE_SET_MODE:
405 {
406 PCONSOLE_MODE pcm = (PCONSOLE_MODE)Irp->AssociatedIrp.SystemBuffer;
407
408 DeviceExtension->Mode = pcm->dwMode;
409
410 Irp->IoStatus.Information = 0;
411 Status = STATUS_SUCCESS;
412 }
413 break;
414
415 case IOCTL_CONSOLE_FILL_OUTPUT_ATTRIBUTE:
416 {
417 POUTPUT_ATTRIBUTE Buf = (POUTPUT_ATTRIBUTE)Irp->AssociatedIrp.SystemBuffer;
418 char *vidmem;
419 int offset;
420 DWORD dwCount;
421
422 vidmem = DeviceExtension->VideoMemory;
423 offset = (Buf->dwCoord.Y * DeviceExtension->Columns * 2) +
424 (Buf->dwCoord.X * 2) + 1;
425
426 for (dwCount = 0; dwCount < Buf->nLength; dwCount++)
427 {
428 vidmem[offset + (dwCount * 2)] = (char) Buf->wAttribute;
429 }
430
431 Buf->dwTransfered = Buf->nLength;
432
433 Irp->IoStatus.Information = 0;
434 Status = STATUS_SUCCESS;
435 }
436 break;
437
438 case IOCTL_CONSOLE_READ_OUTPUT_ATTRIBUTE:
439 {
440 POUTPUT_ATTRIBUTE Buf = (POUTPUT_ATTRIBUTE)Irp->AssociatedIrp.SystemBuffer;
441 PWORD pAttr = (PWORD)MmGetSystemAddressForMdl(Irp->MdlAddress);
442 char *vidmem;
443 int offset;
444 DWORD dwCount;
445
446 vidmem = DeviceExtension->VideoMemory;
447 offset = (Buf->dwCoord.Y * DeviceExtension->Columns * 2) +
448 (Buf->dwCoord.X * 2) + 1;
449
450 for (dwCount = 0; dwCount < stk->Parameters.Write.Length; dwCount++, pAttr++)
451 {
452 (char) *pAttr = vidmem[offset + (dwCount * 2)];
453 }
454
455 Buf->dwTransfered = dwCount;
456
457 Irp->IoStatus.Information = sizeof(OUTPUT_ATTRIBUTE);
458 Status = STATUS_SUCCESS;
459 }
460 break;
461
462 case IOCTL_CONSOLE_WRITE_OUTPUT_ATTRIBUTE:
463 {
464 COORD *pCoord = (COORD *)MmGetSystemAddressForMdl(Irp->MdlAddress);
465 CHAR *pAttr = (CHAR *)(pCoord + 1);
466 char *vidmem;
467 int offset;
468 DWORD dwCount;
469
470 vidmem = DeviceExtension->VideoMemory;
471 offset = (pCoord->Y * DeviceExtension->Columns * 2) +
472 (pCoord->X * 2) + 1;
473
474 for (dwCount = 0; dwCount < (stk->Parameters.Write.Length - sizeof( COORD )); dwCount++, pAttr++)
475 {
476 vidmem[offset + (dwCount * 2)] = *pAttr;
477 }
478 Irp->IoStatus.Information = 0;
479 Status = STATUS_SUCCESS;
480 }
481 break;
482
483 case IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE:
484 DeviceExtension->CharAttribute = (WORD)*(PWORD)Irp->AssociatedIrp.SystemBuffer;
485 Irp->IoStatus.Information = 0;
486 Status = STATUS_SUCCESS;
487 break;
488
489
490 case IOCTL_CONSOLE_FILL_OUTPUT_CHARACTER:
491 {
492 POUTPUT_CHARACTER Buf = (POUTPUT_CHARACTER)Irp->AssociatedIrp.SystemBuffer;
493 char *vidmem;
494 int offset;
495 DWORD dwCount;
496
497 vidmem = DeviceExtension->VideoMemory;
498 offset = (Buf->dwCoord.Y * DeviceExtension->Columns * 2) +
499 (Buf->dwCoord.X * 2);
500
501 CHECKPOINT
502
503 for (dwCount = 0; dwCount < Buf->nLength; dwCount++)
504 {
505 vidmem[offset + (dwCount * 2)] = (char) Buf->cCharacter;
506 }
507
508 Buf->dwTransfered = Buf->nLength;
509
510 Irp->IoStatus.Information = 0;
511 Status = STATUS_SUCCESS;
512 }
513 break;
514
515 case IOCTL_CONSOLE_READ_OUTPUT_CHARACTER:
516 {
517 POUTPUT_CHARACTER Buf = (POUTPUT_CHARACTER)Irp->AssociatedIrp.SystemBuffer;
518 LPSTR pChar = (LPSTR)MmGetSystemAddressForMdl(Irp->MdlAddress);
519 char *vidmem;
520 int offset;
521 DWORD dwCount;
522
523 vidmem = DeviceExtension->VideoMemory;
524 offset = (Buf->dwCoord.Y * DeviceExtension->Columns * 2) +
525 (Buf->dwCoord.X * 2);
526
527 for (dwCount = 0; dwCount < stk->Parameters.Write.Length; dwCount++, pChar++)
528 {
529 *pChar = vidmem[offset + (dwCount * 2)];
530 }
531
532 Buf->dwTransfered = dwCount;
533
534 Irp->IoStatus.Information = sizeof(OUTPUT_ATTRIBUTE);
535 Status = STATUS_SUCCESS;
536 }
537 break;
538
539 case IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER:
540 {
541 COORD *pCoord;
542 LPSTR pChar;
543 char *vidmem;
544 int offset;
545 DWORD dwCount;
546 pCoord = (COORD *)MmGetSystemAddressForMdl(Irp->MdlAddress);
547 pChar = (CHAR *)(pCoord + 1);
548 vidmem = DeviceExtension->VideoMemory;
549 offset = (pCoord->Y * DeviceExtension->Columns * 2) +
550 (pCoord->X * 2);
551
552 for (dwCount = 0; dwCount < (stk->Parameters.Write.Length - sizeof( COORD )); dwCount++, pChar++)
553 {
554 vidmem[offset + (dwCount * 2)] = *pChar;
555 }
556
557 Irp->IoStatus.Information = 0;
558 Status = STATUS_SUCCESS;
559 }
560 break;
561
562
563 default:
564 Status = STATUS_NOT_IMPLEMENTED;
565 }
566
567 Irp->IoStatus.Status = Status;
568 IoCompleteRequest (Irp, IO_NO_INCREMENT);
569
570 return (Status);
571 }
572
573
574 NTSTATUS
575 STDCALL ScrDispatch (PDEVICE_OBJECT DeviceObject, PIRP Irp)
576 {
577 PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
578 NTSTATUS Status;
579
580 switch (stk->MajorFunction)
581 {
582 case IRP_MJ_CLOSE:
583 Status = STATUS_SUCCESS;
584 break;
585
586 default:
587 Status = STATUS_NOT_IMPLEMENTED;
588 break;
589 }
590
591
592 Irp->IoStatus.Status = Status;
593 IoCompleteRequest (Irp, IO_NO_INCREMENT);
594
595 return (Status);
596 }
597
598
599 /*
600 * Module entry point
601 */
602 NTSTATUS
603 STDCALL
604 DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
605 {
606 PDEVICE_OBJECT DeviceObject;
607 UNICODE_STRING DeviceName;
608 UNICODE_STRING SymlinkName;
609
610 DbgPrint ("Screen Driver 0.0.6\n");
611
612 DriverObject->MajorFunction[IRP_MJ_CREATE] = ScrCreate;
613 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScrDispatch;
614 DriverObject->MajorFunction[IRP_MJ_READ] = ScrDispatch;
615 DriverObject->MajorFunction[IRP_MJ_WRITE] = ScrWrite;
616 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL ] = ScrIoControl;
617
618 RtlInitUnicodeString (&DeviceName, L"\\Device\\BlueScreen");
619 IoCreateDevice (DriverObject,
620 sizeof(DEVICE_EXTENSION),
621 &DeviceName,
622 FILE_DEVICE_SCREEN,
623 0,
624 TRUE,
625 &DeviceObject);
626
627 RtlInitUnicodeString (&SymlinkName, L"\\??\\BlueScreen");
628 IoCreateSymbolicLink (&SymlinkName, &DeviceName);
629
630 return (STATUS_SUCCESS);
631 }
632
633 /* EOF */