PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Memory Mapped IO


Gast
2003-08-11, 14:34:54
MMIO is Memory Mapped IO.

i.e. the display adapters physical memory address is mapped to linear address space.Kann mir hierzu jemand irgendwie verdeutlichen, was es mit MMIO auf sich hat, wie es funktioniert, und was man damit anstellen kann? Danke!

Demirug
2003-08-11, 15:30:21
Solange man keine Treiber schreibt oder seine Grafikkarte unter DOS direkt anspricht gar nichts.

Memory Mapped IO bedeutet das sich die Grafikkarte so in das System einbringt das die CPU auf den Grafikkartenspeicher genauso zugreifen kann als wäre es normaler Hauptspeicher.

Gast
2003-08-11, 16:21:55
ok, und wie bekomme ich "the display adapters MMIO base physical addres"?

ScottManDeath
2003-08-11, 16:33:50
du kannst im Gerätemanager die physikalischen basisaddressen grob ablesen z.b. 0x00a000 aber das wird dir nicht viel nutzen da du auf die physikalische speicherbereiche mit win32 bordmitteln keinen zugriff hast und zum anderen der grafikkartentreiber nur genaus weiss was er zu tun hat . google mal nach 'winio' das wird dir helfen

Gast
2003-08-13, 10:16:17
ok, danke jungs, ich habs hinbekommen, die base-adress(es) aus dem pci configuration space auszulesen. Ich hab einen IO driver, der mich auf alle pci-daten zugreifen lässt.

Nun kommt das vermutlich wesentlich schwierigere Problem:
wie genau mappe ich nun diese adressen in den linear adress space?

Ich weiß ich muß nen treiber dafür schreiben, aber was muß der alles können, wie muß der prinzipiell aussehen?
soweit ich das gesehen habe ist das 'winio' nicht als "map-driver" geeignet.

ScottManDeath
2003-08-13, 16:35:45
guckst du hier: http://www.internals.com dort gibts Winio (http://www.internals.com/utilities/winio.zip) das ist ein treiber und eine dll die es dir ermöglichen auf die ports der cpu und den physikalischen speicher direkt zuzugreifen, auch über mapping.

ich hab damit schon gearbeitet( auslesen der addressen des LPT ports aus dem BIOS, und eigene ansteuerung des LPT ports, unter Win2k und WinXP

Demirug
2003-08-13, 16:49:35
Ansonsten bleibt dann nur das DDK : http://msdn.microsoft.com/library/en-us/ddkint/hh/ddkint/legal_5yp3.asp?frame=true

ScottManDeath
2003-08-13, 16:53:10
Original geschrieben von Demirug
Ansonsten bleibt dann nur das DDK : http://msdn.microsoft.com/library/en-us/ddkint/hh/ddkint/legal_5yp3.asp?frame=true

ich das mal durchgesehen, das ist schon recht derbe find ich ;)

