/* * Sanity-check a temperature value. Assume that setpoints * should be between 0C and 200C. */ static void acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what) { if (*val != -1 && (*val < TZ_ZEROC || *val > TZ_ZEROC + 2000)) { /* * If the value we are checking is _TMP, warn the user only * once. This avoids spamming messages if, for instance, the * sensor is broken and always returns an invalid temperature. * * This is only done for _TMP; other values always emit a * warning. */ if (what != acpi_tz_tmp_name || !sc->tz_insane_tmp_notified) { device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n", what, TZ_KELVTOC(*val)); /* Don't warn the user again if the read value doesn't improve. */ if (what == acpi_tz_tmp_name) sc->tz_insane_tmp_notified = 1; } *val = -1; return; } /* This value is correct. Warn if it's incorrect again. */ if (what == acpi_tz_tmp_name) sc->tz_insane_tmp_notified = 0; }
/* * Get the current temperature. */ static int acpi_tz_get_temperature(struct acpi_tz_softc *sc) { int temp; ACPI_STATUS status; ACPI_FUNCTION_NAME ("acpi_tz_get_temperature"); /* Evaluate the thermal zone's _TMP method. */ status = acpi_GetInteger(sc->tz_handle, acpi_tz_tmp_name, &temp); if (ACPI_FAILURE(status)) { ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), "error fetching current temperature -- %s\n", AcpiFormatException(status)); return (FALSE); } /* Check it for validity. */ acpi_tz_sanity(sc, &temp, acpi_tz_tmp_name); if (temp == -1) return (FALSE); ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp))); sc->tz_temperature = temp; return (TRUE); }
/* * Sanity-check a temperature value. Assume that setpoints * should be between 0C and 200C. */ static void acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what) { if (*val != -1 && (*val < TZ_ZEROC || *val > TZ_ZEROC + 2000)) { device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n", what, TZ_KELVTOC(*val)); *val = -1; } }
static int acpi_tz_cpufreq_restore(struct acpi_tz_softc *sc) { device_t dev; int error; if (!sc->tz_cooling_updated) return (0); if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) return (ENXIO); ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), "temperature %d.%dC: resuming previous clock speed (%d MHz)\n", TZ_KELVTOC(sc->tz_temperature), sc->tz_cooling_saved_freq); error = CPUFREQ_SET(dev, NULL, CPUFREQ_PRIO_KERN); if (error == 0) sc->tz_cooling_updated = FALSE; return (error); }
/* * Reads the CPU temperature from /sys/class/thermal/thermal_zone%d/temp (or * the user provided path) and returns the temperature in degree celcius. * */ void print_cpu_temperature_info(yajl_gen json_gen, char *buffer, int zone, const char *path, const char *format, int max_threshold) { char *outwalk = buffer; #ifdef THERMAL_ZONE const char *walk; bool colorful_output = false; char *thermal_zone; if (path == NULL) asprintf(&thermal_zone, THERMAL_ZONE, zone); else { static glob_t globbuf; if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0) die("glob() failed\n"); if (globbuf.gl_pathc == 0) { /* No glob matches, the specified path does not contain a wildcard. */ asprintf(&thermal_zone, path, zone); } else { /* glob matched, we take the first match and ignore the others */ asprintf(&thermal_zone, "%s", globbuf.gl_pathv[0]); } globfree(&globbuf); } INSTANCE(thermal_zone); for (walk = format; *walk != '\0'; walk++) { if (*walk != '%') { *(outwalk++) = *walk; continue; } if (BEGINS_WITH(walk + 1, "degrees")) { #if defined(LINUX) static char buf[16]; long int temp; if (!slurp(thermal_zone, buf, sizeof(buf))) goto error; temp = strtol(buf, NULL, 10); if (temp == LONG_MIN || temp == LONG_MAX || temp <= 0) *(outwalk++) = '?'; else { if ((temp / 1000) >= max_threshold) { START_COLOR("color_bad"); colorful_output = true; } outwalk += sprintf(outwalk, "%ld", (temp / 1000)); if (colorful_output) { END_COLOR; colorful_output = false; } } #elif defined(__DragonFly__) struct sensor th_sensor; size_t th_sensorlen; th_sensorlen = sizeof(th_sensor); if (sysctlbyname(thermal_zone, &th_sensor, &th_sensorlen, NULL, 0) == -1) { perror("sysctlbyname"); goto error; } if (MUKTOC(th_sensor.value) >= max_threshold) { START_COLOR("color_bad"); colorful_output = true; } outwalk += sprintf(outwalk, "%.2f", MUKTOC(th_sensor.value)); if (colorful_output) { END_COLOR; colorful_output = false; } #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) int sysctl_rslt; size_t sysctl_size = sizeof(sysctl_rslt); if (sysctlbyname(thermal_zone, &sysctl_rslt, &sysctl_size, NULL, 0)) goto error; if (TZ_AVG(sysctl_rslt) >= max_threshold) { START_COLOR("color_bad"); colorful_output = true; } outwalk += sprintf(outwalk, "%d.%d", TZ_KELVTOC(sysctl_rslt)); if (colorful_output) { END_COLOR; colorful_output = false; } #elif defined(__OpenBSD__) struct sensordev sensordev; struct sensor sensor; size_t sdlen, slen; int dev, numt, mib[5] = {CTL_HW, HW_SENSORS, 0, 0, 0}; sdlen = sizeof(sensordev); slen = sizeof(sensor); for (dev = 0;; dev++) { mib[2] = dev; if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) { if (errno == ENXIO) continue; if (errno == ENOENT) break; goto error; } /* 'path' is the node within the full path (defaults to acpitz0). */ if (BEGINS_WITH(sensordev.xname, thermal_zone)) { mib[3] = SENSOR_TEMP; /* Limit to temo0, but should retrieve from a full path... */ for (numt = 0; numt < 1 /*sensordev.maxnumt[SENSOR_TEMP]*/; numt++) { mib[4] = numt; if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) { if (errno != ENOENT) { warn("sysctl"); continue; } } if ((int)MUKTOC(sensor.value) >= max_threshold) { START_COLOR("color_bad"); colorful_output = true; } outwalk += sprintf(outwalk, "%.2f", MUKTOC(sensor.value)); if (colorful_output) { END_COLOR; colorful_output = false; } } } } #elif defined(__NetBSD__) int fd, rval; bool err = false; prop_dictionary_t dict; prop_array_t array; prop_object_iterator_t iter; prop_object_iterator_t iter2; prop_object_t obj, obj2, obj3; fd = open("/dev/sysmon", O_RDONLY); if (fd == -1) goto error; rval = prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &dict); if (rval == -1) { err = true; goto error_netbsd1; } /* No drivers registered? */ if (prop_dictionary_count(dict) == 0) { err = true; goto error_netbsd2; } iter = prop_dictionary_iterator(dict); if (iter == NULL) { err = true; goto error_netbsd2; } /* iterate over the dictionary returned by the kernel */ while ((obj = prop_object_iterator_next(iter)) != NULL) { /* skip this dict if it's not what we're looking for */ if ((strlen(prop_dictionary_keysym_cstring_nocopy(obj)) != strlen(thermal_zone)) || (strncmp(thermal_zone, prop_dictionary_keysym_cstring_nocopy(obj), strlen(thermal_zone)) != 0)) continue; array = prop_dictionary_get_keysym(dict, obj); if (prop_object_type(array) != PROP_TYPE_ARRAY) { err = true; goto error_netbsd3; } iter2 = prop_array_iterator(array); if (!iter2) { err = true; goto error_netbsd3; } /* iterate over array of dicts specific to target sensor */ while ((obj2 = prop_object_iterator_next(iter2)) != NULL) { obj3 = prop_dictionary_get(obj2, "cur-value"); float temp = MUKTOC(prop_number_integer_value(obj3)); if ((int)temp >= max_threshold) { START_COLOR("color_bad"); colorful_output = true; } outwalk += sprintf(outwalk, "%.2f", temp); if (colorful_output) { END_COLOR; colorful_output = false; } break; } prop_object_iterator_release(iter2); } error_netbsd3: prop_object_iterator_release(iter); error_netbsd2: prop_object_release(dict); error_netbsd1: close(fd); if (err) goto error; #endif walk += strlen("degrees"); } } free(thermal_zone); OUTPUT_FULL_TEXT(buffer); return; error: free(thermal_zone); #endif OUTPUT_FULL_TEXT("can't read temp"); (void)fputs("i3status: Cannot read temperature. Verify that you have a thermal zone in /sys/class/thermal or disable the cpu_temperature module in your i3status config.\n", stderr); }
/* * Evaluate the condition of a thermal zone, take appropriate actions. */ static void acpi_tz_monitor(void *Context) { struct acpi_tz_softc *sc; struct timespec curtime; int temp; int i; int newactive, newflags; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = (struct acpi_tz_softc *)Context; /* Get the current temperature. */ if (!acpi_tz_get_temperature(sc)) { /* XXX disable zone? go to max cooling? */ return_VOID; } temp = sc->tz_temperature; /* * Work out what we ought to be doing right now. * * Note that the _ACx levels sort from hot to cold. */ newactive = TZ_ACTIVE_NONE; for (i = TZ_NUMLEVELS - 1; i >= 0; i--) { if (sc->tz_zone.ac[i] != -1 && temp >= sc->tz_zone.ac[i]) newactive = i; } /* * We are going to get _ACx level down (colder side), but give a guaranteed * minimum cooling run time if requested. */ if (acpi_tz_min_runtime > 0 && sc->tz_active != TZ_ACTIVE_NONE && sc->tz_active != TZ_ACTIVE_UNKNOWN && (newactive == TZ_ACTIVE_NONE || newactive > sc->tz_active)) { getnanotime(&curtime); timespecsub(&curtime, &sc->tz_cooling_started); if (curtime.tv_sec < acpi_tz_min_runtime) newactive = sc->tz_active; } /* Handle user override of active mode */ if (sc->tz_requested != TZ_ACTIVE_NONE && (newactive == TZ_ACTIVE_NONE || sc->tz_requested < newactive)) newactive = sc->tz_requested; /* update temperature-related flags */ newflags = TZ_THFLAG_NONE; if (sc->tz_zone.psv != -1 && temp >= sc->tz_zone.psv) newflags |= TZ_THFLAG_PSV; if (sc->tz_zone.hot != -1 && temp >= sc->tz_zone.hot) newflags |= TZ_THFLAG_HOT; if (sc->tz_zone.crt != -1 && temp >= sc->tz_zone.crt) newflags |= TZ_THFLAG_CRT; /* If the active cooling state has changed, we have to switch things. */ if (sc->tz_active == TZ_ACTIVE_UNKNOWN) { /* * We don't know which cooling device is on or off, * so stop them all, because we now know which * should be on (if any). */ for (i = 0; i < TZ_NUMLEVELS; i++) { if (sc->tz_zone.al[i].Pointer != NULL) { acpi_ForeachPackageObject( (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer, acpi_tz_switch_cooler_off, sc); } } /* now we know that all devices are off */ sc->tz_active = TZ_ACTIVE_NONE; } if (newactive != sc->tz_active) { /* Turn off unneeded cooling devices that are on, if any are */ for (i = TZ_ACTIVE_LEVEL(sc->tz_active); i < TZ_ACTIVE_LEVEL(newactive); i++) { acpi_ForeachPackageObject( (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer, acpi_tz_switch_cooler_off, sc); } /* Turn on cooling devices that are required, if any are */ for (i = TZ_ACTIVE_LEVEL(sc->tz_active) - 1; i >= TZ_ACTIVE_LEVEL(newactive); i--) { acpi_ForeachPackageObject( (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer, acpi_tz_switch_cooler_on, sc); } ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), "switched from %s to %s: %d.%dC\n", acpi_tz_aclevel_string(sc->tz_active), acpi_tz_aclevel_string(newactive), TZ_KELVTOC(temp)); sc->tz_active = newactive; getnanotime(&sc->tz_cooling_started); } /* XXX (de)activate any passive cooling that may be required. */ /* * If the temperature is at _HOT or _CRT, increment our event count. * If it has occurred enough times, shutdown the system. This is * needed because some systems will report an invalid high temperature * for one poll cycle. It is suspected this is due to the embedded * controller timing out. A typical value is 138C for one cycle on * a system that is otherwise 65C. * * If we're almost at that threshold, notify the user through devd(8). */ if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) != 0) { sc->tz_validchecks++; if (sc->tz_validchecks == TZ_VALIDCHECKS) { device_printf(sc->tz_dev, "WARNING - current temperature (%d.%dC) exceeds safe limits\n", TZ_KELVTOC(sc->tz_temperature)); shutdown_nice(RB_POWEROFF); } else if (sc->tz_validchecks == TZ_NOTIFYCOUNT) acpi_UserNotify("Thermal", sc->tz_handle, TZ_NOTIFY_CRITICAL); } else { sc->tz_validchecks = 0; } sc->tz_thflags = newflags; return_VOID; }
static int acpi_tz_cpufreq_update(struct acpi_tz_softc *sc, int req) { device_t dev; struct cf_level *levels; int num_levels, error, freq, desired_freq, perf, i; levels = malloc(CPUFREQ_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT); if (levels == NULL) return (ENOMEM); /* * Find the main device, cpufreq0. We don't yet support independent * CPU frequency control on SMP. */ if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) { error = ENXIO; goto out; } /* Get the current frequency. */ error = CPUFREQ_GET(dev, &levels[0]); if (error) goto out; freq = levels[0].total_set.freq; /* Get the current available frequency levels. */ num_levels = CPUFREQ_MAX_LEVELS; error = CPUFREQ_LEVELS(dev, levels, &num_levels); if (error) { if (error == E2BIG) printf("cpufreq: need to increase CPUFREQ_MAX_LEVELS\n"); goto out; } /* Calculate the desired frequency as a percent of the max frequency. */ perf = 100 * freq / levels[0].total_set.freq - req; if (perf < 0) perf = 0; else if (perf > 100) perf = 100; desired_freq = levels[0].total_set.freq * perf / 100; if (desired_freq < freq) { /* Find the closest available frequency, rounding down. */ for (i = 0; i < num_levels; i++) if (levels[i].total_set.freq <= desired_freq) break; /* If we didn't find a relevant setting, use the lowest. */ if (i == num_levels) i--; } else { /* If we didn't decrease frequency yet, don't increase it. */ if (!sc->tz_cooling_updated) { sc->tz_cooling_active = FALSE; goto out; } /* Use saved cpu frequency as maximum value. */ if (desired_freq > sc->tz_cooling_saved_freq) desired_freq = sc->tz_cooling_saved_freq; /* Find the closest available frequency, rounding up. */ for (i = num_levels - 1; i >= 0; i--) if (levels[i].total_set.freq >= desired_freq) break; /* If we didn't find a relevant setting, use the highest. */ if (i == -1) i++; /* If we're going to the highest frequency, restore the old setting. */ if (i == 0 || desired_freq == sc->tz_cooling_saved_freq) { error = acpi_tz_cpufreq_restore(sc); if (error == 0) sc->tz_cooling_active = FALSE; goto out; } } /* If we are going to a new frequency, activate it. */ if (levels[i].total_set.freq != freq) { ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), "temperature %d.%dC: %screasing clock speed " "from %d MHz to %d MHz\n", TZ_KELVTOC(sc->tz_temperature), (freq > levels[i].total_set.freq) ? "de" : "in", freq, levels[i].total_set.freq); error = CPUFREQ_SET(dev, &levels[i], CPUFREQ_PRIO_KERN); if (error == 0 && !sc->tz_cooling_updated) { sc->tz_cooling_saved_freq = freq; sc->tz_cooling_updated = TRUE; } } out: if (levels) free(levels, M_TEMP); return (error); }