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