xref: /netbsd-src/sys/dev/sysmon/swsensor.c (revision 1cd43426d582b6650b153797f2db305dcd93c554)
1*1cd43426Sandvar /*	$NetBSD: swsensor.c,v 1.20 2024/02/10 18:43:53 andvar Exp $ */
23ae668c1Spgoyette /*
33ae668c1Spgoyette  * Copyright (c) 2008 The NetBSD Foundation, Inc.
43ae668c1Spgoyette  * All rights reserved.
53ae668c1Spgoyette  *
63ae668c1Spgoyette  * Redistribution and use in source and binary forms, with or without
73ae668c1Spgoyette  * modification, are permitted provided that the following conditions
83ae668c1Spgoyette  * are met:
93ae668c1Spgoyette  * 1. Redistributions of source code must retain the above copyright
103ae668c1Spgoyette  *    notice, this list of conditions and the following disclaimer.
113ae668c1Spgoyette  * 2. Redistributions in binary form must reproduce the above copyright
123ae668c1Spgoyette  *    notice, this list of conditions and the following disclaimer in the
133ae668c1Spgoyette  *    documentation and/or other materials provided with the distribution.
143ae668c1Spgoyette  *
153ae668c1Spgoyette  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
163ae668c1Spgoyette  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
173ae668c1Spgoyette  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
183ae668c1Spgoyette  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
193ae668c1Spgoyette  * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
203ae668c1Spgoyette  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
213ae668c1Spgoyette  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
223ae668c1Spgoyette  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
233ae668c1Spgoyette  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
243ae668c1Spgoyette  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
253ae668c1Spgoyette  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
263ae668c1Spgoyette  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
273ae668c1Spgoyette  */
283ae668c1Spgoyette 
293ae668c1Spgoyette #include <sys/cdefs.h>
30*1cd43426Sandvar __KERNEL_RCSID(0, "$NetBSD: swsensor.c,v 1.20 2024/02/10 18:43:53 andvar Exp $");
313ae668c1Spgoyette 
323ae668c1Spgoyette #include <sys/param.h>
333ae668c1Spgoyette #include <sys/kernel.h>
343ae668c1Spgoyette #include <sys/module.h>
353ae668c1Spgoyette #include <sys/sysctl.h>
363ae668c1Spgoyette 
373ae668c1Spgoyette #include <dev/sysmon/sysmonvar.h>
38eff7c483Spgoyette #include <dev/sysmon/sysmon_envsysvar.h>
393ae668c1Spgoyette 
403ae668c1Spgoyette #include <prop/proplib.h>
413ae668c1Spgoyette 
42cedd9a0bSpgoyette #ifndef _MODULE
43cedd9a0bSpgoyette #include "opt_modular.h"
44cedd9a0bSpgoyette #endif
45cedd9a0bSpgoyette 
463ae668c1Spgoyette int swsensorattach(int);
473ae668c1Spgoyette 
483ae668c1Spgoyette static int sensor_value_sysctl = 0;
494525347aSpgoyette static int sensor_state_sysctl = 0;
503ae668c1Spgoyette 
513ae668c1Spgoyette static struct sysmon_envsys *swsensor_sme;
523ae668c1Spgoyette static envsys_data_t swsensor_edata;
533ae668c1Spgoyette 
543ae668c1Spgoyette static int32_t sw_sensor_value;
554525347aSpgoyette static int32_t sw_sensor_state;
56cedd9a0bSpgoyette static int32_t sw_sensor_limit;
57cedd9a0bSpgoyette static int32_t sw_sensor_mode;
58cedd9a0bSpgoyette static int32_t sw_sensor_defprops;
59cedd9a0bSpgoyette sysmon_envsys_lim_t sw_sensor_deflims;
603ae668c1Spgoyette 
6176a2f91aSpgoyette MODULE(MODULE_CLASS_DRIVER, swsensor, "sysmon_envsys");
623ae668c1Spgoyette 
633ae668c1Spgoyette /*
643ae668c1Spgoyette  * Set-up the sysctl interface for setting the sensor's cur_value
653ae668c1Spgoyette  */
663ae668c1Spgoyette 
679120d451Spgoyette SYSCTL_SETUP(sysctl_swsensor_setup, "swsensor sysctl")
683ae668c1Spgoyette {
693ae668c1Spgoyette 	int ret;
703ae668c1Spgoyette 	int node_sysctl_num;
713ae668c1Spgoyette 	const struct sysctlnode *me = NULL;
724525347aSpgoyette 	const struct sysctlnode *me2;
733ae668c1Spgoyette 
749120d451Spgoyette 	ret = sysctl_createv(clog, 0, NULL, &me,
753ae668c1Spgoyette 			     CTLFLAG_READWRITE,
763ae668c1Spgoyette 			     CTLTYPE_NODE, "swsensor", NULL,
773ae668c1Spgoyette 			     NULL, 0, NULL, 0,
783ae668c1Spgoyette 			     CTL_HW, CTL_CREATE, CTL_EOL);
793ae668c1Spgoyette 	if (ret != 0)
803ae668c1Spgoyette 		return;
813ae668c1Spgoyette 
823ae668c1Spgoyette 	node_sysctl_num = me->sysctl_num;
839120d451Spgoyette 	ret = sysctl_createv(clog, 0, NULL, &me2,
843ae668c1Spgoyette 			     CTLFLAG_READWRITE,
853ae668c1Spgoyette 			     CTLTYPE_INT, "cur_value", NULL,
863ae668c1Spgoyette 			     NULL, 0, &sw_sensor_value, 0,
873ae668c1Spgoyette 			     CTL_HW, node_sysctl_num, CTL_CREATE, CTL_EOL);
883ae668c1Spgoyette 
893ae668c1Spgoyette 	if (ret == 0)
904525347aSpgoyette 		sensor_value_sysctl = me2->sysctl_num;
914525347aSpgoyette 
924525347aSpgoyette 	node_sysctl_num = me->sysctl_num;
939120d451Spgoyette 	ret = sysctl_createv(clog, 0, NULL, &me2,
944525347aSpgoyette 			     CTLFLAG_READWRITE,
954525347aSpgoyette 			     CTLTYPE_INT, "state", NULL,
964525347aSpgoyette 			     NULL, 0, &sw_sensor_state, 0,
974525347aSpgoyette 			     CTL_HW, node_sysctl_num, CTL_CREATE, CTL_EOL);
984525347aSpgoyette 
994525347aSpgoyette 	if (ret == 0)
1004525347aSpgoyette 		sensor_state_sysctl = me2->sysctl_num;
1013ae668c1Spgoyette }
1023ae668c1Spgoyette 
1033ae668c1Spgoyette /*
1043ae668c1Spgoyette  * "Polling" routine to update sensor value
1053ae668c1Spgoyette  */
1063ae668c1Spgoyette static
1073ae668c1Spgoyette void
swsensor_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)1083ae668c1Spgoyette swsensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
1093ae668c1Spgoyette {
1103ae668c1Spgoyette 
1113ae668c1Spgoyette 	edata->value_cur = sw_sensor_value;
112cedd9a0bSpgoyette 
1133dbac3b2Spgoyette 	/* If value outside of legal range, mark it invalid */
1143dbac3b2Spgoyette 	if ((edata->flags & ENVSYS_FVALID_MIN &&
1153dbac3b2Spgoyette 	     edata->value_cur < edata->value_min) ||
1163dbac3b2Spgoyette 	    (edata->flags & ENVSYS_FVALID_MAX &&
1173dbac3b2Spgoyette 	     edata->value_cur > edata->value_max)) {
1183dbac3b2Spgoyette 		edata->state = ENVSYS_SINVALID;
1193dbac3b2Spgoyette 		return;
1203dbac3b2Spgoyette 	}
1213dbac3b2Spgoyette 
122cedd9a0bSpgoyette 	/*
123cedd9a0bSpgoyette 	 * Set state.  If we're handling the limits ourselves, do the
124cedd9a0bSpgoyette 	 * compare; otherwise just assume the value is valid.
1254525347aSpgoyette 	 * If sensor state has been set from userland (via sysctl),
1264525347aSpgoyette 	 * just report that value.
127cedd9a0bSpgoyette 	 */
1284525347aSpgoyette 	if (sw_sensor_state != ENVSYS_SVALID)
1294525347aSpgoyette 		edata->state = sw_sensor_state;
1304525347aSpgoyette 	else if ((sw_sensor_mode == 2) && (edata->upropset & PROP_CRITMIN) &&
131cedd9a0bSpgoyette 	    (edata->upropset & PROP_DRIVER_LIMITS) &&
132cedd9a0bSpgoyette 	    (edata->value_cur < edata->limits.sel_critmin))
133cedd9a0bSpgoyette 		edata->state = ENVSYS_SCRITUNDER;
134cedd9a0bSpgoyette 	else
13541dc37e6Spooka 		edata->state = ENVSYS_SVALID;
1363ae668c1Spgoyette }
1373ae668c1Spgoyette 
1383ae668c1Spgoyette /*
139cedd9a0bSpgoyette  * Sensor get/set limit routines
140cedd9a0bSpgoyette  */
141cedd9a0bSpgoyette 
142cedd9a0bSpgoyette static void
swsensor_get_limits(struct sysmon_envsys * sme,envsys_data_t * edata,sysmon_envsys_lim_t * limits,uint32_t * props)143cedd9a0bSpgoyette swsensor_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
144cedd9a0bSpgoyette                   sysmon_envsys_lim_t *limits, uint32_t *props)
145cedd9a0bSpgoyette {
146cedd9a0bSpgoyette 
147cedd9a0bSpgoyette 	*props = PROP_CRITMIN | PROP_DRIVER_LIMITS;
148cedd9a0bSpgoyette 	limits->sel_critmin = sw_sensor_limit;
149cedd9a0bSpgoyette }
150cedd9a0bSpgoyette 
151cedd9a0bSpgoyette static void
swsensor_set_limits(struct sysmon_envsys * sme,envsys_data_t * edata,sysmon_envsys_lim_t * limits,uint32_t * props)152cedd9a0bSpgoyette swsensor_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
153cedd9a0bSpgoyette                   sysmon_envsys_lim_t *limits, uint32_t *props)
154cedd9a0bSpgoyette {
155cedd9a0bSpgoyette 
156cedd9a0bSpgoyette 	if (limits == NULL) {
157cedd9a0bSpgoyette 		limits = &sw_sensor_deflims;
158cedd9a0bSpgoyette 		props = &sw_sensor_defprops;
159cedd9a0bSpgoyette 	}
160cedd9a0bSpgoyette 	if (*props & PROP_CRITMIN)
161cedd9a0bSpgoyette 		sw_sensor_limit = limits->sel_critmin;
162cedd9a0bSpgoyette 
163cedd9a0bSpgoyette 	/*
164cedd9a0bSpgoyette 	 * If the limit we can handle (crit-min) is set, and no
165cedd9a0bSpgoyette 	 * other limit is set, tell sysmon that the driver will
166cedd9a0bSpgoyette 	 * handle the limit checking.
167cedd9a0bSpgoyette 	 */
168cedd9a0bSpgoyette 	if ((*props & PROP_LIMITS) == PROP_CRITMIN)
169cedd9a0bSpgoyette 		*props |= PROP_DRIVER_LIMITS;
170cedd9a0bSpgoyette 	else
171cedd9a0bSpgoyette 		*props &= ~PROP_DRIVER_LIMITS;
172cedd9a0bSpgoyette }
173cedd9a0bSpgoyette 
174cedd9a0bSpgoyette /*
1753ae668c1Spgoyette  * Module management
1763ae668c1Spgoyette  */
1773ae668c1Spgoyette 
1783ae668c1Spgoyette static
1793ae668c1Spgoyette int
swsensor_init(void * arg)1803ae668c1Spgoyette swsensor_init(void *arg)
1813ae668c1Spgoyette {
1823dbac3b2Spgoyette 	int error, val = 0;
183eff7c483Spgoyette 	const char *key, *str;
1843ae668c1Spgoyette 	prop_dictionary_t pd = (prop_dictionary_t)arg;
185eff7c483Spgoyette 	prop_object_t po, obj;
186eff7c483Spgoyette 	prop_object_iterator_t iter;
187eff7c483Spgoyette 	prop_type_t type;
188eff7c483Spgoyette 	const struct sme_descr_entry *descr;
1893ae668c1Spgoyette 
1903ae668c1Spgoyette 	swsensor_sme = sysmon_envsys_create();
1913ae668c1Spgoyette 	if (swsensor_sme == NULL)
1923ae668c1Spgoyette 		return ENOTTY;
1933ae668c1Spgoyette 
1943ae668c1Spgoyette 	swsensor_sme->sme_name = "swsensor";
1953ae668c1Spgoyette 	swsensor_sme->sme_cookie = &swsensor_edata;
1963ae668c1Spgoyette 	swsensor_sme->sme_refresh = swsensor_refresh;
1973ae668c1Spgoyette 	swsensor_sme->sme_set_limits = NULL;
1983ae668c1Spgoyette 	swsensor_sme->sme_get_limits = NULL;
1993ae668c1Spgoyette 
200eff7c483Spgoyette 	/* Set defaults in case no prop dictionary given */
2013ae668c1Spgoyette 
2023ae668c1Spgoyette 	swsensor_edata.units = ENVSYS_INTEGER;
2033ae668c1Spgoyette 	swsensor_edata.flags = 0;
204eff7c483Spgoyette 	sw_sensor_mode = 0;
205eff7c483Spgoyette 	sw_sensor_value = 0;
206eff7c483Spgoyette 	sw_sensor_limit = 0;
2073ae668c1Spgoyette 
208eff7c483Spgoyette 	/* Iterate over the provided dictionary, if any */
209eff7c483Spgoyette 	if (pd != NULL) {
210eff7c483Spgoyette 		iter = prop_dictionary_iterator(pd);
211eff7c483Spgoyette 		if (iter == NULL)
212eff7c483Spgoyette 			return ENOMEM;
213eff7c483Spgoyette 
214eff7c483Spgoyette 		while ((obj = prop_object_iterator_next(iter)) != NULL) {
215a2b798fdSthorpej 			key = prop_dictionary_keysym_value(obj);
216eff7c483Spgoyette 			po  = prop_dictionary_get_keysym(pd, obj);
217eff7c483Spgoyette 			type = prop_object_type(po);
2183dbac3b2Spgoyette 			if (type == PROP_TYPE_NUMBER)
219814a7798Sthorpej 				val = prop_number_signed_value(po);
220eff7c483Spgoyette 
221eff7c483Spgoyette 			/* Sensor type/units */
222eff7c483Spgoyette 			if (strcmp(key, "type") == 0) {
223eff7c483Spgoyette 				if (type == PROP_TYPE_NUMBER) {
2243dbac3b2Spgoyette 					descr = sme_find_table_entry(
2253dbac3b2Spgoyette 							SME_DESC_UNITS, val);
2263dbac3b2Spgoyette 					if (descr == NULL)
2273dbac3b2Spgoyette 						return EINVAL;
2283dbac3b2Spgoyette 					swsensor_edata.units = descr->type;
229eff7c483Spgoyette 					continue;
230eff7c483Spgoyette 				}
231eff7c483Spgoyette 				if (type != PROP_TYPE_STRING)
232eff7c483Spgoyette 					return EINVAL;
233814a7798Sthorpej 				str = prop_string_value(po);
234eff7c483Spgoyette 				descr = sme_find_table_desc(SME_DESC_UNITS,
235eff7c483Spgoyette 							    str);
2363dbac3b2Spgoyette 				if (descr == NULL)
237eff7c483Spgoyette 					return EINVAL;
238eff7c483Spgoyette 				swsensor_edata.units = descr->type;
239eff7c483Spgoyette 				continue;
240eff7c483Spgoyette 			}
241eff7c483Spgoyette 
242eff7c483Spgoyette 			/* Sensor flags */
243eff7c483Spgoyette 			if (strcmp(key, "flags") == 0) {
244eff7c483Spgoyette 				if (type != PROP_TYPE_NUMBER)
245eff7c483Spgoyette 					return EINVAL;
2463dbac3b2Spgoyette 				swsensor_edata.flags = val;
247eff7c483Spgoyette 				continue;
248eff7c483Spgoyette 			}
249eff7c483Spgoyette 
250eff7c483Spgoyette 			/* Sensor limit behavior
251cedd9a0bSpgoyette 			 *	0 - simple sensor, no hw limits
252eff7c483Spgoyette 			 *	1 - simple sensor, hw provides initial limit
253eff7c483Spgoyette 			 *	2 - complex sensor, hw provides settable
254eff7c483Spgoyette 			 *	    limits and does its own limit checking
255cedd9a0bSpgoyette 			 */
256eff7c483Spgoyette 			if (strcmp(key, "mode") == 0) {
257eff7c483Spgoyette 				if (type != PROP_TYPE_NUMBER)
258eff7c483Spgoyette 					return EINVAL;
2593dbac3b2Spgoyette 				sw_sensor_mode = val;
260cedd9a0bSpgoyette 				if (sw_sensor_mode > 2)
261cedd9a0bSpgoyette 					sw_sensor_mode = 2;
262eff7c483Spgoyette 				else if (sw_sensor_mode < 0)
263cedd9a0bSpgoyette 					sw_sensor_mode = 0;
264eff7c483Spgoyette 				continue;
265eff7c483Spgoyette 			}
266cedd9a0bSpgoyette 
267eff7c483Spgoyette 			/* Grab any limit that might be specified */
268eff7c483Spgoyette 			if (strcmp(key, "limit") == 0) {
269eff7c483Spgoyette 				if (type != PROP_TYPE_NUMBER)
270eff7c483Spgoyette 					return EINVAL;
2713dbac3b2Spgoyette 				sw_sensor_limit = val;
272eff7c483Spgoyette 				continue;
273eff7c483Spgoyette 			}
274eff7c483Spgoyette 
275eff7c483Spgoyette 			/* Grab the initial value */
276eff7c483Spgoyette 			if (strcmp(key, "value") == 0) {
277eff7c483Spgoyette 				if (type != PROP_TYPE_NUMBER)
278eff7c483Spgoyette 					return EINVAL;
2793dbac3b2Spgoyette 				sw_sensor_value = val;
280eff7c483Spgoyette 				continue;
281eff7c483Spgoyette 			}
282eff7c483Spgoyette 
283eff7c483Spgoyette 			/* Grab value_min and value_max */
284eff7c483Spgoyette 			if (strcmp(key, "value_min") == 0) {
285eff7c483Spgoyette 				if (type != PROP_TYPE_NUMBER)
286eff7c483Spgoyette 					return EINVAL;
2873dbac3b2Spgoyette 				swsensor_edata.value_min = val;
288eff7c483Spgoyette 				swsensor_edata.flags |= ENVSYS_FVALID_MIN;
289eff7c483Spgoyette 				continue;
290eff7c483Spgoyette 			}
291eff7c483Spgoyette 			if (strcmp(key, "value_max") == 0) {
292eff7c483Spgoyette 				if (type != PROP_TYPE_NUMBER)
293eff7c483Spgoyette 					return EINVAL;
2943dbac3b2Spgoyette 				swsensor_edata.value_max = val;
295eff7c483Spgoyette 				swsensor_edata.flags |= ENVSYS_FVALID_MAX;
296eff7c483Spgoyette 				continue;
297eff7c483Spgoyette 			}
298eff7c483Spgoyette 
299eff7c483Spgoyette 			/* See if sensor reports percentages vs raw values */
300eff7c483Spgoyette 			if (strcmp(key, "percentage") == 0) {
301eff7c483Spgoyette 				if (type != PROP_TYPE_BOOL)
302eff7c483Spgoyette 					return EINVAL;
303eff7c483Spgoyette 				if (prop_bool_true(po))
304eff7c483Spgoyette 					swsensor_edata.flags |= ENVSYS_FPERCENT;
305eff7c483Spgoyette 				continue;
306eff7c483Spgoyette 			}
307eff7c483Spgoyette 
308*1cd43426Sandvar 			/* Unrecognized dictionary object */
3093dbac3b2Spgoyette #ifdef DEBUG
3103dbac3b2Spgoyette 			printf("%s: unknown attribute %s\n", __func__, key);
3113dbac3b2Spgoyette #endif
312eff7c483Spgoyette 			return EINVAL;
313eff7c483Spgoyette 
314eff7c483Spgoyette 		} /* while */
315eff7c483Spgoyette 		prop_object_iterator_release(iter);
316eff7c483Spgoyette 	}
317eff7c483Spgoyette 
318eff7c483Spgoyette 	/* Initialize limit processing */
319cedd9a0bSpgoyette 	if (sw_sensor_mode >= 1)
320cedd9a0bSpgoyette 		swsensor_sme->sme_get_limits = swsensor_get_limits;
321cedd9a0bSpgoyette 
322cedd9a0bSpgoyette 	if (sw_sensor_mode == 2)
323cedd9a0bSpgoyette 		swsensor_sme->sme_set_limits = swsensor_set_limits;
324cedd9a0bSpgoyette 
325cedd9a0bSpgoyette 	if (sw_sensor_mode != 0) {
326cedd9a0bSpgoyette 		swsensor_edata.flags |= ENVSYS_FMONLIMITS;
327cedd9a0bSpgoyette 		swsensor_get_limits(swsensor_sme, &swsensor_edata,
328cedd9a0bSpgoyette 		    &sw_sensor_deflims, &sw_sensor_defprops);
329cedd9a0bSpgoyette 	}
330cedd9a0bSpgoyette 
3313ae668c1Spgoyette 	strlcpy(swsensor_edata.desc, "sensor", ENVSYS_DESCLEN);
3323ae668c1Spgoyette 
3333dbac3b2Spgoyette 	/* Wait for refresh to validate the sensor value */
3343dbac3b2Spgoyette 	swsensor_edata.state = ENVSYS_SINVALID;
3354525347aSpgoyette 	sw_sensor_state = ENVSYS_SVALID;
3363dbac3b2Spgoyette 
3373ae668c1Spgoyette 	error = sysmon_envsys_sensor_attach(swsensor_sme, &swsensor_edata);
338eff7c483Spgoyette 	if (error != 0) {
3395b13bc43Spooka 		aprint_error("sysmon_envsys_sensor_attach failed: %d\n", error);
3403ae668c1Spgoyette 		return error;
3413ae668c1Spgoyette 	}
3423ae668c1Spgoyette 
343eff7c483Spgoyette 	error = sysmon_envsys_register(swsensor_sme);
3443dbac3b2Spgoyette 	if (error != 0) {
3455b13bc43Spooka 		aprint_error("sysmon_envsys_register failed: %d\n", error);
3463ae668c1Spgoyette 		return error;
3473dbac3b2Spgoyette 	}
348eff7c483Spgoyette 
349eff7c483Spgoyette 	aprint_normal("swsensor: initialized\n");
350eff7c483Spgoyette 
351eff7c483Spgoyette 	return 0;
3523ae668c1Spgoyette }
3533ae668c1Spgoyette 
3543ae668c1Spgoyette static
3553ae668c1Spgoyette int
swsensor_fini(void * arg)3563ae668c1Spgoyette swsensor_fini(void *arg)
3573ae668c1Spgoyette {
3583ae668c1Spgoyette 
3593ae668c1Spgoyette 	sysmon_envsys_unregister(swsensor_sme);
3603ae668c1Spgoyette 
3613ae668c1Spgoyette 	return 0;
3623ae668c1Spgoyette }
3633ae668c1Spgoyette 
3643ae668c1Spgoyette static
3653ae668c1Spgoyette int
swsensor_modcmd(modcmd_t cmd,void * arg)3663ae668c1Spgoyette swsensor_modcmd(modcmd_t cmd, void *arg)
3673ae668c1Spgoyette {
3683ae668c1Spgoyette 	int ret;
3693ae668c1Spgoyette 
3703ae668c1Spgoyette 	switch (cmd) {
3713ae668c1Spgoyette 	case MODULE_CMD_INIT:
3723ae668c1Spgoyette 		ret = swsensor_init(arg);
3733ae668c1Spgoyette 		break;
3743ae668c1Spgoyette 
3753ae668c1Spgoyette 	case MODULE_CMD_FINI:
3763ae668c1Spgoyette 		ret = swsensor_fini(arg);
3773ae668c1Spgoyette 		break;
3783ae668c1Spgoyette 
3793ae668c1Spgoyette 	case MODULE_CMD_STAT:
3803ae668c1Spgoyette 	default:
3813ae668c1Spgoyette 		ret = ENOTTY;
3823ae668c1Spgoyette 	}
3833ae668c1Spgoyette 
3843ae668c1Spgoyette 	return ret;
3853ae668c1Spgoyette }
3861b3fe95dSpooka 
3871b3fe95dSpooka int
swsensorattach(int n __unused)3881b3fe95dSpooka swsensorattach(int n __unused)
3891b3fe95dSpooka {
3901b3fe95dSpooka 
391cedd9a0bSpgoyette #ifdef MODULAR
392cedd9a0bSpgoyette 	/*
393cedd9a0bSpgoyette 	 * Modular kernels will automatically load any built-in modules
394cedd9a0bSpgoyette 	 * and call their modcmd() routine, so we don't need to do it
395cedd9a0bSpgoyette 	 * again as part of pseudo-device configuration.
396cedd9a0bSpgoyette 	 */
397cedd9a0bSpgoyette 	return 0;
398cedd9a0bSpgoyette #else
3991b3fe95dSpooka 	return swsensor_init(NULL);
400cedd9a0bSpgoyette #endif
4011b3fe95dSpooka }
402