Beispiel #1
0
/*
 * 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;
}
Beispiel #2
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);
}
Beispiel #3
0
/*
 * 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;
    }
}
Beispiel #4
0
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);
}
Beispiel #6
0
/*
 * 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;
}
Beispiel #7
0
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);
}