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 *******************************************************************/
22 /* PRIVATE VARIABLES **********************************************************/
24 static const BYTE StrategyRoutine
[] = {
32 static const BYTE InterruptRoutine
[] = {
40 C_ASSERT((sizeof(StrategyRoutine
) + sizeof(InterruptRoutine
)) == DEVICE_CODE_SIZE
);
42 static LIST_ENTRY DeviceList
= { &DeviceList
, &DeviceList
};
43 static PDOS_REQUEST_HEADER DeviceRequest
;
45 /* PRIVATE FUNCTIONS **********************************************************/
47 static VOID
DosCallDriver(DWORD Driver
, PDOS_REQUEST_HEADER Request
)
49 PDOS_DRIVER DriverBlock
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
50 PDOS_REQUEST_HEADER RemoteRequest
= FAR_POINTER(REQUEST_LOCATION
);
54 /* Set ES:BX to the location of the request */
55 setES(HIWORD(REQUEST_LOCATION
));
56 setBX(LOWORD(REQUEST_LOCATION
));
58 /* Copy the request structure to ES:BX */
59 RtlMoveMemory(RemoteRequest
, Request
, Request
->RequestLength
);
61 /* Call the strategy routine, and then the interrupt routine */
62 Call16(HIWORD(Driver
), DriverBlock
->StrategyRoutine
);
63 Call16(HIWORD(Driver
), DriverBlock
->InterruptRoutine
);
65 /* Get the request structure from ES:BX */
66 RtlMoveMemory(Request
, RemoteRequest
, Request
->RequestLength
);
73 static inline WORD NTAPI
DosDriverReadInternal(PDOS_DEVICE_NODE DeviceNode
,
78 DOS_RW_REQUEST Request
;
80 Request
.Header
.RequestLength
= IoControl
? sizeof(DOS_IOCTL_RW_REQUEST
)
81 : sizeof(DOS_RW_REQUEST
);
82 Request
.Header
.CommandCode
= IoControl
? DOS_DEVCMD_IOCTL_READ
: DOS_DEVCMD_READ
;
83 Request
.BufferPointer
= Buffer
;
84 Request
.Length
= *Length
;
86 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
88 *Length
= Request
.Length
;
89 return Request
.Header
.Status
;
92 static inline WORD NTAPI
DosDriverWriteInternal(PDOS_DEVICE_NODE DeviceNode
,
97 DOS_RW_REQUEST Request
;
99 Request
.Header
.RequestLength
= IoControl
? sizeof(DOS_IOCTL_RW_REQUEST
)
100 : sizeof(DOS_RW_REQUEST
);
101 Request
.Header
.CommandCode
= IoControl
? DOS_DEVCMD_IOCTL_WRITE
: DOS_DEVCMD_WRITE
;
102 Request
.BufferPointer
= Buffer
;
103 Request
.Length
= *Length
;
105 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
107 *Length
= Request
.Length
;
108 return Request
.Header
.Status
;
111 static inline WORD NTAPI
DosDriverGenericRequest(PDOS_DEVICE_NODE DeviceNode
,
114 DOS_REQUEST_HEADER Request
;
116 Request
.RequestLength
= sizeof(DOS_REQUEST_HEADER
);
117 Request
.CommandCode
= CommandCode
;
119 DosCallDriver(DeviceNode
->Driver
, &Request
);
121 return Request
.Status
;
124 static WORD NTAPI
DosDriverDispatchIoctlRead(PDOS_DEVICE_NODE DeviceNode
,
128 return DosDriverReadInternal(DeviceNode
, Buffer
, Length
, TRUE
);
131 static WORD NTAPI
DosDriverDispatchRead(PDOS_DEVICE_NODE DeviceNode
,
135 return DosDriverReadInternal(DeviceNode
, Buffer
, Length
, FALSE
);
138 static WORD NTAPI
DosDriverDispatchPeek(PDOS_DEVICE_NODE DeviceNode
,
141 DOS_PEEK_REQUEST Request
;
143 Request
.Header
.RequestLength
= sizeof(DOS_PEEK_REQUEST
);
144 Request
.Header
.CommandCode
= DOS_DEVCMD_PEEK
;
146 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
148 *Character
= Request
.Character
;
149 return Request
.Header
.Status
;
152 static WORD NTAPI
DosDriverDispatchInputStatus(PDOS_DEVICE_NODE DeviceNode
)
154 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_INSTAT
);
157 static WORD NTAPI
DosDriverDispatchFlushInput(PDOS_DEVICE_NODE DeviceNode
)
159 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_FLUSH_INPUT
);
162 static WORD NTAPI
DosDriverDispatchIoctlWrite(PDOS_DEVICE_NODE DeviceNode
,
166 return DosDriverWriteInternal(DeviceNode
, Buffer
, Length
, TRUE
);
169 static WORD NTAPI
DosDriverDispatchWrite(PDOS_DEVICE_NODE DeviceNode
,
173 return DosDriverWriteInternal(DeviceNode
, Buffer
, Length
, FALSE
);
176 static WORD NTAPI
DosDriverDispatchOutputStatus(PDOS_DEVICE_NODE DeviceNode
)
178 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_OUTSTAT
);
181 static WORD NTAPI
DosDriverDispatchFlushOutput(PDOS_DEVICE_NODE DeviceNode
)
183 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_FLUSH_OUTPUT
);
186 static WORD NTAPI
DosDriverDispatchOpen(PDOS_DEVICE_NODE DeviceNode
)
188 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_OPEN
);
191 static WORD NTAPI
DosDriverDispatchClose(PDOS_DEVICE_NODE DeviceNode
)
193 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_CLOSE
);
196 static WORD NTAPI
DosDriverDispatchOutputUntilBusy(PDOS_DEVICE_NODE DeviceNode
,
200 DOS_OUTPUT_BUSY_REQUEST Request
;
202 Request
.Header
.RequestLength
= sizeof(DOS_OUTPUT_BUSY_REQUEST
);
203 Request
.Header
.CommandCode
= DOS_DEVCMD_OUTPUT_BUSY
;
204 Request
.BufferPointer
= Buffer
;
205 Request
.Length
= *Length
;
207 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
209 *Length
= Request
.Length
;
210 return Request
.Header
.Status
;
213 static VOID
DosAddDriver(DWORD Driver
)
215 PDOS_DRIVER LastDriver
= &SysVars
->NullDevice
;
217 /* Find the last driver in the list */
218 while (LOWORD(LastDriver
->Link
) != 0xFFFF)
220 LastDriver
= (PDOS_DRIVER
)FAR_POINTER(LastDriver
->Link
);
223 /* Add the new driver to the list */
224 LastDriver
->Link
= Driver
;
225 LastDriver
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
227 if (LastDriver
->DeviceAttributes
& DOS_DEVATTR_CLOCK
)
229 /* Update the active CLOCK driver */
230 SysVars
->ActiveClock
= Driver
;
233 if (LastDriver
->DeviceAttributes
234 & (DOS_DEVATTR_STDIN
| DOS_DEVATTR_STDOUT
| DOS_DEVATTR_CON
))
236 /* Update the active CON driver */
237 SysVars
->ActiveCon
= Driver
;
241 static VOID
DosRemoveDriver(DWORD Driver
)
243 DWORD CurrentDriver
= MAKELONG(FIELD_OFFSET(DOS_SYSVARS
, NullDevice
), DOS_DATA_SEGMENT
);
245 while (LOWORD(CurrentDriver
) != 0xFFFF)
247 PDOS_DRIVER DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(CurrentDriver
);
249 if (DriverHeader
->Link
== Driver
)
251 /* Remove it from the list */
252 DriverHeader
->Link
= ((PDOS_DRIVER
)FAR_POINTER(DriverHeader
->Link
))->Link
;
256 CurrentDriver
= DriverHeader
->Link
;
260 static PDOS_DEVICE_NODE
DosCreateDeviceNode(DWORD Driver
)
263 PDOS_DRIVER DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
264 PDOS_DEVICE_NODE Node
= RtlAllocateHeap(RtlGetProcessHeap(),
267 if (Node
== NULL
) return NULL
;
269 Node
->Driver
= Driver
;
270 Node
->DeviceAttributes
= DriverHeader
->DeviceAttributes
;
272 /* Initialize the name string */
273 Node
->Name
.Buffer
= Node
->NameBuffer
;
274 Node
->Name
.MaximumLength
= MAX_DEVICE_NAME
;
276 for (i
= 0; i
< MAX_DEVICE_NAME
; i
++)
278 if (DriverHeader
->DeviceName
[i
] == ' ') break;
279 Node
->Name
.Buffer
[i
] = DriverHeader
->DeviceName
[i
];
282 Node
->Name
.Length
= i
;
284 InsertTailList(&DeviceList
, &Node
->Entry
);
288 /* PUBLIC FUNCTIONS ***********************************************************/
290 PDOS_DEVICE_NODE
DosGetDriverNode(DWORD Driver
)
293 PDOS_DEVICE_NODE Node
;
295 for (i
= DeviceList
.Flink
; i
!= &DeviceList
; i
= i
->Flink
)
297 Node
= CONTAINING_RECORD(i
, DOS_DEVICE_NODE
, Entry
);
298 if (Node
->Driver
== Driver
) break;
301 if (i
== &DeviceList
)
303 DPRINT1("The driver at %04X:%04X has no associated device node. "
304 "Installing automagically.\n",
308 /* Create the device node */
309 Node
= DosCreateDeviceNode(Driver
);
310 Node
->IoctlReadRoutine
= DosDriverDispatchIoctlRead
;
311 Node
->ReadRoutine
= DosDriverDispatchRead
;
312 Node
->PeekRoutine
= DosDriverDispatchPeek
;
313 Node
->InputStatusRoutine
= DosDriverDispatchInputStatus
;
314 Node
->FlushInputRoutine
= DosDriverDispatchFlushInput
;
315 Node
->IoctlWriteRoutine
= DosDriverDispatchIoctlWrite
;
316 Node
->WriteRoutine
= DosDriverDispatchWrite
;
317 Node
->OutputStatusRoutine
= DosDriverDispatchOutputStatus
;
318 Node
->FlushOutputRoutine
= DosDriverDispatchFlushOutput
;
319 Node
->OpenRoutine
= DosDriverDispatchOpen
;
320 Node
->CloseRoutine
= DosDriverDispatchClose
;
321 Node
->OutputUntilBusyRoutine
= DosDriverDispatchOutputUntilBusy
;
327 PDOS_DEVICE_NODE
DosGetDevice(LPCSTR DeviceName
)
329 DWORD CurrentDriver
= MAKELONG(FIELD_OFFSET(DOS_SYSVARS
, NullDevice
), DOS_DATA_SEGMENT
);
330 ANSI_STRING DeviceNameString
;
332 RtlInitAnsiString(&DeviceNameString
, DeviceName
);
334 while (LOWORD(CurrentDriver
) != 0xFFFF)
336 PDOS_DEVICE_NODE Node
= DosGetDriverNode(CurrentDriver
);
337 PDOS_DRIVER DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(CurrentDriver
);
339 if (RtlEqualString(&Node
->Name
, &DeviceNameString
, TRUE
)) return Node
;
340 CurrentDriver
= DriverHeader
->Link
;
346 PDOS_DEVICE_NODE
DosCreateDeviceEx(WORD Attributes
, PCHAR DeviceName
, WORD PrivateDataSize
)
350 PDOS_DRIVER DriverHeader
;
351 PDOS_DEVICE_NODE Node
;
353 /* Make sure this is a character device */
354 if (!(Attributes
& DOS_DEVATTR_CHARACTER
))
356 DPRINT1("ERROR: Block devices are not supported.\n");
360 /* Create a driver header for this device */
361 Segment
= DosAllocateMemory(sizeof(DOS_DRIVER
) + 10 + PrivateDataSize
, NULL
);
362 if (Segment
== 0) return NULL
;
364 /* Fill the header with data */
365 DriverHeader
= SEG_OFF_TO_PTR(Segment
, 0);
366 DriverHeader
->Link
= 0xFFFFFFFF;
367 DriverHeader
->DeviceAttributes
= Attributes
;
368 DriverHeader
->StrategyRoutine
= sizeof(DOS_DRIVER
);
369 DriverHeader
->InterruptRoutine
= sizeof(DOS_DRIVER
) + sizeof(StrategyRoutine
);
371 RtlFillMemory(DriverHeader
->DeviceName
, MAX_DEVICE_NAME
, ' ');
372 for (i
= 0; i
< MAX_DEVICE_NAME
; i
++)
374 if (DeviceName
[i
] == '\0' || DeviceName
[i
] == ' ') break;
375 DriverHeader
->DeviceName
[i
] = DeviceName
[i
];
378 /* Write the routines */
379 RtlMoveMemory(SEG_OFF_TO_PTR(Segment
, DriverHeader
->StrategyRoutine
),
381 sizeof(StrategyRoutine
));
382 RtlMoveMemory(SEG_OFF_TO_PTR(Segment
, DriverHeader
->InterruptRoutine
),
384 sizeof(InterruptRoutine
));
386 /* Create the node */
387 Node
= DosCreateDeviceNode(MAKELONG(0, Segment
));
390 DosFreeMemory(Segment
);
394 DosAddDriver(Node
->Driver
);
398 PDOS_DEVICE_NODE
DosCreateDevice(WORD Attributes
, PCHAR DeviceName
)
400 /* Call the extended API */
401 return DosCreateDeviceEx(Attributes
, DeviceName
, 0);
404 VOID
DosDeleteDevice(PDOS_DEVICE_NODE DeviceNode
)
406 DosRemoveDriver(DeviceNode
->Driver
);
408 ASSERT(LOWORD(DeviceNode
->Driver
) == 0);
409 DosFreeMemory(HIWORD(DeviceNode
->Driver
));
411 RemoveEntryList(&DeviceNode
->Entry
);
412 RtlFreeHeap(RtlGetProcessHeap(), 0, DeviceNode
);
415 VOID
DeviceStrategyBop(VOID
)
418 DeviceRequest
= (PDOS_REQUEST_HEADER
)SEG_OFF_TO_PTR(getES(), getBX());
421 VOID
DeviceInterruptBop(VOID
)
424 PDOS_DEVICE_NODE Node
;
425 DWORD DriverAddress
= (getCS() << 4) + getIP() - sizeof(DOS_DRIVER
) - 9;
427 /* Get the device node for this driver */
428 for (i
= DeviceList
.Flink
; i
!= &DeviceList
; i
= i
->Flink
)
430 Node
= CONTAINING_RECORD(i
, DOS_DEVICE_NODE
, Entry
);
431 if (TO_LINEAR(HIWORD(Node
->Driver
), LOWORD(Node
->Driver
)) == DriverAddress
) break;
434 if (i
== &DeviceList
)
436 DPRINT1("Device interrupt BOP from an unknown location.\n");
440 switch (DeviceRequest
->CommandCode
)
442 case DOS_DEVCMD_IOCTL_READ
:
444 PDOS_IOCTL_RW_REQUEST Request
= (PDOS_IOCTL_RW_REQUEST
)DeviceRequest
;
446 DeviceRequest
->Status
= Node
->IoctlReadRoutine(
448 Request
->BufferPointer
,
455 case DOS_DEVCMD_READ
:
457 PDOS_RW_REQUEST Request
= (PDOS_RW_REQUEST
)DeviceRequest
;
459 DeviceRequest
->Status
= Node
->ReadRoutine(
461 Request
->BufferPointer
,
468 case DOS_DEVCMD_PEEK
:
470 PDOS_PEEK_REQUEST Request
= (PDOS_PEEK_REQUEST
)DeviceRequest
;
471 DeviceRequest
->Status
= Node
->PeekRoutine(Node
, &Request
->Character
);
475 case DOS_DEVCMD_INSTAT
:
477 DeviceRequest
->Status
= Node
->InputStatusRoutine(Node
);
481 case DOS_DEVCMD_FLUSH_INPUT
:
483 DeviceRequest
->Status
= Node
->FlushInputRoutine(Node
);
487 case DOS_DEVCMD_IOCTL_WRITE
:
489 PDOS_IOCTL_RW_REQUEST Request
= (PDOS_IOCTL_RW_REQUEST
)DeviceRequest
;
491 DeviceRequest
->Status
= Node
->IoctlWriteRoutine(
493 Request
->BufferPointer
,
500 case DOS_DEVCMD_WRITE
:
502 PDOS_RW_REQUEST Request
= (PDOS_RW_REQUEST
)DeviceRequest
;
504 DeviceRequest
->Status
= Node
->WriteRoutine(Node
,
505 Request
->BufferPointer
,
512 case DOS_DEVCMD_OUTSTAT
:
514 DeviceRequest
->Status
= Node
->OutputStatusRoutine(Node
);
518 case DOS_DEVCMD_FLUSH_OUTPUT
:
520 DeviceRequest
->Status
= Node
->FlushOutputRoutine(Node
);
524 case DOS_DEVCMD_OPEN
:
526 DeviceRequest
->Status
= Node
->OpenRoutine(Node
);
530 case DOS_DEVCMD_CLOSE
:
532 DeviceRequest
->Status
= Node
->CloseRoutine(Node
);
536 case DOS_DEVCMD_OUTPUT_BUSY
:
538 PDOS_OUTPUT_BUSY_REQUEST Request
= (PDOS_OUTPUT_BUSY_REQUEST
)DeviceRequest
;
540 DeviceRequest
->Status
= Node
->OutputUntilBusyRoutine(
542 Request
->BufferPointer
,
551 DPRINT1("Unknown device command code: %u\n", DeviceRequest
->CommandCode
);
556 DWORD
DosLoadDriver(LPCSTR DriverFile
)
558 DWORD Result
= ERROR_SUCCESS
;
559 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
560 LPBYTE Address
= NULL
;
562 PDOS_DRIVER DriverHeader
;
565 DWORD DriversLoaded
= 0;
566 DOS_INIT_REQUEST Request
;
567 PDOS_DEVICE_NODE DeviceNode
;
569 /* Open a handle to the driver file */
570 FileHandle
= CreateFileA(DriverFile
,
575 FILE_ATTRIBUTE_NORMAL
,
577 if (FileHandle
== INVALID_HANDLE_VALUE
)
579 Result
= GetLastError();
583 /* Get the file size */
584 FileSize
= GetFileSize(FileHandle
, NULL
);
586 /* Allocate DOS memory for the driver */
587 Segment
= DosAllocateMemory(FileSize
>> 4, NULL
);
590 Result
= DosLastError
;
594 /* Create a mapping object for the file */
595 FileMapping
= CreateFileMapping(FileHandle
,
601 if (FileMapping
== NULL
)
603 Result
= GetLastError();
607 /* Map the file into memory */
608 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
611 Result
= GetLastError();
615 /* Copy the entire file to the DOS memory */
616 Driver
= MAKELONG(0, Segment
);
617 DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
618 RtlCopyMemory(DriverHeader
, Address
, FileSize
);
620 /* Loop through all the drivers in this file */
623 if (!(DriverHeader
->DeviceAttributes
& DOS_DEVATTR_CHARACTER
))
625 DPRINT1("Error loading driver at %04X:%04X: "
626 "Block device drivers are not supported.\n",
632 /* Send the driver an init request */
633 RtlZeroMemory(&Request
, sizeof(Request
));
634 Request
.Header
.RequestLength
= sizeof(DOS_INIT_REQUEST
);
635 Request
.Header
.CommandCode
= DOS_DEVCMD_INIT
;
636 // TODO: Set Request.DeviceString to the appropriate line in CONFIG.NT!
637 DosCallDriver(Driver
, &Request
.Header
);
639 if (Request
.Header
.Status
& DOS_DEVSTAT_ERROR
)
641 DPRINT1("Error loading driver at %04X:%04X: "
642 "Initialization routine returned error %u.\n",
645 Request
.Header
.Status
& 0x7F);
649 /* Create the device node */
650 DeviceNode
= DosCreateDeviceNode(Driver
);
651 DeviceNode
->IoctlReadRoutine
= DosDriverDispatchIoctlRead
;
652 DeviceNode
->ReadRoutine
= DosDriverDispatchRead
;
653 DeviceNode
->PeekRoutine
= DosDriverDispatchPeek
;
654 DeviceNode
->InputStatusRoutine
= DosDriverDispatchInputStatus
;
655 DeviceNode
->FlushInputRoutine
= DosDriverDispatchFlushInput
;
656 DeviceNode
->IoctlWriteRoutine
= DosDriverDispatchIoctlWrite
;
657 DeviceNode
->WriteRoutine
= DosDriverDispatchWrite
;
658 DeviceNode
->OutputStatusRoutine
= DosDriverDispatchOutputStatus
;
659 DeviceNode
->FlushOutputRoutine
= DosDriverDispatchFlushOutput
;
660 DeviceNode
->OpenRoutine
= DosDriverDispatchOpen
;
661 DeviceNode
->CloseRoutine
= DosDriverDispatchClose
;
662 DeviceNode
->OutputUntilBusyRoutine
= DosDriverDispatchOutputUntilBusy
;
664 DosAddDriver(Driver
);
668 if (LOWORD(DriverHeader
->Link
) == 0xFFFF) break;
669 Driver
= DriverHeader
->Link
;
670 DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
673 DPRINT1("%u drivers loaded from %s.\n", DriversLoaded
, DriverFile
);
676 if (Result
!= ERROR_SUCCESS
)
678 /* It was not successful, cleanup the DOS memory */
679 if (Segment
) DosFreeMemory(Segment
);
683 if (Address
!= NULL
) UnmapViewOfFile(Address
);
685 /* Close the file mapping object */
686 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
688 /* Close the file handle */
689 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);