/* NVClock 0.8 - Linux overclocker for NVIDIA cards
 * 
 * Copyright(C) 2001-2005 Roderick Colenbrander
 *
 * site: http://nvclock.sourceforge.net
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#include "nvclock.h"
#include "backend.h"

#include "WinIo.h"

void *lib_winio = NULL;
WinIO winio = NULL;
int (__stdcall *pInitializeWinIo)();
int (__stdcall *pwinio_get_pci_header)(void* winio_handle, struct winio_pci_common_header *buf);
void* (__stdcall *pMapIO)(void* winio_handle, unsigned long phys_addr, unsigned long size); 
void (__stdcall *pUnMapIO)(void* winio_handle, void* virt_uaddr);

NVClock nvclock;
NVCard *nv_card = NULL;

int init_nvclock()
{
    void *lib_winio = LoadLibrary("WinIo.dll");
    if(lib_winio == NULL)
    {
	set_error_str("Can't open WinIo.dll\n");
	return 0;
    }
	
    pInitializeWinIo = GetProcAddress(lib_winio, "InitializeWinIo");
    pMapIO = GetProcAddress(lib_winio, "MapIO");
    pUnMapIO = GetProcAddress(lib_winio, "UnMapIO");
    pwinio_get_pci_header = GetProcAddress(lib_winio, "winio_get_pci_header");
	
    winio = pInitializeWinIo();
	
    if(winio == NULL)
    {
	set_error_str("Can't initialize WinIO\n");
    	return 0;
    }
    
    /* Detect all nvidia cards; this needs to be done before creating directory and config file as that code needs card access */
    if(!probe_devices())
    {
	/* probe_devices takes care of the error as it isn't certain it failed because of there are no nvidia cards */
	return 0;
    }

    if(!open_config())
	return 0;
		
    return 1;	
}

static int probe_devices()
{
    int dev, irq, reg_addr, i=0;

    struct winio_pci_common_header buf = {0, };
    while(pwinio_get_pci_header(winio, &buf))
    {
        /* Check if the card contains an Nvidia chipset */	
        if(buf.VendorID == 0x10de)
	{
	    /*
	    When we enter this block of code we know that the device contains some 
	    chip designed by Nvidia. In the past Nvidia only produced videochips but
	    now they also make various other devices. Because of this we need to find
	    out if the device is a videocard or not. There are two ways to do this. We can
	    create a list of all Nvidia videochips or we can check the pci header of the device.
	    We will read the pci header from /proc/bus/pci/(bus)/(function).(device). When
	    the card is in our card database we report the name of the card and else we say
	    it is an unknown card.
	    */
	    if(buf.BaseClass != 0x3)
	        continue;

	    nvclock.card[i].device_id = buf.DeviceID;
	    nvclock.card[i].arch = get_gpu_arch(nvclock.card[i].device_id);
	    nvclock.card[i].number = i;
	    nvclock.card[i].card_name = (char*)get_card_name(nvclock.card[i].device_id, &nvclock.card[i].gpu);
	    nvclock.card[i].reg_address = buf.BaseAddresses[0];
//	    nvclock.card[i].devbusfn = devbusfn;
	    nvclock.card[i].irq = buf.InterruptLine;
	    nvclock.card[i].state = 0;

	    i++;
    	}
    }

    if(i==0)
    	set_error(NV_ERR_NO_DEVICES_FOUND);

    nvclock.num_cards = i;
    return 1;
}

int32_t pciReadLong(unsigned short devbusfn, long offset)
{
    return -1;
}

int map_mem(const char *dev_name)
{
    /* Map the registers of the nVidia chip */
    nv_card->PEXTDEV = pMapIO(winio, nv_card->reg_address + 0x101000, 0x1000);
    nv_card->PFB     = pMapIO(winio, nv_card->reg_address + 0x100000, 0x1000);
    /* normally pmc is till 0x2000 but extended it for nv40 */
    nv_card->PMC     = pMapIO(winio, nv_card->reg_address + 0x000000, 0xffff);
    nv_card->PCIO    = pMapIO(winio, nv_card->reg_address + 0x601000, 0x2000);
    nv_card->PRAMDAC = pMapIO(winio, nv_card->reg_address + 0x680000, 0x2000);
    nv_card->PROM    = pMapIO(winio, nv_card->reg_address + 0x300000, 0xffff);

    nv_card->mem_mapped = 1;

    return 1;
}

void unmap_mem()
{
    pUnMapIO(winio, (void*)nv_card->PEXTDEV);
    pUnMapIO(winio, (void*)nv_card->PFB);
    pUnMapIO(winio, (void*)nv_card->PMC);
    pUnMapIO(winio, (void*)nv_card->PCIO);
    pUnMapIO(winio, (void*)nv_card->PRAMDAC);
    pUnMapIO(winio, (void*)nv_card->PROM);
    nv_card->mem_mapped = 0;
}


syntax highlighted by Code2HTML, v. 0.9.1