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