Gast
2003-08-13, 17:37:56
Gibt ja leider für XP kein DDK mehr zum dl, hab mir aber mal das für Win2k besorgt.
Wirklich schwere Kost :) hatte zu Win98-Zeiten schon damit zutun, das schien mir allerdings einfacher. Dummes WDM :( können die nicht bei vxd'en bleiben?

Hmm da hab ich das mit dem Winio wohl nicht richtig gecheckt, ich schaus mir gleich nochmal an. Thx.

Demirug
2003-08-13, 17:40:49
arrrg VXD war das schlimmste überhaupt. Da mag ich das Treiber Model von NT und WDM wesentlich mehr. Es gibt auch ein deutsches Buch dazu. Habe aber den Titel nicht im Kopf. Könnte ich erst morgen im Bücherschrank nachschauen.

Tom Servo
2003-08-23, 18:20:55
Habe mal mit dem WinIO Treiber rumgespielt. Ich wollte auf die Memory-Mappend-Register meiner NVidia Karte von einem User Prozess aus zugreifen.

Ich bekomme aber immer Null zurück. Versucht habe ich es mit der Base-Adresse die in den PCI Registern steht. War bei mir 0xDE000000

Habe deswegen in den Treiber noch ein paar Debug-Aufrufe eingebaut die ich mit DbgView ansehen kann. ZwMapViewOfSection() in WinIO.SYS:MapPhysicalMemoryToLinearSpace() schlägt fehl mit STATUS_INVALID_VIEW_SIZE. Laut Doku gehört dieser Fehlercode aber eigentlich nicht zu denen, welche ZwMapViewOfSection() erzeugen kann.
Und auch mit nur 4byte Size oder wenn ich 0 übergebe kommt dieser Fehler.

Habe im Treiber es dann mal mit MmMapIoSpace() versucht:

if (Phys32Struct.pvPhysAddress == (PVOID)0xDE000000) {
PHYSICAL_ADDRESS pa;
PVOID virt_addr;

pa.QuadPart = (ULONGLONG)Phys32Struct.pvPhysAddress;

virt_addr = MmMapIoSpace(pa, Phys32Struct.dwPhysMemSizeInBytes, MmNonCached);
DbgPrint(" MmMapIoSpace() => 0x%lx\n", virt_addr);
if (virt_addr) {
DbgPrint("((ULONG *)virt_addr)[0]=0x%lx\n", ((ULONG *)virt_addr)[0]);
MmUnmapIoSpace(virt_addr, Phys32Struct.dwPhysMemSizeInBytes);
}
}


Das liefert dann nicht Null sondern 0xf8be9000 zurück, also scheint das zu funktionieren. Aber den mit MmMapIoSpace() besorgten Speicher kann ich laut Google nur im Kernel-Mode-Treiber selber verwenden und nicht an den User-Prozess weitergeben.

Wenn es nicht anders ginge, wäre es auch kein Problem. Müssten halt einige Funktionen in den Treiber wandern. Der R6Probe-Treiber zum Radeon Übertakten macht das auch so.

Aber eigentlich müsste es doch auch so gehen, oder?

edit: Naja, eigentlich ist es schon ein Problem. Wollte das Linux/BSD nvclock Programm unter Windows zum laufen bringen, und wenn ich die Register-Bereiche mappen könnte, dann müsste ich kaum was an dem Programm ändern.

Tom Servo
2003-08-24, 14:11:31
Jetzt habe ich es hinbekommen, allerdings nicht mit ZwMapViewOfSection, sondern durch zweimaliges Mappen der Register zuerst in den Kernel-Space und von da dann in den User-Space. Aber warum funktioniert es nicht mit der Art wie WinIO es macht?




if (Phys32Struct.pvPhysAddress == (PVOID)0xDE000000) {
PHYSICAL_ADDRESS pa;
ULONG size;
PVOID virt_kaddr=0, virt_uaddr=0;
PMDL mdl=0;
unsigned i;

pa.QuadPart = (ULONGLONG)Phys32Struct.pvPhysAddress;
size = Phys32Struct.dwPhysMemSizeInBytes;

if ((virt_kaddr = MmMapIoSpace(pa, size, MmNonCached))) {
if ((mdl = IoAllocateMdl(virt_kaddr, size, FALSE, FALSE, NULL))) {
MmBuildMdlForNonPagedPool(mdl);
if ((virt_uaddr = MmMapLockedPagesSpecifyCache(mdl, UserMode, MmNonCached,
NULL, FALSE, NormalPagePriority))) {
#if 1
for(i=0; i < 4 && ((i+1)) * 4 < size; ++i)
DbgPrint("((ULONG *)virt_uaddr)[%lx]=0x%lx\n", (ULONG)i, ((ULONG *)virt_uaddr)[i]);
#endif

}

#if 1
for(i=0; i < 4 && ((i+1)) * 4 < size; ++i)
DbgPrint("((ULONG *)virt_kaddr)[%lx]=0x%lx\n", (ULONG)i, ((ULONG *)virt_kaddr)[i]);
#endif


}
}

DbgPrint("virt_kaddr=0x%lx\n", virt_kaddr);
DbgPrint("mdl=0x%lx\n", mdl);
DbgPrint("virt_uaddr=0x%lx\n", virt_uaddr);

if (virt_uaddr)
MmUnmapLockedPages(virt_uaddr, mdl);
if (mdl)
IoFreeMdl (mdl);
if (virt_kaddr)
MmUnmapIoSpace(virt_kaddr, size);
}

Tom Servo
2003-08-24, 17:26:02
Habe jetzt einfach mal zwei neue Funktionen/IORequests MapIO und UnMapIO zu WinIO hinzugefügt und der Linux Overclocker liefert mit den damit gemappten Speicher auch sinnvolle Ergebnisse:

Device="NVIDIA GeForce FX 5800"
Memory Range : DE000000 - DEFFFFFF (16 MB)
Memory Range : D0000000 - D7FFFFFF (128 MB)
Memory Range : 000A0000 - 000BFFFF (128 KB)
GPU Speed: 300.857
Mem Speed: 601.714
Mem Size: 128
Fast Write: Enabled
AGP Rate: 4
Mem Type: DDR
SBA Status: Enabled


Würde mich aber trotzdem interessieren, warum es mit dem normalen WinIO und den GeForce Registern nicht geht. Habe mit Google sehr viel gesucht, aber noch keine Idee.

Demirug
2003-08-24, 17:44:46
Speicherschutz. Man darf auf diese Speicherbereiche nur mit den entsprechenden Funktionen aus einem Treiber zugreifen.

ZwMapViewOfSection ist aber wie alle Zw Funktionen nur für Dateioperationen bestimmt. In diesem speziellen fall für Memory maped Files.

Tom Servo
2003-08-24, 18:05:07
Aber WinIO ist doch eigentlich für den von mir genutzen Zweck gedacht. Und ich kann damit z.B. auch die Adresse 0xA0000 mappen. Diese Adresse wird sogar im Beispiel-Programm zufällig benutzt. Und die gehört auch irgendwie zur GeForce:

Device="NVIDIA GeForce FX 5800"
Memory Range : DE000000 - DEFFFFFF (16 MB)
Memory Range : D0000000 - D7FFFFFF (128 MB)
Memory Range : 000A0000 - 000BFFFF (128 KB)


Es wird im Treiber wohl auch die "Datei" \Device\PhysicalMemory geöffnet. Wahrscheinlich kann WinIO wohl deswegen auch Zw-Funktionen benutzen.

In der Doc steht nur folgendes Einschränkung:

Do not use ZwMapViewOfSection to map a memory range in \Device\PhysicalMemory into user mode, unless your driver has allocated the memory range directly with MmAllocatePagesForMdl (or any equivalent method that guarantees no other system component has mapped the same memory range with a different MEMORY_CACHING_TYPE value).


Aber eigentlich hat das doch mit dem Problem nichts zu tun.

Es geht zwar mit den neuen IORequests, aber es wäre natürlich peinlich, wenn ich da nur irgendeine Kleinigkeit am bestehenden Code hätte ändern müssen.

edit: Die ZwMapViewOfSection Funktion wird natürlich im Treiber aufgerufen. Im User-Programm benutze ich nur die WinIO API.

edit2: Wie erkennt man "diese" Speicherbereiche, welche geschützt sind. Ich habe da z.B. die MD_Flags im MEM_RESOURCE:

Device="NVIDIA GeForce FX 5800"
Memory Range : DE000000 - DEFFFFFF (16 MB) MD_Flags: 0x00000003
Memory Range : D0000000 - D7FFFFFF (128 MB) MD_Flags: 0x00000007
Memory Range : 000A0000 - 000BFFFF (128 KB) MD_Flags: 0x00000003

Aber WinIO kann einen 0x03 Bereich mappen und den anderen nicht.

Demirug
2003-08-24, 18:29:09
Ach so jetzt ist mir klar was du machst und auch warum es nicht geht. Die ZwFunktionen verhalten sich aber wie Win32 funktionen und genau hier ist das Problem. "\Device\PhysicalMemory" darf nicht über die Win32 Funktionen benutzt werden.

Willst du diesen Speicher eigentlich nur auslesen? Das geht auch ohne Treiber wenn man die Windows Native-API benutzt. Allerdings muss man dafür Admin Rechte haben.

Tom Servo
2003-08-24, 18:41:52
Ich möchte auch darauf schreibend zugreifen (ist hauptsächlich ein Overclocking Programm).

Würde mich trotzdem interessieren wie es in User-Programmen geht (um Infos auszulesen ohne einen Treiber).

Mit dem naheliegenden MapViewOfFile() und \Device\PhysicalMemory wirds ja wohl genausowenig bei dieser Adresse nicht funktionieren. Und es ist ja auch eine Win32 Funktion.

Ich wundere mich auch, ob die Original-IORequests in WinIO.sys mit den Zw-Funktionen überhaupt noch Sinn haben und irgendwas besser können als die Mm-Funktionen.

edit: Könntest du mal schauen, ob im WinIO.Sys-Originalcode \Device\PhysicalMemory wirklich unzulässig mit Zw-Funktionen benutzt wird?:


NTSTATUS MapPhysicalMemoryToLinearSpace(PVOID pPhysAddress,
ULONG PhysMemSizeInBytes,
PVOID *ppPhysMemLin,
HANDLE *pPhysicalMemoryHandle)
{
UNICODE_STRING PhysicalMemoryUnicodeString;
PVOID PhysicalMemorySection = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
PHYSICAL_ADDRESS ViewBase;
NTSTATUS ntStatus;
PHYSICAL_ADDRESS pStartPhysAddress;
PHYSICAL_ADDRESS pEndPhysAddress;
PHYSICAL_ADDRESS MappingLength;
BOOLEAN Result1, Result2;
ULONG IsIOSpace;
unsigned char *pbPhysMemLin = NULL;

OutputDebugString ("Entering MapPhysicalMemoryToLinearSpace");

RtlInitUnicodeString (&PhysicalMemoryUnicodeString,
L"\\Device\\PhysicalMemory");

InitializeObjectAttributes (&ObjectAttributes,
&PhysicalMemoryUnicodeString,
OBJ_CASE_INSENSITIVE,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL);

*pPhysicalMemoryHandle = NULL;

ntStatus = ZwOpenSection (pPhysicalMemoryHandle,
SECTION_ALL_ACCESS,
&ObjectAttributes);

if (NT_SUCCESS(ntStatus))
{

ntStatus = ObReferenceObjectByHandle (*pPhysicalMemoryHandle,
SECTION_ALL_ACCESS,
(POBJECT_TYPE) NULL,
KernelMode,
&PhysicalMemorySection,
(POBJECT_HANDLE_INFORMATION) NULL);

if (NT_SUCCESS(ntStatus))
{

pStartPhysAddress.QuadPart = (ULONGLONG)pPhysAddress;

pEndPhysAddress = RtlLargeIntegerAdd (pStartPhysAddress,
RtlConvertUlongToLargeInteger(PhysMemSizeInBytes));

IsIOSpace = 0;

Result1 = HalTranslateBusAddress (1, 0, pStartPhysAddress, &IsIOSpace, &pStartPhysAddress);

IsIOSpace = 0;

Result2 = HalTranslateBusAddress (1, 0, pEndPhysAddress, &IsIOSpace, &pEndPhysAddress);

if (Result1 && Result2)
{
MappingLength = RtlLargeIntegerSubtract (pEndPhysAddress, pStartPhysAddress);

if (MappingLength.LowPart)
{

// Let ZwMapViewOfSection pick a linear address

PhysMemSizeInBytes = 0; //MappingLength.LowPart;

ViewBase = pStartPhysAddress;

ntStatus = ZwMapViewOfSection (*pPhysicalMemoryHandle,
(HANDLE) -1,
&pbPhysMemLin,
0L,
PhysMemSizeInBytes,
&ViewBase,
&PhysMemSizeInBytes,
ViewShare,
0,
PAGE_READWRITE | PAGE_NOCACHE);

if (!NT_SUCCESS(ntStatus)) {
D(DbgPrint("ERROR: ZwMapViewOfSection failed (ntStatus=0x%lx (%s))\n",
(long)ntStatus,
((ntStatus == STATUS_CONFLICTING_ADDRESSES) ? "STATUS_CONFLICTING_ADDRESSES"
: ((ntStatus == STATUS_INVALID_PAGE_PROTECTION) ? "STATUS_INVALID_PAGE_PROTECTION"
: ((ntStatus == STATUS_INVALID_VIEW_SIZE) ? "STATUS_INVALID_VIEW_SIZE"
: ((ntStatus == STATUS_SECTION_PROTECTION) ? "STATUS_SECTION_PROTECTION" : "unkown"))))

));
}
else
{
pbPhysMemLin += (ULONG)pStartPhysAddress.LowPart - (ULONG)ViewBase.LowPart;
*ppPhysMemLin = pbPhysMemLin;
}
}
else
OutputDebugString ("ERROR: RtlLargeIntegerSubtract failed");
}
else
OutputDebugString ("ERROR: MappingLength = 0");
}
else
OutputDebugString ("ERROR: ObReferenceObjectByHandle failed");
}
else
OutputDebugString ("ERROR: ZwOpenSection failed");

if (!NT_SUCCESS(ntStatus))
ZwClose(*pPhysicalMemoryHandle);

OutputDebugString ("Leaving MapPhysicalMemoryToLinearSpace");

return ntStatus;
}


NTSTATUS UnmapPhysicalMemory(HANDLE PhysicalMemoryHandle, PVOID pPhysMemLin)
{
NTSTATUS ntStatus;

OutputDebugString ("Entering UnmapPhysicalMemory");

ntStatus = ZwUnmapViewOfSection((HANDLE)-1, pPhysMemLin);

if (!NT_SUCCESS(ntStatus))
OutputDebugString ("ERROR: UnmapViewOfSection failed");

ZwClose(PhysicalMemoryHandle);

OutputDebugString ("Leaving UnmapPhysicalMemory");

return ntStatus;
}

Demirug
2003-08-24, 18:46:31
Original geschrieben von Tom Servo
Ich möchte auch darauf schreibend zugreifen (ist hauptsächlich ein Overclocking Programm).

Würde mich trotzdem interessieren wie es in User-Programmen geht (um Infos auszulesen ohne einen Treiber).

Mit dem naheliegenden MapViewOfFile() und \Device\PhysicalMemory wirds ja wohl genausowenig bei dieser Adresse nicht funktionieren.

Ich wundere mich auch, ob die Original-IORequests in WinIO.sys mit den Zw-Funktionen überhaupt noch Sinn haben und irgendwas besser können als die Mm-Funktionen.

Naja schreibend soll auch irgendwie gehen aber das habe ich jetzt nicht im Kopf.

Um an \Device\PhysicalMemory ranzukommen muss man mit der native API ran. Die ist aber nicht dokumentiert weil man davon eigentlich die Finger lassen soll. Ich müsste aber irgendwo Beispiel-Code rumliegen haben wie man es macht. Ich werden mal suchen gehen.

Tom Servo
2003-08-24, 18:57:33
Danke. Wäre wirklich cool, auch wenn damit das ganze Treiber-Programmieren für die Katz war. Aber vielleicht ist ein Treiber doch nützlich, damit es auch ohne Admin-Account geht?

Tom Servo
2003-08-25, 10:27:19
Habe jetzt einfach mal nach dieser Native API gegooglet. Dabei ist mir aufgefallen, dass PhysMem.exe von SysInternals.com diese Funktionen benutzt (z.B. NtMapViewOfSection).

Mit dem Programm kann man hier aber auch nicht auf 0xDE000000 zugreifen, sondern genauso wie WinIO.Sys nur auf z.B. 0x000A0000.

Vielleicht gehts mit dem \Device\PhysicalMemory Treiber auch einfach nicht?

Gast
2003-08-25, 14:52:03
Tom Servo,

wie ich sehe machst du genau das, was ich auch machen wollte. GF-OC'er. Leider fehlt mir die Zeit, als das ich mich ständig um das Programm kümmern könnte ... :(

Tom Servo
2003-08-25, 17:20:39
Original geschrieben von Gast
Tom Servo,

wie ich sehe machst du genau das, was ich auch machen wollte. GF-OC'er. Leider fehlt mir die Zeit, als das ich mich ständig um das Programm kümmern könnte ... :(

Ich schreibe allerdings den Overclocker nicht extra selber, sondern benutze auf der einen Seite den Linux/BSD Overclocker nvclock-0.7 und auf der anderen Seite gibts ja schon 3DProf. Für Radeons enthält 3DProf bereits einen Übertakter der auch für NVidia Karten funktioniert. Es fehlen aber leider in nvclock noch Möglichkeiten um den Takt für NV3x Karten zu setzen. Auslesen geht aber. Durch die 2D/3D Taktumschaltung wird einem beim Setzen auch immer der Treiber bei der nächsten Umschaltung dazwischenfunken. Der Autor hat leider keine eigenen NV3x zum testen, deshalb hat er das noch nichts weiter in der Richtung implementiert.

Ich werde es demnächst alles uploaden und dann kannst du ja auch die Sourcen für dein Programm benutzen, falls dich die GPL von nvclock nicht stört. Oder du könntest zumindestens den modfizierten WinIO Treiber benutzen, falls du auch W2K/XP benutzt und es bei dir auch nicht geht mit dem originalen WinIO.Sys.

Nvclock kann man sehr einfach benutzen über eine statische Link-Library. Hier der Code für den Output oben:


int _tmain(int argc, _TCHAR* argv[])
{
win32nt_get_io__init_module(); //find device base address with setupapi.lib

FindAllCards();
set_card(0);

std::cout << "Clocks " << nv_card.get_gpu_speed() << "/" << nv_card.get_memory_speed() / 2 << " (core/mem)" << std::endl;
std::cout << "GPU Speed: " << nv_card.get_gpu_speed() << std::endl;
std::cout << "Mem Speed: " << nv_card.get_memory_speed() << std::endl;
std::cout << "Mem Size: " << nv_card.get_memory_size() << std::endl;
std::cout << "Fast Write: " << nv_card.get_fw_status() << std::endl;
std::cout << "AGP Rate: " << nv_card.get_agp_rate() << std::endl;
std::cout << "Mem Type: " << nv_card.get_memory_type() << std::endl;
std::cout << "SBA Status: " << nv_card.get_sba_status() << std::endl;


return 0;
}



Für WinIO-DLL habe ich 2 neue Funktionen hinzugefügt und den Header von C++ nach C geändert. Habe ein WinIO-Object eingeführt, damit man sich nicht für jede Adresse ein Handle merken muss, sondern das Unmappen einfach mit der Adresse machen kann die MapIO zurückliefert.


#ifndef WINIO_H
#define WINIO_H

#ifdef WINIO_DLL
#define WINIO_API _declspec(dllexport)
#else
#define WINIO_API _declspec(dllimport)
#endif

typedef struct winio_instance {
int handles_nmb;
int handles_size;
struct handle *handles;
} *WinIO_Inst;

typedef WinIO_Inst WinIO;

#ifdef __cplusplus
extern "C"
{
#endif

WINIO_API WinIO _stdcall InitializeWinIo(); /* returns an WinIO obj, to use it in later calls to this API from the same process */
WINIO_API VOID _stdcall ShutdownWinIo(WinIO obj);

WINIO_API PBYTE _stdcall MapPhysToLin(PBYTE pbPhysAddr, DWORD dwPhysSize, HANDLE *pPhysicalMemoryHandle);
WINIO_API BOOL _stdcall UnmapPhysicalMemory(HANDLE PhysicalMemoryHandle, PBYTE pbLinAddr);
WINIO_API BOOL _stdcall GetPhysLong(PBYTE pbPhysAddr, PDWORD pdwPhysVal);
WINIO_API BOOL _stdcall SetPhysLong(PBYTE pbPhysAddr, DWORD dwPhysVal);
WINIO_API BOOL _stdcall GetPortVal(WORD wPortAddr, PDWORD pdwPortVal, BYTE bSize);
WINIO_API BOOL _stdcall SetPortVal(WORD wPortAddr, DWORD dwPortVal, BYTE bSize);
WINIO_API BOOL _stdcall InstallWinIoDriver(PSTR pszWinIoDriverPath, BOOL IsDemandLoaded);
WINIO_API BOOL _stdcall RemoveWinIoDriver();


/* Map/UnMap physical RAM to user space.

Note: Both functions do nothing on Win9x, because they aren't implemented there yet.

PHYS_ADDR Points to physical memory address. No aligning to pagesize is required.
SIZE Byte size of memory space
VIRT_UADDR Start address to user space mapped memory (result of MapIO()). Its safe
to pass NULL here, which causes UnMapIO() to do nothing.

MapIO() returns user space address.
UnMapIO() returns nothing
*/
WINIO_API PVOID _stdcall MapIO(WinIO obj, ULONG phys_addr, ULONG size);
WINIO_API VOID _stdcall UnMapIO(WinIO obj, PVOID virt_uaddr);



extern BOOL IsNT;
extern HANDLE hDriver;
extern BOOL IsWinIoInitialized;

BOOL _stdcall StartWinIoDriver();
BOOL _stdcall StopWinIoDriver();

#ifdef __cplusplus
}
#endif

#endif

Gast
2003-08-25, 17:29:35
Ui, das sieht gut aus ... ja ich wäre sehr an den src'es interessiert!
Wusste garnicht, daß der linux nvclock mit sources kommt ?! Cool :)

