SYSMON_ENVSYS(9) | Kernel Developer's Manual | SYSMON_ENVSYS(9)

# NAME

`sysmon_envsys` —
kernel part of the envsys 2 framework

# SYNOPSIS

`#include <dev/sysmon/sysmonvar.h>`

struct sysmon_envsys *
`sysmon_envsys_create`(void);

void
`sysmon_envsys_destroy`(struct sysmon_envsys *);

int
`sysmon_envsys_register`(struct sysmon_envsys *);

void
`sysmon_envsys_unregister`(struct sysmon_envsys *);

int
`sysmon_envsys_sensor_attach`(struct sysmon_envsys *, envsys_data_t *);

int
`sysmon_envsys_sensor_detach`(struct sysmon_envsys *, envsys_data_t *);

void
`sysmon_envsys_sensor_event`(struct sysmon_envsys *, envsys_data_t *, int);

void
`sysmon_envsys_foreach_sensor`(sysmon_envsys_callback_t, void *, bool);

int
`sysmon_envsys_update_limits`(struct sysmon_envsys *, envsys_data_t *);

# DESCRIPTION

`sysmon_envsys` is the kernel part of the envsys(4) framework. With this framework you are able to register and unregister a `sysmon_envsys` device, attach or detach sensors into a device, and enable or disable automatic monitoring for some sensors without any user interactivity, among other things.

## HOW TO USE THE FRAMEWORK

To register a new driver to the `sysmon_envsys` framework, a sysmon_envsys object must be allocated and initialized; the `sysmon_envsys_create`() function is used for this. This returns a zero'ed pointer to a sysmon_envsys structure.

Once the object has been initialized, actual sensors may be initialized and attached (see the SENSOR DETAILS section for more information). This is accomplished by the `sysmon_envsys_sensor_attach`() function, which will attach the envsys_data_t (a sensor) specified as second argument into the sysmon_envsys object specified in the first argument.

Finally, after all sensors have been attached, the device needs to set some required (and optional) members of the sysmon_envsys structure before calling the `sysmon_envsys_register`() function to register the device.

In case of errors during the initialization, the `sysmon_envsys_destroy`() function should be used. This detaches all previously attached sensors and deallocates the sysmon_envsys object.

Some sensors can be monitored, and when the sensor value changes an event can be delivered to the powerd(8) daemon. Sensor monitoring can be performed by the `sysmon_envsys` framework on a polled basis. Alternatively, the sensor's device driver can call the `sysmon_envsys_sensor_event`() function to deliver the event without waiting for the device to be polled.

The `sysmon_envsys_foreach_sensor`() function can be used by other parts of the kernel to iterate over all registered sensors. This capability is used by the i386/apm(4) driver to summarize the state of all battery sensors.

Drivers can also call the `sysmon_envsys_update_limits`() function when it is necessary to reinitialize a sensor's threshold values. This is used by the acpibat(4) driver when a new battery is inserted.

The sysmon_envsys structure is defined as follows (only the public members are shown):

    struct sysmon_envsys {
    	const char 	*sme_name;
    	int		sme_flags;
    	int		sme_class;
    	uint64_t	sme_events_timeout;
    	void 		*sme_cookie;
    	void (*sme_refresh)(struct sysmon_envsys *, envsys_data_t *);
    	void (*sme_set_limits)(struct sysmon_envsys *, envsys_data_t *,
    			       sysmon_envsys_lim_t *, uint32_t *);
    	void (*sme_get_limits)(struct sysmon_envsys *, envsys_data_t *,
    			       sysmon_envsys_lim_t *, uint32_t *);
    };

The members have the following meaning:

sme_class
  This specifies the class of the sysmon envsys device. See the DEVICE CLASSES section for more information (OPTIONAL).
sme_name
  The name that will be used in the driver (REQUIRED).
sme_flags
  Additional flags for the `sysmon_envsys` device. Currently supporting SME_DISABLE_REFRESH. If enabled, the sme_refresh function callback won't be used to refresh sensor data and the driver will use its own method (OPTIONAL).
sme_events_timeout
  This is used to specify the default timeout value (in seconds) that will be used to check for critical events if any monitoring flag was set (OPTIONAL).

If the driver wants to refresh sensors data via the `sysmon_envsys` framework, the following members may be specified:

sme_cookie
  Typically a pointer to the device struct (also called “softc”). This may be used in the sme_refresh, sme_get_limits, or sme_set_limits function callbacks.
