SYSMON_ENVSYS(9)
π SYSMON_ENVSYS(9) π
okay so bestie, you found the kernel dev manual page for sysmon_envsys β that's the envsys 2 framework, kernel side, from the Kernel Developer's Manual. grab a snack, this one's a whole journey π
β¨ NAME β¨
sysmon_envsys β the kernel part of the envsys 2 framework, no cap π
πΈ 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. so basically, with this framework you can:
- register and unregister a
sysmon_envsysdevice π - attach or detach sensors into a device π
- enable or disable automatic monitoring for some sensors without any user interactivity
β¦among other things, iykyk π«
π HOW TO USE THE FRAMEWORK π
so to register a new driver to the sysmon_envsys framework, you first gotta allocate and initialize a sysmon_envsys object β and the function for that is sysmon_envsys_create() β¨ it returns a zero'ed pointer to a sysmon_envsys structure. clean. fresh. iconic.
once the object is initialized, actual sensors may be initialized and attached (peep the βSENSOR DETAILSβ section if you want the tea β). this is done with the sysmon_envsys_sensor_attach() function, which takes the envsys_data_t (that's a sensor, bestie) in the second argument and attaches it into the sysmon_envsys object from the first argument. slay.
finally, after all the sensors are 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. don't skip this step fr fr π
if something goes sideways during initialization and you hit errors, that's what sysmon_envsys_destroy() is for π it detaches all previously attached sensors and deallocates the sysmon_envsys object. clean exit, no mess.
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 done by the sysmon_envsys framework on a polled basis. OR, alternatively, the sensor's device driver can just call the sysmon_envsys_sensor_event() function to deliver the event without waiting for the device to be polled. main character energy, honestly.
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. cute.
drivers can also call the sysmon_envsys_update_limits() function when it's necessary to reinitialize a sensor's threshold values π
this is used by the acpibat(4) driver when a new battery is inserted. very demure, very mindful.
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, bestie:
sme_classβ this specifies the class of the sysmon envsys device. see the DEVICE CLASSES section for more info (OPTIONAL).sme_nameβ the name that will be used in the driver (REQUIRED) βsme_flagsβ additional flags for thesysmon_envsysdevice. currently supportingSME_DISABLE_REFRESH. if enabled, thesme_refreshfunction 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 thesme_refresh,sme_get_limits, orsme_set_limitsfunction 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 theedata->sensorindex π― if this member is not specified, the device driver will be totally responsible for all updates of this sensor; thesysmon_envsysframework will not be able to update the sensor value. no thoughts, head empty, your problem now.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 info.) if this member is not specified, theENVSYS_FMONLIMITSflag will be ignored, and limit monitoring will not occur until appropriate limits are enabled from userland viaenvstat(8). so likeβ¦ nothing happens until you say so π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-specified 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. very handy.
to unregister a driver previously registered with the sysmon_envsys framework, you must use the sysmon_envsys_unregister() function π if there were monitoring events registered for the driver, they all get 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(). don't double-dip, bestie π
π§ 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). simple, effective, iconic.
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. abrupt, but like, necessary.
NOTE: if aSME_CLASS_ACADAPTERorSME_CLASS_BATTERYclass device doesn't have the sensors required, the low-power event will never be sent, and the graceful shutdown won't be possible. so pls. attach your sensors. i'm begging π₯Ί
π 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 theENVSYS_FMONxxxflags 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 thepowerd(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β the resistor-factor value for voltage sensors, bestie πvalue_curβ the current valuevalue_maxβ the max valuevalue_minβ the min valuevalue_avgβ the average valuelimitsβ the struct that holds the sensor's alarm thresholds π¨upropsetβ keeps track of which sensor properties are actually setdescβ the description string. NOTE that it must be unique within a device, and sensors with duplicate or empty descriptions just get straight-up ignored π
Okay so if you're using this framework, there's a whole checklist of stuff you literally cannot skip:
- The
descmember has to have a valid description β unique in the device, non-empty, or it's not valid. Periodt. - The units type must be valid. Here's the whole roster of defined units, no cap:
ENVSYS_STEMPβ temperature sensors, in microkelvins.ENVSYS_SFANRPMβ fan sensors.ENVSYS_SVOLTS_ACβ AC Voltage.ENVSYS_SVOLTS_DCβ DC Voltage.ENVSYS_SOHMSβ Ohms.ENVSYS_SWATTSβ Watts.ENVSYS_SAMPSβ Ampere.ENVSYS_SWATTHOURβ Watts hour.ENVSYS_SAMPHOURβ 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 theENVSYS_BATTERY_CAPACITY_*values invalue_curto report its current capacity to userland. Mandatory ifsme_classis set toSME_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 what state the Battery is in: true if the battery is currently charging, false otherwise. Mandatory ifsme_classis set toSME_CLASS_BATTERY.
- When you're initializing or refreshing the sensor, the
statemember should be set to a known state β otherwise it just chills in unknown state, awkward π. 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
flagsmember accepts one or more of these flags, and yes you can stack them π :ENVSYS_FCHANGERFACTβ marks the sensor with the ability to change therfactvalue on the fly (in voltage sensors). Therfactmember must be used in the correct place of the code that retrieves and converts the value of the sensor.ENVSYS_FPERCENTβ this uses thevalue_curandvalue_maxmembers to make a percentage. Both values must be enabled and have data.ENVSYS_FVALID_MAXβ marks thevalue_maxvalue as valid.ENVSYS_FVALID_MINβ marks thevalue_minvalue as valid.ENVSYS_FVALID_AVGβ marks thevalue_avgvalue 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. This flag is not effective if the units member is notENVSYS_DRIVEorENVSYS_BATTERY_CAPACITY.ENVSYS_FMONNOTSUPPβ disallows setting of limits (or thresholds) via theENVSYS_SETDICTIONARYioctl(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 forrnd(4)β¨
And if the driver has to use any of the value_max, value_min, or value_avg members, it should mark them as valid with the appropriate flag. No sneaky unmarked values, bestie π«Ά
- If
unitsis set toENVSYS_DRIVE, thevalue_curmember must be set to one of these 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
unitsis set toENVSYS_BATTERY_CAPACITY, thevalue_curmember must be set to one of these 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 the values to be converted to a unit that can be converted to another one easily. That means you (the user) should convert the value returned by the driver to the appropriate unit. Like voltage sensors to mV, temperature sensors to uK, Watts to mW, Ampere to mA, etc.
These types shouldn't need any conversion at all: 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. π Like, seriously, do not. Floating point in kernel is not the vibe.
πΈ HOW TO ENABLE AUTOMATIC MONITORING IN SENSORS πΈ
Okay this example is literally iconic β it shows 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 these files, bestie:
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) β go read those next, iykyk π«Ά
π HISTORY π
The first envsys framework first appeared in NetBSD 1.5. The envsys 2 framework first appeared in NetBSD 5.0. Glow-up of the century, honestly.
π 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.
And that's the whole page, bestie! π This one's from the Debian distro source, dated July 13, 2012, and it goes by SYSMON_ENVSYS(9) β go forth and monitor those sensors, slay β¨π
π¦ where this page comes from
- nixpkgs unstable: netbsd-source 11.0
- Copyright (c) 2007, 2008 The NetBSD Foundation, Inc
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms