1 1.20 andvar /* $NetBSD: swsensor.c,v 1.20 2024/02/10 18:43:53 andvar Exp $ */ 2 1.1 pgoyette /* 3 1.1 pgoyette * Copyright (c) 2008 The NetBSD Foundation, Inc. 4 1.1 pgoyette * All rights reserved. 5 1.1 pgoyette * 6 1.1 pgoyette * Redistribution and use in source and binary forms, with or without 7 1.1 pgoyette * modification, are permitted provided that the following conditions 8 1.1 pgoyette * are met: 9 1.1 pgoyette * 1. Redistributions of source code must retain the above copyright 10 1.1 pgoyette * notice, this list of conditions and the following disclaimer. 11 1.1 pgoyette * 2. Redistributions in binary form must reproduce the above copyright 12 1.1 pgoyette * notice, this list of conditions and the following disclaimer in the 13 1.1 pgoyette * documentation and/or other materials provided with the distribution. 14 1.1 pgoyette * 15 1.1 pgoyette * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 16 1.1 pgoyette * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 17 1.1 pgoyette * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 1.1 pgoyette * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 1.1 pgoyette * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 20 1.1 pgoyette * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 1.1 pgoyette * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 22 1.1 pgoyette * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 pgoyette * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 24 1.1 pgoyette * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 1.1 pgoyette * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26 1.1 pgoyette * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 1.1 pgoyette */ 28 1.1 pgoyette 29 1.1 pgoyette #include <sys/cdefs.h> 30 1.20 andvar __KERNEL_RCSID(0, "$NetBSD: swsensor.c,v 1.20 2024/02/10 18:43:53 andvar Exp $"); 31 1.1 pgoyette 32 1.1 pgoyette #include <sys/param.h> 33 1.1 pgoyette #include <sys/kernel.h> 34 1.1 pgoyette #include <sys/module.h> 35 1.1 pgoyette #include <sys/sysctl.h> 36 1.1 pgoyette 37 1.1 pgoyette #include <dev/sysmon/sysmonvar.h> 38 1.10 pgoyette #include <dev/sysmon/sysmon_envsysvar.h> 39 1.1 pgoyette 40 1.1 pgoyette #include <prop/proplib.h> 41 1.1 pgoyette 42 1.5 pgoyette #ifndef _MODULE 43 1.5 pgoyette #include "opt_modular.h" 44 1.5 pgoyette #endif 45 1.5 pgoyette 46 1.1 pgoyette int swsensorattach(int); 47 1.1 pgoyette 48 1.1 pgoyette static int sensor_value_sysctl = 0; 49 1.13 pgoyette static int sensor_state_sysctl = 0; 50 1.1 pgoyette 51 1.1 pgoyette static struct sysmon_envsys *swsensor_sme; 52 1.1 pgoyette static envsys_data_t swsensor_edata; 53 1.1 pgoyette 54 1.1 pgoyette static int32_t sw_sensor_value; 55 1.13 pgoyette static int32_t sw_sensor_state; 56 1.5 pgoyette static int32_t sw_sensor_limit; 57 1.5 pgoyette static int32_t sw_sensor_mode; 58 1.5 pgoyette static int32_t sw_sensor_defprops; 59 1.5 pgoyette sysmon_envsys_lim_t sw_sensor_deflims; 60 1.1 pgoyette 61 1.14 pgoyette MODULE(MODULE_CLASS_DRIVER, swsensor, "sysmon_envsys"); 62 1.1 pgoyette 63 1.1 pgoyette /* 64 1.1 pgoyette * Set-up the sysctl interface for setting the sensor's cur_value 65 1.1 pgoyette */ 66 1.1 pgoyette 67 1.16 pgoyette SYSCTL_SETUP(sysctl_swsensor_setup, "swsensor sysctl") 68 1.1 pgoyette { 69 1.1 pgoyette int ret; 70 1.1 pgoyette int node_sysctl_num; 71 1.1 pgoyette const struct sysctlnode *me = NULL; 72 1.13 pgoyette const struct sysctlnode *me2; 73 1.1 pgoyette 74 1.16 pgoyette ret = sysctl_createv(clog, 0, NULL, &me, 75 1.1 pgoyette CTLFLAG_READWRITE, 76 1.1 pgoyette CTLTYPE_NODE, "swsensor", NULL, 77 1.1 pgoyette NULL, 0, NULL, 0, 78 1.1 pgoyette CTL_HW, CTL_CREATE, CTL_EOL); 79 1.1 pgoyette if (ret != 0) 80 1.1 pgoyette return; 81 1.1 pgoyette 82 1.1 pgoyette node_sysctl_num = me->sysctl_num; 83 1.16 pgoyette ret = sysctl_createv(clog, 0, NULL, &me2, 84 1.1 pgoyette CTLFLAG_READWRITE, 85 1.1 pgoyette CTLTYPE_INT, "cur_value", NULL, 86 1.1 pgoyette NULL, 0, &sw_sensor_value, 0, 87 1.1 pgoyette CTL_HW, node_sysctl_num, CTL_CREATE, CTL_EOL); 88 1.1 pgoyette 89 1.1 pgoyette if (ret == 0) 90 1.13 pgoyette sensor_value_sysctl = me2->sysctl_num; 91 1.13 pgoyette 92 1.13 pgoyette node_sysctl_num = me->sysctl_num; 93 1.16 pgoyette ret = sysctl_createv(clog, 0, NULL, &me2, 94 1.13 pgoyette CTLFLAG_READWRITE, 95 1.13 pgoyette CTLTYPE_INT, "state", NULL, 96 1.13 pgoyette NULL, 0, &sw_sensor_state, 0, 97 1.13 pgoyette CTL_HW, node_sysctl_num, CTL_CREATE, CTL_EOL); 98 1.13 pgoyette 99 1.13 pgoyette if (ret == 0) 100 1.13 pgoyette sensor_state_sysctl = me2->sysctl_num; 101 1.1 pgoyette } 102 1.1 pgoyette 103 1.1 pgoyette /* 104 1.1 pgoyette * "Polling" routine to update sensor value 105 1.1 pgoyette */ 106 1.1 pgoyette static 107 1.1 pgoyette void 108 1.1 pgoyette swsensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 109 1.1 pgoyette { 110 1.1 pgoyette 111 1.1 pgoyette edata->value_cur = sw_sensor_value; 112 1.5 pgoyette 113 1.12 pgoyette /* If value outside of legal range, mark it invalid */ 114 1.12 pgoyette if ((edata->flags & ENVSYS_FVALID_MIN && 115 1.12 pgoyette edata->value_cur < edata->value_min) || 116 1.12 pgoyette (edata->flags & ENVSYS_FVALID_MAX && 117 1.12 pgoyette edata->value_cur > edata->value_max)) { 118 1.12 pgoyette edata->state = ENVSYS_SINVALID; 119 1.12 pgoyette return; 120 1.12 pgoyette } 121 1.12 pgoyette 122 1.5 pgoyette /* 123 1.5 pgoyette * Set state. If we're handling the limits ourselves, do the 124 1.5 pgoyette * compare; otherwise just assume the value is valid. 125 1.13 pgoyette * If sensor state has been set from userland (via sysctl), 126 1.13 pgoyette * just report that value. 127 1.5 pgoyette */ 128 1.13 pgoyette if (sw_sensor_state != ENVSYS_SVALID) 129 1.13 pgoyette edata->state = sw_sensor_state; 130 1.13 pgoyette else if ((sw_sensor_mode == 2) && (edata->upropset & PROP_CRITMIN) && 131 1.5 pgoyette (edata->upropset & PROP_DRIVER_LIMITS) && 132 1.5 pgoyette (edata->value_cur < edata->limits.sel_critmin)) 133 1.5 pgoyette edata->state = ENVSYS_SCRITUNDER; 134 1.5 pgoyette else 135 1.5 pgoyette edata->state = ENVSYS_SVALID; 136 1.5 pgoyette } 137 1.5 pgoyette 138 1.5 pgoyette /* 139 1.5 pgoyette * Sensor get/set limit routines 140 1.5 pgoyette */ 141 1.5 pgoyette 142 1.19 riastrad static void 143 1.5 pgoyette swsensor_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 144 1.19 riastrad sysmon_envsys_lim_t *limits, uint32_t *props) 145 1.5 pgoyette { 146 1.5 pgoyette 147 1.5 pgoyette *props = PROP_CRITMIN | PROP_DRIVER_LIMITS; 148 1.5 pgoyette limits->sel_critmin = sw_sensor_limit; 149 1.5 pgoyette } 150 1.5 pgoyette 151 1.19 riastrad static void 152 1.5 pgoyette swsensor_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 153 1.19 riastrad sysmon_envsys_lim_t *limits, uint32_t *props) 154 1.5 pgoyette { 155 1.5 pgoyette 156 1.5 pgoyette if (limits == NULL) { 157 1.5 pgoyette limits = &sw_sensor_deflims; 158 1.5 pgoyette props = &sw_sensor_defprops; 159 1.5 pgoyette } 160 1.5 pgoyette if (*props & PROP_CRITMIN) 161 1.5 pgoyette sw_sensor_limit = limits->sel_critmin; 162 1.5 pgoyette 163 1.5 pgoyette /* 164 1.5 pgoyette * If the limit we can handle (crit-min) is set, and no 165 1.5 pgoyette * other limit is set, tell sysmon that the driver will 166 1.5 pgoyette * handle the limit checking. 167 1.5 pgoyette */ 168 1.5 pgoyette if ((*props & PROP_LIMITS) == PROP_CRITMIN) 169 1.5 pgoyette *props |= PROP_DRIVER_LIMITS; 170 1.5 pgoyette else 171 1.5 pgoyette *props &= ~PROP_DRIVER_LIMITS; 172 1.1 pgoyette } 173 1.1 pgoyette 174 1.1 pgoyette /* 175 1.1 pgoyette * Module management 176 1.1 pgoyette */ 177 1.1 pgoyette 178 1.1 pgoyette static 179 1.1 pgoyette int 180 1.1 pgoyette swsensor_init(void *arg) 181 1.1 pgoyette { 182 1.12 pgoyette int error, val = 0; 183 1.10 pgoyette const char *key, *str; 184 1.1 pgoyette prop_dictionary_t pd = (prop_dictionary_t)arg; 185 1.10 pgoyette prop_object_t po, obj; 186 1.10 pgoyette prop_object_iterator_t iter; 187 1.10 pgoyette prop_type_t type; 188 1.10 pgoyette const struct sme_descr_entry *descr; 189 1.1 pgoyette 190 1.1 pgoyette swsensor_sme = sysmon_envsys_create(); 191 1.1 pgoyette if (swsensor_sme == NULL) 192 1.1 pgoyette return ENOTTY; 193 1.1 pgoyette 194 1.1 pgoyette swsensor_sme->sme_name = "swsensor"; 195 1.1 pgoyette swsensor_sme->sme_cookie = &swsensor_edata; 196 1.1 pgoyette swsensor_sme->sme_refresh = swsensor_refresh; 197 1.1 pgoyette swsensor_sme->sme_set_limits = NULL; 198 1.1 pgoyette swsensor_sme->sme_get_limits = NULL; 199 1.1 pgoyette 200 1.10 pgoyette /* Set defaults in case no prop dictionary given */ 201 1.1 pgoyette 202 1.10 pgoyette swsensor_edata.units = ENVSYS_INTEGER; 203 1.10 pgoyette swsensor_edata.flags = 0; 204 1.10 pgoyette sw_sensor_mode = 0; 205 1.10 pgoyette sw_sensor_value = 0; 206 1.10 pgoyette sw_sensor_limit = 0; 207 1.1 pgoyette 208 1.10 pgoyette /* Iterate over the provided dictionary, if any */ 209 1.10 pgoyette if (pd != NULL) { 210 1.10 pgoyette iter = prop_dictionary_iterator(pd); 211 1.10 pgoyette if (iter == NULL) 212 1.10 pgoyette return ENOMEM; 213 1.10 pgoyette 214 1.10 pgoyette while ((obj = prop_object_iterator_next(iter)) != NULL) { 215 1.17 thorpej key = prop_dictionary_keysym_value(obj); 216 1.10 pgoyette po = prop_dictionary_get_keysym(pd, obj); 217 1.10 pgoyette type = prop_object_type(po); 218 1.12 pgoyette if (type == PROP_TYPE_NUMBER) 219 1.18 thorpej val = prop_number_signed_value(po); 220 1.10 pgoyette 221 1.10 pgoyette /* Sensor type/units */ 222 1.10 pgoyette if (strcmp(key, "type") == 0) { 223 1.10 pgoyette if (type == PROP_TYPE_NUMBER) { 224 1.12 pgoyette descr = sme_find_table_entry( 225 1.12 pgoyette SME_DESC_UNITS, val); 226 1.12 pgoyette if (descr == NULL) 227 1.12 pgoyette return EINVAL; 228 1.12 pgoyette swsensor_edata.units = descr->type; 229 1.10 pgoyette continue; 230 1.10 pgoyette } 231 1.10 pgoyette if (type != PROP_TYPE_STRING) 232 1.10 pgoyette return EINVAL; 233 1.18 thorpej str = prop_string_value(po); 234 1.10 pgoyette descr = sme_find_table_desc(SME_DESC_UNITS, 235 1.10 pgoyette str); 236 1.12 pgoyette if (descr == NULL) 237 1.10 pgoyette return EINVAL; 238 1.10 pgoyette swsensor_edata.units = descr->type; 239 1.10 pgoyette continue; 240 1.10 pgoyette } 241 1.10 pgoyette 242 1.10 pgoyette /* Sensor flags */ 243 1.10 pgoyette if (strcmp(key, "flags") == 0) { 244 1.10 pgoyette if (type != PROP_TYPE_NUMBER) 245 1.10 pgoyette return EINVAL; 246 1.12 pgoyette swsensor_edata.flags = val; 247 1.10 pgoyette continue; 248 1.10 pgoyette } 249 1.10 pgoyette 250 1.10 pgoyette /* Sensor limit behavior 251 1.10 pgoyette * 0 - simple sensor, no hw limits 252 1.10 pgoyette * 1 - simple sensor, hw provides initial limit 253 1.19 riastrad * 2 - complex sensor, hw provides settable 254 1.10 pgoyette * limits and does its own limit checking 255 1.10 pgoyette */ 256 1.10 pgoyette if (strcmp(key, "mode") == 0) { 257 1.10 pgoyette if (type != PROP_TYPE_NUMBER) 258 1.10 pgoyette return EINVAL; 259 1.12 pgoyette sw_sensor_mode = val; 260 1.10 pgoyette if (sw_sensor_mode > 2) 261 1.10 pgoyette sw_sensor_mode = 2; 262 1.10 pgoyette else if (sw_sensor_mode < 0) 263 1.10 pgoyette sw_sensor_mode = 0; 264 1.10 pgoyette continue; 265 1.10 pgoyette } 266 1.10 pgoyette 267 1.10 pgoyette /* Grab any limit that might be specified */ 268 1.10 pgoyette if (strcmp(key, "limit") == 0) { 269 1.10 pgoyette if (type != PROP_TYPE_NUMBER) 270 1.10 pgoyette return EINVAL; 271 1.12 pgoyette sw_sensor_limit = val; 272 1.10 pgoyette continue; 273 1.10 pgoyette } 274 1.10 pgoyette 275 1.10 pgoyette /* Grab the initial value */ 276 1.10 pgoyette if (strcmp(key, "value") == 0) { 277 1.10 pgoyette if (type != PROP_TYPE_NUMBER) 278 1.10 pgoyette return EINVAL; 279 1.12 pgoyette sw_sensor_value = val; 280 1.10 pgoyette continue; 281 1.10 pgoyette } 282 1.10 pgoyette 283 1.10 pgoyette /* Grab value_min and value_max */ 284 1.10 pgoyette if (strcmp(key, "value_min") == 0) { 285 1.10 pgoyette if (type != PROP_TYPE_NUMBER) 286 1.10 pgoyette return EINVAL; 287 1.12 pgoyette swsensor_edata.value_min = val; 288 1.10 pgoyette swsensor_edata.flags |= ENVSYS_FVALID_MIN; 289 1.10 pgoyette continue; 290 1.10 pgoyette } 291 1.10 pgoyette if (strcmp(key, "value_max") == 0) { 292 1.10 pgoyette if (type != PROP_TYPE_NUMBER) 293 1.10 pgoyette return EINVAL; 294 1.12 pgoyette swsensor_edata.value_max = val; 295 1.10 pgoyette swsensor_edata.flags |= ENVSYS_FVALID_MAX; 296 1.10 pgoyette continue; 297 1.10 pgoyette } 298 1.10 pgoyette 299 1.10 pgoyette /* See if sensor reports percentages vs raw values */ 300 1.10 pgoyette if (strcmp(key, "percentage") == 0) { 301 1.10 pgoyette if (type != PROP_TYPE_BOOL) 302 1.10 pgoyette return EINVAL; 303 1.10 pgoyette if (prop_bool_true(po)) 304 1.10 pgoyette swsensor_edata.flags |= ENVSYS_FPERCENT; 305 1.10 pgoyette continue; 306 1.10 pgoyette } 307 1.1 pgoyette 308 1.20 andvar /* Unrecognized dictionary object */ 309 1.12 pgoyette #ifdef DEBUG 310 1.12 pgoyette printf("%s: unknown attribute %s\n", __func__, key); 311 1.12 pgoyette #endif 312 1.10 pgoyette return EINVAL; 313 1.1 pgoyette 314 1.10 pgoyette } /* while */ 315 1.10 pgoyette prop_object_iterator_release(iter); 316 1.10 pgoyette } 317 1.5 pgoyette 318 1.10 pgoyette /* Initialize limit processing */ 319 1.5 pgoyette if (sw_sensor_mode >= 1) 320 1.5 pgoyette swsensor_sme->sme_get_limits = swsensor_get_limits; 321 1.5 pgoyette 322 1.5 pgoyette if (sw_sensor_mode == 2) 323 1.5 pgoyette swsensor_sme->sme_set_limits = swsensor_set_limits; 324 1.5 pgoyette 325 1.5 pgoyette if (sw_sensor_mode != 0) { 326 1.5 pgoyette swsensor_edata.flags |= ENVSYS_FMONLIMITS; 327 1.5 pgoyette swsensor_get_limits(swsensor_sme, &swsensor_edata, 328 1.5 pgoyette &sw_sensor_deflims, &sw_sensor_defprops); 329 1.5 pgoyette } 330 1.5 pgoyette 331 1.12 pgoyette strlcpy(swsensor_edata.desc, "sensor", ENVSYS_DESCLEN); 332 1.5 pgoyette 333 1.12 pgoyette /* Wait for refresh to validate the sensor value */ 334 1.12 pgoyette swsensor_edata.state = ENVSYS_SINVALID; 335 1.13 pgoyette sw_sensor_state = ENVSYS_SVALID; 336 1.1 pgoyette 337 1.1 pgoyette error = sysmon_envsys_sensor_attach(swsensor_sme, &swsensor_edata); 338 1.10 pgoyette if (error != 0) { 339 1.7 pooka aprint_error("sysmon_envsys_sensor_attach failed: %d\n", error); 340 1.1 pgoyette return error; 341 1.1 pgoyette } 342 1.1 pgoyette 343 1.10 pgoyette error = sysmon_envsys_register(swsensor_sme); 344 1.12 pgoyette if (error != 0) { 345 1.7 pooka aprint_error("sysmon_envsys_register failed: %d\n", error); 346 1.10 pgoyette return error; 347 1.12 pgoyette } 348 1.10 pgoyette 349 1.10 pgoyette aprint_normal("swsensor: initialized\n"); 350 1.1 pgoyette 351 1.10 pgoyette return 0; 352 1.1 pgoyette } 353 1.1 pgoyette 354 1.1 pgoyette static 355 1.1 pgoyette int 356 1.1 pgoyette swsensor_fini(void *arg) 357 1.1 pgoyette { 358 1.1 pgoyette 359 1.1 pgoyette sysmon_envsys_unregister(swsensor_sme); 360 1.1 pgoyette 361 1.1 pgoyette return 0; 362 1.1 pgoyette } 363 1.1 pgoyette 364 1.1 pgoyette static 365 1.1 pgoyette int 366 1.1 pgoyette swsensor_modcmd(modcmd_t cmd, void *arg) 367 1.1 pgoyette { 368 1.1 pgoyette int ret; 369 1.1 pgoyette 370 1.1 pgoyette switch (cmd) { 371 1.1 pgoyette case MODULE_CMD_INIT: 372 1.1 pgoyette ret = swsensor_init(arg); 373 1.1 pgoyette break; 374 1.1 pgoyette 375 1.1 pgoyette case MODULE_CMD_FINI: 376 1.1 pgoyette ret = swsensor_fini(arg); 377 1.1 pgoyette break; 378 1.1 pgoyette 379 1.1 pgoyette case MODULE_CMD_STAT: 380 1.1 pgoyette default: 381 1.1 pgoyette ret = ENOTTY; 382 1.1 pgoyette } 383 1.1 pgoyette 384 1.1 pgoyette return ret; 385 1.1 pgoyette } 386 1.4 pooka 387 1.4 pooka int 388 1.4 pooka swsensorattach(int n __unused) 389 1.4 pooka { 390 1.4 pooka 391 1.5 pgoyette #ifdef MODULAR 392 1.5 pgoyette /* 393 1.5 pgoyette * Modular kernels will automatically load any built-in modules 394 1.5 pgoyette * and call their modcmd() routine, so we don't need to do it 395 1.5 pgoyette * again as part of pseudo-device configuration. 396 1.5 pgoyette */ 397 1.5 pgoyette return 0; 398 1.5 pgoyette #else 399 1.4 pooka return swsensor_init(NULL); 400 1.5 pgoyette #endif 401 1.4 pooka } 402