sme_refresh
  Pointer to a function that will be used to refresh sensor data in the device. This can be used to set the state and other properties of the sensor depending on the data returned by the driver. NOTE: You don't have to refresh all sensors, only the sensor specified by the edata->sensor index. If this member is not specified, the device driver will be totally responsible for all updates of this sensor; the `sysmon_envsys` framework will not be able to update the sensor value.
sme_get_limits
  Pointer to a function that will be used to obtain from the driver the initial limits (or thresholds) used when monitoring a sensor's value. (See the SENSOR DETAILS section for more information.) If this member is not specified, the `ENVSYS_FMONLIMITS` flag will be ignored, and limit monitoring will not occur until appropriate limits are enabled from userland via envstat(8).
sme_set_limits
  Pointer to a function that alerts the device driver whenever monitoring limits (or thresholds) are updated by the user. Setting this function allows the device driver to reprogram hardware limits (if provided by the device) when the user-specificied limits are updated, and gives the driver direct control over setting the sensor's state based on hardware status.

  The sme_set_limits callback can be invoked with the third argument (a pointer to the new limits) set to a `NULL` pointer. Device drivers must recognize this as a request to restore the sensor limits to their original, boot-time values.

  If the sme_set_limits member is not specified, the device driver is not informed of changes to the sensor's limit values, and the `sysmon_envsys` framework performs all limit checks in software.

Note that it's not necessary to refresh the sensors data before the driver is registered, only do it if you need the data in your driver to check for a specific condition.

The timeout value for the monitoring events on a device may be changed via the `ENVSYS_SETDICTIONARY` ioctl(2) or the envstat(8) command.

To unregister a driver previously registered with the `sysmon_envsys` framework, the `sysmon_envsys_unregister`() function must be used. If there were monitoring events registered for the driver, they all will be destroyed before the device is unregistered and its sensors are detached. Finally the sysmon_envsys object will be freed, so there's no need to call `sysmon_envsys_destroy`().

## DEVICE CLASSES

The sme_class member of the sysmon_envsys structure is an optional flag that specifies the class of the sysmon envsys device. Currently there are two classes:

SME_CLASS_ACADAPTER

  This class is for devices that want to act as an AC adapter. The device writer must ensure that at least there is a sensor with units of `ENVSYS_INDICATOR`. This will be used to report its current state (on/off).

SME_CLASS_BATTERY

  This class is for devices that want to act as a Battery. The device writer must ensure that at least there are two sensors with units of `ENVSYS_BATTERY_CAPACITY` and `ENVSYS_BATTERY_CHARGE`.

  These two sensors are used to ensure that the battery device can send a low-power event to the powerd(8) daemon (if running) when all battery devices are in a critical state. (The critical state occurs when a battery is not currently charging and its charge state is low or critical.) When the low-power condition is met, an event is sent to the powerd(8) daemon (if running), which will shutdown the system gracefully by executing the /etc/powerd/scripts/sensor_battery script.

  If powerd(8) is not running, the system will be powered off via the cpu_reboot(9) call with the `RB_POWERDOWN` flag.

NOTE: If a `SME_CLASS_ACADAPTER` or `SME_CLASS_BATTERY` class device doesn't have the sensors required, the low-power event will never be sent, and the graceful shutdown won't be possible.

## SENSOR DETAILS

Each sensor uses a envsys_data_t structure, it's defined as follows (only the public members are shown);

    typedef struct envsys_data {
    	uint32_t	units;
    	uint32_t	state;
    	uint32_t	flags;
    	uint32_t	rpms;
    	int32_t		rfact;
    	int32_t		value_cur;
    	int32_t		value_max;
    	int32_t		value_min;
    	int32_t		value_avg;
    	sysmon_envsys_lim_t limits;
    	int		upropset;
    	char		desc[ENVSYS_DESCLEN];
    } envsys_data_t;

The members for the envsys_data_t structure have the following meaning:

units
  Used to set the units type.
state
  Used to set the current state.
flags
  Used to set additional flags. Among other uses, if one or more of the `ENVSYS_FMONxxx` flags are set, automatic sensor monitoring will be enabled. Periodically, the sensor's value will be checked and if certain conditions are met, an event will be sent to the powerd(8) daemon. NOTE that limits (or thresholds) can be set at any time to enable monitoring that the sensor's value remains within those limits.
rpms
  Used to set the nominal RPM value for fan sensors.
rfact
  Used to set the rfact value for voltage sensors.
value_cur
  Used to set the current value.
value_max
  Used to set the maximum value.
