d16e48685bc2017ff5354ac6849205bddecd9565
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / device.c
1 /*
2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: device.c
5 * PURPOSE: DOS Device Support
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "ntvdm.h"
14 #include "emulator.h"
15 #include "device.h"
16
17 #include "dos.h"
18 #include "dos/dem.h"
19 #include "memory.h"
20
21 /* PRIVATE VARIABLES **********************************************************/
22
23 static LIST_ENTRY DeviceList = { &DeviceList, &DeviceList };
24
25 /* PRIVATE FUNCTIONS **********************************************************/
26
27 static VOID DosCallDriver(DWORD Driver, PDOS_REQUEST_HEADER Request)
28 {
29 PDOS_DRIVER DriverBlock = (PDOS_DRIVER)FAR_POINTER(Driver);
30 PDOS_REQUEST_HEADER RemoteRequest;
31
32 /* Call the strategy routine first */
33 Call16(HIWORD(Driver), DriverBlock->StrategyRoutine);
34 RemoteRequest = (PDOS_REQUEST_HEADER)SEG_OFF_TO_PTR(getES(), getBX());
35
36 /* Copy the request structure to ES:BX */
37 RtlMoveMemory(RemoteRequest, Request, Request->RequestLength);
38
39 /* Call the interrupt routine */
40 Call16(HIWORD(Driver), DriverBlock->InterruptRoutine);
41
42 /* Get the request structure from ES:BX */
43 RtlMoveMemory(Request, RemoteRequest, RemoteRequest->RequestLength);
44 }
45
46 static inline WORD NTAPI DosDriverReadInternal(PDOS_DEVICE_NODE DeviceNode,
47 DWORD Buffer,
48 PWORD Length,
49 BOOLEAN IoControl)
50 {
51 DOS_RW_REQUEST Request;
52
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;
58
59 DosCallDriver(DeviceNode->Driver, &Request.Header);
60
61 *Length = Request.Length;
62 return Request.Header.Status;
63 }
64
65 static inline WORD NTAPI DosDriverWriteInternal(PDOS_DEVICE_NODE DeviceNode,
66 DWORD Buffer,
67 PWORD Length,
68 BOOLEAN IoControl)
69 {
70 DOS_RW_REQUEST Request;
71
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;
77
78 DosCallDriver(DeviceNode->Driver, &Request.Header);
79
80 *Length = Request.Length;
81 return Request.Header.Status;
82 }
83
84 static inline WORD NTAPI DosDriverGenericRequest(PDOS_DEVICE_NODE DeviceNode,
85 BYTE CommandCode)
86 {
87 DOS_REQUEST_HEADER Request;
88
89 Request.RequestLength = sizeof(DOS_REQUEST_HEADER);
90 Request.CommandCode = CommandCode;
91
92 DosCallDriver(DeviceNode->Driver, &Request);
93
94 return Request.Status;
95 }
96
97 static WORD NTAPI DosDriverDispatchIoctlRead(PDOS_DEVICE_NODE DeviceNode,
98 DWORD Buffer,
99 PWORD Length)
100 {
101 return DosDriverReadInternal(DeviceNode, Buffer, Length, TRUE);
102 }
103
104 static WORD NTAPI DosDriverDispatchRead(PDOS_DEVICE_NODE DeviceNode,
105 DWORD Buffer,
106 PWORD Length)
107 {
108 return DosDriverReadInternal(DeviceNode, Buffer, Length, FALSE);
109 }
110
111 static WORD NTAPI DosDriverDispatchPeek(PDOS_DEVICE_NODE DeviceNode,
112 PBYTE Character)
113 {
114 DOS_PEEK_REQUEST Request;
115
116 Request.Header.RequestLength = sizeof(DOS_PEEK_REQUEST);
117 Request.Header.CommandCode = DOS_DEVCMD_PEEK;
118
119 DosCallDriver(DeviceNode->Driver, &Request.Header);
120
121 *Character = Request.Character;
122 return Request.Header.Status;
123 }
124
125 static WORD NTAPI DosDriverDispatchInputStatus(PDOS_DEVICE_NODE DeviceNode)
126 {
127 return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_INSTAT);
128 }
129
130 static WORD NTAPI DosDriverDispatchFlushInput(PDOS_DEVICE_NODE DeviceNode)
131 {
132 return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_FLUSH_INPUT);
133 }
134
135 static WORD NTAPI DosDriverDispatchIoctlWrite(PDOS_DEVICE_NODE DeviceNode,
136 DWORD Buffer,
137 PWORD Length)
138 {
139 return DosDriverWriteInternal(DeviceNode, Buffer, Length, TRUE);
140 }
141
142 static WORD NTAPI DosDriverDispatchWrite(PDOS_DEVICE_NODE DeviceNode,
143 DWORD Buffer,
144 PWORD Length)
145 {
146 return DosDriverWriteInternal(DeviceNode, Buffer, Length, FALSE);
147 }
148
149 static WORD NTAPI DosDriverDispatchOutputStatus(PDOS_DEVICE_NODE DeviceNode)
150 {
151 return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_OUTSTAT);
152 }
153
154 static WORD NTAPI DosDriverDispatchFlushOutput(PDOS_DEVICE_NODE DeviceNode)
155 {
156 return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_FLUSH_OUTPUT);
157 }
158
159 static WORD NTAPI DosDriverDispatchOpen(PDOS_DEVICE_NODE DeviceNode)
160 {
161 return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_OPEN);
162 }
163
164 static WORD NTAPI DosDriverDispatchClose(PDOS_DEVICE_NODE DeviceNode)
165 {
166 return DosDriverGenericRequest(DeviceNode, DOS_DEVCMD_CLOSE);
167 }
168
169 static WORD NTAPI DosDriverDispatchOutputUntilBusy(PDOS_DEVICE_NODE DeviceNode,
170 DWORD Buffer,
171 PWORD Length)
172 {
173 DOS_OUTPUT_BUSY_REQUEST Request;
174
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;
179
180 DosCallDriver(DeviceNode->Driver, &Request.Header);
181
182 *Length = Request.Length;
183 return Request.Header.Status;
184 }
185
186 /* PUBLIC FUNCTIONS ***********************************************************/
187
188 PDOS_DEVICE_NODE DosGetDevice(LPCSTR DeviceName)
189 {
190 PLIST_ENTRY i;
191 PDOS_DEVICE_NODE Node;
192 ANSI_STRING DeviceNameString;
193
194 RtlInitAnsiString(&DeviceNameString, DeviceName);
195
196 for (i = DeviceList.Flink; i != &DeviceList; i = i->Flink)
197 {
198 Node = CONTAINING_RECORD(i, DOS_DEVICE_NODE, Entry);
199 if (RtlEqualString(&Node->Name, &DeviceNameString, TRUE)) return Node;
200 }
201
202 return NULL;
203 }
204
205 PDOS_DEVICE_NODE DosCreateDevice(WORD Attributes, PCHAR DeviceName)
206 {
207 BYTE i;
208 PDOS_DEVICE_NODE Node;
209
210 /* Make sure this is a character device */
211 if (!(Attributes & DOS_DEVATTR_CHARACTER))
212 {
213 DPRINT1("ERROR: Block devices are not supported.\n");
214 return FALSE;
215 }
216
217 Node = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*Node));
218 if (Node == NULL) return NULL;
219
220 Node->DeviceAttributes = Attributes;
221
222 /* Initialize the name string */
223 Node->Name.Buffer = Node->NameBuffer;
224 Node->Name.MaximumLength = MAX_DEVICE_NAME;
225
226 for (i = 0; i < MAX_DEVICE_NAME; i++)
227 {
228 if (DeviceName[i] == '\0' || DeviceName[i] == ' ') break;
229 Node->Name.Buffer[i] = DeviceName[i];
230 }
231
232 Node->Name.Length = i;
233
234 InsertTailList(&DeviceList, &Node->Entry);
235 return Node;
236 }
237
238 VOID DosDeleteDevice(PDOS_DEVICE_NODE DeviceNode)
239 {
240 RemoveEntryList(&DeviceNode->Entry);
241 RtlFreeHeap(RtlGetProcessHeap(), 0, DeviceNode);
242 }
243
244 DWORD DosLoadDriver(LPCSTR DriverFile)
245 {
246 DWORD Result = ERROR_SUCCESS;
247 HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
248 LPBYTE Address = NULL;
249 DWORD Driver;
250 PDOS_DRIVER DriverHeader;
251 WORD Segment = 0;
252 DWORD FileSize;
253 DWORD DriversLoaded = 0;
254 DOS_INIT_REQUEST Request;
255 PDOS_DEVICE_NODE DeviceNode;
256
257 /* Open a handle to the driver file */
258 FileHandle = CreateFileA(DriverFile,
259 GENERIC_READ,
260 FILE_SHARE_READ,
261 NULL,
262 OPEN_EXISTING,
263 FILE_ATTRIBUTE_NORMAL,
264 NULL);
265 if (FileHandle == INVALID_HANDLE_VALUE)
266 {
267 Result = GetLastError();
268 goto Cleanup;
269 }
270
271 /* Get the file size */
272 FileSize = GetFileSize(FileHandle, NULL);
273
274 /* Allocate DOS memory for the driver */
275 Segment = DosAllocateMemory(FileSize >> 4, NULL);
276 if (Segment == 0)
277 {
278 Result = DosLastError;
279 goto Cleanup;
280 }
281
282 /* Create a mapping object for the file */
283 FileMapping = CreateFileMapping(FileHandle,
284 NULL,
285 PAGE_READONLY,
286 0,
287 0,
288 NULL);
289 if (FileMapping == NULL)
290 {
291 Result = GetLastError();
292 goto Cleanup;
293 }
294
295 /* Map the file into memory */
296 Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
297 if (Address == NULL)
298 {
299 Result = GetLastError();
300 goto Cleanup;
301 }
302
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);
307
308 /* Loop through all the drivers in this file */
309 while (TRUE)
310 {
311 if (!(DriverHeader->DeviceAttributes & DOS_DEVATTR_CHARACTER))
312 {
313 DPRINT1("Error loading driver at %04X:%04X: "
314 "Block device drivers are not supported.\n",
315 HIWORD(Driver),
316 LOWORD(Driver));
317 goto Next;
318 }
319
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);
326
327 if (Request.Header.Status & DOS_DEVSTAT_ERROR)
328 {
329 DPRINT1("Error loading driver at %04X:%04X: "
330 "Initialization routine returned error %u.\n",
331 HIWORD(Driver),
332 LOWORD(Driver),
333 Request.Header.Status & 0x7F);
334 goto Next;
335 }
336
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;
353
354 DriversLoaded++;
355
356 Next:
357 if (LOWORD(DriverHeader->Link) == 0xFFFF) break;
358 Driver = DriverHeader->Link;
359 DriverHeader = (PDOS_DRIVER)FAR_POINTER(Driver);
360 }
361
362 DPRINT1("%u drivers loaded from %s.\n", DriversLoaded, DriverFile);
363
364 Cleanup:
365 if (Result != ERROR_SUCCESS)
366 {
367 /* It was not successful, cleanup the DOS memory */
368 if (Segment) DosFreeMemory(Segment);
369 }
370
371 /* Unmap the file */
372 if (Address != NULL) UnmapViewOfFile(Address);
373
374 /* Close the file mapping object */
375 if (FileMapping != NULL) CloseHandle(FileMapping);
376
377 /* Close the file handle */
378 if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
379
380 return Result;
381 }
382
383 /* EOF */