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