value_min
  Used to set the minimum value.
value_avg
  Used to set the average value.
limits
  Structure used to contain the sensor's alarm thresholds.
upropset
  Used to keep track of which sensor properties are set.
desc
  Used to set the description string. NOTE that the description string must be unique in a device, and sensors with duplicate or empty description will simply be ignored.

Users of this framework must take care about the following points:

- The desc member needs to have a valid description, unique in a device and non empty to be valid.
- The units type must be valid. The following units are defined:

  `ENVSYS_STEMP`
    For temperature sensors, in microkelvins.
  `ENVSYS_SFANRPM`
    For fan sensors.
  `ENVSYS_SVOLTS_AC`
    For AC Voltage.
  `ENVSYS_SVOLTS_DC`
    For DC Voltage.
  `ENVSYS_SOHMS`
    For Ohms.
  `ENVSYS_SWATTS`
    For Watts.
  `ENVSYS_SAMPS`
    For Ampere.
  `ENVSYS_SWATTHOUR`
    For Watts hour.
  `ENVSYS_SAMPHOUR`
    For Ampere hour.
  `ENVSYS_INDICATOR`
    For sensors that only want a boolean type.
  `ENVSYS_INTEGER`
    For sensors that only want an integer type.
  `ENVSYS_DRIVE`
    For drive sensors.
  `ENVSYS_BATTERY_CAPACITY`
    For Battery device classes. This sensor unit uses the `ENVSYS_BATTERY_CAPACITY_*` values in value_cur to report its current capacity to userland. Mandatory if sme_class is set to `SME_CLASS_BATTERY`.
  `ENVSYS_BATTERY_CHARGE`
    For Battery device classes. This sensor is equivalent to the Indicator type, it's a boolean. Use it to specify in what state is the Battery state: true if the battery is currently charging or false otherwise. Mandatory if sme_class is set to `SME_CLASS_BATTERY`.
- When initializing or refreshing the sensor, the state member should be set to a known state (otherwise it will be in unknown state). Possible values:

  `ENVSYS_SVALID`
    Sets the sensor to a valid state.
  `ENVSYS_SINVALID`
    Sets the sensor to an invalid state.
  `ENVSYS_SCRITICAL`
    Sets the sensor to a critical state.
  `ENVSYS_SCRITUNDER`
    Sets the sensor to a critical under state.
  `ENVSYS_SCRITOVER`
    Sets the sensor to a critical over state.
  `ENVSYS_SWARNUNDER`
    Sets the sensor to a warning under state.
  `ENVSYS_SWARNOVER`
    Sets the sensor to a warning over state.
- The flags member accepts one or more of the following flags:
  `ENVSYS_FCHANGERFACT`
    Marks the sensor with ability to change the rfact value on the fly (in voltage sensors). The rfact member must be used in the correct place of the code that retrieves and converts the value of the sensor.
  `ENVSYS_FPERCENT`
    This uses the value_cur and value_max members to make a percentage. Both values must be enabled and have data.
  `ENVSYS_FVALID_MAX`
    Marks the value_max value as valid.
  `ENVSYS_FVALID_MIN`
    Marks the value_min value as valid.
  `ENVSYS_FVALID_AVG`
    Marks the value_avg value as valid.
  `ENVSYS_FMONCRITICAL`
    Enables and registers a new event to monitor a critical state.
  `ENVSYS_FMONLIMITS`
    Enables and registers a new event to monitor a sensor's value crossing limits or thresholds.
  `ENVSYS_FMONSTCHANGED`
    Enables and registers a new event to monitor battery capacity or drive state sensors. The flag is not effective if the units member is not `ENVSYS_DRIVE` or `ENVSYS_BATTERY_CAPACITY`.
  `ENVSYS_FMONNOTSUPP`
    Disallows setting of limits (or thresholds) via the `ENVSYS_SETDICTIONARY` ioctl(2). This flag only disables setting the limits from userland. It has no effect on monitoring flags set by the driver.
  `ENVSYS_FHAS_ENTROPY`
    Allows this sensor to provide entropy for rnd(4).

  If the driver has to use any of the value_max, value_min, or value_avg members, they should be marked as valid with the appropriate flag.

