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