Bin selber grad erst soweit, daß ich die Sachen aus dem PCI Config-Space auslesen konnte, und wäre (nach meiner nächsten Uni-Klausur :( ) zum WinIO gekommen...

Tom Servo
2003-08-25, 18:11:03
Ist momentan noch experimentell (Vorsicht: hardcodierte Basis-Adresse!):

http://home.tiscalinet.de/bertw/proj/tdprof/misc/nvclock_w32.zip

Ist eine VS7 Solution. Das WinIO-Sys Projekt ist bereits compiliert, so dass du nicht unbedingt das DDK brauchst.

Zum Auslesen der PCI-Baseaddress benutze ich momentan Funktionen aus setupapi.lib (Momentan benutze ich das Ergebnis allerdings nicht sondern setze 0xDE000000 per Hand irgendwo). Man könnte es aber auch in den WinIO.sys Treiber einbauen. Vielleicht wäre es aus Sicherheitsgründen sowieso besser, wenn der Treiber nur die GeForce Register mappen könnte. Dann müsste er auch die ganze Erkennung mitmachen. Macht R6Probe bei Radeons auch so. Vielleicht sogar deswegen. Ist schwer zu sagen, weil es leider alles in Assembler ist :-/.

edit Merke gerade, dass ich da vorhin einige Änderungen eingebaut hatte und dann musste ich mal kurz weg. Ich mach die paar Bugs noch schnell raus (z.B. ein fehledes Semikolon in WinIO.h).

edit2 Jetzt sollte es gehen.

Gast
2003-08-25, 18:44:02
Ich dank dir, ich schaue es mir gleich an!

Ich habe meinen Teil der Arbeit in Delphi geschrieben, aber das krieg ich schon konvertiert.

Vielleicht wäre es aus Sicherheitsgründen sowieso besser, wenn der Treiber nur die GeForce Register mappen könnte.

Falls du damit meinst, daß man erst die Register aus dem PCI-Config-Space auslesen sollte, und dann diese Register weiterverwendet: Das habe ich genau so vor :)

