mirror of
https://github.com/reactos/reactos.git
synced 2024-08-26 05:08:39 +00:00
710 lines
21 KiB
C
710 lines
21 KiB
C
/*
|
|
* COPYRIGHT: GPLv2+ - See COPYING in the top level directory
|
|
* PROJECT: ReactOS Virtual DOS Machine
|
|
* FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/device.c
|
|
* PURPOSE: DOS Device Support
|
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "ntvdm.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#include "emulator.h"
|
|
#include "cpu/bop.h"
|
|
#include "device.h"
|
|
|
|
#include "dos.h"
|
|
#include "dos/dem.h"
|
|
#include "memory.h"
|
|
|
|
/* PRIVATE VARIABLES **********************************************************/
|
|
|
|
static const BYTE StrategyRoutine[] = {
|
|
LOBYTE(EMULATOR_BOP),
|
|
HIBYTE(EMULATOR_BOP),
|
|
BOP_DOS,
|
|
BOP_DRV_STRATEGY,
|
|
0xCB // retf
|
|
};
|
|
|
|
static const BYTE InterruptRoutine[] = {
|
|
LOBYTE(EMULATOR_BOP),
|
|
HIBYTE(EMULATOR_BOP),
|
|
BOP_DOS,
|
|
BOP_DRV_INTERRUPT,
|
|
0xCB // retf
|
|
};
|
|
|
|
C_ASSERT((sizeof(StrategyRoutine) + sizeof(InterruptRoutine)) == DEVICE_CODE_SIZE);
|
|
|
|
static LIST_ENTRY DeviceList = { &DeviceList, &DeviceList };
|
|
static PDOS_REQUEST_HEADER DeviceRequest;
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
static VOID DosCallDriver(DWORD Driver, PDOS_REQUEST_HEADER Request)
|
|
{
|
|
PDOS_DRIVER DriverBlock = (PDOS_DRIVER)FAR_POINTER(Driver);
|
|
WORD AX = getAX();
|
|
WORD CX = getCX();
|
|
WORD DX = getDX();
|
|
WORD BX = getBX();
|
|
WORD BP = getBP();
|
|
WORD SI = getSI();
|
|
WORD DI = getDI();
|
|
WORD DS = getDS();
|
|
WORD ES = getES();
|
|
|
|
/* Set ES:BX to the location of the request */
|
|
setES(DOS_DATA_SEGMENT);
|
|
setBX(DOS_DATA_OFFSET(Sda.Request));
|
|
|
|
/* Copy the request structure to ES:BX */
|
|
RtlMoveMemory(&Sda->Request, Request, Request->RequestLength);
|
|
|
|
/* Call the strategy routine, and then the interrupt routine */
|
|
RunCallback16(&DosContext, MAKELONG(DriverBlock->StrategyRoutine , HIWORD(Driver)));
|
|
RunCallback16(&DosContext, MAKELONG(DriverBlock->InterruptRoutine, HIWORD(Driver)));
|
|
|
|
/* Get the request structure from ES:BX */
|
|
RtlMoveMemory(Request, &Sda->Request, Request->RequestLength);
|
|
|
|
/* Restore the registers */
|
|
setAX(AX);
|
|
setCX(CX);
|
|
setDX(DX);
|
|
setBX(BX);
|
|
setBP(BP);
|
|
setSI(SI);
|
|
setDI(DI);
|
|
setDS(DS);
|
|
setES(ES);
|
|
}
|
|
|
|
static inline WORD NTAPI DosDriverReadInternal(PDOS_DEVICE_NODE DeviceNode,
|
|
DWORD Buffer,
|
|
PWORD Length,
|
|
BOOLEAN IoControl)
|
|
{
|
|
DOS_RW_REQUEST Request;
|
|
|
|
Request.Header.RequestLength = IoControl ? sizeof(DOS_IOCTL_RW_REQUEST)
|
|
: sizeof(DOS_RW_REQUEST);
|
|
Request.Header.CommandCode = IoControl ? DOS_DEVCMD_IOCTL_READ : DOS_DEVCMD_READ;
|
|
Request.BufferPointer = Buffer;
|
|
Request.Length = *Length;
|
|
|
|
DosCallDriver(DeviceNode->Driver, &Request.Header);
|
|
|
|
*Length = Request.Length;
|
|
return Request.Header.Status;
|
|
}
|
|
|
|
static inline WORD NTAPI DosDriverWriteInternal(PDOS_DEVICE_NODE DeviceNode,
|
|
DWORD Buffer,
|
|
PWORD Length,
|
|
BOOLEAN IoControl)
|
|
{
|
|
DOS_RW_REQUEST Request;
|
|
|
|
Request.Header.RequestLength = IoControl ? sizeof(DOS_IOCTL_RW_REQUEST)
|
|
: sizeof(DOS_RW_REQUEST);
|
|
Request.Header.CommandCode = IoControl ? DOS_DEVCMD_IOCTL_WRITE : DOS_DEVCMD_WRITE;
|
|
Request.BufferPointer = Buffer;
|
|
Request.Length = *Length;
|
|
|
|
DosCallDriver(DeviceNode->Driver, &Request.Header);
|
|
|
|
*Length = Request.Length;
|
|
return Request.Header.Status;
|
|
}
|
|
|
|
static inline WORD NTAPI DosDriverGenericRequest(PDOS_DEVICE_NODE DeviceNode,
|
|
BYTE CommandCode)
|
|
{
|
|
DOS_REQUEST_HEADER Request;
|
|
|
|
Request.RequestLength = sizeof(DOS_REQUEST_HEADER);
|
|
Request.CommandCode = CommandCode;
|
|
|
|
DosCallDriver(DeviceNode->Driver, &Request);
|
|
|
|
return Request.Status;
|
|
}
|
|
|
|
static WORD NTAPI DosDriverDispatchIoctlRead(PDOS_DEVICE_NODE DeviceNode,
|
|
DWORD Buffer,
|
|
PWORD Length)
|
|
{
|
|
return DosDriverReadInternal(DeviceNode, Buffer, Length, TRUE);
|
|
}
|
|
|
|
static WORD NTAPI DosDriverDispatchRead(PDOS_DEVICE_NODE DeviceNode,
|
|
DWORD Buffer,
|
|
PWORD Length)
|
|
{
|
|
return DosDriverReadInternal(DeviceNode, Buffer, Length, FALSE);
|
|
}
|
|
|
|
static WORD NTAPI DosDriverDispatchPeek(PDOS_DEVICE_NODE DeviceNode,
|
|
PBYTE Character)
|
|
{
|
|
DOS_PEEK_REQUEST Request;
|
|
|
|
Request.Header.RequestLength = sizeof(DOS_PEEK_REQUEST);
|
|
Request.Header.CommandCode = DOS_DEVCMD_PEEK;
|
|
|
|
DosCallDriver(DeviceNode->Driver, &Request.Header);
|
|
|
|
*Character = Request.Character;
|
|
return Request.Header.Status;
|
|
}
|
|
|
|
static WORD NTAPI DosDriverDispatchInputStatus(PDOS_DEVICE_NODE DeviceNode)
|
|
{
|
|
return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_INSTAT);
|
|
}
|
|
|
|
static WORD NTAPI DosDriverDispatchFlushInput(PDOS_DEVICE_NODE DeviceNode)
|
|
{
|
|
return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_FLUSH_INPUT);
|
|
}
|
|
|
|
static WORD NTAPI DosDriverDispatchIoctlWrite(PDOS_DEVICE_NODE DeviceNode,
|
|
DWORD Buffer,
|
|
PWORD Length)
|
|
{
|
|
return DosDriverWriteInternal(DeviceNode, Buffer, Length, TRUE);
|
|
}
|
|
|
|
static WORD NTAPI DosDriverDispatchWrite(PDOS_DEVICE_NODE DeviceNode,
|
|
DWORD Buffer,
|
|
PWORD Length)
|
|
{
|
|
return DosDriverWriteInternal(DeviceNode, Buffer, Length, FALSE);
|
|
}
|
|
|
|
static WORD NTAPI DosDriverDispatchOutputStatus(PDOS_DEVICE_NODE DeviceNode)
|
|
{
|
|
return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_OUTSTAT);
|
|
}
|
|
|
|
static WORD NTAPI DosDriverDispatchFlushOutput(PDOS_DEVICE_NODE DeviceNode)
|
|
{
|
|
return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_FLUSH_OUTPUT);
|
|
}
|
|
|
|
static WORD NTAPI DosDriverDispatchOpen(PDOS_DEVICE_NODE DeviceNode)
|
|
{
|
|
return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_OPEN);
|
|
}
|
|
|
|
static WORD NTAPI DosDriverDispatchClose(PDOS_DEVICE_NODE DeviceNode)
|
|
{
|
|
return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_CLOSE);
|
|
}
|
|
|
|
static WORD NTAPI DosDriverDispatchOutputUntilBusy(PDOS_DEVICE_NODE DeviceNode,
|
|
DWORD Buffer,
|
|
PWORD Length)
|
|
{
|
|
DOS_OUTPUT_BUSY_REQUEST Request;
|
|
|
|
Request.Header.RequestLength = sizeof(DOS_OUTPUT_BUSY_REQUEST);
|
|
Request.Header.CommandCode = DOS_DEVCMD_OUTPUT_BUSY;
|
|
Request.BufferPointer = Buffer;
|
|
Request.Length = *Length;
|
|
|
|
DosCallDriver(DeviceNode->Driver, &Request.Header);
|
|
|
|
*Length = Request.Length;
|
|
return Request.Header.Status;
|
|
}
|
|
|
|
static VOID DosAddDriver(DWORD Driver)
|
|
{
|
|
PDOS_DRIVER LastDriver = &SysVars->NullDevice;
|
|
|
|
/* Find the last driver in the list */
|
|
while (LOWORD(LastDriver->Link) != 0xFFFF)
|
|
{
|
|
LastDriver = (PDOS_DRIVER)FAR_POINTER(LastDriver->Link);
|
|
}
|
|
|
|
/* Add the new driver to the list */
|
|
LastDriver->Link = Driver;
|
|
LastDriver = (PDOS_DRIVER)FAR_POINTER(Driver);
|
|
|
|
if (LastDriver->DeviceAttributes & DOS_DEVATTR_CLOCK)
|
|
{
|
|
/* Update the active CLOCK driver */
|
|
SysVars->ActiveClock = Driver;
|
|
}
|
|
|
|
if (LastDriver->DeviceAttributes
|
|
& (DOS_DEVATTR_STDIN | DOS_DEVATTR_STDOUT | DOS_DEVATTR_CON))
|
|
{
|
|
/* Update the active CON driver */
|
|
SysVars->ActiveCon = Driver;
|
|
}
|
|
}
|
|
|
|
static VOID DosRemoveDriver(DWORD Driver)
|
|
{
|
|
DWORD CurrentDriver = MAKELONG(DOS_DATA_OFFSET(SysVars.NullDevice), DOS_DATA_SEGMENT);
|
|
|
|
while (LOWORD(CurrentDriver) != 0xFFFF)
|
|
{
|
|
PDOS_DRIVER DriverHeader = (PDOS_DRIVER)FAR_POINTER(CurrentDriver);
|
|
|
|
if (DriverHeader->Link == Driver)
|
|
{
|
|
/* Remove it from the list */
|
|
DriverHeader->Link = ((PDOS_DRIVER)FAR_POINTER(DriverHeader->Link))->Link;
|
|
return;
|
|
}
|
|
|
|
CurrentDriver = DriverHeader->Link;
|
|
}
|
|
}
|
|
|
|
static PDOS_DEVICE_NODE DosCreateDeviceNode(DWORD Driver)
|
|
{
|
|
BYTE i;
|
|
PDOS_DRIVER DriverHeader = (PDOS_DRIVER)FAR_POINTER(Driver);
|
|
PDOS_DEVICE_NODE Node = RtlAllocateHeap(RtlGetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
sizeof(*Node));
|
|
if (Node == NULL) return NULL;
|
|
|
|
Node->Driver = Driver;
|
|
Node->DeviceAttributes = DriverHeader->DeviceAttributes;
|
|
|
|
/* Initialize the name string */
|
|
Node->Name.Buffer = Node->NameBuffer;
|
|
Node->Name.MaximumLength = MAX_DEVICE_NAME;
|
|
|
|
for (i = 0; i < MAX_DEVICE_NAME; i++)
|
|
{
|
|
if (DriverHeader->DeviceName[i] == ' ') break;
|
|
Node->Name.Buffer[i] = DriverHeader->DeviceName[i];
|
|
}
|
|
|
|
Node->Name.Length = i;
|
|
|
|
InsertTailList(&DeviceList, &Node->Entry);
|
|
return Node;
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
PDOS_DEVICE_NODE DosGetDriverNode(DWORD Driver)
|
|
{
|
|
PLIST_ENTRY i;
|
|
PDOS_DEVICE_NODE Node;
|
|
|
|
for (i = DeviceList.Flink; i != &DeviceList; i = i->Flink)
|
|
{
|
|
Node = CONTAINING_RECORD(i, DOS_DEVICE_NODE, Entry);
|
|
if (Node->Driver == Driver) break;
|
|
}
|
|
|
|
if (i == &DeviceList)
|
|
{
|
|
DPRINT1("The driver at %04X:%04X has no associated device node. "
|
|
"Installing automagically.\n",
|
|
HIWORD(Driver),
|
|
LOWORD(Driver));
|
|
|
|
/* Create the device node */
|
|
Node = DosCreateDeviceNode(Driver);
|
|
Node->IoctlReadRoutine = DosDriverDispatchIoctlRead;
|
|
Node->ReadRoutine = DosDriverDispatchRead;
|
|
Node->PeekRoutine = DosDriverDispatchPeek;
|
|
Node->InputStatusRoutine = DosDriverDispatchInputStatus;
|
|
Node->FlushInputRoutine = DosDriverDispatchFlushInput;
|
|
Node->IoctlWriteRoutine = DosDriverDispatchIoctlWrite;
|
|
Node->WriteRoutine = DosDriverDispatchWrite;
|
|
Node->OutputStatusRoutine = DosDriverDispatchOutputStatus;
|
|
Node->FlushOutputRoutine = DosDriverDispatchFlushOutput;
|
|
Node->OpenRoutine = DosDriverDispatchOpen;
|
|
Node->CloseRoutine = DosDriverDispatchClose;
|
|
Node->OutputUntilBusyRoutine = DosDriverDispatchOutputUntilBusy;
|
|
}
|
|
|
|
return Node;
|
|
}
|
|
|
|
PDOS_DEVICE_NODE DosGetDevice(LPCSTR DeviceName)
|
|
{
|
|
DWORD CurrentDriver = MAKELONG(DOS_DATA_OFFSET(SysVars.NullDevice), DOS_DATA_SEGMENT);
|
|
ANSI_STRING DeviceNameString;
|
|
|
|
RtlInitAnsiString(&DeviceNameString, DeviceName);
|
|
|
|
while (LOWORD(CurrentDriver) != 0xFFFF)
|
|
{
|
|
PDOS_DEVICE_NODE Node = DosGetDriverNode(CurrentDriver);
|
|
PDOS_DRIVER DriverHeader = (PDOS_DRIVER)FAR_POINTER(CurrentDriver);
|
|
|
|
if (RtlEqualString(&Node->Name, &DeviceNameString, TRUE)) return Node;
|
|
CurrentDriver = DriverHeader->Link;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PDOS_DEVICE_NODE DosCreateDeviceEx(WORD Attributes, PCHAR DeviceName, WORD PrivateDataSize)
|
|
{
|
|
BYTE i;
|
|
WORD Segment;
|
|
PDOS_DRIVER DriverHeader;
|
|
PDOS_DEVICE_NODE Node;
|
|
|
|
/* Make sure this is a character device */
|
|
if (!(Attributes & DOS_DEVATTR_CHARACTER))
|
|
{
|
|
DPRINT1("ERROR: Block devices are not supported.\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Create a driver header for this device */
|
|
Segment = DosAllocateMemory(sizeof(DOS_DRIVER) + DEVICE_CODE_SIZE + PrivateDataSize, NULL);
|
|
if (Segment == 0) return NULL;
|
|
|
|
/* Fill the header with data */
|
|
DriverHeader = SEG_OFF_TO_PTR(Segment, 0);
|
|
DriverHeader->Link = 0xFFFFFFFF;
|
|
DriverHeader->DeviceAttributes = Attributes;
|
|
DriverHeader->StrategyRoutine = sizeof(DOS_DRIVER);
|
|
DriverHeader->InterruptRoutine = sizeof(DOS_DRIVER) + sizeof(StrategyRoutine);
|
|
|
|
RtlFillMemory(DriverHeader->DeviceName, MAX_DEVICE_NAME, ' ');
|
|
for (i = 0; i < MAX_DEVICE_NAME; i++)
|
|
{
|
|
if (DeviceName[i] == '\0' || DeviceName[i] == ' ') break;
|
|
DriverHeader->DeviceName[i] = DeviceName[i];
|
|
}
|
|
|
|
/* Write the routines */
|
|
RtlMoveMemory(SEG_OFF_TO_PTR(Segment, DriverHeader->StrategyRoutine),
|
|
StrategyRoutine,
|
|
sizeof(StrategyRoutine));
|
|
RtlMoveMemory(SEG_OFF_TO_PTR(Segment, DriverHeader->InterruptRoutine),
|
|
InterruptRoutine,
|
|
sizeof(InterruptRoutine));
|
|
|
|
/* Create the node */
|
|
Node = DosCreateDeviceNode(MAKELONG(0, Segment));
|
|
if (Node == NULL)
|
|
{
|
|
DosFreeMemory(Segment);
|
|
return NULL;
|
|
}
|
|
|
|
DosAddDriver(Node->Driver);
|
|
return Node;
|
|
}
|
|
|
|
PDOS_DEVICE_NODE DosCreateDevice(WORD Attributes, PCHAR DeviceName)
|
|
{
|
|
/* Call the extended API */
|
|
return DosCreateDeviceEx(Attributes, DeviceName, 0);
|
|
}
|
|
|
|
VOID DosDeleteDevice(PDOS_DEVICE_NODE DeviceNode)
|
|
{
|
|
DosRemoveDriver(DeviceNode->Driver);
|
|
|
|
ASSERT(LOWORD(DeviceNode->Driver) == 0);
|
|
DosFreeMemory(HIWORD(DeviceNode->Driver));
|
|
|
|
RemoveEntryList(&DeviceNode->Entry);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, DeviceNode);
|
|
}
|
|
|
|
VOID DeviceStrategyBop(VOID)
|
|
{
|
|
/* Save ES:BX */
|
|
DeviceRequest = (PDOS_REQUEST_HEADER)SEG_OFF_TO_PTR(getES(), getBX());
|
|
}
|
|
|
|
VOID DeviceInterruptBop(VOID)
|
|
{
|
|
PLIST_ENTRY i;
|
|
PDOS_DEVICE_NODE Node;
|
|
DWORD DriverAddress = (getCS() << 4) + getIP() - sizeof(DOS_DRIVER) - 9;
|
|
|
|
/* Get the device node for this driver */
|
|
for (i = DeviceList.Flink; i != &DeviceList; i = i->Flink)
|
|
{
|
|
Node = CONTAINING_RECORD(i, DOS_DEVICE_NODE, Entry);
|
|
if (TO_LINEAR(HIWORD(Node->Driver), LOWORD(Node->Driver)) == DriverAddress) break;
|
|
}
|
|
|
|
if (i == &DeviceList)
|
|
{
|
|
DPRINT1("Device interrupt BOP from an unknown location.\n");
|
|
return;
|
|
}
|
|
|
|
switch (DeviceRequest->CommandCode)
|
|
{
|
|
case DOS_DEVCMD_IOCTL_READ:
|
|
{
|
|
PDOS_IOCTL_RW_REQUEST Request = (PDOS_IOCTL_RW_REQUEST)DeviceRequest;
|
|
|
|
DeviceRequest->Status = Node->IoctlReadRoutine(
|
|
Node,
|
|
Request->BufferPointer,
|
|
&Request->Length
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
case DOS_DEVCMD_READ:
|
|
{
|
|
PDOS_RW_REQUEST Request = (PDOS_RW_REQUEST)DeviceRequest;
|
|
|
|
DeviceRequest->Status = Node->ReadRoutine(
|
|
Node,
|
|
Request->BufferPointer,
|
|
&Request->Length
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
case DOS_DEVCMD_PEEK:
|
|
{
|
|
PDOS_PEEK_REQUEST Request = (PDOS_PEEK_REQUEST)DeviceRequest;
|
|
DeviceRequest->Status = Node->PeekRoutine(Node, &Request->Character);
|
|
break;
|
|
}
|
|
|
|
case DOS_DEVCMD_INSTAT:
|
|
{
|
|
DeviceRequest->Status = Node->InputStatusRoutine(Node);
|
|
break;
|
|
}
|
|
|
|
case DOS_DEVCMD_FLUSH_INPUT:
|
|
{
|
|
DeviceRequest->Status = Node->FlushInputRoutine(Node);
|
|
break;
|
|
}
|
|
|
|
case DOS_DEVCMD_IOCTL_WRITE:
|
|
{
|
|
PDOS_IOCTL_RW_REQUEST Request = (PDOS_IOCTL_RW_REQUEST)DeviceRequest;
|
|
|
|
DeviceRequest->Status = Node->IoctlWriteRoutine(
|
|
Node,
|
|
Request->BufferPointer,
|
|
&Request->Length
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
case DOS_DEVCMD_WRITE:
|
|
{
|
|
PDOS_RW_REQUEST Request = (PDOS_RW_REQUEST)DeviceRequest;
|
|
|
|
DeviceRequest->Status = Node->WriteRoutine(Node,
|
|
Request->BufferPointer,
|
|
&Request->Length
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
case DOS_DEVCMD_OUTSTAT:
|
|
{
|
|
DeviceRequest->Status = Node->OutputStatusRoutine(Node);
|
|
break;
|
|
}
|
|
|
|
case DOS_DEVCMD_FLUSH_OUTPUT:
|
|
{
|
|
DeviceRequest->Status = Node->FlushOutputRoutine(Node);
|
|
break;
|
|
}
|
|
|
|
case DOS_DEVCMD_OPEN:
|
|
{
|
|
DeviceRequest->Status = Node->OpenRoutine(Node);
|
|
break;
|
|
}
|
|
|
|
case DOS_DEVCMD_CLOSE:
|
|
{
|
|
DeviceRequest->Status = Node->CloseRoutine(Node);
|
|
break;
|
|
}
|
|
|
|
case DOS_DEVCMD_OUTPUT_BUSY:
|
|
{
|
|
PDOS_OUTPUT_BUSY_REQUEST Request = (PDOS_OUTPUT_BUSY_REQUEST)DeviceRequest;
|
|
|
|
DeviceRequest->Status = Node->OutputUntilBusyRoutine(
|
|
Node,
|
|
Request->BufferPointer,
|
|
&Request->Length
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPRINT1("Unknown device command code: %u\n", DeviceRequest->CommandCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
DWORD DosLoadDriver(LPCSTR DriverFile)
|
|
{
|
|
DWORD Result = ERROR_SUCCESS;
|
|
HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
|
|
LPBYTE Address = NULL;
|
|
DWORD Driver;
|
|
PDOS_DRIVER DriverHeader;
|
|
WORD Segment = 0;
|
|
DWORD FileSize;
|
|
DWORD DriversLoaded = 0;
|
|
DOS_INIT_REQUEST Request;
|
|
PDOS_DEVICE_NODE DeviceNode;
|
|
|
|
/* Open a handle to the driver file */
|
|
FileHandle = CreateFileA(DriverFile,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
if (FileHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
Result = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Get the file size */
|
|
FileSize = GetFileSize(FileHandle, NULL);
|
|
|
|
/* Allocate DOS memory for the driver */
|
|
Segment = DosAllocateMemory(FileSize >> 4, NULL);
|
|
if (Segment == 0)
|
|
{
|
|
Result = Sda->LastErrorCode;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Create a mapping object for the file */
|
|
FileMapping = CreateFileMapping(FileHandle,
|
|
NULL,
|
|
PAGE_READONLY,
|
|
0,
|
|
0,
|
|
NULL);
|
|
if (FileMapping == NULL)
|
|
{
|
|
Result = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Map the file into memory */
|
|
Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
|
|
if (Address == NULL)
|
|
{
|
|
Result = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Copy the entire file to the DOS memory */
|
|
Driver = MAKELONG(0, Segment);
|
|
DriverHeader = (PDOS_DRIVER)FAR_POINTER(Driver);
|
|
RtlCopyMemory(DriverHeader, Address, FileSize);
|
|
|
|
/* Loop through all the drivers in this file */
|
|
while (TRUE)
|
|
{
|
|
if (!(DriverHeader->DeviceAttributes & DOS_DEVATTR_CHARACTER))
|
|
{
|
|
DPRINT1("Error loading driver at %04X:%04X: "
|
|
"Block device drivers are not supported.\n",
|
|
HIWORD(Driver),
|
|
LOWORD(Driver));
|
|
goto Next;
|
|
}
|
|
|
|
/* Send the driver an init request */
|
|
RtlZeroMemory(&Request, sizeof(Request));
|
|
Request.Header.RequestLength = sizeof(DOS_INIT_REQUEST);
|
|
Request.Header.CommandCode = DOS_DEVCMD_INIT;
|
|
// TODO: Set Request.DeviceString to the appropriate line in CONFIG.NT!
|
|
DosCallDriver(Driver, &Request.Header);
|
|
|
|
if (Request.Header.Status & DOS_DEVSTAT_ERROR)
|
|
{
|
|
DPRINT1("Error loading driver at %04X:%04X: "
|
|
"Initialization routine returned error %u.\n",
|
|
HIWORD(Driver),
|
|
LOWORD(Driver),
|
|
Request.Header.Status & 0x7F);
|
|
goto Next;
|
|
}
|
|
|
|
/* Create the device node */
|
|
DeviceNode = DosCreateDeviceNode(Driver);
|
|
DeviceNode->IoctlReadRoutine = DosDriverDispatchIoctlRead;
|
|
DeviceNode->ReadRoutine = DosDriverDispatchRead;
|
|
DeviceNode->PeekRoutine = DosDriverDispatchPeek;
|
|
DeviceNode->InputStatusRoutine = DosDriverDispatchInputStatus;
|
|
DeviceNode->FlushInputRoutine = DosDriverDispatchFlushInput;
|
|
DeviceNode->IoctlWriteRoutine = DosDriverDispatchIoctlWrite;
|
|
DeviceNode->WriteRoutine = DosDriverDispatchWrite;
|
|
DeviceNode->OutputStatusRoutine = DosDriverDispatchOutputStatus;
|
|
DeviceNode->FlushOutputRoutine = DosDriverDispatchFlushOutput;
|
|
DeviceNode->OpenRoutine = DosDriverDispatchOpen;
|
|
DeviceNode->CloseRoutine = DosDriverDispatchClose;
|
|
DeviceNode->OutputUntilBusyRoutine = DosDriverDispatchOutputUntilBusy;
|
|
|
|
DosAddDriver(Driver);
|
|
DriversLoaded++;
|
|
|
|
Next:
|
|
if (LOWORD(DriverHeader->Link) == 0xFFFF) break;
|
|
Driver = DriverHeader->Link;
|
|
DriverHeader = (PDOS_DRIVER)FAR_POINTER(Driver);
|
|
}
|
|
|
|
DPRINT1("%u drivers loaded from %s.\n", DriversLoaded, DriverFile);
|
|
|
|
Cleanup:
|
|
if (Result != ERROR_SUCCESS)
|
|
{
|
|
/* It was not successful, cleanup the DOS memory */
|
|
if (Segment) DosFreeMemory(Segment);
|
|
}
|
|
|
|
/* Unmap the file */
|
|
if (Address != NULL) UnmapViewOfFile(Address);
|
|
|
|
/* Close the file mapping object */
|
|
if (FileMapping != NULL) CloseHandle(FileMapping);
|
|
|
|
/* Close the file handle */
|
|
if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
|
|
|
|
return Result;
|
|
}
|
|
|
|
/* EOF */
|