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 DWORD FirstDriver
= 0xFFFFFFFF;
44 static PDOS_REQUEST_HEADER DeviceRequest
;
46 /* PRIVATE FUNCTIONS **********************************************************/
48 static VOID
DosCallDriver(DWORD Driver
, PDOS_REQUEST_HEADER Request
)
50 PDOS_DRIVER DriverBlock
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
51 PDOS_REQUEST_HEADER RemoteRequest
= FAR_POINTER(REQUEST_LOCATION
);
55 /* Set ES:BX to the location of the request */
56 setES(HIWORD(REQUEST_LOCATION
));
57 setBX(LOWORD(REQUEST_LOCATION
));
59 /* Copy the request structure to ES:BX */
60 RtlMoveMemory(RemoteRequest
, Request
, Request
->RequestLength
);
62 /* Call the strategy routine, and then the interrupt routine */
63 Call16(HIWORD(Driver
), DriverBlock
->StrategyRoutine
);
64 Call16(HIWORD(Driver
), DriverBlock
->InterruptRoutine
);
66 /* Get the request structure from ES:BX */
67 RtlMoveMemory(Request
, RemoteRequest
, Request
->RequestLength
);
74 static inline WORD NTAPI
DosDriverReadInternal(PDOS_DEVICE_NODE DeviceNode
,
79 DOS_RW_REQUEST Request
;
81 Request
.Header
.RequestLength
= IoControl
? sizeof(DOS_IOCTL_RW_REQUEST
)
82 : sizeof(DOS_RW_REQUEST
);
83 Request
.Header
.CommandCode
= IoControl
? DOS_DEVCMD_IOCTL_READ
: DOS_DEVCMD_READ
;
84 Request
.BufferPointer
= Buffer
;
85 Request
.Length
= *Length
;
87 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
89 *Length
= Request
.Length
;
90 return Request
.Header
.Status
;
93 static inline WORD NTAPI
DosDriverWriteInternal(PDOS_DEVICE_NODE DeviceNode
,
98 DOS_RW_REQUEST Request
;
100 Request
.Header
.RequestLength
= IoControl
? sizeof(DOS_IOCTL_RW_REQUEST
)
101 : sizeof(DOS_RW_REQUEST
);
102 Request
.Header
.CommandCode
= IoControl
? DOS_DEVCMD_IOCTL_WRITE
: DOS_DEVCMD_WRITE
;
103 Request
.BufferPointer
= Buffer
;
104 Request
.Length
= *Length
;
106 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
108 *Length
= Request
.Length
;
109 return Request
.Header
.Status
;
112 static inline WORD NTAPI
DosDriverGenericRequest(PDOS_DEVICE_NODE DeviceNode
,
115 DOS_REQUEST_HEADER Request
;
117 Request
.RequestLength
= sizeof(DOS_REQUEST_HEADER
);
118 Request
.CommandCode
= CommandCode
;
120 DosCallDriver(DeviceNode
->Driver
, &Request
);
122 return Request
.Status
;
125 static WORD NTAPI
DosDriverDispatchIoctlRead(PDOS_DEVICE_NODE DeviceNode
,
129 return DosDriverReadInternal(DeviceNode
, Buffer
, Length
, TRUE
);
132 static WORD NTAPI
DosDriverDispatchRead(PDOS_DEVICE_NODE DeviceNode
,
136 return DosDriverReadInternal(DeviceNode
, Buffer
, Length
, FALSE
);
139 static WORD NTAPI
DosDriverDispatchPeek(PDOS_DEVICE_NODE DeviceNode
,
142 DOS_PEEK_REQUEST Request
;
144 Request
.Header
.RequestLength
= sizeof(DOS_PEEK_REQUEST
);
145 Request
.Header
.CommandCode
= DOS_DEVCMD_PEEK
;
147 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
149 *Character
= Request
.Character
;
150 return Request
.Header
.Status
;
153 static WORD NTAPI
DosDriverDispatchInputStatus(PDOS_DEVICE_NODE DeviceNode
)
155 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_INSTAT
);
158 static WORD NTAPI
DosDriverDispatchFlushInput(PDOS_DEVICE_NODE DeviceNode
)
160 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_FLUSH_INPUT
);
163 static WORD NTAPI
DosDriverDispatchIoctlWrite(PDOS_DEVICE_NODE DeviceNode
,
167 return DosDriverWriteInternal(DeviceNode
, Buffer
, Length
, TRUE
);
170 static WORD NTAPI
DosDriverDispatchWrite(PDOS_DEVICE_NODE DeviceNode
,
174 return DosDriverWriteInternal(DeviceNode
, Buffer
, Length
, FALSE
);
177 static WORD NTAPI
DosDriverDispatchOutputStatus(PDOS_DEVICE_NODE DeviceNode
)
179 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_OUTSTAT
);
182 static WORD NTAPI
DosDriverDispatchFlushOutput(PDOS_DEVICE_NODE DeviceNode
)
184 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_FLUSH_OUTPUT
);
187 static WORD NTAPI
DosDriverDispatchOpen(PDOS_DEVICE_NODE DeviceNode
)
189 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_OPEN
);
192 static WORD NTAPI
DosDriverDispatchClose(PDOS_DEVICE_NODE DeviceNode
)
194 return DosDriverGenericRequest(DeviceNode
, DOS_DEVCMD_CLOSE
);
197 static WORD NTAPI
DosDriverDispatchOutputUntilBusy(PDOS_DEVICE_NODE DeviceNode
,
201 DOS_OUTPUT_BUSY_REQUEST Request
;
203 Request
.Header
.RequestLength
= sizeof(DOS_OUTPUT_BUSY_REQUEST
);
204 Request
.Header
.CommandCode
= DOS_DEVCMD_OUTPUT_BUSY
;
205 Request
.BufferPointer
= Buffer
;
206 Request
.Length
= *Length
;
208 DosCallDriver(DeviceNode
->Driver
, &Request
.Header
);
210 *Length
= Request
.Length
;
211 return Request
.Header
.Status
;
214 static VOID
DosAddDriver(DWORD Driver
)
216 PDOS_DRIVER LastDriver
;
218 if (LOWORD(FirstDriver
) == 0xFFFF)
220 /* This is the first driver */
221 FirstDriver
= Driver
;
225 /* The list isn't empty, so find the last driver in it */
226 LastDriver
= (PDOS_DRIVER
)FAR_POINTER(FirstDriver
);
227 while (LOWORD(LastDriver
->Link
) != 0xFFFF)
229 LastDriver
= (PDOS_DRIVER
)FAR_POINTER(LastDriver
->Link
);
232 /* Add the new driver to the list */
233 LastDriver
->Link
= Driver
;
236 static VOID
DosRemoveDriver(DWORD Driver
)
238 DWORD CurrentDriver
= FirstDriver
;
240 if (FirstDriver
== Driver
)
242 /* Update the first driver */
243 FirstDriver
= ((PDOS_DRIVER
)FAR_POINTER(FirstDriver
))->Link
;
247 while (LOWORD(CurrentDriver
) != 0xFFFF)
249 PDOS_DRIVER DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(CurrentDriver
);
251 if (DriverHeader
->Link
== Driver
)
253 /* Remove it from the list */
254 DriverHeader
->Link
= ((PDOS_DRIVER
)FAR_POINTER(DriverHeader
->Link
))->Link
;
258 CurrentDriver
= DriverHeader
->Link
;
262 static PDOS_DEVICE_NODE
DosCreateDeviceNode(DWORD Driver
)
265 PDOS_DRIVER DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
266 PDOS_DEVICE_NODE Node
= RtlAllocateHeap(RtlGetProcessHeap(),
269 if (Node
== NULL
) return NULL
;
271 Node
->Driver
= Driver
;
272 Node
->DeviceAttributes
= DriverHeader
->DeviceAttributes
;
274 /* Initialize the name string */
275 Node
->Name
.Buffer
= Node
->NameBuffer
;
276 Node
->Name
.MaximumLength
= MAX_DEVICE_NAME
;
278 for (i
= 0; i
< MAX_DEVICE_NAME
; i
++)
280 if (DriverHeader
->DeviceName
[i
] == ' ') break;
281 Node
->Name
.Buffer
[i
] = DriverHeader
->DeviceName
[i
];
284 Node
->Name
.Length
= i
;
286 InsertTailList(&DeviceList
, &Node
->Entry
);
290 /* PUBLIC FUNCTIONS ***********************************************************/
292 PDOS_DEVICE_NODE
DosGetDevice(LPCSTR DeviceName
)
295 DWORD CurrentDriver
= FirstDriver
;
296 ANSI_STRING DeviceNameString
;
298 RtlInitAnsiString(&DeviceNameString
, DeviceName
);
300 while (LOWORD(CurrentDriver
) != 0xFFFF)
302 PDOS_DEVICE_NODE Node
;
303 PDOS_DRIVER DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(CurrentDriver
);
305 /* Get the device node for this driver */
306 for (i
= DeviceList
.Flink
; i
!= &DeviceList
; i
= i
->Flink
)
308 Node
= CONTAINING_RECORD(i
, DOS_DEVICE_NODE
, Entry
);
309 if (Node
->Driver
== CurrentDriver
) break;
312 if (i
== &DeviceList
)
314 DPRINT1("The driver at %04X:%04X has no associated device node. "
315 "Installing automagically.\n",
316 HIWORD(CurrentDriver
),
317 LOWORD(CurrentDriver
));
319 /* Create the device node */
320 Node
= DosCreateDeviceNode(CurrentDriver
);
321 Node
->IoctlReadRoutine
= DosDriverDispatchIoctlRead
;
322 Node
->ReadRoutine
= DosDriverDispatchRead
;
323 Node
->PeekRoutine
= DosDriverDispatchPeek
;
324 Node
->InputStatusRoutine
= DosDriverDispatchInputStatus
;
325 Node
->FlushInputRoutine
= DosDriverDispatchFlushInput
;
326 Node
->IoctlWriteRoutine
= DosDriverDispatchIoctlWrite
;
327 Node
->WriteRoutine
= DosDriverDispatchWrite
;
328 Node
->OutputStatusRoutine
= DosDriverDispatchOutputStatus
;
329 Node
->FlushOutputRoutine
= DosDriverDispatchFlushOutput
;
330 Node
->OpenRoutine
= DosDriverDispatchOpen
;
331 Node
->CloseRoutine
= DosDriverDispatchClose
;
332 Node
->OutputUntilBusyRoutine
= DosDriverDispatchOutputUntilBusy
;
335 if (RtlEqualString(&Node
->Name
, &DeviceNameString
, TRUE
)) return Node
;
336 CurrentDriver
= DriverHeader
->Link
;
342 PDOS_DEVICE_NODE
DosCreateDeviceEx(WORD Attributes
, PCHAR DeviceName
, WORD PrivateDataSize
)
346 PDOS_DRIVER DriverHeader
;
347 PDOS_DEVICE_NODE Node
;
349 /* Make sure this is a character device */
350 if (!(Attributes
& DOS_DEVATTR_CHARACTER
))
352 DPRINT1("ERROR: Block devices are not supported.\n");
356 /* Create a driver header for this device */
357 Segment
= DosAllocateMemory(sizeof(DOS_DRIVER
) + 10 + PrivateDataSize
, NULL
);
358 if (Segment
== 0) return NULL
;
360 /* Fill the header with data */
361 DriverHeader
= SEG_OFF_TO_PTR(Segment
, 0);
362 DriverHeader
->Link
= 0xFFFFFFFF;
363 DriverHeader
->DeviceAttributes
= Attributes
;
364 DriverHeader
->StrategyRoutine
= sizeof(DOS_DRIVER
);
365 DriverHeader
->InterruptRoutine
= sizeof(DOS_DRIVER
) + sizeof(StrategyRoutine
);
367 RtlFillMemory(DriverHeader
->DeviceName
, MAX_DEVICE_NAME
, ' ');
368 for (i
= 0; i
< MAX_DEVICE_NAME
; i
++)
370 if (DeviceName
[i
] == '\0' || DeviceName
[i
] == ' ') break;
371 DriverHeader
->DeviceName
[i
] = DeviceName
[i
];
374 /* Write the routines */
375 RtlMoveMemory(SEG_OFF_TO_PTR(Segment
, DriverHeader
->StrategyRoutine
),
377 sizeof(StrategyRoutine
));
378 RtlMoveMemory(SEG_OFF_TO_PTR(Segment
, DriverHeader
->StrategyRoutine
),
380 sizeof(InterruptRoutine
));
382 /* Create the node */
383 Node
= DosCreateDeviceNode(MAKELONG(0, Segment
));
386 DosFreeMemory(Segment
);
390 DosAddDriver(Node
->Driver
);
394 PDOS_DEVICE_NODE
DosCreateDevice(WORD Attributes
, PCHAR DeviceName
)
396 /* Call the extended API */
397 return DosCreateDeviceEx(Attributes
, DeviceName
, 0);
400 VOID
DosDeleteDevice(PDOS_DEVICE_NODE DeviceNode
)
402 DosRemoveDriver(DeviceNode
->Driver
);
404 ASSERT(LOWORD(DeviceNode
->Driver
) == 0);
405 DosFreeMemory(HIWORD(DeviceNode
->Driver
));
407 RemoveEntryList(&DeviceNode
->Entry
);
408 RtlFreeHeap(RtlGetProcessHeap(), 0, DeviceNode
);
411 VOID
DeviceStrategyBop(VOID
)
414 DeviceRequest
= (PDOS_REQUEST_HEADER
)SEG_OFF_TO_PTR(getES(), getBX());
417 VOID
DeviceInterruptBop(VOID
)
420 PDOS_DEVICE_NODE Node
;
421 DWORD DriverAddress
= (getCS() << 4) + getIP() - sizeof(DOS_DRIVER
) - 9;
423 /* Get the device node for this driver */
424 for (i
= DeviceList
.Flink
; i
!= &DeviceList
; i
= i
->Flink
)
426 Node
= CONTAINING_RECORD(i
, DOS_DEVICE_NODE
, Entry
);
427 if (TO_LINEAR(HIWORD(Node
->Driver
), LOWORD(Node
->Driver
)) == DriverAddress
) break;
430 if (i
== &DeviceList
)
432 DPRINT1("Device interrupt BOP from an unknown location.\n");
436 switch (DeviceRequest
->CommandCode
)
438 case DOS_DEVCMD_IOCTL_READ
:
440 PDOS_IOCTL_RW_REQUEST Request
= (PDOS_IOCTL_RW_REQUEST
)DeviceRequest
;
442 DeviceRequest
->Status
= Node
->IoctlReadRoutine(
444 Request
->BufferPointer
,
451 case DOS_DEVCMD_READ
:
453 PDOS_RW_REQUEST Request
= (PDOS_RW_REQUEST
)DeviceRequest
;
455 DeviceRequest
->Status
= Node
->ReadRoutine(
457 Request
->BufferPointer
,
464 case DOS_DEVCMD_PEEK
:
466 PDOS_PEEK_REQUEST Request
= (PDOS_PEEK_REQUEST
)DeviceRequest
;
467 DeviceRequest
->Status
= Node
->PeekRoutine(Node
, &Request
->Character
);
471 case DOS_DEVCMD_INSTAT
:
473 DeviceRequest
->Status
= Node
->InputStatusRoutine(Node
);
477 case DOS_DEVCMD_FLUSH_INPUT
:
479 DeviceRequest
->Status
= Node
->FlushInputRoutine(Node
);
483 case DOS_DEVCMD_IOCTL_WRITE
:
485 PDOS_IOCTL_RW_REQUEST Request
= (PDOS_IOCTL_RW_REQUEST
)DeviceRequest
;
487 DeviceRequest
->Status
= Node
->IoctlWriteRoutine(
489 Request
->BufferPointer
,
496 case DOS_DEVCMD_WRITE
:
498 PDOS_RW_REQUEST Request
= (PDOS_RW_REQUEST
)DeviceRequest
;
500 DeviceRequest
->Status
= Node
->WriteRoutine(Node
,
501 Request
->BufferPointer
,
508 case DOS_DEVCMD_OUTSTAT
:
510 DeviceRequest
->Status
= Node
->OutputStatusRoutine(Node
);
514 case DOS_DEVCMD_FLUSH_OUTPUT
:
516 DeviceRequest
->Status
= Node
->FlushOutputRoutine(Node
);
520 case DOS_DEVCMD_OPEN
:
522 DeviceRequest
->Status
= Node
->OpenRoutine(Node
);
526 case DOS_DEVCMD_CLOSE
:
528 DeviceRequest
->Status
= Node
->CloseRoutine(Node
);
532 case DOS_DEVCMD_OUTPUT_BUSY
:
534 PDOS_OUTPUT_BUSY_REQUEST Request
= (PDOS_OUTPUT_BUSY_REQUEST
)DeviceRequest
;
536 DeviceRequest
->Status
= Node
->OutputUntilBusyRoutine(
538 Request
->BufferPointer
,
547 DPRINT1("Unknown device command code: %u\n", DeviceRequest
->CommandCode
);
552 DWORD
DosLoadDriver(LPCSTR DriverFile
)
554 DWORD Result
= ERROR_SUCCESS
;
555 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
556 LPBYTE Address
= NULL
;
558 PDOS_DRIVER DriverHeader
;
561 DWORD DriversLoaded
= 0;
562 DOS_INIT_REQUEST Request
;
563 PDOS_DEVICE_NODE DeviceNode
;
565 /* Open a handle to the driver file */
566 FileHandle
= CreateFileA(DriverFile
,
571 FILE_ATTRIBUTE_NORMAL
,
573 if (FileHandle
== INVALID_HANDLE_VALUE
)
575 Result
= GetLastError();
579 /* Get the file size */
580 FileSize
= GetFileSize(FileHandle
, NULL
);
582 /* Allocate DOS memory for the driver */
583 Segment
= DosAllocateMemory(FileSize
>> 4, NULL
);
586 Result
= DosLastError
;
590 /* Create a mapping object for the file */
591 FileMapping
= CreateFileMapping(FileHandle
,
597 if (FileMapping
== NULL
)
599 Result
= GetLastError();
603 /* Map the file into memory */
604 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
607 Result
= GetLastError();
611 /* Copy the entire file to the DOS memory */
612 Driver
= MAKELONG(0, Segment
);
613 DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
614 RtlCopyMemory(DriverHeader
, Address
, FileSize
);
616 /* Loop through all the drivers in this file */
619 if (!(DriverHeader
->DeviceAttributes
& DOS_DEVATTR_CHARACTER
))
621 DPRINT1("Error loading driver at %04X:%04X: "
622 "Block device drivers are not supported.\n",
628 /* Send the driver an init request */
629 RtlZeroMemory(&Request
, sizeof(Request
));
630 Request
.Header
.RequestLength
= sizeof(DOS_INIT_REQUEST
);
631 Request
.Header
.CommandCode
= DOS_DEVCMD_INIT
;
632 // TODO: Set Request.DeviceString to the appropriate line in CONFIG.NT!
633 DosCallDriver(Driver
, &Request
.Header
);
635 if (Request
.Header
.Status
& DOS_DEVSTAT_ERROR
)
637 DPRINT1("Error loading driver at %04X:%04X: "
638 "Initialization routine returned error %u.\n",
641 Request
.Header
.Status
& 0x7F);
645 /* Create the device node */
646 DeviceNode
= DosCreateDeviceNode(Driver
);
647 DeviceNode
->IoctlReadRoutine
= DosDriverDispatchIoctlRead
;
648 DeviceNode
->ReadRoutine
= DosDriverDispatchRead
;
649 DeviceNode
->PeekRoutine
= DosDriverDispatchPeek
;
650 DeviceNode
->InputStatusRoutine
= DosDriverDispatchInputStatus
;
651 DeviceNode
->FlushInputRoutine
= DosDriverDispatchFlushInput
;
652 DeviceNode
->IoctlWriteRoutine
= DosDriverDispatchIoctlWrite
;
653 DeviceNode
->WriteRoutine
= DosDriverDispatchWrite
;
654 DeviceNode
->OutputStatusRoutine
= DosDriverDispatchOutputStatus
;
655 DeviceNode
->FlushOutputRoutine
= DosDriverDispatchFlushOutput
;
656 DeviceNode
->OpenRoutine
= DosDriverDispatchOpen
;
657 DeviceNode
->CloseRoutine
= DosDriverDispatchClose
;
658 DeviceNode
->OutputUntilBusyRoutine
= DosDriverDispatchOutputUntilBusy
;
660 DosAddDriver(Driver
);
664 if (LOWORD(DriverHeader
->Link
) == 0xFFFF) break;
665 Driver
= DriverHeader
->Link
;
666 DriverHeader
= (PDOS_DRIVER
)FAR_POINTER(Driver
);
669 DPRINT1("%u drivers loaded from %s.\n", DriversLoaded
, DriverFile
);
672 if (Result
!= ERROR_SUCCESS
)
674 /* It was not successful, cleanup the DOS memory */
675 if (Segment
) DosFreeMemory(Segment
);
679 if (Address
!= NULL
) UnmapViewOfFile(Address
);
681 /* Close the file mapping object */
682 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
684 /* Close the file handle */
685 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);