Tom Servo
2003-08-25, 19:12:59
Ich denke nur, es ist etwas unsicher, einen generischen Treiber wie WinIO zu installieren, über den dann jedes User Programm Zugriff auf den kompletten Speicher des Rechners hat. Nicht jeder würde solch einen Treiber installieren wollen. Ein spezialisierter Treiber würde nur Zugriff auf die GeForce Register ermöglichen.


Ich werde bei 3DProf das ganze übrigens auch nicht direkt einbauen bzw. hinzulinken, sondern nur ein Kommandozeilenprogramm schreiben. Das wird dann von 3DProf mit den entsprechenden Parametern aufgerufen und dessen Standard-Ausgabe eingelesen. Genauso mache ich es bei der Radeon auch, und das funktioniert einwandfrei.

Das win_nvclock.exe benutze ich schon um den Takt meiner NV30 in 3DProf anzuzeigen.

3DProf ist ja auch kein C Programm sondern C# und das Übertakten über eine EXE ist einfacher und m.E. sogar besser, weil das Kommandozeilenprogramm sicher gut wiederverwendet werden kann (z.B. in Batch-Dateien, wo man nun wirklch keine Library dazulinken kann).

edit: Wobei nvclock sicher bereits ein gutes Kommandozeilenprogramm enthält bzw. ist. Ich habe es leider nie unter Linux gesehen und bisher mir nur den Source-Code des Backends angesehen.

