2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: DOS Device Support
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
21 /* PRIVATE VARIABLES **********************************************************/
23 static LIST_ENTRY DeviceList
= { &DeviceList
, &DeviceList
};
25 /* PRIVATE FUNCTIONS **********************************************************/
27 static VOID
DosCallDriver(DWORD Driver
, PDOS_REQUEST_HEADER Request
)
29 PDOS_DRIVER DriverBlock
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
30 PDOS_REQUEST_HEADER RemoteRequest
;
32 /* Call the strategy routine first */
33 Call16(HIWORD(Driver
), DriverBlock
->StrategyRoutine
);
34 RemoteRequest
= (PDOS_REQUEST_HEADER
)SEG_OFF_TO_PTR(getES(), getBX());
36 /* Copy the request structure to ES:BX */
37 RtlMoveMemory(RemoteRequest
, Request
, Request
->RequestLength
);
39 /* Call the interrupt routine */
40 Call16(HIWORD(Driver
), DriverBlock
->InterruptRoutine
);
42 /* Get the request structure from ES:BX */
43 RtlMoveMemory(Request
, RemoteRequest
, RemoteRequest
->RequestLength
);
46 static inline WORD NTAPI
DosDriverReadInternal(PDOS_DEVICE_NODE DeviceNode
,
51 DOS_RW_REQUEST Request
;
53 Request
.Header
.RequestLength
= IoControl
? sizeof(DOS_IOCTL_RW_REQUEST
)
54 : sizeof(DOS_RW_REQUEST
);
55 Request
.Header
.CommandCode
= IoControl
? DOS_DEVCMD_IOCTL_READ
: DOS_DEVCMD_READ
;
56 Request
.BufferPointer
= Buffer
;
57 Request
.Length
= *Length
;
59 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
61 *Length
= Request
.Length
;
62 return Request
.Header
.Status
;
65 static inline WORD NTAPI
DosDriverWriteInternal(PDOS_DEVICE_NODE DeviceNode
,
70 DOS_RW_REQUEST Request
;
72 Request
.Header
.RequestLength
= IoControl
? sizeof(DOS_IOCTL_RW_REQUEST
)
73 : sizeof(DOS_RW_REQUEST
);
74 Request
.Header
.CommandCode
= IoControl
? DOS_DEVCMD_IOCTL_WRITE
: DOS_DEVCMD_WRITE
;
75 Request
.BufferPointer
= Buffer
;
76 Request
.Length
= *Length
;
78 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
80 *Length
= Request
.Length
;
81 return Request
.Header
.Status
;
84 static inline WORD NTAPI
DosDriverGenericRequest(PDOS_DEVICE_NODE DeviceNode
,
87 DOS_REQUEST_HEADER Request
;
89 Request
.RequestLength
= sizeof(DOS_REQUEST_HEADER
);
90 Request
.CommandCode
= CommandCode
;
92 DosCallDriver(DeviceNode
->Driver
, &Request
);
94 return Request
.Status
;
97 static WORD NTAPI
DosDriverDispatchIoctlRead(PDOS_DEVICE_NODE DeviceNode
,
101 return DosDriverReadInternal(DeviceNode
, Buffer
, Length
, TRUE
);
104 static WORD NTAPI
DosDriverDispatchRead(PDOS_DEVICE_NODE DeviceNode
,
108 return DosDriverReadInternal(DeviceNode
, Buffer
, Length
, FALSE
);
111 static WORD NTAPI
DosDriverDispatchPeek(PDOS_DEVICE_NODE DeviceNode
,
114 DOS_PEEK_REQUEST Request
;
116 Request
.Header
.RequestLength
= sizeof(DOS_PEEK_REQUEST
);
117 Request
.Header
.CommandCode
= DOS_DEVCMD_PEEK
;
119 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
121 *Character
= Request
.Character
;
122 return Request
.Header
.Status
;
125 static WORD NTAPI
DosDriverDispatchInputStatus(PDOS_DEVICE_NODE DeviceNode
)
127 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_INSTAT
);
130 static WORD NTAPI
DosDriverDispatchFlushInput(PDOS_DEVICE_NODE DeviceNode
)
132 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_FLUSH_INPUT
);
135 static WORD NTAPI
DosDriverDispatchIoctlWrite(PDOS_DEVICE_NODE DeviceNode
,
139 return DosDriverWriteInternal(DeviceNode
, Buffer
, Length
, TRUE
);
142 static WORD NTAPI
DosDriverDispatchWrite(PDOS_DEVICE_NODE DeviceNode
,
146 return DosDriverWriteInternal(DeviceNode
, Buffer
, Length
, FALSE
);
149 static WORD NTAPI
DosDriverDispatchOutputStatus(PDOS_DEVICE_NODE DeviceNode
)
151 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_OUTSTAT
);
154 static WORD NTAPI
DosDriverDispatchFlushOutput(PDOS_DEVICE_NODE DeviceNode
)
156 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_FLUSH_OUTPUT
);
159 static WORD NTAPI
DosDriverDispatchOpen(PDOS_DEVICE_NODE DeviceNode
)
161 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_OPEN
);
164 static WORD NTAPI
DosDriverDispatchClose(PDOS_DEVICE_NODE DeviceNode
)
166 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_CLOSE
);
169 static WORD NTAPI
DosDriverDispatchOutputUntilBusy(PDOS_DEVICE_NODE DeviceNode
,
173 DOS_OUTPUT_BUSY_REQUEST Request
;
175 Request
.Header
.RequestLength
= sizeof(DOS_OUTPUT_BUSY_REQUEST
);
176 Request
.Header
.CommandCode
= DOS_DEVCMD_OUTPUT_BUSY
;
177 Request
.BufferPointer
= Buffer
;
178 Request
.Length
= *Length
;
180 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
182 *Length
= Request
.Length
;
183 return Request
.Header
.Status
;
186 /* PUBLIC FUNCTIONS ***********************************************************/
188 PDOS_DEVICE_NODE
DosGetDevice(LPCSTR DeviceName
)
191 PDOS_DEVICE_NODE Node
;
192 ANSI_STRING DeviceNameString
;
194 RtlInitAnsiString(&DeviceNameString
, DeviceName
);
196 for (i
= DeviceList
.Flink
; i
!= &DeviceList
; i
= i
->Flink
)
198 Node
= CONTAINING_RECORD(i
, DOS_DEVICE_NODE
, Entry
);
199 if (RtlEqualString(&Node
->Name
, &DeviceNameString
, TRUE
)) return Node
;
205 PDOS_DEVICE_NODE
DosCreateDevice(WORD Attributes
, PCHAR DeviceName
)
208 PDOS_DEVICE_NODE Node
;
210 /* Make sure this is a character device */
211 if (!(Attributes
& DOS_DEVATTR_CHARACTER
))
213 DPRINT1("ERROR: Block devices are not supported.\n");
217 Node
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*Node
));
218 if (Node
== NULL
) return NULL
;
220 Node
->DeviceAttributes
= Attributes
;
222 /* Initialize the name string */
223 Node
->Name
.Buffer
= Node
->NameBuffer
;
224 Node
->Name
.MaximumLength
= MAX_DEVICE_NAME
;
226 for (i
= 0; i
< MAX_DEVICE_NAME
; i
++)
228 if (DeviceName
[i
] == '\0' || DeviceName
[i
] == ' ') break;
229 Node
->Name
.Buffer
[i
] = DeviceName
[i
];
232 Node
->Name
.Length
= i
;
234 InsertTailList(&DeviceList
, &Node
->Entry
);
238 VOID
DosDeleteDevice(PDOS_DEVICE_NODE DeviceNode
)
240 RemoveEntryList(&DeviceNode
->Entry
);
241 RtlFreeHeap(RtlGetProcessHeap(), 0, DeviceNode
);
244 DWORD
DosLoadDriver(LPCSTR DriverFile
)
246 DWORD Result
= ERROR_SUCCESS
;
247 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
248 LPBYTE Address
= NULL
;
250 PDOS_DRIVER DriverHeader
;
253 DWORD DriversLoaded
= 0;
254 DOS_INIT_REQUEST Request
;
255 PDOS_DEVICE_NODE DeviceNode
;
257 /* Open a handle to the driver file */
258 FileHandle
= CreateFileA(DriverFile
,
263 FILE_ATTRIBUTE_NORMAL
,
265 if (FileHandle
== INVALID_HANDLE_VALUE
)
267 Result
= GetLastError();
271 /* Get the file size */
272 FileSize
= GetFileSize(FileHandle
, NULL
);
274 /* Allocate DOS memory for the driver */
275 Segment
= DosAllocateMemory(FileSize
>> 4, NULL
);
278 Result
= DosLastError
;
282 /* Create a mapping object for the file */
283 FileMapping
= CreateFileMapping(FileHandle
,
289 if (FileMapping
== NULL
)
291 Result
= GetLastError();
295 /* Map the file into memory */
296 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
299 Result
= GetLastError();
303 /* Copy the entire file to the DOS memory */
304 Driver
= MAKELONG(0, Segment
);
305 DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
306 RtlCopyMemory(DriverHeader
, Address
, FileSize
);
308 /* Loop through all the drivers in this file */
311 if (!(DriverHeader
->DeviceAttributes
& DOS_DEVATTR_CHARACTER
))
313 DPRINT1("Error loading driver at %04X:%04X: "
314 "Block device drivers are not supported.\n",
320 /* Send the driver an init request */
321 RtlZeroMemory(&Request
, sizeof(Request
));
322 Request
.Header
.RequestLength
= sizeof(DOS_INIT_REQUEST
);
323 Request
.Header
.CommandCode
= DOS_DEVCMD_INIT
;
324 // TODO: Set Request.DeviceString to the appropriate line in CONFIG.NT!
325 DosCallDriver(Driver
, &Request
.Header
);
327 if (Request
.Header
.Status
& DOS_DEVSTAT_ERROR
)
329 DPRINT1("Error loading driver at %04X:%04X: "
330 "Initialization routine returned error %u.\n",
333 Request
.Header
.Status
& 0x7F);
337 /* Create the device */
338 DeviceNode
= DosCreateDevice(DriverHeader
->DeviceAttributes
,
339 DriverHeader
->DeviceName
);
340 DeviceNode
->Driver
= Driver
;
341 DeviceNode
->IoctlReadRoutine
= DosDriverDispatchIoctlRead
;
342 DeviceNode
->ReadRoutine
= DosDriverDispatchRead
;
343 DeviceNode
->PeekRoutine
= DosDriverDispatchPeek
;
344 DeviceNode
->InputStatusRoutine
= DosDriverDispatchInputStatus
;
345 DeviceNode
->FlushInputRoutine
= DosDriverDispatchFlushInput
;
346 DeviceNode
->IoctlWriteRoutine
= DosDriverDispatchIoctlWrite
;
347 DeviceNode
->WriteRoutine
= DosDriverDispatchWrite
;
348 DeviceNode
->OutputStatusRoutine
= DosDriverDispatchOutputStatus
;
349 DeviceNode
->FlushOutputRoutine
= DosDriverDispatchFlushOutput
;
350 DeviceNode
->OpenRoutine
= DosDriverDispatchOpen
;
351 DeviceNode
->CloseRoutine
= DosDriverDispatchClose
;
352 DeviceNode
->OutputUntilBusyRoutine
= DosDriverDispatchOutputUntilBusy
;
357 if (LOWORD(DriverHeader
->Link
) == 0xFFFF) break;
358 Driver
= DriverHeader
->Link
;
359 DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
362 DPRINT1("%u drivers loaded from %s.\n", DriversLoaded
, DriverFile
);
365 if (Result
!= ERROR_SUCCESS
)
367 /* It was not successful, cleanup the DOS memory */
368 if (Segment
) DosFreeMemory(Segment
);
372 if (Address
!= NULL
) UnmapViewOfFile(Address
);
374 /* Close the file mapping object */
375 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
377 /* Close the file handle */
378 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);