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 = MAXDWORD; 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 */ 710