rendered paste body/* update should also submit card number !! */
#include <dbus/dbus-glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "nvclock.h"
#define NVCLOCKD_VERSION 1
static void lose (const char *fmt, ...) G_GNUC_NORETURN G_GNUC_PRINTF (1, 2);
static void lose_gerror (const char *prefix, GError *error) G_GNUC_NORETURN;
static void lose (const char *str, ...)
{
va_list args;
va_start (args, str);
vfprintf (stderr, str, args);
fputc ('\n', stderr);
va_end (args);
exit (1);
}
static void lose_gerror (const char *prefix, GError *error)
{
lose ("%s: %s", prefix, error->message);
}
typedef struct NVClockObject NVClockObject;
typedef struct NVClockObjectClass NVClockObjectClass;
GType nvclockd_get_type (void);
#define CAP_GPU_CLOCK (1<<0)
#define CAP_MEM_CLOCK (1<<1)
#define CAP_BOARD_TEMP (1<<2)
#define CAP_GPU_TEMP (1<<3)
#define CAP_DUTY_CYCLE (1<<4)
#define CAP_FANSPEED (1<<5)
#define CAP_SMARTDIMMER (1<<6)
typedef struct {
float gpu_clock;
float mem_clock;
int board_temp;
int gpu_temp;
float duty_cycle;
int fanspeed;
int smartdimmer;
} updates;
struct NVClockObject
{
GObject parent;
};
struct NVClockObjectClass
{
GObjectClass parent;
};
char *tmpname;
#define NVCLOCKD_TYPE_OBJECT (nvclockd_get_type ())
#define NVCLOCKD_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), NVCLOCKD_TYPE_OBJECT, NVClockObject))
#define NVCLOCKD_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NVCLOCKD_TYPE_OBJECT, NVClockObjectClass))
#define TEST_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), NVCLOCKD_TYPE_OBJECT))
#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NVCLOCKD_TYPE_OBJECT))
#define NVCLOCKD_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NVCLOCKD_TYPE_OBJECT, NVClockObjectClass))
G_DEFINE_TYPE(NVClockObject, nvclockd, G_TYPE_OBJECT)
gboolean nvclockd_get_cards (NVClockObject *obj, guint32 *cards, GError **error);
gboolean nvclockd_get_card_name (NVClockObject *obj, guint32 card, gchar **name, GError **error);
gboolean nvclockd_get_capabilities(NVClockObject *obj, guint32 card, guint32 *caps, GError **error);
gboolean nvclockd_get_gpu_clock (NVClockObject *obj, guint32 card, gdouble *clock, GError **error);
gboolean nvclockd_get_memory_clock (NVClockObject *obj, guint32 card, gdouble *clock, GError **error);
gboolean nvclockd_get_board_temperature (NVClockObject *obj, guint32 card, guint32 *temperature, GError **error);
gboolean nvclockd_get_gpu_temperature (NVClockObject *obj, guint32 card, guint32 *temperature, GError **error);
gboolean nvclockd_get_fan_duty_cycle (NVClockObject *obj, guint32 card, gdouble *duty_cycle, GError **error);
gboolean nvclockd_set_fan_duty_cycle (NVClockObject *obj, guint32 card, gdouble duty_cycle, gdouble *new_duty_cycle, GError **error);
gboolean nvclockd_get_fan_speed (NVClockObject *obj, guint32 card, guint32 *rpm, GError **error);
gboolean nvclockd_get_smartdimmer (NVClockObject *obj, guint32 card, guint32 *level, GError **error);
gboolean nvclockd_set_smartdimmer (NVClockObject *obj, guint32 card, guint32 level, guint32 *new_level, GError **error);
gboolean nvclockd_get_version (NVClockObject *obj, guint32 *version, GError **error);
#define NVCLOCKD_CHECK_CAPS(x,y) if(!(nv_card->caps & (x))) { \
g_set_error (error, g_quark_from_static_string ("error"), 0, y); \
nvclockd_unlock(); \
return FALSE; } while(0)
#define NVCLOCKD_GET_CARD(x) ((x>>29) & 0x3)
#define NVCLOCKD_SET_CARD(x,y) x |= ((y & 0x3) << 29)
enum
{
UPDATE_SIGNAL,
LAST_SIGNAL
};
static guint nvclockd_signals[LAST_SIGNAL] = { 0 };
#include "example-signal-emitter-glue.h"
static int lock = 0;
/* NVClock isn't threadsafe only one card can be accessed at a time, so we need some locking */
inline void nvclockd_lock()
{
while(lock)
{
g_usleep(1000);
}
lock = 1;
}
inline void nvclockd_unlock()
{
lock = 0;
}
static void nvclockd_init (NVClockObject *obj)
{
GError *error = NULL;
DBusGConnection *bus;
DBusGProxy *bus_proxy;
guint request_name_result;
dbus_g_object_type_install_info (NVCLOCKD_TYPE_OBJECT, &dbus_glib_nvclockd_object_info);
bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
if (!bus)
lose_gerror ("Couldn't connect to session bus", error);
bus_proxy = dbus_g_proxy_new_for_name (bus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error,
G_TYPE_STRING, "org.nvclock",
G_TYPE_UINT, 0,
G_TYPE_INVALID,
G_TYPE_UINT, &request_name_result,
G_TYPE_INVALID))
lose_gerror ("Failed to acquire org.nvclock", error);
dbus_g_connection_register_g_object (bus, "/org/nvclock/object", G_OBJECT (obj));
FILE *fp=fopen("/tmp/nvclockd.lock","w");
fclose(fp);
}
static void nvclockd_class_init (NVClockObjectClass *klass)
{
nvclockd_signals[UPDATE_SIGNAL] =
g_signal_new ("update_notification_event",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1, G_TYPE_UINT);
}
/* This function changes the internal nvclock nv_card object to the requested videocard.
* Further it does a lot of error handling.
*/
gboolean nvclockd_set_card(guint32 card, GError **error)
{
if(nvclock.num_cards == 0)
{
g_set_error (error, g_quark_from_static_string ("error"), 0, "no nvidia cards found in this system");
nvclockd_unlock();
return FALSE;
}
if(card >= nvclock.num_cards)
{
g_set_error (error, g_quark_from_static_string ("error"), 0, "invalid card");
nvclockd_unlock();
return FALSE;
}
if(card != nv_card->number)
{
if(!set_card(card))
{
gchar buf[80];
g_set_error (error, g_quark_from_static_string ("error"), 0, buf);
nvclockd_unlock();
return FALSE;
}
}
return TRUE;
}
/* Get the number of Nvidia videocards in the system */
gboolean nvclockd_get_cards (NVClockObject *obj, guint32 *cards, GError **error)
{
*cards = nvclock.num_cards;
return TRUE;
}
/* Get the card name */
gboolean nvclockd_get_card_name (NVClockObject *obj, guint32 card, gchar **name, GError **error)
{
nvclockd_lock();
if(!nvclockd_set_card(card, error))
return FALSE;
*name = g_strdup(nv_card->card_name);
nvclockd_unlock();
return TRUE;
}
/* Get the capabilities of the videocard. The function translates NVClock's internal capabilities to NVClockD capabilities. */
gboolean nvclockd_get_capabilities(NVClockObject *obj, guint32 card, guint32 *caps, GError **error)
{
int res = 0;
nvclockd_lock();
if(!nvclockd_set_card(card, error))
return FALSE;
res = CAP_GPU_CLOCK | CAP_MEM_CLOCK;
if(nv_card->caps & BOARD_TEMP_MONITORING)
res |= CAP_BOARD_TEMP;
if(nv_card->caps & GPU_TEMP_MONITORING)
res |= CAP_GPU_TEMP;
if(nv_card->caps & GPU_FANSPEED_MONITORING)
res |= CAP_DUTY_CYCLE;
if(nv_card->caps & I2C_FANSPEED_MONITORING)
res |= CAP_DUTY_CYCLE | CAP_FANSPEED;
if(nv_card->caps & SMARTDIMMER)
res |= CAP_SMARTDIMMER;
*caps = res;
nvclockd_unlock();
return TRUE;
}
/* Get the GPU clockspeed in MHz */
gboolean nvclockd_get_gpu_clock (NVClockObject *obj, guint32 card, gdouble *clock, GError **error)
{
nvclockd_lock();
if(!nvclockd_set_card(card, error))
return FALSE;
*clock = nv_card->get_gpu_speed();
nvclockd_unlock();
return TRUE;
}
/* Get the memory clockspeed in MHz */
gboolean nvclockd_get_memory_clock (NVClockObject *obj, guint32 card, gdouble *clock, GError **error)
{
nvclockd_lock();
if(!nvclockd_set_card(card, error))
return FALSE;
*clock = nv_card->get_memory_speed();
nvclockd_unlock();
return TRUE;
}
/* Get the temperature of the videocard in degrees Celsius. This is supported on most cards containing an external sensor chip. */
gboolean nvclockd_get_board_temperature (NVClockObject *obj, guint32 card, guint *temperature, GError **error)
{
nvclockd_lock();
if(!nvclockd_set_card(card, error))
return FALSE;
NVCLOCKD_CHECK_CAPS(BOARD_TEMP_MONITORING, "Board temperature monitoring isn't support on this card");
*temperature = nv_card->get_board_temp(nv_card->sensor);
nvclockd_unlock();
return TRUE;
}
/* Get the temperature of the GPU in degrees Celsius. This is supported on most Geforce6/7 cards and on cards containing sensor chips. */
gboolean nvclockd_get_gpu_temperature (NVClockObject *obj, guint32 card, guint32 *temperature, GError **error)
{
nvclockd_lock();
if(!nvclockd_set_card(card, error))
return FALSE;
NVCLOCKD_CHECK_CAPS(GPU_TEMP_MONITORING, "GPU temperature monitoring isn't support on this card");
*temperature = nv_card->get_gpu_temp(nv_card->sensor);
nvclockd_unlock();
return TRUE;
}
/* Get the duty cycle of the GPU fan in %. This is supported on most GeforceFX/6/7 cards and on most cards containing sensor chips. */
gboolean nvclockd_get_fan_duty_cycle (NVClockObject *obj, guint32 card, gdouble *duty_cycle, GError **error)
{
nvclockd_lock();
if(!nvclockd_set_card(card, error))
return FALSE;
NVCLOCKD_CHECK_CAPS(GPU_FANSPEED_MONITORING | I2C_FANSPEED_MONITORING, "Fan duty cycle monitoring isn't support on this card");
if(nv_card->caps & I2C_FANSPEED_MONITORING)
*duty_cycle = nv_card->get_i2c_fanspeed_pwm(nv_card->sensor);
else
*duty_cycle = nv_card->get_fanspeed();
nvclockd_unlock();
return TRUE;
}
/* Set the duty cycle of the GPU fan in %. This is supported on most GeforceFX/6/7 cards and on most cards containing sensor chips. */
gboolean nvclockd_set_fan_duty_cycle (NVClockObject *obj, guint32 card, gdouble duty_cycle, gdouble *new_duty_cycle, GError **error)
{
nvclockd_lock();
if(!nvclockd_set_card(card, error))
return FALSE;
NVCLOCKD_CHECK_CAPS(GPU_FANSPEED_MONITORING | I2C_FANSPEED_MONITORING, "Fan duty cycle adjusting isn't support on this card");
if(nv_card->caps & I2C_FANSPEED_MONITORING)
{
nv_card->set_i2c_fanspeed_pwm(nv_card->sensor, duty_cycle);
*new_duty_cycle = nv_card->get_i2c_fanspeed_pwm(nv_card->sensor);
}
else
{
nv_card->set_fanspeed(duty_cycle);
*new_duty_cycle = nv_card->get_fanspeed();
}
nvclockd_unlock();
return TRUE;
}
/* Get the speed of the GPU fan in rpm. This is supported on some Geforce cards using advanced sensor chips like the ADT7473/F75375S */
gboolean nvclockd_get_fan_speed (NVClockObject *obj, guint32 card, guint32 *rpm, GError **error)
{
nvclockd_lock();
if(!nvclockd_set_card(card, error))
return FALSE;
NVCLOCKD_CHECK_CAPS(I2C_FANSPEED_MONITORING, "Fanspeed monitoring isn't support on this card");
*rpm = nv_card->get_i2c_fanspeed_rpm(nv_card->sensor);
nvclockd_unlock();
return TRUE;
}
/* Get the Smartdimmer brightness level of the display. This is supported on some Geforce6/7 laptops */
gboolean nvclockd_get_smartdimmer (NVClockObject *obj, guint32 card, guint32 *level, GError **error)
{
nvclockd_lock();
if(!nvclockd_set_card(card, error))
return FALSE;
NVCLOCKD_CHECK_CAPS(SMARTDIMMER, "Smartdimmer isn't support on this card");
*level = nv_card->get_smartdimmer();
nvclockd_unlock();
return TRUE;
}
/* Set the Smartdimmer brightness level of the display. This is supported on some Geforce6/7 laptops */
gboolean nvclockd_set_smartdimmer (NVClockObject *obj, guint32 card, guint32 level, guint32 *new_level, GError **error)
{
nvclockd_lock();
if(!nvclockd_set_card(card, error))
return FALSE;
NVCLOCKD_CHECK_CAPS(SMARTDIMMER, "Smartdimmer isn't support on this card");
nv_card->set_smartdimmer(level);
*new_level = nv_card->get_smartdimmer();
nvclockd_unlock();
return TRUE;
}
gboolean nvclockd_get_version (NVClockObject *obj, guint32 *version, GError **error)
{
*version = NVCLOCKD_VERSION;
return TRUE;
}
/* This function monitors the clockspeed/temperature/CAP_FANSPEED changes.
* When a variable has changed it sends an update notification after which the client
* needs to retrieve the variable again if it wants the updated value.
*/
gboolean nvclockd_update_callback(gpointer data)
{
NVClockObject* obj = NVCLOCKD_OBJECT(data);
static updates *cards;
static int init=0;
int i;
if(!init)
{
cards = calloc(0, nvclock.num_cards * sizeof(updates));
init=1;
}
nvclockd_lock();
for(i=0; i<nvclock.num_cards; i++)
{
int changes = 0;
float clock;
set_card(i);
clock = nv_card->get_gpu_speed();
if(cards[i].gpu_clock != clock)
{
cards[i].gpu_clock = clock;
changes |= CAP_GPU_CLOCK;
}
clock = nv_card->get_memory_speed();
if(cards[i].mem_clock != clock)
{
cards[i].mem_clock = clock;
changes |= CAP_MEM_CLOCK;
}
if(nv_card->caps & BOARD_TEMP_MONITORING)
{
int board_temp = nv_card->get_board_temp(nv_card->sensor);
if(cards[i].board_temp != board_temp)
{
cards[i].board_temp = board_temp;
changes |= CAP_BOARD_TEMP;
}
/* I2C bus reading increases cpu usage if we do it quickly after the previous read for some reason */
g_usleep(1000);
}
if(nv_card->caps & GPU_TEMP_MONITORING)
{
int gpu_temp = nv_card->get_gpu_temp(nv_card->sensor);
if(cards[i].gpu_temp != gpu_temp)
{
cards[i].gpu_temp = gpu_temp;
changes |= CAP_GPU_TEMP;
}
/* I2C bus reading increases cpu usage if we do it quickly after the previous read for some reason */
g_usleep(1000);
}
if(nv_card->caps & GPU_FANSPEED_MONITORING)
{
float duty_cycle = nv_card->get_fanspeed();
if(cards[i].duty_cycle != duty_cycle)
{
cards[i].duty_cycle = duty_cycle;
changes |= CAP_DUTY_CYCLE;
}
/* I2C bus reading increases cpu usage if we do it quickly after the previous read for some reason */
g_usleep(1000);
}
if(nv_card->caps & I2C_FANSPEED_MONITORING)
{
float duty_cycle = nv_card->get_i2c_fanspeed_pwm(nv_card->sensor);
int fanspeed = nv_card->get_i2c_fanspeed_rpm(nv_card->sensor);
if(cards[i].duty_cycle != duty_cycle)
{
cards[i].duty_cycle = duty_cycle;
changes |= CAP_DUTY_CYCLE;
}
if(cards[i].fanspeed != fanspeed)
{
cards[i].fanspeed = fanspeed;
changes |= CAP_FANSPEED;
}
/* I2C bus reading increases cpu usage if we do it quickly after the previous read for some reason */
g_usleep(1000);
}
if(nv_card->caps & SMARTDIMMER)
{
int smartdimmer = nv_card->get_smartdimmer();
if(cards[i].smartdimmer != smartdimmer)
{
cards[i].smartdimmer = smartdimmer;
changes |= CAP_SMARTDIMMER;
}
}
if(changes)
{
/* D-BUS doesn't provide a void__uint_uint marshaller by default, further messing with arrays might
* be relatively complex for dbus clients (depending on the language), so for now store the card number
* in the two highest bits which is what the macro does.
*/
NVCLOCKD_SET_CARD(changes, i);
g_signal_emit(obj, nvclockd_signals[UPDATE_SIGNAL], 0, changes);
}
}
nvclockd_unlock();
return TRUE;
}
void nvclockd_cleanup() {
unlink("/tmp/nvclockd.lock");
exit (0);
}
int main (int argc, char **argv)
{
NVClockObject *obj;
GMainLoop *mainloop;
g_type_init ();
mainloop = g_main_loop_new (NULL, FALSE);
obj = g_object_new (NVCLOCKD_TYPE_OBJECT, NULL);
if(!init_nvclock())
{
char buf[80];
printf("Error: %s\n", get_error(buf, 80));
}
else
set_card(0);
/*signals for lockfile*/
signal(SIGINT, nvclockd_cleanup);
signal(SIGSEGV, nvclockd_cleanup);
signal(SIGQUIT, nvclockd_cleanup);
signal(SIGTERM, nvclockd_cleanup);
signal(SIGHUP, nvclockd_cleanup);
/* Update thread */
g_timeout_add (200, (GSourceFunc) nvclockd_update_callback, obj);
g_main_loop_run (mainloop);
nvclockd_cleanup();
return 0;
}