1/* $NetBSD$ */ 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$"); 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 46int swsensorattach(int); 47 48static struct sysctllog *swsensor_sysctllog = NULL; 49 50static int sensor_value_sysctl = 0; 51static int sensor_state_sysctl = 0; 52 53static struct sysmon_envsys *swsensor_sme; 54static envsys_data_t swsensor_edata; 55 56static int32_t sw_sensor_value; 57static int32_t sw_sensor_state; 58static int32_t sw_sensor_limit; 59static int32_t sw_sensor_mode; 60static int32_t sw_sensor_defprops; 61sysmon_envsys_lim_t sw_sensor_deflims; 62 63MODULE(MODULE_CLASS_DRIVER, swsensor, NULL); 64 65/* 66 * Set-up the sysctl interface for setting the sensor's cur_value 67 */ 68 69static 70void 71sysctl_swsensor_setup(void) 72{ 73 int ret; 74 int node_sysctl_num; 75 const struct sysctlnode *me = NULL; 76 const struct sysctlnode *me2; 77 78 KASSERT(swsensor_sysctllog == NULL); 79 80 ret = sysctl_createv(&swsensor_sysctllog, 0, NULL, &me, 81 CTLFLAG_READWRITE, 82 CTLTYPE_NODE, "swsensor", NULL, 83 NULL, 0, NULL, 0, 84 CTL_HW, CTL_CREATE, CTL_EOL); 85 if (ret != 0) 86 return; 87 88 node_sysctl_num = me->sysctl_num; 89 ret = sysctl_createv(&swsensor_sysctllog, 0, NULL, &me2, 90 CTLFLAG_READWRITE, 91 CTLTYPE_INT, "cur_value", NULL, 92 NULL, 0, &sw_sensor_value, 0, 93 CTL_HW, node_sysctl_num, CTL_CREATE, CTL_EOL); 94 95 if (ret == 0) 96 sensor_value_sysctl = me2->sysctl_num; 97 98 node_sysctl_num = me->sysctl_num; 99 ret = sysctl_createv(&swsensor_sysctllog, 0, NULL, &me2, 100 CTLFLAG_READWRITE, 101 CTLTYPE_INT, "state", NULL, 102 NULL, 0, &sw_sensor_state, 0, 103 CTL_HW, node_sysctl_num, CTL_CREATE, CTL_EOL); 104 105 if (ret == 0) 106 sensor_state_sysctl = me2->sysctl_num; 107} 108 109/* 110 * "Polling" routine to update sensor value 111 */ 112static 113void 114swsensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 115{ 116 117 edata->value_cur = sw_sensor_value; 118 119 /* If value outside of legal range, mark it invalid */ 120 if ((edata->flags & ENVSYS_FVALID_MIN && 121 edata->value_cur < edata->value_min) || 122 (edata->flags & ENVSYS_FVALID_MAX && 123 edata->value_cur > edata->value_max)) { 124 edata->state = ENVSYS_SINVALID; 125 return; 126 } 127 128 /* 129 * Set state. If we're handling the limits ourselves, do the 130 * compare; otherwise just assume the value is valid. 131 * If sensor state has been set from userland (via sysctl), 132 * just report that value. 133 */ 134 if (sw_sensor_state != ENVSYS_SVALID) 135 edata->state = sw_sensor_state; 136 else if ((sw_sensor_mode == 2) && (edata->upropset & PROP_CRITMIN) && 137 (edata->upropset & PROP_DRIVER_LIMITS) && 138 (edata->value_cur < edata->limits.sel_critmin)) 139 edata->state = ENVSYS_SCRITUNDER; 140 else 141 edata->state = ENVSYS_SVALID; 142} 143 144/* 145 * Sensor get/set limit routines 146 */ 147 148static void 149swsensor_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 150 sysmon_envsys_lim_t *limits, uint32_t *props) 151{ 152 153 *props = PROP_CRITMIN | PROP_DRIVER_LIMITS; 154 limits->sel_critmin = sw_sensor_limit; 155} 156 157static void 158swsensor_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 159 sysmon_envsys_lim_t *limits, uint32_t *props) 160{ 161 162 if (limits == NULL) { 163 limits = &sw_sensor_deflims; 164 props = &sw_sensor_defprops; 165 } 166 if (*props & PROP_CRITMIN) 167 sw_sensor_limit = limits->sel_critmin; 168 169 /* 170 * If the limit we can handle (crit-min) is set, and no 171 * other limit is set, tell sysmon that the driver will 172 * handle the limit checking. 173 */ 174 if ((*props & PROP_LIMITS) == PROP_CRITMIN) 175 *props |= PROP_DRIVER_LIMITS; 176 else 177 *props &= ~PROP_DRIVER_LIMITS; 178} 179 180/* 181 * Module management 182 */ 183 184static 185int 186swsensor_init(void *arg) 187{ 188 int error, val = 0; 189 const char *key, *str; 190 prop_dictionary_t pd = (prop_dictionary_t)arg; 191 prop_object_t po, obj; 192 prop_object_iterator_t iter; 193 prop_type_t type; 194 const struct sme_descr_entry *descr; 195 196 swsensor_sme = sysmon_envsys_create(); 197 if (swsensor_sme == NULL) 198 return ENOTTY; 199 200 swsensor_sme->sme_name = "swsensor"; 201 swsensor_sme->sme_cookie = &swsensor_edata; 202 swsensor_sme->sme_refresh = swsensor_refresh; 203 swsensor_sme->sme_set_limits = NULL; 204 swsensor_sme->sme_get_limits = NULL; 205 206 /* Set defaults in case no prop dictionary given */ 207 208 swsensor_edata.units = ENVSYS_INTEGER; 209 swsensor_edata.flags = 0; 210 sw_sensor_mode = 0; 211 sw_sensor_value = 0; 212 sw_sensor_limit = 0; 213 214 /* Iterate over the provided dictionary, if any */ 215 if (pd != NULL) { 216 iter = prop_dictionary_iterator(pd); 217 if (iter == NULL) 218 return ENOMEM; 219 220 while ((obj = prop_object_iterator_next(iter)) != NULL) { 221 key = prop_dictionary_keysym_cstring_nocopy(obj); 222 po = prop_dictionary_get_keysym(pd, obj); 223 type = prop_object_type(po); 224 if (type == PROP_TYPE_NUMBER) 225 val = prop_number_integer_value(po); 226 227 /* Sensor type/units */ 228 if (strcmp(key, "type") == 0) { 229 if (type == PROP_TYPE_NUMBER) { 230 descr = sme_find_table_entry( 231 SME_DESC_UNITS, val); 232 if (descr == NULL) 233 return EINVAL; 234 swsensor_edata.units = descr->type; 235 continue; 236 } 237 if (type != PROP_TYPE_STRING) 238 return EINVAL; 239 str = prop_string_cstring_nocopy(po); 240 descr = sme_find_table_desc(SME_DESC_UNITS, 241 str); 242 if (descr == NULL) 243 return EINVAL; 244 swsensor_edata.units = descr->type; 245 continue; 246 } 247 248 /* Sensor flags */ 249 if (strcmp(key, "flags") == 0) { 250 if (type != PROP_TYPE_NUMBER) 251 return EINVAL; 252 swsensor_edata.flags = val; 253 continue; 254 } 255 256 /* Sensor limit behavior 257 * 0 - simple sensor, no hw limits 258 * 1 - simple sensor, hw provides initial limit 259 * 2 - complex sensor, hw provides settable 260 * limits and does its own limit checking 261 */ 262 if (strcmp(key, "mode") == 0) { 263 if (type != PROP_TYPE_NUMBER) 264 return EINVAL; 265 sw_sensor_mode = val; 266 if (sw_sensor_mode > 2) 267 sw_sensor_mode = 2; 268 else if (sw_sensor_mode < 0) 269 sw_sensor_mode = 0; 270 continue; 271 } 272 273 /* Grab any limit that might be specified */ 274 if (strcmp(key, "limit") == 0) { 275 if (type != PROP_TYPE_NUMBER) 276 return EINVAL; 277 sw_sensor_limit = val; 278 continue; 279 } 280 281 /* Grab the initial value */ 282 if (strcmp(key, "value") == 0) { 283 if (type != PROP_TYPE_NUMBER) 284 return EINVAL; 285 sw_sensor_value = val; 286 continue; 287 } 288 289 /* Grab value_min and value_max */ 290 if (strcmp(key, "value_min") == 0) { 291 if (type != PROP_TYPE_NUMBER) 292 return EINVAL; 293 swsensor_edata.value_min = val; 294 swsensor_edata.flags |= ENVSYS_FVALID_MIN; 295 continue; 296 } 297 if (strcmp(key, "value_max") == 0) { 298 if (type != PROP_TYPE_NUMBER) 299 return EINVAL; 300 swsensor_edata.value_max = val; 301 swsensor_edata.flags |= ENVSYS_FVALID_MAX; 302 continue; 303 } 304 305 /* See if sensor reports percentages vs raw values */ 306 if (strcmp(key, "percentage") == 0) { 307 if (type != PROP_TYPE_BOOL) 308 return EINVAL; 309 if (prop_bool_true(po)) 310 swsensor_edata.flags |= ENVSYS_FPERCENT; 311 continue; 312 } 313 314 /* Unrecognized dicttionary object */ 315#ifdef DEBUG 316 printf("%s: unknown attribute %s\n", __func__, key); 317#endif 318 return EINVAL; 319 320 } /* while */ 321 prop_object_iterator_release(iter); 322 } 323 324 /* Initialize limit processing */ 325 if (sw_sensor_mode >= 1) 326 swsensor_sme->sme_get_limits = swsensor_get_limits; 327 328 if (sw_sensor_mode == 2) 329 swsensor_sme->sme_set_limits = swsensor_set_limits; 330 331 if (sw_sensor_mode != 0) { 332 swsensor_edata.flags |= ENVSYS_FMONLIMITS; 333 swsensor_get_limits(swsensor_sme, &swsensor_edata, 334 &sw_sensor_deflims, &sw_sensor_defprops); 335 } 336 337 strlcpy(swsensor_edata.desc, "sensor", ENVSYS_DESCLEN); 338 339 /* Wait for refresh to validate the sensor value */ 340 swsensor_edata.state = ENVSYS_SINVALID; 341 sw_sensor_state = ENVSYS_SVALID; 342 343 error = sysmon_envsys_sensor_attach(swsensor_sme, &swsensor_edata); 344 if (error != 0) { 345 aprint_error("sysmon_envsys_sensor_attach failed: %d\n", error); 346 return error; 347 } 348 349 error = sysmon_envsys_register(swsensor_sme); 350 if (error != 0) { 351 aprint_error("sysmon_envsys_register failed: %d\n", error); 352 return error; 353 } 354 355 sysctl_swsensor_setup(); 356 aprint_normal("swsensor: initialized\n"); 357 358 return 0; 359} 360 361static 362int 363swsensor_fini(void *arg) 364{ 365 366 sysmon_envsys_unregister(swsensor_sme); 367 368 sysctl_teardown(&swsensor_sysctllog); 369 370 return 0; 371} 372 373static 374int 375swsensor_modcmd(modcmd_t cmd, void *arg) 376{ 377 int ret; 378 379 switch (cmd) { 380 case MODULE_CMD_INIT: 381 ret = swsensor_init(arg); 382 break; 383 384 case MODULE_CMD_FINI: 385 ret = swsensor_fini(arg); 386 break; 387 388 case MODULE_CMD_STAT: 389 default: 390 ret = ENOTTY; 391 } 392 393 return ret; 394} 395 396int 397swsensorattach(int n __unused) 398{ 399 400#ifdef MODULAR 401 /* 402 * Modular kernels will automatically load any built-in modules 403 * and call their modcmd() routine, so we don't need to do it 404 * again as part of pseudo-device configuration. 405 */ 406 return 0; 407#else 408 return swsensor_init(NULL); 409#endif 410} 411