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>
9 /* INCLUDES *******************************************************************/
24 /* PRIVATE VARIABLES **********************************************************/
26 static const BYTE StrategyRoutine
[] = {
34 static const BYTE InterruptRoutine
[] = {
42 C_ASSERT((sizeof(StrategyRoutine
) + sizeof(InterruptRoutine
)) == DEVICE_CODE_SIZE
);
44 static LIST_ENTRY DeviceList
= { &DeviceList
, &DeviceList
};
45 static PDOS_REQUEST_HEADER DeviceRequest
;
47 /* PRIVATE FUNCTIONS **********************************************************/
49 static VOID
DosCallDriver(DWORD Driver
, PDOS_REQUEST_HEADER Request
)
51 PDOS_DRIVER DriverBlock
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
62 /* Set ES:BX to the location of the request */
63 setES(DOS_DATA_SEGMENT
);
64 setBX(DOS_DATA_OFFSET(Sda
.Request
));
66 /* Copy the request structure to ES:BX */
67 RtlMoveMemory(&Sda
->Request
, Request
, Request
->RequestLength
);
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
)));
73 /* Get the request structure from ES:BX */
74 RtlMoveMemory(Request
, &Sda
->Request
, Request
->RequestLength
);
76 /* Restore the registers */
88 static inline WORD NTAPI
DosDriverReadInternal(PDOS_DEVICE_NODE DeviceNode
,
93 DOS_RW_REQUEST Request
;
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
;
101 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
103 *Length
= Request
.Length
;
104 return Request
.Header
.Status
;
107 static inline WORD NTAPI
DosDriverWriteInternal(PDOS_DEVICE_NODE DeviceNode
,
112 DOS_RW_REQUEST Request
;
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
;
120 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
122 *Length
= Request
.Length
;
123 return Request
.Header
.Status
;
126 static inline WORD NTAPI
DosDriverGenericRequest(PDOS_DEVICE_NODE DeviceNode
,
129 DOS_REQUEST_HEADER Request
;
131 Request
.RequestLength
= sizeof(DOS_REQUEST_HEADER
);
132 Request
.CommandCode
= CommandCode
;
134 DosCallDriver(DeviceNode
->Driver
, &Request
);
136 return Request
.Status
;
139 static WORD NTAPI
DosDriverDispatchIoctlRead(PDOS_DEVICE_NODE DeviceNode
,
143 return DosDriverReadInternal(DeviceNode
, Buffer
, Length
, TRUE
);
146 static WORD NTAPI
DosDriverDispatchRead(PDOS_DEVICE_NODE DeviceNode
,
150 return DosDriverReadInternal(DeviceNode
, Buffer
, Length
, FALSE
);
153 static WORD NTAPI
DosDriverDispatchPeek(PDOS_DEVICE_NODE DeviceNode
,
156 DOS_PEEK_REQUEST Request
;
158 Request
.Header
.RequestLength
= sizeof(DOS_PEEK_REQUEST
);
159 Request
.Header
.CommandCode
= DOS_DEVCMD_PEEK
;
161 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
163 *Character
= Request
.Character
;
164 return Request
.Header
.Status
;
167 static WORD NTAPI
DosDriverDispatchInputStatus(PDOS_DEVICE_NODE DeviceNode
)
169 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_INSTAT
);
172 static WORD NTAPI
DosDriverDispatchFlushInput(PDOS_DEVICE_NODE DeviceNode
)
174 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_FLUSH_INPUT
);
177 static WORD NTAPI
DosDriverDispatchIoctlWrite(PDOS_DEVICE_NODE DeviceNode
,
181 return DosDriverWriteInternal(DeviceNode
, Buffer
, Length
, TRUE
);
184 static WORD NTAPI
DosDriverDispatchWrite(PDOS_DEVICE_NODE DeviceNode
,
188 return DosDriverWriteInternal(DeviceNode
, Buffer
, Length
, FALSE
);
191 static WORD NTAPI
DosDriverDispatchOutputStatus(PDOS_DEVICE_NODE DeviceNode
)
193 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_OUTSTAT
);
196 static WORD NTAPI
DosDriverDispatchFlushOutput(PDOS_DEVICE_NODE DeviceNode
)
198 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_FLUSH_OUTPUT
);
201 static WORD NTAPI
DosDriverDispatchOpen(PDOS_DEVICE_NODE DeviceNode
)
203 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_OPEN
);
206 static WORD NTAPI
DosDriverDispatchClose(PDOS_DEVICE_NODE DeviceNode
)
208 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_CLOSE
);
211 static WORD NTAPI
DosDriverDispatchOutputUntilBusy(PDOS_DEVICE_NODE DeviceNode
,
215 DOS_OUTPUT_BUSY_REQUEST Request
;
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
;
222 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
224 *Length
= Request
.Length
;
225 return Request
.Header
.Status
;
228 static VOID
DosAddDriver(DWORD Driver
)
230 PDOS_DRIVER LastDriver
= &SysVars
->NullDevice
;
232 /* Find the last driver in the list */
233 while (LOWORD(LastDriver
->Link
) != 0xFFFF)
235 LastDriver
= (PDOS_DRIVER
)FAR_POINTER(LastDriver
->Link
);
238 /* Add the new driver to the list */
239 LastDriver
->Link
= Driver
;
240 LastDriver
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
242 if (LastDriver
->DeviceAttributes
& DOS_DEVATTR_CLOCK
)
244 /* Update the active CLOCK driver */
245 SysVars
->ActiveClock
= Driver
;
248 if (LastDriver
->DeviceAttributes
249 & (DOS_DEVATTR_STDIN
| DOS_DEVATTR_STDOUT
| DOS_DEVATTR_CON
))
251 /* Update the active CON driver */
252 SysVars
->ActiveCon
= Driver
;
256 static VOID
DosRemoveDriver(DWORD Driver
)
258 DWORD CurrentDriver
= MAKELONG(DOS_DATA_OFFSET(SysVars
.NullDevice
), DOS_DATA_SEGMENT
);
260 while (LOWORD(CurrentDriver
) != 0xFFFF)
262 PDOS_DRIVER DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(CurrentDriver
);
264 if (DriverHeader
->Link
== Driver
)
266 /* Remove it from the list */
267 DriverHeader
->Link
= ((PDOS_DRIVER
)FAR_POINTER(DriverHeader
->Link
))->Link
;
271 CurrentDriver
= DriverHeader
->Link
;
275 static PDOS_DEVICE_NODE
DosCreateDeviceNode(DWORD Driver
)
278 PDOS_DRIVER DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
279 PDOS_DEVICE_NODE Node
= RtlAllocateHeap(RtlGetProcessHeap(),
282 if (Node
== NULL
) return NULL
;
284 Node
->Driver
= Driver
;
285 Node
->DeviceAttributes
= DriverHeader
->DeviceAttributes
;
287 /* Initialize the name string */
288 Node
->Name
.Buffer
= Node
->NameBuffer
;
289 Node
->Name
.MaximumLength
= MAX_DEVICE_NAME
;
291 for (i
= 0; i
< MAX_DEVICE_NAME
; i
++)
293 if (DriverHeader
->DeviceName
[i
] == ' ') break;
294 Node
->Name
.Buffer
[i
] = DriverHeader
->DeviceName
[i
];
297 Node
->Name
.Length
= i
;
299 InsertTailList(&DeviceList
, &Node
->Entry
);
303 /* PUBLIC FUNCTIONS ***********************************************************/
305 PDOS_DEVICE_NODE
DosGetDriverNode(DWORD Driver
)
308 PDOS_DEVICE_NODE Node
;
310 for (i
= DeviceList
.Flink
; i
!= &DeviceList
; i
= i
->Flink
)
312 Node
= CONTAINING_RECORD(i
, DOS_DEVICE_NODE
, Entry
);
313 if (Node
->Driver
== Driver
) break;
316 if (i
== &DeviceList
)
318 DPRINT1("The driver at %04X:%04X has no associated device node. "
319 "Installing automagically.\n",
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
;
342 PDOS_DEVICE_NODE
DosGetDevice(LPCSTR DeviceName
)
344 DWORD CurrentDriver
= MAKELONG(DOS_DATA_OFFSET(SysVars
.NullDevice
), DOS_DATA_SEGMENT
);
345 ANSI_STRING DeviceNameString
;
347 RtlInitAnsiString(&DeviceNameString
, DeviceName
);
349 while (LOWORD(CurrentDriver
) != 0xFFFF)
351 PDOS_DEVICE_NODE Node
= DosGetDriverNode(CurrentDriver
);
352 PDOS_DRIVER DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(CurrentDriver
);
354 if (RtlEqualString(&Node
->Name
, &DeviceNameString
, TRUE
)) return Node
;
355 CurrentDriver
= DriverHeader
->Link
;
361 PDOS_DEVICE_NODE
DosCreateDeviceEx(WORD Attributes
, PCHAR DeviceName
, WORD PrivateDataSize
)
365 PDOS_DRIVER DriverHeader
;
366 PDOS_DEVICE_NODE Node
;
368 /* Make sure this is a character device */
369 if (!(Attributes
& DOS_DEVATTR_CHARACTER
))
371 DPRINT1("ERROR: Block devices are not supported.\n");
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
;
379 /* Fill the header with data */
380 DriverHeader
= SEG_OFF_TO_PTR(Segment
, 0);
381 DriverHeader
->Link
= 0xFFFFFFFF;
382 DriverHeader
->DeviceAttributes
= Attributes
;
383 DriverHeader
->StrategyRoutine
= sizeof(DOS_DRIVER
);
384 DriverHeader
->InterruptRoutine
= sizeof(DOS_DRIVER
) + sizeof(StrategyRoutine
);
386 RtlFillMemory(DriverHeader
->DeviceName
, MAX_DEVICE_NAME
, ' ');
387 for (i
= 0; i
< MAX_DEVICE_NAME
; i
++)
389 if (DeviceName
[i
] == '\0' || DeviceName
[i
] == ' ') break;
390 DriverHeader
->DeviceName
[i
] = DeviceName
[i
];
393 /* Write the routines */
394 RtlMoveMemory(SEG_OFF_TO_PTR(Segment
, DriverHeader
->StrategyRoutine
),
396 sizeof(StrategyRoutine
));
397 RtlMoveMemory(SEG_OFF_TO_PTR(Segment
, DriverHeader
->InterruptRoutine
),
399 sizeof(InterruptRoutine
));
401 /* Create the node */
402 Node
= DosCreateDeviceNode(MAKELONG(0, Segment
));
405 DosFreeMemory(Segment
);
409 DosAddDriver(Node
->Driver
);
413 PDOS_DEVICE_NODE
DosCreateDevice(WORD Attributes
, PCHAR DeviceName
)
415 /* Call the extended API */
416 return DosCreateDeviceEx(Attributes
, DeviceName
, 0);
419 VOID
DosDeleteDevice(PDOS_DEVICE_NODE DeviceNode
)
421 DosRemoveDriver(DeviceNode
->Driver
);
423 ASSERT(LOWORD(DeviceNode
->Driver
) == 0);
424 DosFreeMemory(HIWORD(DeviceNode
->Driver
));
426 RemoveEntryList(&DeviceNode
->Entry
);
427 RtlFreeHeap(RtlGetProcessHeap(), 0, DeviceNode
);
430 VOID
DeviceStrategyBop(VOID
)
433 DeviceRequest
= (PDOS_REQUEST_HEADER
)SEG_OFF_TO_PTR(getES(), getBX());
436 VOID
DeviceInterruptBop(VOID
)
439 PDOS_DEVICE_NODE Node
;
440 DWORD DriverAddress
= (getCS() << 4) + getIP() - sizeof(DOS_DRIVER
) - 9;
442 /* Get the device node for this driver */
443 for (i
= DeviceList
.Flink
; i
!= &DeviceList
; i
= i
->Flink
)
445 Node
= CONTAINING_RECORD(i
, DOS_DEVICE_NODE
, Entry
);
446 if (TO_LINEAR(HIWORD(Node
->Driver
), LOWORD(Node
->Driver
)) == DriverAddress
) break;
449 if (i
== &DeviceList
)
451 DPRINT1("Device interrupt BOP from an unknown location.\n");
455 switch (DeviceRequest
->CommandCode
)
457 case DOS_DEVCMD_IOCTL_READ
:
459 PDOS_IOCTL_RW_REQUEST Request
= (PDOS_IOCTL_RW_REQUEST
)DeviceRequest
;
461 DeviceRequest
->Status
= Node
->IoctlReadRoutine(
463 Request
->BufferPointer
,
470 case DOS_DEVCMD_READ
:
472 PDOS_RW_REQUEST Request
= (PDOS_RW_REQUEST
)DeviceRequest
;
474 DeviceRequest
->Status
= Node
->ReadRoutine(
476 Request
->BufferPointer
,
483 case DOS_DEVCMD_PEEK
:
485 PDOS_PEEK_REQUEST Request
= (PDOS_PEEK_REQUEST
)DeviceRequest
;
486 DeviceRequest
->Status
= Node
->PeekRoutine(Node
, &Request
->Character
);
490 case DOS_DEVCMD_INSTAT
:
492 DeviceRequest
->Status
= Node
->InputStatusRoutine(Node
);
496 case DOS_DEVCMD_FLUSH_INPUT
:
498 DeviceRequest
->Status
= Node
->FlushInputRoutine(Node
);
502 case DOS_DEVCMD_IOCTL_WRITE
:
504 PDOS_IOCTL_RW_REQUEST Request
= (PDOS_IOCTL_RW_REQUEST
)DeviceRequest
;
506 DeviceRequest
->Status
= Node
->IoctlWriteRoutine(
508 Request
->BufferPointer
,
515 case DOS_DEVCMD_WRITE
:
517 PDOS_RW_REQUEST Request
= (PDOS_RW_REQUEST
)DeviceRequest
;
519 DeviceRequest
->Status
= Node
->WriteRoutine(Node
,
520 Request
->BufferPointer
,
527 case DOS_DEVCMD_OUTSTAT
:
529 DeviceRequest
->Status
= Node
->OutputStatusRoutine(Node
);
533 case DOS_DEVCMD_FLUSH_OUTPUT
:
535 DeviceRequest
->Status
= Node
->FlushOutputRoutine(Node
);
539 case DOS_DEVCMD_OPEN
:
541 DeviceRequest
->Status
= Node
->OpenRoutine(Node
);
545 case DOS_DEVCMD_CLOSE
:
547 DeviceRequest
->Status
= Node
->CloseRoutine(Node
);
551 case DOS_DEVCMD_OUTPUT_BUSY
:
553 PDOS_OUTPUT_BUSY_REQUEST Request
= (PDOS_OUTPUT_BUSY_REQUEST
)DeviceRequest
;
555 DeviceRequest
->Status
= Node
->OutputUntilBusyRoutine(
557 Request
->BufferPointer
,
566 DPRINT1("Unknown device command code: %u\n", DeviceRequest
->CommandCode
);
571 DWORD
DosLoadDriver(LPCSTR DriverFile
)
573 DWORD Result
= ERROR_SUCCESS
;
574 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
575 LPBYTE Address
= NULL
;
577 PDOS_DRIVER DriverHeader
;
580 DWORD DriversLoaded
= 0;
581 DOS_INIT_REQUEST Request
;
582 PDOS_DEVICE_NODE DeviceNode
;
584 /* Open a handle to the driver file */
585 FileHandle
= CreateFileA(DriverFile
,
590 FILE_ATTRIBUTE_NORMAL
,
592 if (FileHandle
== INVALID_HANDLE_VALUE
)
594 Result
= GetLastError();
598 /* Get the file size */
599 FileSize
= GetFileSize(FileHandle
, NULL
);
601 /* Allocate DOS memory for the driver */
602 Segment
= DosAllocateMemory(FileSize
>> 4, NULL
);
605 Result
= Sda
->LastErrorCode
;
609 /* Create a mapping object for the file */
610 FileMapping
= CreateFileMapping(FileHandle
,
616 if (FileMapping
== NULL
)
618 Result
= GetLastError();
622 /* Map the file into memory */
623 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
626 Result
= GetLastError();
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
);
635 /* Loop through all the drivers in this file */
638 if (!(DriverHeader
->DeviceAttributes
& DOS_DEVATTR_CHARACTER
))
640 DPRINT1("Error loading driver at %04X:%04X: "
641 "Block device drivers are not supported.\n",
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
);
654 if (Request
.Header
.Status
& DOS_DEVSTAT_ERROR
)
656 DPRINT1("Error loading driver at %04X:%04X: "
657 "Initialization routine returned error %u.\n",
660 Request
.Header
.Status
& 0x7F);
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
;
679 DosAddDriver(Driver
);
683 if (LOWORD(DriverHeader
->Link
) == 0xFFFF) break;
684 Driver
= DriverHeader
->Link
;
685 DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
688 DPRINT1("%u drivers loaded from %s.\n", DriversLoaded
, DriverFile
);
691 if (Result
!= ERROR_SUCCESS
)
693 /* It was not successful, cleanup the DOS memory */
694 if (Segment
) DosFreeMemory(Segment
);
698 if (Address
!= NULL
) UnmapViewOfFile(Address
);
700 /* Close the file mapping object */
701 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
703 /* Close the file handle */
704 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);