Gast
2003-08-26, 12:36:12
Das ist strange.procedure TForm1.SpeedButton1Click(Sender: TObject);
var
ptr: pbyte;
hPhys: THandle;
physicaladdress: integer;
begin
physicaladdress := $D4000000 + $00101000;
ptr:=MapPhysToLin(Pointer(physicaladdress), 255, @hPhys);
edit2.text := inttostr(byte(ptr^));
edit3.text := char(ptr^);
UnmapPhysicalMemory(hPhys, ptr);
end;
Obwohl ich vorher ganz normal InitializeWinIo aufgerufen habe, krieg ich ne Access Violation ... und zwar ist der zurückgegebene Pointer ptr immer NULL (bzw nil in delphi :) ).

Warum ist das so?
Hab auch mal mit verschiedenen anderen Sachen gespielt, so zum Beispiel mit einem Codeschnipsel, welcher den Status der Tastaturleuchtdioden anzeigen soll: procedure TForm1.Button2Click(Sender: TObject);
var
ptr: pbyte;
hPhys: THandle;

begin
ptr:=MapPhysToLin(pointer($417), 1, @hPhys);

Edit2.Text:=Format('NumLock=%d, CapsLock=%d, ScrollLock=%d',
[integer(ptr^ AND $20 <> 0),
integer(ptr^ AND $40 <> 0),
integer(ptr^ AND $10 <> 0)]);

UnmapPhysicalMemory(hPhys, ptr);
end;Leider funktioniert das ebenfalls nicht, weil Nullpointer.

Gast
2003-08-26, 13:03:35
ah ehm scheint wohl das gleiche problem zu sein, wie das, was du am anfang hattest ... ?!

Tom Servo
2003-08-26, 13:42:55
Naja, vielleicht solltest du die von mir hinzugefügten Funktionen benutzen (also MapIO und UnMapIO). Was du benutzt, ist ja genau das, was nicht geht. ;)

Bei der aktuellen Version meiner Sourcen sind übrigens einen Haufen Bugs weniger drin und es ist im Prinzip alles fertig (keine hardcodierte Base Adresse und Device ID mehr).

Auch die Kommandozeilenversion von nvclock läuft:


NVClock v0.7

-- General info --
Card: nVidia GeforceFX 5800
PCI id: 0x302
GPU speed: 300.857 MHz
Bustype: AGP

-- Memory info --
Amount: 128 MB
Type: 128 bit DDR
Speed: 601.714 MHz

-- AGP info --
Status: Enabled
Rate: 4X
AGP rates: 1X 2X 4X
Fast Writes: Enabled
SBA: Enabled




NVClock v0.7

Using NVClock you can overclock your Nvidia videocard under Linux and FreeBSD.
Use this program at your own risk, because it can damage your system!

Usage: ./NVClock [options]

Overclock options:
-c --card number Number of the card to overclock
-m --memclk speed Memory speed in MHz
-n --nvclk speed Core speed in MHz

-r --reset Restore the original speeds

Other options:
-d --debug Enable/Disable debug info
-f --force Force a speed, NVClock won't check min/max speeds
-h --help Show this help info
-i --info Print detailed card info
-s --speeds Print current speeds in MHz

Gast
2003-08-26, 13:52:37
oh äh moment ...

Gast
2003-08-27, 12:39:26
Hab es auch damit nicht hinbekommen. Kenne aber vermutlich den Grund:

In der WinIOTest.cpp wird am Anfang ja
'InstallWinIoDriver("winio.sys", false)'
aufgerufen.
Diese Methode besitzt mein Delphiprojekt nicht. Und wenn die sys nicht geladen ist, gehts halt nicht.

Hab versucht, die nötigen Methoden zu portieren, das wird noch dauern.

Tom Servo
2003-08-27, 13:01:53
Habe WinIO.h übrigens inzwischen etwas vereinfacht (also alles rausgeworfen was für den User nicht wichtig ist und aus dem WinIO-Objekt einen generischen Pointer (PVOID) gemacht. Vielleicht wirds dadurch für Pascal auch etwas einfacher.

Ob der Treiber installiert ist, merkst du daran ob InitializeWinIo() fehlschlägt (also NIL zurückliefert). Wenn es nicht fehlschlägt, könnte es aber trotzdem noch möglich sein, dass du einen ältere WinIO.sys Version hast, welche dann MapIO/UnMapIO noch nicht eingebaut hat. Schau mal in den Windows\system32\drivers Ordner

In meinem nvclock Port wird der Treiber automatisch mittels InstallWinIoDriver() installiert wenn nötig. Allerdings wird der Treiber nicht nach Windows\system32\drivers kopiert sondern direkt vor Programm-Ordner aus benutzt.

Du bräuchtest nur 3D Prof downloaden (Link in meiner Sig; unstable binary ZIP Version) und dort sind im Ordner etc die folgenden Dateien enthalten:

nvclock.exe, WinIO.dll, WinIO.sys

Wenn du die Dateien in den Ordner deiner Pascal-Programms kopierst und dann als Administrator mal nvlock startest sollte der Treiber danach installiert und für dein Pascal Programm über die DLL benutzbar sein.

Kannst auch einfach tdprof.exe starten und im Clocking Tab rumtakten. Geht aber nur, wenn die .net Runtime auf deinem System installiert ist.

Kannst ja mal schreiben, ob nvclock auf deinem System funktioniert. Täte mich interessieren.

Gast
2003-08-27, 13:08:08
Jo, dein Kompilat funktioniert bei mir, wird alles einwandfrei erkannt. Taktraten, SBA, FW, alles ist i.O.

Ich teste jetzt mal deine .dll + .sys

Gast
2003-08-27, 13:10:16
Ach so, übrigens liefert InitializeWinIo einen Wert <> 0. Also wird es korrekt initialisiert.

Ich kopiere jetzt einfach mal die sys ins win32/system32/drivers, dann müßte der treiber beim neustart ja gleich geladen sein. (oder?)
Wie gesagt, InstallWinIoDriver funzt bei mir noch nicht.

Tom Servo
2003-08-27, 13:15:57
Eigentlich sollte der Treiber nach erfolgreichem Ausführen von nvclock.exe zu Verfügung stehen und dein Programm funktionieren.

Kannst du mit nvclock.exe eigentlich auch übertakten? Ich habe nur eine FX und dort kann man sich die Takt-Werte nur ansehen aber nicht ändern. Wäre cool wenn du das mal testen könntest.

Bis zur GF4Ti soll mit nvclock alles übertaktbar sein.

Gast
2003-08-27, 13:22:47
:)

C:\DOWNLO~1\TOMSER~1\TDPROF~1\etc>nvclock -m470 -n210
NVClock v0.7