- If units is set to `ENVSYS_DRIVE`, the value_cur member must be set to one of the following predefined states:

  `ENVSYS_DRIVE_EMPTY`
    Drive state is unknown.
  `ENVSYS_DRIVE_READY`
    Drive is ready.
  `ENVSYS_DRIVE_POWERUP`
    Drive is powering up.
  `ENVSYS_DRIVE_ONLINE`
    Drive is online.
  `ENVSYS_DRIVE_OFFLINE`
    Drive is offline.
  `ENVSYS_DRIVE_IDLE`
    Drive is idle.
  `ENVSYS_DRIVE_ACTIVE`
    Drive is active.
  `ENVSYS_DRIVE_BUILD`
    Drive is building.
  `ENVSYS_DRIVE_REBUILD`
    Drive is rebuilding.
  `ENVSYS_DRIVE_POWERDOWN`
    Drive is powering down.
  `ENVSYS_DRIVE_FAIL`
    Drive has failed.
  `ENVSYS_DRIVE_PFAIL`
    Drive has been degraded.
  `ENVSYS_DRIVE_MIGRATING`
    Drive is migrating.
  `ENVSYS_DRIVE_CHECK`
    Drive is checking its state.
- If units is set to `ENVSYS_BATTERY_CAPACITY`, the value_cur member must be set to one of the following predefined capacity states:

  `ENVSYS_BATTERY_CAPACITY_NORMAL`
    Battery charge is normal.
  `ENVSYS_BATTERY_CAPACITY_CRITICAL`
    Battery charge is critical.
  `ENVSYS_BATTERY_CAPACITY_LOW`
    Battery charge is low.
  `ENVSYS_BATTERY_CAPACITY_WARNING`
    Battery charge is on or below the warning capacity.
- The envsys(4) framework expects to have the values converted to a unit that can be converted to another one easily. That means the user should convert the value returned by the driver to the appropriate unit. For example voltage sensors to mV, temperature sensors to uK, Watts to mW, Ampere to mA, etc.

  The following types shouldn't need any conversion: `ENVSYS_BATTERY_CAPACITY`, `ENVSYS_BATTERY_CHARGE`, `ENVSYS_INDICATOR`, `ENVSYS_INTEGER`, and `ENVSYS_DRIVE`.

  PLEASE NOTE THAT YOU MUST AVOID USING FLOATING POINT OPERATIONS IN KERNEL WHEN CONVERTING THE DATA RETURNED BY THE DRIVER TO THE APPROPRIATE UNIT, IT'S NOT ALLOWED.

## HOW TO ENABLE AUTOMATIC MONITORING IN SENSORS

The following example illustrates how to enable automatic monitoring in a virtual driver for a critical state in the first sensor (sc_sensor[0]):

    int
    mydriver_initialize_sensors(struct mysoftc *sc)
    {
    	...
    	/* sensor is initialized with a valid state */
    	sc->sc_sensor[0].state = ENVSYS_SVALID;

    	/*
    	 * the monitor member must be true to enable
    	 * automatic monitoring.
    	 */
    	sc->sc_sensor[0].monitor = true;

    	/* and now we specify the type of the monitoring event */
    	sc->sc_sensor[0].flags |= ENVSYS_FMONCRITICAL;
    	...
    }

    int
    mydriver_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    {
    	struct mysoftc *sc = sme->sme_cookie;

    	/* we get current data from the driver */
    	edata->value_cur = sc->sc_getdata();

    	/*
    	 * if value is too high, mark the sensor in
    	 * critical state.
    	 */
    	if (edata->value_cur > MYDRIVER_SENSOR0_HIWAT) {
    		edata->state = ENVSYS_SCRITICAL;
    		/* a critical event will be sent now automatically */
    	} else {
    		/*
    		 * if value is within the limits, and we came from
    		 * a critical state make sure to change sensor's state
    		 * to valid.
    		 */
    		edata->state = ENVSYS_SVALID;
    	}
    	...
    }

# CODE REFERENCES

The envsys 2 framework is implemented within the files:

sys/dev/sysmon/sysmon_envsys.c

sys/dev/sysmon/sysmon_envsys_events.c

sys/dev/sysmon/sysmon_envsys_tables.c

sys/dev/sysmon/sysmon_envsys_util.c

# SEE ALSO

envsys(4), envstat(8)

# HISTORY

The first envsys framework first appeared in NetBSD 1.5. The envsys 2 framework first appeared in NetBSD 5.0.

# AUTHORS

The (current) envsys 2 framework was implemented by Juan Romero Pardines. Additional input on the design was provided by many NetBSD developers around the world.

The first envsys framework was implemented by Jason R. Thorpe, Tim Rightnour, and Bill Squier.

July 13, 2012 | NetBSD 9.3