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