Requested memory speed: 470.000 MHz
Requested core speed: 210.000 MHz
nVidia Geforce 3
Memory speed: 472.494 MHz
Core speed: 210.295 MHzDas geht schon. Jedoch will mein Prog noch immer nicht. Trotz geladenem Treiber.

Das ist ganz ulkig, weil Delphi keinen Fehler schmeisst, sondern mich direkt ins debug-fenster bringt, und mir irgendwelchen ASM-Code vorführt.

Selbst das hier funktioniert übrigens nicht:

procedure TForm1.Button1Click(Sender: TObject);
function ReadCMOSByte(Offset: byte): byte;
var
Inp: byte;
begin
SetPortVal($70, Offset, sizeof(Offset));
GetPortVal($71, @Inp, sizeof(Inp));
Result:=Inp;
end;

begin
Edit1.Text:=Format('%.2x:%.2x:%.2x', [ReadCMOSByte($4), ReadCMOSByte($2), ReadCMOSByte($0)]);
end;

Tom Servo
2003-08-27, 13:35:14
Prima, das nvclock wirklich geht.


Wobei SetPortVal() nicht funktionieren dürfte, weil nur die von mir hinzugeführte MapIO Funktion geht. Alle anderen Funktionen kannst du momentan vergessen. Geht wahrscheinlich alles nicht, da alles noch im Original-Zustand.


Der Treiber sollte geladen sein. Wenn du WinIO.dll und nvclock in deinem Pascal Verzeichnis hast, und dein Pascal Programm dieselbe WinIO.dll benutzt wie nvclock.exe, dann sollte diese WinIO.dll doch m.E. auch immer den selben WinIO.sys Treiber benutzen.

Aber falls du evtl. doch noch einen alten Treiber in system32/drivers hast, dann vielleicht doch den rauswerfen oder updaten. Auf meinem System ist WinIO.sys nur im Programm-Verzeichnis von nvclock.exe vorhanden. Und der wird auch nur einemal installiert, und bleibt dann zumindestens solange aktiv bis Windows nicht neugestartet wird.


static BOOL module_winio_init()
{
if (!winio_instance) {
if (!(winio_instance = InitializeWinIo())) {
InstallWinIoDriver("WinIO.sys", FALSE);
winio_instance = InitializeWinIo();
}
if (winio_instance)
atexit(module_winio_exit);
}

return (winio_instance != NULL);
}

Gast
2003-08-27, 13:55:37
Mal schauen, ich werd noch ein paar Sachen probieren, muß dann aber leider wieder zu anderen Dingen übergehen.
Irgendwann in den nächsten 4 Wochen werd ichs schon hinbekommen, hoffe ich :-/

Ich dank dir erstmal.

Tom Servo
2003-08-27, 14:11:18
Wahrscheinlich ist es nur irgendwas einfaches. Es könnte die physische Adresse falsch sein. Da es ein ULONG-Parameter ist, kannst du ja direkt die Hex-Zahl hinschreiben. Dann bliebe nur noch die Size und der WinIO Zeiger Das sollte der sein, den InitializeWinIO() zurückgeliefert hat. Die DLL sollte Fehlermeldungen liefern, dann wäre es einfacher.

Hier mal die Benutzung der WinIO Funktionen in back_w32.c.




static void *map_dev_mem (unsigned long base, unsigned long size)
{
if (!module_winio_init())
return NULL;
return MapIO(winio_instance, base, size);
}

static void unmap_dev_mem(volatile unsigned *base, unsigned long size)
{
(void)size; // size doesn't matter ;)
if (!module_winio_init())
return;
UnMapIO((void *)winio_instance, base);
}


static void module_winio_exit(void)
{
if (winio_instance) {
ShutdownWinIo(winio_instance);
winio_instance = NULL;
}
}

static BOOL module_winio_init()
{
if (!winio_instance) {
if (!(winio_instance = InitializeWinIo())) {
InstallWinIoDriver("WinIO.sys", FALSE);
winio_instance = InitializeWinIo();
}
if (winio_instance)
atexit(module_winio_exit);
}

return (winio_instance != NULL);
}



btw: Ich überlege gerade, ob man sich nicht einfach über den nVidia Treiber die gemappten Speicherbereiche besorgen könnte, ganz ohne WinIO. Unter Linux benutzt nvclock für den originalen XFree86-nvidia-Treiber /dev/mem, aber für den closed Source nVidia Treiber wird direkt /dev/nvidia geöffnet und als Datei gemappt. Müsste man wohl mal den RivaTuner Autor fragen.

Gast
2003-08-27, 16:03:05
Original geschrieben von Tom Servo
btw: Ich überlege gerade, ob man sich nicht einfach über den nVidia Treiber die gemappten Speicherbereiche besorgen könnte, ganz ohne WinIO. Unter Linux benutzt nvclock für den originalen XFree86-nvidia-Treiber /dev/mem, aber für den closed Source nVidia Treiber wird direkt /dev/nvidia geöffnet und als Datei gemappt. Müsste man wohl mal den RivaTuner Autor fragen. Du hast 3 Möglichkeiten die nV-Karte zu übertakten:

- Exported DLL functions. Über die nvcpl.dll gibt es eine nvStartup-Funktion, welche man nutzen kann. Dazu müssen nur die Simpson-Werte in der Registry decodiert werden, was nicht weiter schwer ist.
- ExtEscape Calls. Die Treiber bieten verschiedene Calls an, dummerweise wechseln diese alle paar Treiberversionen. Was vorher mal 7012 war ist nun beispielsweise 7020. Da soll mal einer Hinterherkommen. Aus diesem Grund will ich LowLevel ...
- ... Registerzugriffe. Hier kann niemand tricksen.

Über apihooks kriegst du ne Menge infos über die ExtEscapes, ABER wie gesagt, kommt ein neuer Treiber, kannst du davon ausgehen, daß du dein Programm neu anpassen darfst.
Bleib lieber beim Low Level.

Gast
2003-08-27, 16:22:48
Ehm, mal so eine kleine Frage ... was für einen Wert muss eigentlich das Handle haben? Bzw. was weist man diesem zu?
du machst ja sowas:

WinIO winio_instance;

Leider habe ich keine Klasse WinIO in meinem Programm, die ich dem Handle zuweisen könnte ...

Liegts evtl. daran?program winio_1;
{$APPTYPE CONSOLE}
uses sysutils,winio,windows;

procedure mache;
var
ptr: pbyte;
hPhys: THandle;

begin
ptr:=MapIO(@hPhys, pointer($417), 1);

writeln(Format('NumLock=%d, CapsLock=%d, ScrollLock=%d',
[integer(ptr^ AND $20 <> 0),
integer(ptr^ AND $40 <> 0),
integer(ptr^ AND $10 <> 0)]));

UnmapIO(hPhys, ptr);
end;

begin
InitializeWinIO;
mache;
ShutdownWinIo;
end.

Tom Servo
2003-08-27, 16:47:40
Das Hardware overclocking ist allerdings problematisch mit der FX (wegen der 2D/3D Umschaltung). Vielleicht helfen da die von dir genannten Sachen doch weiter.


