/* update should also submit card number !! */ #include #include #include #include #include #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; iget_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; }