mirror of
https://github.com/gryf/wmtemp.git
synced 2025-12-18 03:50:27 +01:00
Attempt to use nvclock code for reading GPU temperature, since running
nvidia-settings couple of times for a second is highly inefficient.
This commit is contained in:
606
nvclock/config.c
Normal file
606
nvclock/config.c
Normal file
@@ -0,0 +1,606 @@
|
||||
/* NVClock 0.8 - Linux overclocker for NVIDIA cards
|
||||
*
|
||||
* site: http://nvclock.sourceforge.net
|
||||
*
|
||||
* Copyright(C) 2001-2007 Roderick Colenbrander
|
||||
*
|
||||
* 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 <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "backend.h"
|
||||
#include "nvclock.h"
|
||||
|
||||
static void insert_entry(cfg_entry **cfg, cfg_entry *entry);
|
||||
|
||||
/* Free the config file structure */
|
||||
void destroy(cfg_entry **cfg)
|
||||
{
|
||||
cfg_entry *pCfg, *pPrevious;
|
||||
|
||||
pCfg = *cfg;
|
||||
while(pCfg)
|
||||
{
|
||||
pPrevious = pCfg;
|
||||
free(pPrevious->name);
|
||||
free(pPrevious->section);
|
||||
pCfg = pCfg->next;
|
||||
free(pPrevious);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Add a new config file entry section.name=value */
|
||||
void add_entry(cfg_entry **cfg, char *section, char *name, int value)
|
||||
{
|
||||
cfg_entry *entry;
|
||||
|
||||
entry = (cfg_entry*)calloc(1, sizeof(cfg_entry));
|
||||
entry->name = (char*)strdup(name);
|
||||
entry->section = (char*)strdup(section);
|
||||
entry->value = value;
|
||||
entry->next = NULL;
|
||||
|
||||
insert_entry(cfg, entry);
|
||||
}
|
||||
|
||||
|
||||
/* Change the value of an entry or add an entry when it doesn't exist */
|
||||
void change_entry(cfg_entry **cfg, char *section, char *name, int value)
|
||||
{
|
||||
cfg_entry *entry = lookup_entry(cfg, section, name);
|
||||
|
||||
/* When an entry is found update it */
|
||||
if(entry)
|
||||
{
|
||||
entry->value=value;
|
||||
}
|
||||
/* Create a new entry */
|
||||
else
|
||||
{
|
||||
add_entry(cfg, section, name, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Insert an entry into the config file structure */
|
||||
static void insert_entry(cfg_entry **cfg, cfg_entry *entry)
|
||||
{
|
||||
cfg_entry *pCfg = NULL;
|
||||
cfg_entry *pPrev = NULL;
|
||||
cfg_entry *pNext = NULL;
|
||||
|
||||
/* When the cfg list is still empty, add the first entry */
|
||||
if(!*cfg)
|
||||
{
|
||||
*cfg = entry;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Sort the list by section and add the entry */
|
||||
for(pCfg = *cfg; pCfg != NULL; pPrev=pCfg, pCfg = pCfg->next, pNext = pCfg ? pCfg->next : NULL )
|
||||
{
|
||||
/* Check if the entry belongs to a section below the current section */
|
||||
if(strcmp(entry->section, pCfg->section) < 0)
|
||||
{
|
||||
if(!pPrev)
|
||||
{
|
||||
*cfg = entry;
|
||||
entry->next = pCfg;
|
||||
}
|
||||
else
|
||||
{
|
||||
pPrev->next = entry;
|
||||
entry->next = pCfg;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(strcmp(entry->section, pCfg->section) == 0)
|
||||
{
|
||||
/* The entry needs to be placed before the current entry */
|
||||
if(strcmp(entry->name, pCfg->name) < 0)
|
||||
{
|
||||
if(!pPrev)
|
||||
{
|
||||
*cfg = entry;
|
||||
entry->next = pCfg;
|
||||
}
|
||||
else
|
||||
{
|
||||
pPrev->next = entry;
|
||||
entry->next = pCfg;
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* When there's a next entry, check if it belongs to the same section or
|
||||
/ else add the option to the current section.
|
||||
*/
|
||||
if(pNext)
|
||||
{
|
||||
/* The sections don't match, so add the option to the current one */
|
||||
if(strcmp(entry->section, pNext->section) != 0)
|
||||
{
|
||||
pCfg->next = entry;
|
||||
entry->next = pNext;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Entry should become the last one */
|
||||
if(!pCfg->next)
|
||||
{
|
||||
pCfg->next = entry;
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Look up section.name */
|
||||
cfg_entry* lookup_entry(cfg_entry **cfg, char *section, char *name)
|
||||
{
|
||||
cfg_entry *entry = *cfg;
|
||||
|
||||
while(entry)
|
||||
{
|
||||
/* If everything matches, the option is found */
|
||||
if(!strcmp(entry->section, section) && !strcmp(entry->name, name))
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
|
||||
entry = (cfg_entry*)entry->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Helper function that splits lines of the form section.name=value */
|
||||
static int split_line(const char *line, char **section, char **name, char **value)
|
||||
{
|
||||
char *a, *b;
|
||||
|
||||
if(!(a = strchr(line, '.')))
|
||||
return 0;
|
||||
|
||||
/* overwrite '.' with '\0' */
|
||||
a[0] = '\0';
|
||||
*section = (char*)strdup(line);
|
||||
|
||||
a++;
|
||||
|
||||
if(!(b = strchr(a, '=')))
|
||||
return 0;
|
||||
/* overwrite '=' with '\0' */
|
||||
b[0] = '\0';
|
||||
*name = (char*)strdup(a);
|
||||
b++;
|
||||
|
||||
/* overwrite '\n' with '\0' if '\n' present */
|
||||
if((a = strchr(b, '\n')))
|
||||
{
|
||||
a[0] = '\0';
|
||||
}
|
||||
*value = (char*)strdup(b);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Reads the config file from disc and stores it in cfg */
|
||||
int read_config(cfg_entry **cfg, char *file)
|
||||
{
|
||||
char line[80];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(file, "r");
|
||||
|
||||
if(!fp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while(fgets(line, 80, fp) != NULL)
|
||||
{
|
||||
char *name, *section, *value;
|
||||
cfg_entry *entry = NULL;
|
||||
if((line[0] == '#'))
|
||||
continue; /* Skip comments */
|
||||
|
||||
if((line[0] == '<'))
|
||||
continue; /* There's no section on this line */
|
||||
|
||||
if((line[0] == '\n'))
|
||||
continue; /* There's no section on this line */
|
||||
|
||||
if(strchr(line, '=') == NULL)
|
||||
continue;
|
||||
|
||||
if(!split_line(line, §ion, &name, &value))
|
||||
{
|
||||
free(name);
|
||||
free(section);
|
||||
free(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Add the entry when it doesn't exist; we don't want double options*/
|
||||
if(!(entry = lookup_entry(cfg, section, name)))
|
||||
{
|
||||
/* Use strtoul instead of atoi as on some nv40 cards we get issues regarding signed/unsigned */
|
||||
add_entry(cfg, section, name, strtoul(value, (char**)NULL, 10));
|
||||
}
|
||||
|
||||
free(name);
|
||||
free(section);
|
||||
free(value);
|
||||
}
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Looks if a config file exists and then makes sure it will be read and parsed.
|
||||
/ When no config file exists create it.
|
||||
*/
|
||||
int open_config()
|
||||
{
|
||||
char *file = NULL;
|
||||
char *home = NULL;
|
||||
struct stat stat_buf;
|
||||
|
||||
if(!(home = getenv("HOME")))
|
||||
{
|
||||
/* error */
|
||||
}
|
||||
|
||||
nvclock.path = malloc(strlen(home) + strlen("/.nvclock") +1);
|
||||
sprintf(nvclock.path, "%s/.nvclock", home);
|
||||
file = malloc(strlen(nvclock.path) + strlen("/config") +1);
|
||||
sprintf(file, "%s/config", nvclock.path);
|
||||
|
||||
/* Check if our ~/.nvclock directory exists if not create it */
|
||||
if(stat(nvclock.path, &stat_buf))
|
||||
{
|
||||
if(mkdir(nvclock.path, 0755))
|
||||
{
|
||||
char buf[80];
|
||||
sprintf(buf, "Can't create '%s'. Do you have sufficient permissions?\n", nvclock.path);
|
||||
set_error_str(buf);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if .nvclock really is a directory. For some users it was a file and this led to a segfault. */
|
||||
if(!S_ISDIR(stat_buf.st_mode))
|
||||
{
|
||||
char buf[80];
|
||||
sprintf(buf, "Can't open '%s'. Is it really a directory?\n", nvclock.path);
|
||||
set_error_str(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If there's no config or if the config is corrupt create a new one */
|
||||
if(!parse_config(file))
|
||||
{
|
||||
create_config(file);
|
||||
}
|
||||
free(file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void write_config(cfg_entry *cfg, char *file)
|
||||
{
|
||||
FILE *fp = fopen(file, "w+");
|
||||
cfg_entry *pCfg = NULL;
|
||||
|
||||
pCfg = cfg;
|
||||
|
||||
fprintf(fp, "#This is NVClock's config file. Don't edit the hw and general section!\n");
|
||||
while(pCfg != NULL)
|
||||
{
|
||||
fprintf(fp, "%s.%s=%u\n", pCfg->section, pCfg->name, pCfg->value);
|
||||
pCfg = pCfg->next;
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
|
||||
/* Some basic check to see if frequencies can be valid */
|
||||
static int validate_clock(int arch, int freq)
|
||||
{
|
||||
if(arch & (NV5))
|
||||
{
|
||||
if((freq > 75) && (freq < 250))
|
||||
return 1;
|
||||
}
|
||||
/* Geforce2/2MX/4MX */
|
||||
else if(arch & (NV1X))
|
||||
{
|
||||
if((freq > 125) && (freq < 500))
|
||||
return 1;
|
||||
}
|
||||
/* Geforce3/3Ti/4Ti/FX5200/FX5500 */
|
||||
else if(arch & (NV2X))
|
||||
{
|
||||
if((freq > 200) && (freq < 800))
|
||||
return 1;
|
||||
}
|
||||
/* GeforceFX */
|
||||
else if(arch & (NV3X))
|
||||
{
|
||||
if((freq > 250) && (freq < 1100))
|
||||
return 1;
|
||||
}
|
||||
/* Geforce6/7/8*/
|
||||
else if(arch & (NV4X | NV5X))
|
||||
{
|
||||
if((freq > 250) && (freq < 1200))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Some basic check to verify if a stored pll can be correct */
|
||||
static int validate_pll(int arch, int base_freq, unsigned int pll, unsigned int pll2)
|
||||
{
|
||||
int freq;
|
||||
|
||||
if(arch & (NV5 | NV1X | NV2X))
|
||||
{
|
||||
freq = (int)GetClock(base_freq, pll);
|
||||
if(validate_clock(arch, freq))
|
||||
return 1;
|
||||
}
|
||||
else if(arch & (NV3X))
|
||||
{
|
||||
freq = (int)GetClock_nv30(base_freq, pll, pll2);
|
||||
if(validate_clock(arch, freq))
|
||||
return 1;
|
||||
}
|
||||
else if(arch & (NV4X))
|
||||
{
|
||||
freq = (int)GetClock_nv40(base_freq, pll, pll2);
|
||||
if(validate_clock(arch, freq))
|
||||
return 1;
|
||||
}
|
||||
else if(arch & (NV5X))
|
||||
{
|
||||
freq = (int)GetClock_nv50(base_freq, pll, pll2);
|
||||
if(validate_clock(arch, freq))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Parse the config file and do something with its contents */
|
||||
int parse_config(char *file)
|
||||
{
|
||||
int i;
|
||||
cfg_entry *general;
|
||||
|
||||
if(!read_config(&nvclock.cfg, file))
|
||||
return 0;
|
||||
|
||||
if((general = (cfg_entry*)lookup_entry(&nvclock.cfg, "general", "cards")) == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check if we have the same number of videocards as before */
|
||||
if(general->value != nvclock.num_cards)
|
||||
return 0;
|
||||
|
||||
/* Walk through all detected cards */
|
||||
for(i=0; i < nvclock.num_cards; i++)
|
||||
{
|
||||
cfg_entry *entry;
|
||||
char section[4];
|
||||
char filename[80];
|
||||
int base_freq=0;
|
||||
struct stat tmp;
|
||||
struct nvbios *bios;
|
||||
|
||||
if(!set_card_info(i))
|
||||
return 0; /* Make us fail; set_card_info already set the error */
|
||||
|
||||
sprintf(section, "hw%d", i);
|
||||
|
||||
if((entry = (cfg_entry*)lookup_entry(&nvclock.cfg, section, "card")))
|
||||
{
|
||||
/* The order in wich the detected cards are listed should match the order of the ones in the config file. Mask the last digit for device_id modding purposes.*/
|
||||
if((nvclock.card[i].device_id & 0xfff0) != (entry->value & 0xfff0))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if((entry = (cfg_entry*)lookup_entry(&nvclock.cfg, section, "basefreq")))
|
||||
{
|
||||
base_freq = entry->value;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
/* The bios works differently on ppc and other architectures; it is also stored in a different place */
|
||||
#if defined(__i386__) || defined(__ia64__) || defined(__x86_64__)
|
||||
/* During this stage we also need to parse the nvidia bios.
|
||||
/ This can't be done in probe_devices as it depends on the config file
|
||||
/ which might not exist at that time yet
|
||||
*/
|
||||
sprintf(filename, "%s/bios%d.rom", nvclock.path, i);
|
||||
|
||||
/* Redump the bios when the file doesn't exist */
|
||||
if(stat(filename, &tmp))
|
||||
return 0;
|
||||
|
||||
/* Read the bios. Note the result can be NULL in case the
|
||||
/ bios is corrupt. We don't redump the bios because the bios
|
||||
/ is not dumpable on some systems. For example on various laptops
|
||||
/ the bios is stored at a different place not reachable by us.
|
||||
*/
|
||||
bios = read_bios(filename); /* GCC 4.0.1 (what about others?) doesn't like it when we directly do nclock.card[i].bios = readbios(filename); works fine without optimizations */
|
||||
nvclock.card[i].bios = bios;
|
||||
#else
|
||||
nvclock.card[i].bios = NULL;
|
||||
#endif
|
||||
|
||||
if((entry = (cfg_entry*)lookup_entry(&nvclock.cfg, section, "mpll")))
|
||||
nvclock.card[i].mpll = entry->value;
|
||||
else
|
||||
/* corrupted config file */
|
||||
return 0;
|
||||
|
||||
if((entry = (cfg_entry*)lookup_entry(&nvclock.cfg, section, "nvpll")))
|
||||
nvclock.card[i].nvpll = entry->value;
|
||||
else
|
||||
return 0;
|
||||
|
||||
/* NV31, NV4X and NV5X cards have extra pll registers; in case of NV30 the second pll is ignored but that happens in GetClock_nv30 */
|
||||
if(nv_card->arch & (NV3X | NV4X | NV5X))
|
||||
{
|
||||
if((entry = (cfg_entry*)lookup_entry(&nvclock.cfg, section, "mpll2")))
|
||||
nvclock.card[i].mpll2 = entry->value;
|
||||
else
|
||||
return 0;
|
||||
|
||||
if((entry = (cfg_entry*)lookup_entry(&nvclock.cfg, section, "nvpll2")))
|
||||
nvclock.card[i].nvpll2 = entry->value;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Do some basic check on mpll/nvpll to see if they can be correct */
|
||||
if(!validate_pll(nvclock.card[i].arch, base_freq, nvclock.card[i].mpll, nvclock.card[i].mpll2))
|
||||
return 0;
|
||||
if(!validate_pll(nvclock.card[i].arch, base_freq, nvclock.card[i].nvpll, nvclock.card[i].nvpll2))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the nv_card object else things might go wrong */
|
||||
unset_card();
|
||||
|
||||
/* Return succes */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Create a config file based on info we get from the low-level part of nvclock */
|
||||
int create_config(char *file)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(nvclock.cfg)
|
||||
{
|
||||
destroy(&nvclock.cfg);
|
||||
nvclock.cfg = NULL;
|
||||
}
|
||||
add_entry(&nvclock.cfg, "general", "cards", nvclock.num_cards);
|
||||
|
||||
/* Write the config file */
|
||||
for(i=0; i < nvclock.num_cards; i++)
|
||||
{
|
||||
char section[4];
|
||||
char bios[80];
|
||||
int base_freq;
|
||||
|
||||
/* Set the nv_card object to the card; Note this is a bit basic; function pointers can't be used */
|
||||
if(!set_card_info(i))
|
||||
return 0; /* Make us fail; set_card_info already set the error */
|
||||
|
||||
sprintf(section, "hw%d", i);
|
||||
add_entry(&nvclock.cfg, section, "card", nv_card->device_id);
|
||||
|
||||
#if defined(__i386__) || defined(__ia64__) || defined(__x86_64__)
|
||||
/* needs to be changed to dump the file in the home dir; further we need some CRC check */
|
||||
sprintf(bios, "%s/bios%d.rom", nvclock.path, i);
|
||||
dump_bios(bios);
|
||||
nvclock.card[i].bios = read_bios(bios);
|
||||
#else
|
||||
nvclock.card[i].bios = NULL;
|
||||
#endif
|
||||
|
||||
base_freq = (nv_card->PEXTDEV[0x0000/4] & 0x40) ? 14318 : 13500;
|
||||
if(nv_card->arch & (NV17 | NV25 | NV3X | NV4X | NV5X))
|
||||
{
|
||||
if (nv_card->PEXTDEV[0x0000/4] & (1<<22))
|
||||
base_freq = 27000;
|
||||
}
|
||||
add_entry(&nvclock.cfg, section, "basefreq", base_freq);
|
||||
|
||||
/* TNT(1/2) and Geforce(1/2/3/4) */
|
||||
if(nv_card->arch & (NV5 | NV1X | NV2X))
|
||||
{
|
||||
add_entry(&nvclock.cfg, section, "mpll", nv_card->PRAMDAC[0x504/4]);
|
||||
add_entry(&nvclock.cfg, section, "nvpll", nv_card->PRAMDAC[0x500/4]);
|
||||
nvclock.card[i].nvpll = nv_card->PRAMDAC[0x500/4];
|
||||
nvclock.card[i].mpll = nv_card->PRAMDAC[0x504/4];
|
||||
}
|
||||
/* GeforceFX 5200/5500/5600/5700/5800/5900, note that FX5600/5700 require two PLLs */
|
||||
else if(nv_card->arch & NV3X)
|
||||
{
|
||||
add_entry(&nvclock.cfg, section, "mpll", nv_card->PRAMDAC[0x504/4]);
|
||||
add_entry(&nvclock.cfg, section, "mpll2", nv_card->PRAMDAC[0x574/4]);
|
||||
add_entry(&nvclock.cfg, section, "nvpll", nv_card->PRAMDAC[0x500/4]);
|
||||
add_entry(&nvclock.cfg, section, "nvpll2", nv_card->PRAMDAC[0x570/4]);
|
||||
nvclock.card[i].mpll = nv_card->PRAMDAC[0x504/4];
|
||||
nvclock.card[i].mpll2 = nv_card->PRAMDAC[0x574/4];
|
||||
nvclock.card[i].nvpll = nv_card->PRAMDAC[0x500/4];
|
||||
nvclock.card[i].nvpll2 = nv_card->PRAMDAC[0x570/4];
|
||||
}
|
||||
/* Geforce6/7 */
|
||||
else if(nv_card->arch & NV4X)
|
||||
{
|
||||
add_entry(&nvclock.cfg, section, "mpll", nv_card->PMC[0x4020/4]);
|
||||
add_entry(&nvclock.cfg, section, "mpll2", nv_card->PMC[0x4024/4]);
|
||||
add_entry(&nvclock.cfg, section, "nvpll", nv_card->PMC[0x4000/4]);
|
||||
add_entry(&nvclock.cfg, section, "nvpll2", nv_card->PMC[0x4004/4]);
|
||||
nvclock.card[i].mpll = nv_card->PMC[0x4020/4];
|
||||
nvclock.card[i].mpll2 = nv_card->PMC[0x4024/4];
|
||||
nvclock.card[i].nvpll = nv_card->PMC[0x4000/4];
|
||||
nvclock.card[i].nvpll2 = nv_card->PMC[0x4004/4];
|
||||
}
|
||||
/* Geforce8 */
|
||||
else if(nv_card->arch & NV5X)
|
||||
{
|
||||
add_entry(&nvclock.cfg, section, "mpll", nv_card->PMC[0x4008/4]);
|
||||
add_entry(&nvclock.cfg, section, "mpll2", nv_card->PMC[0x400c/4]);
|
||||
add_entry(&nvclock.cfg, section, "nvpll", nv_card->PMC[0x4028/4]);
|
||||
add_entry(&nvclock.cfg, section, "nvpll2", nv_card->PMC[0x402c/4]);
|
||||
nvclock.card[i].mpll = nv_card->PMC[0x4008/4];
|
||||
nvclock.card[i].mpll2 = nv_card->PMC[0x400c/4];
|
||||
nvclock.card[i].nvpll = nv_card->PMC[0x4028/4];
|
||||
nvclock.card[i].nvpll2 = nv_card->PMC[0x402c/4];
|
||||
}
|
||||
|
||||
unset_card();
|
||||
}
|
||||
write_config(nvclock.cfg, file);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user