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