xref: /illumos-gate/usr/src/lib/libipmi/common/ipmi_sdr.c (revision a724c049b7e0dd8612bc3aaec84e96e80511050d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <libipmi.h>
28 #include <stddef.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <math.h>
32 
33 #include "ipmi_impl.h"
34 
35 /*
36  * This macros are used by ipmi_sdr_conv_reading.  They were taken verbatim from
37  * the source for ipmitool (v1.88)
38  */
39 #define	tos32(val, bits)	((val & ((1<<((bits)-1)))) ? (-((val) & \
40 				(1<<((bits)-1))) | (val)) : (val))
41 
42 #define	__TO_TOL(mtol)	(uint16_t)(BSWAP_16(mtol) & 0x3f)
43 
44 #define	__TO_M(mtol)	(int16_t)(tos32((((BSWAP_16(mtol) & 0xff00) >> 8) | \
45 				((BSWAP_16(mtol) & 0xc0) << 2)), 10))
46 
47 #define	__TO_B(bacc)	(int32_t)(tos32((((BSWAP_32(bacc) & \
48 				0xff000000) >> 24) | \
49 				((BSWAP_32(bacc) & 0xc00000) >> 14)), 10))
50 
51 #define	__TO_ACC(bacc)	(uint32_t)(((BSWAP_32(bacc) & 0x3f0000) >> 16) | \
52 				((BSWAP_32(bacc) & 0xf000) >> 6))
53 
54 #define	__TO_ACC_EXP(bacc)	(uint32_t)((BSWAP_32(bacc) & 0xc00) >> 10)
55 #define	__TO_R_EXP(bacc)	(int32_t)(tos32(((BSWAP_32(bacc) & 0xf0) >> 4),\
56 				4))
57 #define	__TO_B_EXP(bacc)	(int32_t)(tos32((BSWAP_32(bacc) & 0xf), 4))
58 
59 #define	SDR_SENSOR_L_LINEAR	0x00
60 #define	SDR_SENSOR_L_LN		0x01
61 #define	SDR_SENSOR_L_LOG10	0x02
62 #define	SDR_SENSOR_L_LOG2	0x03
63 #define	SDR_SENSOR_L_E		0x04
64 #define	SDR_SENSOR_L_EXP10	0x05
65 #define	SDR_SENSOR_L_EXP2	0x06
66 #define	SDR_SENSOR_L_1_X	0x07
67 #define	SDR_SENSOR_L_SQR	0x08
68 #define	SDR_SENSOR_L_CUBE	0x09
69 #define	SDR_SENSOR_L_SQRT	0x0a
70 #define	SDR_SENSOR_L_CUBERT	0x0b
71 #define	SDR_SENSOR_L_NONLINEAR	0x70
72 
73 /*
74  * Analog sensor reading data formats
75  *
76  * See Section 43.1
77  */
78 #define	IPMI_DATA_FMT_UNSIGNED	0
79 #define	IPMI_DATA_FMT_ONESCOMP	1
80 #define	IPMI_DATA_FMT_TWOSCOMP	2
81 
82 typedef struct ipmi_sdr_cache_ent {
83 	char				*isc_name;
84 	struct ipmi_sdr			*isc_sdr;
85 	ipmi_hash_link_t		isc_link;
86 } ipmi_sdr_cache_ent_t;
87 
88 typedef struct ipmi_cmd_get_sdr {
89 	uint16_t	ic_gs_resid;
90 	uint16_t	ic_gs_recid;
91 	uint8_t		ic_gs_offset;
92 	uint8_t		ic_gs_len;
93 } ipmi_cmd_get_sdr_t;
94 
95 typedef struct ipmi_rsp_get_sdr {
96 	uint16_t	ir_gs_next;
97 	uint8_t		ir_gs_record[1];
98 } ipmi_rsp_get_sdr_t;
99 
100 /*
101  * "Get SDR Repostiory Info" command.
102  */
103 ipmi_sdr_info_t *
104 ipmi_sdr_get_info(ipmi_handle_t *ihp)
105 {
106 	ipmi_cmd_t cmd, *rsp;
107 	ipmi_sdr_info_t *sip;
108 	uint16_t tmp16;
109 	uint32_t tmp32;
110 
111 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
112 	cmd.ic_lun = 0;
113 	cmd.ic_cmd = IPMI_CMD_GET_SDR_INFO;
114 	cmd.ic_dlen = 0;
115 	cmd.ic_data = NULL;
116 
117 	if ((rsp = ipmi_send(ihp, &cmd)) == NULL)
118 		return (NULL);
119 
120 	sip = rsp->ic_data;
121 
122 	tmp16 = LE_IN16(&sip->isi_record_count);
123 	(void) memcpy(&sip->isi_record_count, &tmp16, sizeof (tmp16));
124 
125 	tmp16 = LE_IN16(&sip->isi_free_space);
126 	(void) memcpy(&sip->isi_free_space, &tmp16, sizeof (tmp16));
127 
128 	tmp32 = LE_IN32(&sip->isi_add_ts);
129 	(void) memcpy(&sip->isi_add_ts, &tmp32, sizeof (tmp32));
130 
131 	tmp32 = LE_IN32(&sip->isi_erase_ts);
132 	(void) memcpy(&sip->isi_erase_ts, &tmp32, sizeof (tmp32));
133 
134 	return (sip);
135 }
136 
137 /*
138  * Issue the "Reserve SDR Repository" command.
139  */
140 static int
141 ipmi_sdr_reserve_repository(ipmi_handle_t *ihp)
142 {
143 	ipmi_cmd_t cmd, *rsp;
144 
145 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
146 	cmd.ic_lun = 0;
147 	cmd.ic_cmd = IPMI_CMD_RESERVE_SDR_REPOSITORY;
148 	cmd.ic_dlen = 0;
149 	cmd.ic_data = NULL;
150 
151 	if ((rsp = ipmi_send(ihp, &cmd)) == NULL)
152 		return (-1);
153 
154 	ihp->ih_reservation = *((uint16_t *)rsp->ic_data);
155 	return (0);
156 }
157 
158 /*
159  * Returns B_TRUE if the repository has changed since the cached copy was last
160  * referenced.
161  */
162 boolean_t
163 ipmi_sdr_changed(ipmi_handle_t *ihp)
164 {
165 	ipmi_sdr_info_t *sip;
166 
167 	if ((sip = ipmi_sdr_get_info(ihp)) == NULL)
168 		return (B_TRUE);
169 
170 	return (sip->isi_add_ts > ihp->ih_sdr_ts ||
171 	    sip->isi_erase_ts > ihp->ih_sdr_ts ||
172 	    ipmi_hash_first(ihp->ih_sdr_cache) == NULL);
173 }
174 
175 /*
176  * Refresh the cache of sensor data records.
177  */
178 int
179 ipmi_sdr_refresh(ipmi_handle_t *ihp)
180 {
181 	uint16_t id;
182 	ipmi_sdr_t *sdr;
183 	ipmi_sdr_cache_ent_t *ent;
184 	size_t namelen, len;
185 	uint8_t type;
186 	char *name;
187 	ipmi_sdr_info_t *sip;
188 
189 	if ((sip = ipmi_sdr_get_info(ihp)) == NULL)
190 		return (-1);
191 
192 	if (sip->isi_add_ts <= ihp->ih_sdr_ts &&
193 	    sip->isi_erase_ts <= ihp->ih_sdr_ts &&
194 	    ipmi_hash_first(ihp->ih_sdr_cache) != NULL)
195 		return (0);
196 
197 	ipmi_sdr_clear(ihp);
198 	ipmi_entity_clear(ihp);
199 	ihp->ih_sdr_ts = MAX(sip->isi_add_ts, sip->isi_erase_ts);
200 
201 	/*
202 	 * Iterate over all existing SDRs and add them to the cache.
203 	 */
204 	id = IPMI_SDR_FIRST;
205 	while (id != IPMI_SDR_LAST) {
206 		if ((sdr = ipmi_sdr_get(ihp, id, &id)) == NULL)
207 			goto error;
208 
209 		/*
210 		 * Extract the name from the record-specific data.
211 		 */
212 		switch (sdr->is_type) {
213 		case IPMI_SDR_TYPE_GENERIC_LOCATOR:
214 			{
215 				ipmi_sdr_generic_locator_t *glp =
216 				    (ipmi_sdr_generic_locator_t *)
217 				    sdr->is_record;
218 				namelen = glp->is_gl_idlen;
219 				type = glp->is_gl_idtype;
220 				name = glp->is_gl_idstring;
221 				break;
222 			}
223 
224 		case IPMI_SDR_TYPE_FRU_LOCATOR:
225 			{
226 				ipmi_sdr_fru_locator_t *flp =
227 				    (ipmi_sdr_fru_locator_t *)
228 				    sdr->is_record;
229 				namelen = flp->is_fl_idlen;
230 				name = flp->is_fl_idstring;
231 				type = flp->is_fl_idtype;
232 				break;
233 			}
234 
235 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
236 			{
237 				ipmi_sdr_compact_sensor_t *csp =
238 				    (ipmi_sdr_compact_sensor_t *)
239 				    sdr->is_record;
240 				uint16_t tmp;
241 
242 				namelen = csp->is_cs_idlen;
243 				type = csp->is_cs_idtype;
244 				name = csp->is_cs_idstring;
245 
246 				tmp = LE_IN16(&csp->is_cs_assert_mask);
247 				(void) memcpy(&csp->is_cs_assert_mask, &tmp,
248 				    sizeof (tmp));
249 
250 				tmp = LE_IN16(&csp->is_cs_deassert_mask);
251 				(void) memcpy(&csp->is_cs_deassert_mask, &tmp,
252 				    sizeof (tmp));
253 
254 				tmp = LE_IN16(&csp->is_cs_reading_mask);
255 				(void) memcpy(&csp->is_cs_reading_mask, &tmp,
256 				    sizeof (tmp));
257 				break;
258 			}
259 
260 		case IPMI_SDR_TYPE_FULL_SENSOR:
261 			{
262 				ipmi_sdr_full_sensor_t *fsp =
263 				    (ipmi_sdr_full_sensor_t *)
264 				    sdr->is_record;
265 				uint16_t tmp;
266 
267 				namelen = fsp->is_fs_idlen;
268 				type = fsp->is_fs_idtype;
269 				name = fsp->is_fs_idstring;
270 
271 				tmp = LE_IN16(&fsp->is_fs_assert_mask);
272 				(void) memcpy(&fsp->is_fs_assert_mask, &tmp,
273 				    sizeof (tmp));
274 
275 				tmp = LE_IN16(&fsp->is_fs_deassert_mask);
276 				(void) memcpy(&fsp->is_fs_deassert_mask, &tmp,
277 				    sizeof (tmp));
278 
279 				tmp = LE_IN16(&fsp->is_fs_reading_mask);
280 				(void) memcpy(&fsp->is_fs_reading_mask, &tmp,
281 				    sizeof (tmp));
282 				break;
283 			}
284 
285 		case IPMI_SDR_TYPE_EVENT_ONLY:
286 			{
287 				ipmi_sdr_event_only_t *esp =
288 				    (ipmi_sdr_event_only_t *)
289 				    sdr->is_record;
290 				namelen = esp->is_eo_idlen;
291 				type = esp->is_eo_idtype;
292 				name = esp->is_eo_idstring;
293 				break;
294 			}
295 
296 		case IPMI_SDR_TYPE_MANAGEMENT_LOCATOR:
297 			{
298 				ipmi_sdr_management_locator_t *msp =
299 				    (ipmi_sdr_management_locator_t *)
300 				    sdr->is_record;
301 				namelen = msp->is_ml_idlen;
302 				type = msp->is_ml_idtype;
303 				name = msp->is_ml_idstring;
304 				break;
305 			}
306 
307 		case IPMI_SDR_TYPE_MANAGEMENT_CONFIRMATION:
308 			{
309 				ipmi_sdr_management_confirmation_t *mcp =
310 				    (ipmi_sdr_management_confirmation_t *)
311 				    sdr->is_record;
312 				uint16_t tmp;
313 
314 				name = NULL;
315 				tmp = LE_IN16(&mcp->is_mc_product);
316 				(void) memcpy(&mcp->is_mc_product, &tmp,
317 				    sizeof (tmp));
318 				break;
319 			}
320 
321 		default:
322 			name = NULL;
323 		}
324 
325 		if ((ent = ipmi_zalloc(ihp,
326 		    sizeof (ipmi_sdr_cache_ent_t))) == NULL)
327 			goto error;
328 
329 		len = sdr->is_length + offsetof(ipmi_sdr_t, is_record);
330 		if ((ent->isc_sdr = ipmi_alloc(ihp, len)) == NULL) {
331 			ipmi_free(ihp, ent);
332 			goto error;
333 		}
334 		bcopy(sdr, ent->isc_sdr, len);
335 
336 		if (name != NULL) {
337 			if ((ent->isc_name = ipmi_alloc(ihp, namelen + 1)) ==
338 			    NULL) {
339 				ipmi_free(ihp, ent->isc_sdr);
340 				ipmi_free(ihp, ent);
341 				goto error;
342 			}
343 
344 			ipmi_decode_string(type, namelen, name, ent->isc_name);
345 		}
346 
347 		/*
348 		 * This should never happen.  It means that the SP has returned
349 		 * a SDR record twice, with the same name and ID.  This has
350 		 * been observed on service processors that don't correctly
351 		 * return SDR_LAST during iteration, so assume we've looped in
352 		 * the SDR and return gracefully.
353 		 */
354 		if (ipmi_hash_lookup(ihp->ih_sdr_cache, ent) != NULL) {
355 			ipmi_free(ihp, ent->isc_sdr);
356 			ipmi_free(ihp, ent->isc_name);
357 			ipmi_free(ihp, ent);
358 			break;
359 		}
360 
361 		ipmi_hash_insert(ihp->ih_sdr_cache, ent);
362 	}
363 
364 	return (0);
365 
366 error:
367 	ipmi_sdr_clear(ihp);
368 	ipmi_entity_clear(ihp);
369 	return (-1);
370 }
371 
372 /*
373  * Hash routines.  We allow lookup by name, but since not all entries have
374  * names, we fall back to the entry pointer, which is guaranteed to be unique.
375  * The end result is that entities without names cannot be looked up, but will
376  * show up during iteration.
377  */
378 static const void *
379 ipmi_sdr_hash_convert(const void *p)
380 {
381 	return (p);
382 }
383 
384 static ulong_t
385 ipmi_sdr_hash_compute(const void *p)
386 {
387 	const ipmi_sdr_cache_ent_t *ep = p;
388 
389 	if (ep->isc_name)
390 		return (ipmi_hash_strhash(ep->isc_name));
391 	else
392 		return (ipmi_hash_ptrhash(ep));
393 }
394 
395 static int
396 ipmi_sdr_hash_compare(const void *a, const void *b)
397 {
398 	const ipmi_sdr_cache_ent_t *ap = a;
399 	const ipmi_sdr_cache_ent_t *bp = b;
400 
401 	if (ap->isc_name == NULL || bp->isc_name == NULL)
402 		return (-1);
403 
404 	if (strcmp(ap->isc_name, bp->isc_name) != 0)
405 		return (-1);
406 
407 	/*
408 	 * While it is strange for a service processor to report multiple
409 	 * entries with the same name, we allow it by treating the (name, id)
410 	 * as the unique identifier.  When looking up by name, the SDR pointer
411 	 * is NULL, and we return the first matching name.
412 	 */
413 	if (ap->isc_sdr == NULL || bp->isc_sdr == NULL)
414 		return (0);
415 
416 	if (ap->isc_sdr->is_id == bp->isc_sdr->is_id)
417 		return (0);
418 	else
419 		return (-1);
420 }
421 
422 int
423 ipmi_sdr_init(ipmi_handle_t *ihp)
424 {
425 	if ((ihp->ih_sdr_cache = ipmi_hash_create(ihp,
426 	    offsetof(ipmi_sdr_cache_ent_t, isc_link),
427 	    ipmi_sdr_hash_convert, ipmi_sdr_hash_compute,
428 	    ipmi_sdr_hash_compare)) == NULL)
429 		return (-1);
430 
431 	return (0);
432 }
433 
434 void
435 ipmi_sdr_clear(ipmi_handle_t *ihp)
436 {
437 	ipmi_sdr_cache_ent_t *ent;
438 
439 	while ((ent = ipmi_hash_first(ihp->ih_sdr_cache)) != NULL) {
440 		ipmi_hash_remove(ihp->ih_sdr_cache, ent);
441 		ipmi_free(ihp, ent->isc_sdr);
442 		ipmi_free(ihp, ent->isc_name);
443 		ipmi_free(ihp, ent);
444 	}
445 }
446 
447 void
448 ipmi_sdr_fini(ipmi_handle_t *ihp)
449 {
450 	if (ihp->ih_sdr_cache != NULL) {
451 		ipmi_sdr_clear(ihp);
452 		ipmi_hash_destroy(ihp->ih_sdr_cache);
453 	}
454 }
455 
456 ipmi_sdr_t *
457 ipmi_sdr_get(ipmi_handle_t *ihp, uint16_t id, uint16_t *next)
458 {
459 	ipmi_cmd_t cmd, *rsp;
460 	ipmi_cmd_get_sdr_t req;
461 	ipmi_rsp_get_sdr_t *sdr;
462 	int i;
463 
464 	req.ic_gs_resid = ihp->ih_reservation;
465 	req.ic_gs_recid = id;
466 	req.ic_gs_offset = 0;
467 	req.ic_gs_len = 0xFF;
468 
469 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
470 	cmd.ic_lun = 0;
471 	cmd.ic_cmd = IPMI_CMD_GET_SDR;
472 	cmd.ic_dlen = sizeof (req);
473 	cmd.ic_data = &req;
474 
475 	for (i = 0; i < ihp->ih_retries; i++) {
476 		if ((rsp = ipmi_send(ihp, &cmd)) != NULL)
477 			break;
478 
479 		if (ipmi_errno(ihp) != EIPMI_INVALID_RESERVATION)
480 			return (NULL);
481 
482 		if (ipmi_sdr_reserve_repository(ihp) != 0)
483 			return (NULL);
484 		req.ic_gs_resid = ihp->ih_reservation;
485 	}
486 
487 	if (rsp == NULL)
488 		return (NULL);
489 
490 	if (rsp->ic_dlen < sizeof (uint16_t) + sizeof (ipmi_sdr_t)) {
491 		(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
492 		return (NULL);
493 	}
494 
495 	sdr = rsp->ic_data;
496 	*next = sdr->ir_gs_next;
497 
498 	return ((ipmi_sdr_t *)sdr->ir_gs_record);
499 }
500 
501 int
502 ipmi_sdr_iter(ipmi_handle_t *ihp, int (*func)(ipmi_handle_t *,
503     const char *, ipmi_sdr_t *, void *), void *data)
504 {
505 	ipmi_sdr_cache_ent_t *ent;
506 	int ret;
507 
508 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
509 	    ipmi_sdr_refresh(ihp) != 0)
510 		return (-1);
511 
512 	for (ent = ipmi_hash_first(ihp->ih_sdr_cache); ent != NULL;
513 	    ent = ipmi_hash_next(ihp->ih_sdr_cache, ent)) {
514 		if ((ret = func(ihp, ent->isc_name, ent->isc_sdr, data)) != 0)
515 			return (ret);
516 	}
517 
518 	return (0);
519 }
520 
521 ipmi_sdr_t *
522 ipmi_sdr_lookup(ipmi_handle_t *ihp, const char *idstr)
523 {
524 	ipmi_sdr_cache_ent_t *ent, search;
525 
526 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
527 	    ipmi_sdr_refresh(ihp) != 0)
528 		return (NULL);
529 
530 	search.isc_name = (char *)idstr;
531 	search.isc_sdr = NULL;
532 	if ((ent = ipmi_hash_lookup(ihp->ih_sdr_cache, &search)) == NULL) {
533 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
534 		return (NULL);
535 	}
536 
537 	return (ent->isc_sdr);
538 }
539 
540 static void *
541 ipmi_sdr_lookup_common(ipmi_handle_t *ihp, const char *idstr,
542     uint8_t type)
543 {
544 	ipmi_sdr_t *sdrp;
545 
546 	if ((sdrp = ipmi_sdr_lookup(ihp, idstr)) == NULL)
547 		return (NULL);
548 
549 	if (sdrp->is_type != type) {
550 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
551 		return (NULL);
552 	}
553 
554 	return (sdrp->is_record);
555 }
556 
557 ipmi_sdr_fru_locator_t *
558 ipmi_sdr_lookup_fru(ipmi_handle_t *ihp, const char *idstr)
559 {
560 	return (ipmi_sdr_lookup_common(ihp, idstr,
561 	    IPMI_SDR_TYPE_FRU_LOCATOR));
562 }
563 
564 ipmi_sdr_generic_locator_t *
565 ipmi_sdr_lookup_generic(ipmi_handle_t *ihp, const char *idstr)
566 {
567 	return (ipmi_sdr_lookup_common(ihp, idstr,
568 	    IPMI_SDR_TYPE_GENERIC_LOCATOR));
569 }
570 
571 ipmi_sdr_compact_sensor_t *
572 ipmi_sdr_lookup_compact_sensor(ipmi_handle_t *ihp, const char *idstr)
573 {
574 	return (ipmi_sdr_lookup_common(ihp, idstr,
575 	    IPMI_SDR_TYPE_COMPACT_SENSOR));
576 }
577 
578 ipmi_sdr_full_sensor_t *
579 ipmi_sdr_lookup_full_sensor(ipmi_handle_t *ihp, const char *idstr)
580 {
581 	return (ipmi_sdr_lookup_common(ihp, idstr,
582 	    IPMI_SDR_TYPE_FULL_SENSOR));
583 }
584 
585 /*
586  * Mostly taken from ipmitool source v1.88
587  *
588  * This function converts the raw sensor reading returned by
589  * ipmi_get_sensor_reading to a unit-based value of type double.
590  */
591 int
592 ipmi_sdr_conv_reading(ipmi_sdr_full_sensor_t *sensor, uint8_t val,
593     double *result)
594 {
595 	int m, b, k1, k2;
596 
597 	m = __TO_M(sensor->is_fs_mtol);
598 	b = __TO_B(sensor->is_fs_bacc);
599 	k1 = __TO_B_EXP(sensor->is_fs_bacc);
600 	k2 = __TO_R_EXP(sensor->is_fs_bacc);
601 
602 	switch (sensor->is_fs_analog_fmt) {
603 	case IPMI_DATA_FMT_UNSIGNED:
604 		*result = (double)(((m * val) +
605 		    (b * pow(10, k1))) * pow(10, k2));
606 		break;
607 	case IPMI_DATA_FMT_ONESCOMP:
608 		if (val & 0x80)
609 			val++;
610 		/* FALLTHRU */
611 	case IPMI_DATA_FMT_TWOSCOMP:
612 		*result = (double)(((m * (int8_t)val) +
613 		    (b * pow(10, k1))) * pow(10, k2));
614 		break;
615 	default:
616 		/* This sensor does not return a numeric reading */
617 		return (-1);
618 	}
619 
620 	switch (sensor->is_fs_sensor_linear_type) {
621 	case SDR_SENSOR_L_LN:
622 		*result = log(*result);
623 		break;
624 	case SDR_SENSOR_L_LOG10:
625 		*result = log10(*result);
626 		break;
627 	case SDR_SENSOR_L_LOG2:
628 		*result = (double)(log(*result) / log(2.0));
629 		break;
630 	case SDR_SENSOR_L_E:
631 		*result = exp(*result);
632 		break;
633 	case SDR_SENSOR_L_EXP10:
634 		*result = pow(10.0, *result);
635 		break;
636 	case SDR_SENSOR_L_EXP2:
637 		*result = pow(2.0, *result);
638 		break;
639 	case SDR_SENSOR_L_1_X:
640 		*result = pow(*result, -1.0);	/* 1/x w/o exception */
641 		break;
642 	case SDR_SENSOR_L_SQR:
643 		*result = pow(*result, 2.0);
644 		break;
645 	case SDR_SENSOR_L_CUBE:
646 		*result = pow(*result, 3.0);
647 		break;
648 	case SDR_SENSOR_L_SQRT:
649 		*result = sqrt(*result);
650 		break;
651 	case SDR_SENSOR_L_CUBERT:
652 		*result = cbrt(*result);
653 		break;
654 	case SDR_SENSOR_L_LINEAR:
655 	default:
656 		break;
657 	}
658 	return (0);
659 }
660