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