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