Das WinIO Object (aktuell übrigens nur noch als generischer Pointer deklariert in WinIO.h) bekommt man von InitializeWinIo() geliefert. Das ist ein Speicherbereich zum Speichern von DLL-privaten Daten.

Ist praktisch das gleiche wie FILE für Dateien:
FILE *f = fopen(...)
fread(..., f);
fwrite(..., f);
fclose(f);


Steht z.Zt. alles nicht im orignalen Help-File zu WinIO sondern nur implizit im Header File:




#ifndef WINIO_H_
#define WINIO_H_

#ifdef WINIO_DLL
# define WINIO_API _declspec(dllexport)
#else
# define WINIO_API _declspec(dllimport)
#endif


typedef PVOID WinIO;

#ifdef __cplusplus
extern "C" {
#endif

/* install / uninstall driver to system */
WINIO_API BOOL _stdcall InstallWinIoDriver(PSTR pszWinIoDriverPath, BOOL IsDemandLoaded);
WINIO_API BOOL _stdcall RemoveWinIoDriver();

/* open / close driver */
WINIO_API WinIO _stdcall InitializeWinIo();
WINIO_API VOID _stdcall ShutdownWinIo(WinIO winio_handle);

/* map / unmap physical RAM into user space (broken ?) */
WINIO_API PBYTE _stdcall MapPhysToLin(PBYTE pbPhysAddr, DWORD dwPhysSize, HANDLE *pPhysicalMemoryHandle);
WINIO_API BOOL _stdcall UnmapPhysicalMemory(HANDLE PhysicalMemoryHandle, PBYTE pbLinAddr);

/* read / write single DWORD from/to a physical address */
WINIO_API BOOL _stdcall GetPhysLong(PBYTE pbPhysAddr, PDWORD pdwPhysVal);
WINIO_API BOOL _stdcall SetPhysLong(PBYTE pbPhysAddr, DWORD dwPhysVal);

/* read / write single BYTE from/to a physical address */
WINIO_API BOOL _stdcall GetPortVal(WORD wPortAddr, PDWORD pdwPortVal, BYTE bSize);
WINIO_API BOOL _stdcall SetPortVal(WORD wPortAddr, DWORD dwPortVal, BYTE bSize);


/* Map/UnMap physical RAM to user space.
(Replacement for possible broken MapPhysToLin/UnmapPhysicalMemory pair)

Note: Both functions do nothing on Win9x, because they aren't implemented there yet.

PHYS_ADDR Points to physical memory address. No alinging to pagesize is required.
SIZE Byte size of memory space
VIRT_UADDR Start address to user space mapped memory (result of MapIO()). Its safe
to pass NULL here, which causes UnMapIO() to do nothing.

MapIO() returns user space address.
UnMapIO() returns nothing
*/
WINIO_API PVOID _stdcall MapIO(WinIO winio_handle, ULONG phys_addr, ULONG size);
WINIO_API VOID _stdcall UnMapIO(WinIO winio_handle, PVOID virt_uaddr);

#ifdef __cplusplus
}
#endif


/* recent changes to this file:

-bw/26-Aug-2003
===============
- private stuff moved to new header file WinIO_private.h
- type of WinIO no longer visible to user. Is a generic (PVOID) pointer now.
- 2 new functions: MapIO, UnMapIO. Do the same as MapPhysToLin/UnmapPhysicalMemory but
work with addresses the old functions cannot map. The userinterface ist more easy too,
so UnMapIO() only needs the base addres returned by MapIO().

*/
#endif /* WINIO_H_ */

Gast
2003-08-30, 18:30:31
Ich glaub ich habs :)

Wenn ich das richtig sehe war das Problem bei mir folgendes:
1) Ich rufe InitWinio auf
2) SOFORT danach mache ich meine Abfrage(n)
3) ShutdownWinio

ABER: Die DLL war bei 2. anscheinend noch garnicht geladen! Daher die Fehler.
Jetzt, nachdem ich da eine kleine Wartepause eingebaut habe, liefert der Pointer mal ausnahmsweise einen Wert zurück :)
So, jetzt mal gucken, wie es von nun an weitergeht.

Tom Servo
2003-08-30, 18:57:15
Redest du jetzt eigentlch von den Original-WinIO Funktionen? Also die, welche bei mir nicht funktioniert haben. Die gehen jetzt mit der Pause?

Da ja bereits InitializeWinIO() eine DLL Funktion ist, sollte die DLL schon geladen sein, und auch wenn nicht, würde der Prozess einfach so lange von Windows schlafen gelegt bis die DLL geladen ist. Bist du da sicher, mit der notwendigen Pause? Das wäre ein Zeichen, dass irgendwas faul ist. Und das wäre bei einem Treiber nicht so gut.

btw: Habe jetzt mal auch InitializeWinIO() und ShutDownWinIO() angesehen und debuggt, und diese Funktionen rufen von sich aus bereits InstallWinIODriver() bzw. RemoveWinIODriver() auf.

Man muss also einfach nur WinIO.sys im Programmverzeichnis haben und um das Installieren/Deinstallieren des Treibers muss man sich dann gar nicht kümmern. InstallWinIODriver() braucht man wohl nur, wenn WinIO.sys irgendwo anders ist und man den Pfad angeben möchte.

Gast
2003-08-30, 19:06:01
stimmt, da hast du recht .. eigentlich sollte die geladen sein, hab ich nicht dran gedacht.
Jedenfalls geht es tatsächlich nur mit einer Pause (ich mache vorher einfach ein readln ...). Ohne diese funktioniert es nicht.

Gast
2003-08-30, 19:08:34
ach so und nein, ich rede von deinen funktionen.

Gast
2003-08-30, 19:59:39
Hab mir ne kleine Testumgebung gebastelt ... wie geil, das funktioniert echt :D :D :D

Ich dank dir ganz doll für die Hilfe!

Jetzt gehts ans portieren der ganzen Funktionen nach Delphi.

Kennung Eins
2003-08-30, 20:38:10
. (sorry)

Tom Servo
2003-08-30, 20:54:08
Du könnstest auch alternativ das Interface der nvclock-backend.lib nach Pascal umsetzen. Darüber hättest du Zugriff auf die nvclock Funktionen, aber auch direkt auf die bereits gemappten Registerbereiche.

Ich könnte mittels Wrapper auch einen Header zur Verfügung stellen der nur mit einfachen Prozeduren auskommt, also ohne das mit Funktionszeigern gespickte nv_card Objekt.

Wäre recht einfach zu machen und es wäre ohnehin sinnvoll die Backend-Bibliothek auch für andere Sprachen als C leicht benutzbar zu haben.

Ein Nutzer von Datei-Funktionen greift ja auch nicht direkt auf Funktionspointer in irgendwelchen File-Objekten zu.

Tom Servo
2003-08-30, 23:13:13
Habe mal eine DLL zur Benutzung des Backends gemacht: http://home.tiscalinet.de/bertw/proj/tdprof/misc/nvclock-dll-dist.zip.

Habe mir gedacht, dass eine statische Link-Library wegen der ganzen C-Library Funktionen vielleicht nicht so gut mit anderen Sprachen benutzbar ist.

