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