2 * direct hardware access under Windows NT/2000/XP
4 * Copyright (c) 2004 Sascha Sommer <saschasommer@freenet.de>.
5 * Patched to compile with MinGW by Kevin Kofler:
6 * Copyright (c) 2007 Kevin Kofler
8 * This file is part of MPlayer.
10 * MPlayer is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * MPlayer is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #define STDCALL /* nothing */
31 #elif defined(__MINGW32__)
32 #include <ddk/ntddk.h>
33 #define NO_SEH /* FIXME */
35 #error Unsupported compiler. This driver requires MSVC+DDK or MinGW to build.
38 #include "dhahelper.h"
40 #define OutputDebugString DbgPrint
42 #define IOPM_SIZE 0x2000
43 typedef char IOPM
[IOPM_SIZE
];
44 static IOPM
*pIOPM
= NULL
;
50 PVOID SystemVirtualAddress
;
51 PVOID UserVirtualAddress
;
52 ULONG PhysMemSizeInBytes
;
54 static alloc_priv
* alloclist
;
55 static unsigned int alloccount
=0;
63 static STDCALL NTSTATUS
dhahelperdispatch(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
);
64 static STDCALL
void dhahelperunload(IN PDRIVER_OBJECT DriverObject
);
65 static STDCALL NTSTATUS
UnmapPhysicalMemory(PVOID UserVirtualAddress
);
66 static STDCALL NTSTATUS
MapPhysicalMemoryToLinearSpace(PVOID pPhysAddress
,ULONG PhysMemSizeInBytes
,PVOID
*PhysMemLin
);
68 void STDCALL
Ke386SetIoAccessMap(int, IOPM
*);
69 void STDCALL
Ke386QueryIoAccessMap(int, IOPM
*);
70 void STDCALL
Ke386IoSetAccessProcess(PEPROCESS
, int);
76 STDCALL NTSTATUS
DriverEntry (IN PDRIVER_OBJECT DriverObject
,IN PUNICODE_STRING RegistryPath
){
77 UNICODE_STRING DeviceNameUnicodeString
;
78 UNICODE_STRING DeviceLinkUnicodeString
;
80 PDEVICE_OBJECT DeviceObject
= NULL
;
82 OutputDebugString ("dhahelper: entering DriverEntry");
84 RtlInitUnicodeString (&DeviceNameUnicodeString
, L
"\\Device\\DHAHELPER");
86 // Create an EXCLUSIVE device object (only 1 thread at a time
87 // can make requests to this device).
89 ntStatus
= IoCreateDevice(DriverObject
,0,&DeviceNameUnicodeString
,FILE_DEVICE_DHAHELPER
,0,TRUE
,&DeviceObject
);
91 if (NT_SUCCESS(ntStatus
)){
92 // Create dispatch points for device control, create, close.
93 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] =
94 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] =
95 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = dhahelperdispatch
;
96 DriverObject
->DriverUnload
= dhahelperunload
;
98 // Create a symbolic link, e.g. a name that a Win32 app can specify
99 // to open the device.
101 RtlInitUnicodeString (&DeviceLinkUnicodeString
, L
"\\DosDevices\\DHAHELPER");
103 ntStatus
= IoCreateSymbolicLink(&DeviceLinkUnicodeString
,&DeviceNameUnicodeString
);
105 if (!NT_SUCCESS(ntStatus
)){
106 // Symbolic link creation failed- note this & then delete the
107 // device object (it's useless if a Win32 app can't get at it).
108 OutputDebugString ("dhahelper: IoCreateSymbolicLink failed");
109 IoDeleteDevice (DeviceObject
);
113 OutputDebugString ("dhahelper: IoCreateDevice failed");
115 OutputDebugString ("dhahelper: leaving DriverEntry");
120 // Process the IRPs sent to this device
122 static STDCALL NTSTATUS
dhahelperdispatch(IN PDEVICE_OBJECT DeviceObject
,IN PIRP Irp
){
123 PIO_STACK_LOCATION IrpStack
;
124 ULONG dwInputBufferLength
;
125 ULONG dwOutputBufferLength
;
126 ULONG dwIoControlCode
;
129 dhahelper_t dhahelper_priv
;
131 OutputDebugString ("dhahelper: entering dhahelperdispatch");
133 // Init to default settings
135 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
136 Irp
->IoStatus
.Information
= 0;
138 IrpStack
= IoGetCurrentIrpStackLocation(Irp
);
140 // Get the pointer to the input/output buffer and it's length
142 pvIOBuffer
= Irp
->AssociatedIrp
.SystemBuffer
;
143 dwInputBufferLength
= IrpStack
->Parameters
.DeviceIoControl
.InputBufferLength
;
144 dwOutputBufferLength
= IrpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
;
146 switch (IrpStack
->MajorFunction
){
148 OutputDebugString("dhahelper: IRP_MJ_CREATE");
151 OutputDebugString("dhahelper: IRP_MJ_CLOSE");
153 case IRP_MJ_DEVICE_CONTROL
:
154 OutputDebugString("dhahelper: IRP_MJ_DEVICE_CONTROL");
155 dwIoControlCode
= IrpStack
->Parameters
.DeviceIoControl
.IoControlCode
;
156 switch (dwIoControlCode
){
157 case IOCTL_DHAHELPER_ENABLEDIRECTIO
:
158 OutputDebugString("dhahelper: IOCTL_DHAHELPER_ENABLEDIRECTIO");
159 pIOPM
= MmAllocateNonCachedMemory(sizeof(IOPM
));
161 RtlZeroMemory(pIOPM
, sizeof(IOPM
));
162 Ke386IoSetAccessProcess(PsGetCurrentProcess(), 1);
163 Ke386SetIoAccessMap(1, pIOPM
);
165 else Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
167 case IOCTL_DHAHELPER_DISABLEDIRECTIO
:
168 OutputDebugString("dhahelper: IOCTL_DHAHELPER_DISABLEDIRECTIO");
170 Ke386IoSetAccessProcess(PsGetCurrentProcess(), 0);
171 Ke386SetIoAccessMap(1, pIOPM
);
172 MmFreeNonCachedMemory(pIOPM
, sizeof(IOPM
));
176 case IOCTL_DHAHELPER_MAPPHYSTOLIN
:
177 OutputDebugString("dhahelper: IOCTL_DHAHELPER_MAPPHYSTOLIN");
178 if (dwInputBufferLength
){
179 memcpy (&dhahelper_priv
, pvIOBuffer
, dwInputBufferLength
);
180 ntStatus
= MapPhysicalMemoryToLinearSpace(dhahelper_priv
.base
,dhahelper_priv
.size
,&dhahelper_priv
.ptr
);
181 if (NT_SUCCESS(ntStatus
)){
182 memcpy (pvIOBuffer
, &dhahelper_priv
, dwInputBufferLength
);
183 Irp
->IoStatus
.Information
= dwInputBufferLength
;
185 Irp
->IoStatus
.Status
= ntStatus
;
187 else Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
189 case IOCTL_DHAHELPER_UNMAPPHYSADDR
:
190 OutputDebugString("dhahelper: IOCTL_DHAHELPER_UNMAPPHYSADDR");
191 if (dwInputBufferLength
){
192 memcpy (&dhahelper_priv
, pvIOBuffer
, dwInputBufferLength
);
193 ntStatus
= UnmapPhysicalMemory(dhahelper_priv
.ptr
);
194 Irp
->IoStatus
.Status
= ntStatus
;
197 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
200 OutputDebugString("dhahelper: unknown IRP_MJ_DEVICE_CONTROL");
201 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
207 // DON'T get cute and try to use the status field of the irp in the
208 // return status. That IRP IS GONE as soon as you call IoCompleteRequest.
210 ntStatus
= Irp
->IoStatus
.Status
;
212 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
214 // We never have pending operation so always return the status code.
216 OutputDebugString("dhahelper: leaving dhahelperdispatch");
221 // Delete the associated device and return
223 static STDCALL
void dhahelperunload(IN PDRIVER_OBJECT DriverObject
){
224 UNICODE_STRING DeviceLinkUnicodeString
;
225 NTSTATUS ntStatus
=STATUS_SUCCESS
;
226 OutputDebugString ("dhahelper: entering dhahelperunload");
227 OutputDebugString ("dhahelper: unmapping remaining memory");
229 while(alloccount
&& (ntStatus
==STATUS_SUCCESS
))ntStatus
= UnmapPhysicalMemory(alloclist
[alloccount
-1].UserVirtualAddress
);
230 RtlInitUnicodeString (&DeviceLinkUnicodeString
, L
"\\DosDevices\\DHAHELPER");
231 ntStatus
= IoDeleteSymbolicLink (&DeviceLinkUnicodeString
);
233 if (NT_SUCCESS(ntStatus
)){
234 IoDeleteDevice (DriverObject
->DeviceObject
);
237 OutputDebugString ("dhahelper: IoDeleteSymbolicLink failed");
239 OutputDebugString ("dhahelper: leaving dhahelperunload");
247 /************************* memory mapping functions ******************************/
248 //unlike the functions of other io helpers these functions allow to map adapter memory on windows xp
249 //even if it has alread been mapped by the original driver
250 //the technique used is described in
251 //http://support.microsoft.com/default.aspx?scid=kb;en-us;q189327
252 //furthermore it keeps a list of mapped areas to free them when the driver gets unloaded
253 //I'm not sure what the limitations of ZwMapViewOfSection are but mapping 128MB videoram (that is probably already mapped by the gfxcard driver)
254 //won't work so it is generally a good idea to map only the memory you really need
256 static STDCALL NTSTATUS
MapPhysicalMemoryToLinearSpace(PVOID pPhysAddress
,ULONG PhysMemSizeInBytes
,PVOID
*PhysMemLin
){
257 alloc_priv
* alloclisttmp
;
259 PVOID SystemVirtualAddress
=NULL
;
260 PVOID UserVirtualAddress
=NULL
;
261 PHYSICAL_ADDRESS pStartPhysAddress
;
262 OutputDebugString ("dhahelper: entering MapPhysicalMemoryToLinearSpace");
265 pStartPhysAddress
.QuadPart
= (ULONGLONG
)pPhysAddress
;
267 pStartPhysAddress
.QuadPart
= (ULONGLONG
)(ULONG
)pPhysAddress
;
272 SystemVirtualAddress
=MmMapIoSpace(pStartPhysAddress
,PhysMemSizeInBytes
, /*MmWriteCombined*/MmNonCached
);
273 if(!SystemVirtualAddress
){
274 OutputDebugString("dhahelper: MmMapIoSpace failed");
275 return STATUS_INVALID_PARAMETER
;
277 OutputDebugString("dhahelper: SystemVirtualAddress 0x%x",SystemVirtualAddress
);
278 Mdl
=IoAllocateMdl(SystemVirtualAddress
, PhysMemSizeInBytes
, FALSE
, FALSE
,NULL
);
280 OutputDebugString("dhahelper: IoAllocateMdl failed");
281 return STATUS_INSUFFICIENT_RESOURCES
;
283 OutputDebugString("dhahelper: Mdl 0x%x",Mdl
);
284 MmBuildMdlForNonPagedPool(Mdl
);
286 UserVirtualAddress
= (PVOID
)(((ULONGLONG
)PAGE_ALIGN(MmMapLockedPages(Mdl
,UserMode
))) + MmGetMdlByteOffset(Mdl
));
288 UserVirtualAddress
= (PVOID
)(((ULONG
)PAGE_ALIGN(MmMapLockedPages(Mdl
,UserMode
))) + MmGetMdlByteOffset(Mdl
));
290 if(!UserVirtualAddress
){
291 OutputDebugString("dhahelper: MmMapLockedPages failed");
292 return STATUS_INSUFFICIENT_RESOURCES
;
294 OutputDebugString("dhahelper: UserVirtualAddress 0x%x",UserVirtualAddress
);
296 }__except(EXCEPTION_EXECUTE_HANDLER
){
298 ntStatus
= GetExceptionCode();
299 OutputDebugString("dhahelper: MapPhysicalMemoryToLinearSpace failed due to exception 0x%0x\n", ntStatus
);
305 OutputDebugString("dhahelper: adding data to internal allocation list");
306 alloclisttmp
=MmAllocateNonCachedMemory((alloccount
+1)*sizeof(alloc_priv
));
310 OutputDebugString("dhahelper: not enough memory to create temporary allocation list");
311 MmUnmapLockedPages(UserVirtualAddress
, Mdl
);
313 return STATUS_INSUFFICIENT_RESOURCES
;
316 memcpy(alloclisttmp
,alloclist
,alloccount
* sizeof(alloc_priv
));
317 MmFreeNonCachedMemory(alloclist
,alloccount
*sizeof(alloc_priv
));
319 alloclist
=alloclisttmp
;
320 alloclist
[alloccount
].Mdl
=Mdl
;
321 alloclist
[alloccount
].SystemVirtualAddress
=SystemVirtualAddress
;
322 alloclist
[alloccount
].UserVirtualAddress
=UserVirtualAddress
;
323 alloclist
[alloccount
].PhysMemSizeInBytes
=PhysMemSizeInBytes
;
326 *PhysMemLin
=UserVirtualAddress
;
328 OutputDebugString("dhahelper: leaving MapPhysicalMemoryToLinearSpace");
329 return STATUS_SUCCESS
;
332 static STDCALL NTSTATUS
UnmapPhysicalMemory(PVOID UserVirtualAddress
){
335 unsigned int alloccounttmp
=alloccount
;
336 OutputDebugString("dhahelper: entering UnmapPhysicalMemory to unmapp 0x%x",UserVirtualAddress
);
338 OutputDebugString("dhahelper: UnmapPhysicalMemory: nothing todo -> leaving...");
339 return STATUS_SUCCESS
;
342 for(i
=0;i
<alloccount
;i
++){
343 if(alloclist
[i
].UserVirtualAddress
!=UserVirtualAddress
){
345 alloclist
[x
].Mdl
=alloclist
[i
].Mdl
;
346 alloclist
[x
].SystemVirtualAddress
=alloclist
[i
].SystemVirtualAddress
;
347 alloclist
[x
].UserVirtualAddress
=alloclist
[i
].UserVirtualAddress
;
348 alloclist
[x
].PhysMemSizeInBytes
=alloclist
[i
].PhysMemSizeInBytes
;
353 else if(alloclist
[i
].UserVirtualAddress
==UserVirtualAddress
){
358 MmUnmapLockedPages(alloclist
[x
].UserVirtualAddress
, alloclist
[x
].Mdl
);
359 IoFreeMdl(alloclist
[x
].Mdl
);
360 MmUnmapIoSpace(alloclist
[x
].SystemVirtualAddress
,alloclist
[x
].PhysMemSizeInBytes
);
362 }__except(EXCEPTION_EXECUTE_HANDLER
){
364 ntStatus
= GetExceptionCode();
365 OutputDebugString("dhahelper: UnmapPhysicalMemory failed due to exception 0x%0x (Mdl 0x%x)\n", ntStatus
,alloclist
[x
].Mdl
);
376 alloc_priv
* alloclisttmp
;
377 alloclisttmp
=MmAllocateNonCachedMemory(alloccounttmp
*sizeof(alloc_priv
));
379 OutputDebugString("dhahelper: not enough memory to create temporary allocation list");
380 return STATUS_INSUFFICIENT_RESOURCES
;
382 memcpy(alloclisttmp
,alloclist
,alloccounttmp
* sizeof(alloc_priv
));
383 MmFreeNonCachedMemory(alloclist
,alloccount
*sizeof(alloc_priv
));
384 alloclist
=alloclisttmp
;
386 alloccount
=alloccounttmp
;
388 OutputDebugString("dhahelper: leaving UnmapPhysicalMemory");
389 return STATUS_SUCCESS
;