[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / device.c
1 /*
2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: device.c
5 * PURPOSE: DOS Device Support
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "ntvdm.h"
14 #include "emulator.h"
15 #include "cpu/bop.h"
16 #include "device.h"
17
18 #include "dos.h"
19 #include "dos/dem.h"
20 #include "memory.h"
21
22 /* PRIVATE VARIABLES **********************************************************/
23
24 static const BYTE StrategyRoutine[] = {
25 LOBYTE(EMULATOR_BOP),
26 HIBYTE(EMULATOR_BOP),
27 BOP_DOS,
28 BOP_DRV_STRATEGY,
29 0xCB // retf
30 };
31
32 static const BYTE InterruptRoutine[] = {
33 LOBYTE(EMULATOR_BOP),
34 HIBYTE(EMULATOR_BOP),
35 BOP_DOS,
36 BOP_DRV_INTERRUPT,
37 0xCB // retf
38 };
39
40 C_ASSERT((sizeof(StrategyRoutine) + sizeof(InterruptRoutine)) == DEVICE_CODE_SIZE);
41
42 static LIST_ENTRY DeviceList = { &DeviceList, &DeviceList };
43 static PDOS_REQUEST_HEADER DeviceRequest;
44
45 /* PRIVATE FUNCTIONS **********************************************************/
46
47 static VOID DosCallDriver(DWORD Driver, PDOS_REQUEST_HEADER Request)
48 {
49 PDOS_DRIVER DriverBlock = (PDOS_DRIVER)FAR_POINTER(Driver);
50 WORD ES = getES();
51 WORD BX = getBX();
52
53 /* Set ES:BX to the location of the request */
54 setES(DOS_DATA_SEGMENT);
55 setBX(DOS_DATA_OFFSET(Sda.Request));
56
57 /* Copy the request structure to ES:BX */
58 RtlMoveMemory(&Sda->Request, Request, Request->RequestLength);
59
60 /* Call the strategy routine, and then the interrupt routine */
61 RunCallback16(&DosContext, MAKELONG(DriverBlock->StrategyRoutine , HIWORD(Driver)));
62 RunCallback16(&DosContext, MAKELONG(DriverBlock->InterruptRoutine, HIWORD(Driver)));
63
64 /* Get the request structure from ES:BX */
65 RtlMoveMemory(Request, &Sda->Request, Request->RequestLength);
66
67 /* Restore ES:BX */
68 setES(ES);
69 setBX(BX);
70 }
71
72 static inline WORD NTAPI DosDriverReadInternal(PDOS_DEVICE_NODE DeviceNode,
73 DWORD Buffer,
74 PWORD Length,
75 BOOLEAN IoControl)
76 {
77 DOS_RW_REQUEST Request;
78
79 Request.Header.RequestLength = IoControl ? sizeof(DOS_IOCTL_RW_REQUEST)
80 : sizeof(DOS_RW_REQUEST);
81 Request.Header.CommandCode = IoControl ? DOS_DEVCMD_IOCTL_READ : DOS_DEVCMD_READ;
82 Request.BufferPointer = Buffer;
83 Request.Length = *Length;
84
85 DosCallDriver(DeviceNode->Driver, &Request.Header);
86
87 *Length = Request.Length;
88 return Request.Header.Status;
89 }
90
91 static inline WORD NTAPI DosDriverWriteInternal(PDOS_DEVICE_NODE DeviceNode,
92 DWORD Buffer,
93 PWORD Length,
94 BOOLEAN IoControl)
95 {
96 DOS_RW_REQUEST Request;
97
98 Request.Header.RequestLength = IoControl ? sizeof(DOS_IOCTL_RW_REQUEST)
99 : sizeof(DOS_RW_REQUEST);
100 Request.Header.CommandCode = IoControl ? DOS_DEVCMD_IOCTL_WRITE : DOS_DEVCMD_WRITE;
101 Request.BufferPointer = Buffer;
102 Request.Length = *Length;
103
104 DosCallDriver(DeviceNode->Driver, &Request.Header);
105
106 *Length = Request.Length;
107 return Request.Header.Status;
108 }
109
110 static inline WORD NTAPI DosDriverGenericRequest(PDOS_DEVICE_NODE DeviceNode,
111 BYTE CommandCode)
112 {
113 DOS_REQUEST_HEADER Request;
114
115 Request.RequestLength = sizeof(DOS_REQUEST_HEADER);
116 Request.CommandCode = CommandCode;
117
118 DosCallDriver(DeviceNode->Driver, &Request);
119
120 return Request.Status;
121 }
122
123 static WORD NTAPI DosDriverDispatchIoctlRead(PDOS_DEVICE_NODE DeviceNode,
124 DWORD Buffer,
125 PWORD Length)
126 {
127 return DosDriverReadInternal(DeviceNode, Buffer, Length, TRUE);
128 }
129
130 static WORD NTAPI DosDriverDispatchRead(PDOS_DEVICE_NODE DeviceNode,
131 DWORD Buffer,
132 PWORD Length)
133 {
134 return DosDriverReadInternal(DeviceNode, Buffer, Length, FALSE);
135 }
136
137 static WORD NTAPI DosDriverDispatchPeek(PDOS_DEVICE_NODE DeviceNode,
138 PBYTE Character)
139 {
140 DOS_PEEK_REQUEST Request;
141
142 Request.Header.RequestLength = sizeof(DOS_PEEK_REQUEST);
143 Request.Header.CommandCode = DOS_DEVCMD_PEEK;
144
145 DosCallDriver(DeviceNode->Driver, &Request.Header);
146
147 *Character = Request.Character;
148 return Request.Header.Status;
149 }
150
151 static WORD NTAPI DosDriverDispatchInputStatus(PDOS_DEVICE_NODE DeviceNode)
152 {
153 return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_INSTAT);
154 }
155
156 static WORD NTAPI DosDriverDispatchFlushInput(PDOS_DEVICE_NODE DeviceNode)
157 {
158 return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_FLUSH_INPUT);
159 }
160
161 static WORD NTAPI DosDriverDispatchIoctlWrite(PDOS_DEVICE_NODE DeviceNode,
162 DWORD Buffer,
163 PWORD Length)
164 {
165 return DosDriverWriteInternal(DeviceNode, Buffer, Length, TRUE);
166 }
167
168 static WORD NTAPI DosDriverDispatchWrite(PDOS_DEVICE_NODE DeviceNode,
169 DWORD Buffer,
170 PWORD Length)
171 {
172 return DosDriverWriteInternal(DeviceNode, Buffer, Length, FALSE);
173 }
174
175 static WORD NTAPI DosDriverDispatchOutputStatus(PDOS_DEVICE_NODE DeviceNode)
176 {
177 return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_OUTSTAT);
178 }
179
180 static WORD NTAPI DosDriverDispatchFlushOutput(PDOS_DEVICE_NODE DeviceNode)
181 {
182 return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_FLUSH_OUTPUT);
183 }
184
185 static WORD NTAPI DosDriverDispatchOpen(PDOS_DEVICE_NODE DeviceNode)
186 {
187 return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_OPEN);
188 }
189
190 static WORD NTAPI DosDriverDispatchClose(PDOS_DEVICE_NODE DeviceNode)
191 {
192 return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_CLOSE);
193 }
194
195 static WORD NTAPI DosDriverDispatchOutputUntilBusy(PDOS_DEVICE_NODE DeviceNode,
196 DWORD Buffer,
197 PWORD Length)
198 {
199 DOS_OUTPUT_BUSY_REQUEST Request;
200
201 Request.Header.RequestLength = sizeof(DOS_OUTPUT_BUSY_REQUEST);
202 Request.Header.CommandCode = DOS_DEVCMD_OUTPUT_BUSY;
203 Request.BufferPointer = Buffer;
204 Request.Length = *Length;
205
206 DosCallDriver(DeviceNode->Driver, &Request.Header);
207
208 *Length = Request.Length;
209 return Request.Header.Status;
210 }
211
212 static VOID DosAddDriver(DWORD Driver)
213 {
214 PDOS_DRIVER LastDriver = &SysVars->NullDevice;
215
216 /* Find the last driver in the list */
217 while (LOWORD(LastDriver->Link) != 0xFFFF)
218 {
219 LastDriver = (PDOS_DRIVER)FAR_POINTER(LastDriver->Link);
220 }
221
222 /* Add the new driver to the list */
223 LastDriver->Link = Driver;
224 LastDriver = (PDOS_DRIVER)FAR_POINTER(Driver);
225
226 if (LastDriver->DeviceAttributes & DOS_DEVATTR_CLOCK)
227 {
228 /* Update the active CLOCK driver */
229 SysVars->ActiveClock = Driver;
230 }
231
232 if (LastDriver->DeviceAttributes
233 & (DOS_DEVATTR_STDIN | DOS_DEVATTR_STDOUT | DOS_DEVATTR_CON))
234 {
235 /* Update the active CON driver */
236 SysVars->ActiveCon = Driver;
237 }
238 }
239
240 static VOID DosRemoveDriver(DWORD Driver)
241 {
242 DWORD CurrentDriver = MAKELONG(DOS_DATA_OFFSET(SysVars.NullDevice), DOS_DATA_SEGMENT);
243
244 while (LOWORD(CurrentDriver) != 0xFFFF)
245 {
246 PDOS_DRIVER DriverHeader = (PDOS_DRIVER)FAR_POINTER(CurrentDriver);
247
248 if (DriverHeader->Link == Driver)
249 {
250 /* Remove it from the list */
251 DriverHeader->Link = ((PDOS_DRIVER)FAR_POINTER(DriverHeader->Link))->Link;
252 return;
253 }
254
255 CurrentDriver = DriverHeader->Link;
256 }
257 }
258
259 static PDOS_DEVICE_NODE DosCreateDeviceNode(DWORD Driver)
260 {
261 BYTE i;
262 PDOS_DRIVER DriverHeader = (PDOS_DRIVER)FAR_POINTER(Driver);
263 PDOS_DEVICE_NODE Node = RtlAllocateHeap(RtlGetProcessHeap(),
264 HEAP_ZERO_MEMORY,
265 sizeof(*Node));
266 if (Node == NULL) return NULL;
267
268 Node->Driver = Driver;
269 Node->DeviceAttributes = DriverHeader->DeviceAttributes;
270
271 /* Initialize the name string */
272 Node->Name.Buffer = Node->NameBuffer;
273 Node->Name.MaximumLength = MAX_DEVICE_NAME;
274
275 for (i = 0; i < MAX_DEVICE_NAME; i++)
276 {
277 if (DriverHeader->DeviceName[i] == ' ') break;
278 Node->Name.Buffer[i] = DriverHeader->DeviceName[i];
279 }
280
281 Node->Name.Length = i;
282
283 InsertTailList(&DeviceList, &Node->Entry);
284 return Node;
285 }
286
287 /* PUBLIC FUNCTIONS ***********************************************************/
288
289 PDOS_DEVICE_NODE DosGetDriverNode(DWORD Driver)
290 {
291 PLIST_ENTRY i;
292 PDOS_DEVICE_NODE Node;
293
294 for (i = DeviceList.Flink; i != &DeviceList; i = i->Flink)
295 {
296 Node = CONTAINING_RECORD(i, DOS_DEVICE_NODE, Entry);
297 if (Node->Driver == Driver) break;
298 }
299
300 if (i == &DeviceList)
301 {
302 DPRINT1("The driver at %04X:%04X has no associated device node. "
303 "Installing automagically.\n",
304 HIWORD(Driver),
305 LOWORD(Driver));
306
307 /* Create the device node */
308 Node = DosCreateDeviceNode(Driver);
309 Node->IoctlReadRoutine = DosDriverDispatchIoctlRead;
310 Node->ReadRoutine = DosDriverDispatchRead;
311 Node->PeekRoutine = DosDriverDispatchPeek;
312 Node->InputStatusRoutine = DosDriverDispatchInputStatus;
313 Node->FlushInputRoutine = DosDriverDispatchFlushInput;
314 Node->IoctlWriteRoutine = DosDriverDispatchIoctlWrite;
315 Node->WriteRoutine = DosDriverDispatchWrite;
316 Node->OutputStatusRoutine = DosDriverDispatchOutputStatus;
317 Node->FlushOutputRoutine = DosDriverDispatchFlushOutput;
318 Node->OpenRoutine = DosDriverDispatchOpen;
319 Node->CloseRoutine = DosDriverDispatchClose;
320 Node->OutputUntilBusyRoutine = DosDriverDispatchOutputUntilBusy;
321 }
322
323 return Node;
324 }
325
326 PDOS_DEVICE_NODE DosGetDevice(LPCSTR DeviceName)
327 {
328 DWORD CurrentDriver = MAKELONG(DOS_DATA_OFFSET(SysVars.NullDevice), DOS_DATA_SEGMENT);
329 ANSI_STRING DeviceNameString;
330
331 RtlInitAnsiString(&DeviceNameString, DeviceName);
332
333 while (LOWORD(CurrentDriver) != 0xFFFF)
334 {
335 PDOS_DEVICE_NODE Node = DosGetDriverNode(CurrentDriver);
336 PDOS_DRIVER DriverHeader = (PDOS_DRIVER)FAR_POINTER(CurrentDriver);
337
338 if (RtlEqualString(&Node->Name, &DeviceNameString, TRUE)) return Node;
339 CurrentDriver = DriverHeader->Link;
340 }
341
342 return NULL;
343 }
344
345 PDOS_DEVICE_NODE DosCreateDeviceEx(WORD Attributes, PCHAR DeviceName, WORD PrivateDataSize)
346 {
347 BYTE i;
348 WORD Segment;
349 PDOS_DRIVER DriverHeader;
350 PDOS_DEVICE_NODE Node;
351
352 /* Make sure this is a character device */
353 if (!(Attributes & DOS_DEVATTR_CHARACTER))
354 {
355 DPRINT1("ERROR: Block devices are not supported.\n");
356 return NULL;
357 }
358
359 /* Create a driver header for this device */
360 Segment = DosAllocateMemory(sizeof(DOS_DRIVER) + DEVICE_CODE_SIZE + PrivateDataSize, NULL);
361 if (Segment == 0) return NULL;
362
363 /* Fill the header with data */
364 DriverHeader = SEG_OFF_TO_PTR(Segment, 0);
365 DriverHeader->Link = 0xFFFFFFFF;
366 DriverHeader->DeviceAttributes = Attributes;
367 DriverHeader->StrategyRoutine = sizeof(DOS_DRIVER);
368 DriverHeader->InterruptRoutine = sizeof(DOS_DRIVER) + sizeof(StrategyRoutine);
369
370 RtlFillMemory(DriverHeader->DeviceName, MAX_DEVICE_NAME, ' ');
371 for (i = 0; i < MAX_DEVICE_NAME; i++)
372 {
373 if (DeviceName[i] == '\0' || DeviceName[i] == ' ') break;
374 DriverHeader->DeviceName[i] = DeviceName[i];
375 }
376
377 /* Write the routines */
378 RtlMoveMemory(SEG_OFF_TO_PTR(Segment, DriverHeader->StrategyRoutine),
379 StrategyRoutine,
380 sizeof(StrategyRoutine));
381 RtlMoveMemory(SEG_OFF_TO_PTR(Segment, DriverHeader->InterruptRoutine),
382 InterruptRoutine,
383 sizeof(InterruptRoutine));
384
385 /* Create the node */
386 Node = DosCreateDeviceNode(MAKELONG(0, Segment));
387 if (Node == NULL)
388 {
389 DosFreeMemory(Segment);
390 return NULL;
391 }
392
393 DosAddDriver(Node->Driver);
394 return Node;
395 }
396
397 PDOS_DEVICE_NODE DosCreateDevice(WORD Attributes, PCHAR DeviceName)
398 {
399 /* Call the extended API */
400 return DosCreateDeviceEx(Attributes, DeviceName, 0);
401 }
402
403 VOID DosDeleteDevice(PDOS_DEVICE_NODE DeviceNode)
404 {
405 DosRemoveDriver(DeviceNode->Driver);
406
407 ASSERT(LOWORD(DeviceNode->Driver) == 0);
408 DosFreeMemory(HIWORD(DeviceNode->Driver));
409
410 RemoveEntryList(&DeviceNode->Entry);
411 RtlFreeHeap(RtlGetProcessHeap(), 0, DeviceNode);
412 }
413
414 VOID DeviceStrategyBop(VOID)
415 {
416 /* Save ES:BX */
417 DeviceRequest = (PDOS_REQUEST_HEADER)SEG_OFF_TO_PTR(getES(), getBX());
418 }
419
420 VOID DeviceInterruptBop(VOID)
421 {
422 PLIST_ENTRY i;
423 PDOS_DEVICE_NODE Node;
424 DWORD DriverAddress = (getCS() << 4) + getIP() - sizeof(DOS_DRIVER) - 9;
425
426 /* Get the device node for this driver */
427 for (i = DeviceList.Flink; i != &DeviceList; i = i->Flink)
428 {
429 Node = CONTAINING_RECORD(i, DOS_DEVICE_NODE, Entry);
430 if (TO_LINEAR(HIWORD(Node->Driver), LOWORD(Node->Driver)) == DriverAddress) break;
431 }
432
433 if (i == &DeviceList)
434 {
435 DPRINT1("Device interrupt BOP from an unknown location.\n");
436 return;
437 }
438
439 switch (DeviceRequest->CommandCode)
440 {
441 case DOS_DEVCMD_IOCTL_READ:
442 {
443 PDOS_IOCTL_RW_REQUEST Request = (PDOS_IOCTL_RW_REQUEST)DeviceRequest;
444
445 DeviceRequest->Status = Node->IoctlReadRoutine(
446 Node,
447 Request->BufferPointer,
448 &Request->Length
449 );
450
451 break;
452 }
453
454 case DOS_DEVCMD_READ:
455 {
456 PDOS_RW_REQUEST Request = (PDOS_RW_REQUEST)DeviceRequest;
457
458 DeviceRequest->Status = Node->ReadRoutine(
459 Node,
460 Request->BufferPointer,
461 &Request->Length
462 );
463
464 break;
465 }
466
467 case DOS_DEVCMD_PEEK:
468 {
469 PDOS_PEEK_REQUEST Request = (PDOS_PEEK_REQUEST)DeviceRequest;
470 DeviceRequest->Status = Node->PeekRoutine(Node, &Request->Character);
471 break;
472 }
473
474 case DOS_DEVCMD_INSTAT:
475 {
476 DeviceRequest->Status = Node->InputStatusRoutine(Node);
477 break;
478 }
479
480 case DOS_DEVCMD_FLUSH_INPUT:
481 {
482 DeviceRequest->Status = Node->FlushInputRoutine(Node);
483 break;
484 }
485
486 case DOS_DEVCMD_IOCTL_WRITE:
487 {
488 PDOS_IOCTL_RW_REQUEST Request = (PDOS_IOCTL_RW_REQUEST)DeviceRequest;
489
490 DeviceRequest->Status = Node->IoctlWriteRoutine(
491 Node,
492 Request->BufferPointer,
493 &Request->Length
494 );
495
496 break;
497 }
498
499 case DOS_DEVCMD_WRITE:
500 {
501 PDOS_RW_REQUEST Request = (PDOS_RW_REQUEST)DeviceRequest;
502
503 DeviceRequest->Status = Node->WriteRoutine(Node,
504 Request->BufferPointer,
505 &Request->Length
506 );
507
508 break;
509 }
510
511 case DOS_DEVCMD_OUTSTAT:
512 {
513 DeviceRequest->Status = Node->OutputStatusRoutine(Node);
514 break;
515 }
516
517 case DOS_DEVCMD_FLUSH_OUTPUT:
518 {
519 DeviceRequest->Status = Node->FlushOutputRoutine(Node);
520 break;
521 }
522
523 case DOS_DEVCMD_OPEN:
524 {
525 DeviceRequest->Status = Node->OpenRoutine(Node);
526 break;
527 }
528
529 case DOS_DEVCMD_CLOSE:
530 {
531 DeviceRequest->Status = Node->CloseRoutine(Node);
532 break;
533 }
534
535 case DOS_DEVCMD_OUTPUT_BUSY:
536 {
537 PDOS_OUTPUT_BUSY_REQUEST Request = (PDOS_OUTPUT_BUSY_REQUEST)DeviceRequest;
538
539 DeviceRequest->Status = Node->OutputUntilBusyRoutine(
540 Node,
541 Request->BufferPointer,
542 &Request->Length
543 );
544
545 break;
546 }
547
548 default:
549 {
550 DPRINT1("Unknown device command code: %u\n", DeviceRequest->CommandCode);
551 }
552 }
553 }
554
555 DWORD DosLoadDriver(LPCSTR DriverFile)
556 {
557 DWORD Result = ERROR_SUCCESS;
558 HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
559 LPBYTE Address = NULL;
560 DWORD Driver;
561 PDOS_DRIVER DriverHeader;
562 WORD Segment = 0;
563 DWORD FileSize;
564 DWORD DriversLoaded = 0;
565 DOS_INIT_REQUEST Request;
566 PDOS_DEVICE_NODE DeviceNode;
567
568 /* Open a handle to the driver file */
569 FileHandle = CreateFileA(DriverFile,
570 GENERIC_READ,
571 FILE_SHARE_READ,
572 NULL,
573 OPEN_EXISTING,
574 FILE_ATTRIBUTE_NORMAL,
575 NULL);
576 if (FileHandle == INVALID_HANDLE_VALUE)
577 {
578 Result = GetLastError();
579 goto Cleanup;
580 }
581
582 /* Get the file size */
583 FileSize = GetFileSize(FileHandle, NULL);
584
585 /* Allocate DOS memory for the driver */
586 Segment = DosAllocateMemory(FileSize >> 4, NULL);
587 if (Segment == 0)
588 {
589 Result = Sda->LastErrorCode;
590 goto Cleanup;
591 }
592
593 /* Create a mapping object for the file */
594 FileMapping = CreateFileMapping(FileHandle,
595 NULL,
596 PAGE_READONLY,
597 0,
598 0,
599 NULL);
600 if (FileMapping == NULL)
601 {
602 Result = GetLastError();
603 goto Cleanup;
604 }
605
606 /* Map the file into memory */
607 Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
608 if (Address == NULL)
609 {
610 Result = GetLastError();
611 goto Cleanup;
612 }
613
614 /* Copy the entire file to the DOS memory */
615 Driver = MAKELONG(0, Segment);
616 DriverHeader = (PDOS_DRIVER)FAR_POINTER(Driver);
617 RtlCopyMemory(DriverHeader, Address, FileSize);
618
619 /* Loop through all the drivers in this file */
620 while (TRUE)
621 {
622 if (!(DriverHeader->DeviceAttributes & DOS_DEVATTR_CHARACTER))
623 {
624 DPRINT1("Error loading driver at %04X:%04X: "
625 "Block device drivers are not supported.\n",
626 HIWORD(Driver),
627 LOWORD(Driver));
628 goto Next;
629 }
630
631 /* Send the driver an init request */
632 RtlZeroMemory(&Request, sizeof(Request));
633 Request.Header.RequestLength = sizeof(DOS_INIT_REQUEST);
634 Request.Header.CommandCode = DOS_DEVCMD_INIT;
635 // TODO: Set Request.DeviceString to the appropriate line in CONFIG.NT!
636 DosCallDriver(Driver, &Request.Header);
637
638 if (Request.Header.Status & DOS_DEVSTAT_ERROR)
639 {
640 DPRINT1("Error loading driver at %04X:%04X: "
641 "Initialization routine returned error %u.\n",
642 HIWORD(Driver),
643 LOWORD(Driver),
644 Request.Header.Status & 0x7F);
645 goto Next;
646 }
647
648 /* Create the device node */
649 DeviceNode = DosCreateDeviceNode(Driver);
650 DeviceNode->IoctlReadRoutine = DosDriverDispatchIoctlRead;
651 DeviceNode->ReadRoutine = DosDriverDispatchRead;
652 DeviceNode->PeekRoutine = DosDriverDispatchPeek;
653 DeviceNode->InputStatusRoutine = DosDriverDispatchInputStatus;
654 DeviceNode->FlushInputRoutine = DosDriverDispatchFlushInput;
655 DeviceNode->IoctlWriteRoutine = DosDriverDispatchIoctlWrite;
656 DeviceNode->WriteRoutine = DosDriverDispatchWrite;
657 DeviceNode->OutputStatusRoutine = DosDriverDispatchOutputStatus;
658 DeviceNode->FlushOutputRoutine = DosDriverDispatchFlushOutput;
659 DeviceNode->OpenRoutine = DosDriverDispatchOpen;
660 DeviceNode->CloseRoutine = DosDriverDispatchClose;
661 DeviceNode->OutputUntilBusyRoutine = DosDriverDispatchOutputUntilBusy;
662
663 DosAddDriver(Driver);
664 DriversLoaded++;
665
666 Next:
667 if (LOWORD(DriverHeader->Link) == 0xFFFF) break;
668 Driver = DriverHeader->Link;
669 DriverHeader = (PDOS_DRIVER)FAR_POINTER(Driver);
670 }
671
672 DPRINT1("%u drivers loaded from %s.\n", DriversLoaded, DriverFile);
673
674 Cleanup:
675 if (Result != ERROR_SUCCESS)
676 {
677 /* It was not successful, cleanup the DOS memory */
678 if (Segment) DosFreeMemory(Segment);
679 }
680
681 /* Unmap the file */
682 if (Address != NULL) UnmapViewOfFile(Address);
683
684 /* Close the file mapping object */
685 if (FileMapping != NULL) CloseHandle(FileMapping);
686
687 /* Close the file handle */
688 if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
689
690 return Result;
691 }
692
693 /* EOF */