Man muss nur WinIO.sys, WinIO.dll und nvclock.dll im Programm-Verzeichnis haben und kann nach nvc_init() und nvc_set_card() die übrigen nvc_-Funktionen aus dem Headerfile sofort benutzen. Also ohne irgendwas direkt mit WinIO zu tun zu haben.


#ifdef NVCLOCKDLL_EXPORTS
#define NVC_API __declspec(dllexport)
#else
#define NVC_API __declspec(dllimport)
#endif

#ifdef __cplusplus
extern "C" {
#endif

/* Example:
...
nvc_init(); // init
nvc_set_card(0); // set current card to the first one
...now use other functions from this header...
*/

/* init / card select */
NVC_API int nvc_init(void); /* init and returns number of found cards */
NVC_API void nvc_set_card(int number); /* choose current card. */
NVC_API void nvc_leave(void); /* free all used resources */

/* Memory info */
NVC_API char * nvc_get_memory_type(void); /* Memory type: SDR/DDR */
NVC_API unsigned nvc_get_memory_width(void); /* Memory width 64bit or 128bit */
NVC_API unsigned nvc_get_memory_size(void); /* Amount of memory between 4 and 128 MB */

/* AGP info */
NVC_API char * nvc_get_bus_type(void); /* Bus type: AGP/PCI */
NVC_API unsigned nvc_get_agp_rate(void); /* Current AGP rate: 1, 2, 4 or 8*/
NVC_API char * nvc_get_agp_status(void); /* Current AGP status: Enabled/Disabled */
NVC_API char * nvc_get_fw_status(void); /* Current FW status: Enabled/Disabled */
NVC_API char * nvc_get_sba_status(void); /* Current SBA status: Enabled/Disabled */
NVC_API char * nvc_get_supported_agp_rates(void); /* Supported AGP rates */

/* Overclocking */
NVC_API float nvc_get_gpu_speed(void);
NVC_API void nvc_set_gpu_speed(unsigned nvclk);
NVC_API float nvc_get_memory_speed(void);
NVC_API void nvc_set_memory_speed(unsigned memclk);
NVC_API void nvc_reset_speeds(void);


/* Register Space */
NVC_API unsigned *nvc_get_regspace_extdev(void);
NVC_API unsigned *nvc_get_regspace_fb(void);
NVC_API unsigned *nvc_get_regspace_mc(void);
NVC_API unsigned *nvc_get_regspace_ramdac(void);

#ifdef __cplusplus
}
#endif

Gast
2003-08-31, 15:47:50
Wart mal, ich bin grad dabei, das hinzukriegen :) Erst mal gucken. Auslesen funktioniert schon prächtig, setzen hab ich aber noch nicht probiert.

Gleich mal ne Frage:
Warum schreibt man "0x0/4" anstelle von "0" bzw "0x4/4" anstelle von "1"? fprintf(fp, "mpll=%08x\n", PRAMDAC[0x4/4]);
fprintf(fp, "nvpll=%08x\n", PRAMDAC[0x0/4]);Das ist mir total unklar.

Und noch was:nv_card.base_freq = (nv_card.PEXTDEV[0x0000/4] & 0x40) ? 14318 : 13500;normalerweise bedeutet x = a ? b : c folgendes:
Wenn Bedingung a = b erfüllt, dann ist x = b. Ansonsten ist x = c.
Aber was ist bei "(nv_card.PEXTDEV[0x0000/4] & 0x40)" die Bedingung ??? Das ist doch einfach nur eine Und-Verknüpfung, wie kann man daraus eine Bedingung ableiten?
Oder ganz einfach: Wie kann man das noch ausdrücken? :)

ScottManDeath
2003-08-31, 16:08:48
nv_card.base_freq = (nv_card.PEXTDEV[0x0000/4] & 0x40) ? 14318 : 13500;


damit wird geprüft ob ein bit [im array nv_card.PEXTDEV] gesetzt ist, in dem fall das bit 0x40 = 64 = 01000000.

ist es gesetzt ist der ausdruck (nv_card.PEXTDEV[0x0000/4] & 0x40) != 0 , ansonsten 0. es wird implizit als bool gecastest. so mit kannst du dann auch den ?: operator nutzen

Tom Servo
2003-08-31, 16:43:59
Das mit dem dividieren durch 4 wird den Sinn haben, dass der Programmierer die Registerbereiche als Integer-Array definiert hat aber wohl die Offsets für ein Byte-Array im Kopf hatte.

Würde man das ganze einfach in ein char-Array umcasten, dann würde man das /4 weglassen können, da ja ein char nur ein Viertel der Bit-Grösse hat. Ich hätte vielleicht einfach ein char Array genommen aber sowieso auch keine Literale benutzt, sondern jeden Offset einen Namen gegeben, wenn auch nur offset_42, falls mir momentan kein Name einfallen sollte.

In C ist der & Operator ein bitweises Und. Das normale Und ist der && Operator. Genauso auch beim Or (| vs ||). Beim bitweisen Und ist das Ergebnis ein Wert der nur an den Bit-Positionen 1 hat wo beide Operanden 1 hatten. Man kann das Ergebnis dann in einem if() benutzen, weil in C jede Zahl ungleich 0 auch "True" ist.

edit: Vielleicht war es auch in einer älteren nvclock Version oder einem Vorläuferprogramm mal ein Byte-Array und das dividieren durch 4 ist nur übergangsweise. Ein Integer-Array wird wohl wirklich besser sein, da die Register wohl real sicherlich in 32bit gegliedert sind.

Sieht man auch beim Linux Header der Radeon, dass 32bit benutzt werden und ist in den Hardwaredocs ja auch immer so:


#define RADEON_CRTC2_GEN_CNTL 0x03f8
# define RADEON_CRTC2_DBL_SCAN_EN (1 << 0)
# define RADEON_CRTC2_INTERLACE_EN (1 << 1)
# define RADEON_CRTC2_SYNC_TRISTAT (1 << 4)
# define RADEON_CRTC2_HSYNC_TRISTAT (1 << 5)
# define RADEON_CRTC2_VSYNC_TRISTAT (1 << 6)
# define RADEON_CRTC2_CRT2_ON (1 << 7)
# define RADEON_CRTC2_ICON_EN (1 << 15)
# define RADEON_CRTC2_CUR_EN (1 << 16)
# define RADEON_CRTC2_CUR_MODE_MASK (7 << 20)
# define RADEON_CRTC2_DISP_DIS (1 << 23)
# define RADEON_CRTC2_EN (1 << 25)
# define RADEON_CRTC2_DISP_REQ_EN_B (1 << 26)
# define RADEON_CRTC2_HSYNC_DIS (1 << 28)
# define RADEON_CRTC2_VSYNC_DIS (1 << 29)



Also würde ich Konstanten definieren wie:

#define NVIDIA_RAMDAC_0x0000 0x0000
#define NVIDIA_RAMDAC_0x0000_BIT_0 (1 << 0)

Da kann man dann später mit einem Texteditor einen vernünftigen Namen verpassen.

Gast
2003-08-31, 17:37:55
Alles klar, soweit sind alle Fragen geklärt.
Dann will ich meine Testapp mal erweitern.

Ich dank euch beiden!