xref: /illumos-gate/usr/src/lib/libipmi/common/ipmi_sdr.c (revision 9d468d1aa2ff370e3e4b0437df3e0b065fb645ea)
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 2010 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 #define	IPMI_SDR_HDR_SZ		offsetof(ipmi_sdr_t, is_record)
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;
187 	uint8_t type;
188 	char *name;
189 	ipmi_sdr_info_t *sip;
190 	uint32_t isi_add_ts, isi_erase_ts;
191 
192 	if ((sip = ipmi_sdr_get_info(ihp)) == NULL)
193 		return (-1);
194 
195 	(void) memcpy(&isi_add_ts, &sip->isi_add_ts, sizeof (uint32_t));
196 	(void) memcpy(&isi_erase_ts, &sip->isi_erase_ts, sizeof (uint32_t));
197 	if (isi_add_ts <= ihp->ih_sdr_ts &&
198 	    isi_erase_ts <= ihp->ih_sdr_ts &&
199 	    ipmi_hash_first(ihp->ih_sdr_cache) != NULL)
200 		return (0);
201 
202 	ipmi_sdr_clear(ihp);
203 	ipmi_entity_clear(ihp);
204 	ihp->ih_sdr_ts = MAX(isi_add_ts, isi_erase_ts);
205 
206 	/*
207 	 * Iterate over all existing SDRs and add them to the cache.
208 	 */
209 	id = IPMI_SDR_FIRST;
210 	while (id != IPMI_SDR_LAST) {
211 		if ((sdr = ipmi_sdr_get(ihp, id, &id)) == NULL)
212 			goto error;
213 
214 		/*
215 		 * Extract the name from the record-specific data.
216 		 */
217 		switch (sdr->is_type) {
218 		case IPMI_SDR_TYPE_GENERIC_LOCATOR:
219 			{
220 				ipmi_sdr_generic_locator_t *glp =
221 				    (ipmi_sdr_generic_locator_t *)
222 				    sdr->is_record;
223 				namelen = glp->is_gl_idlen;
224 				type = glp->is_gl_idtype;
225 				name = glp->is_gl_idstring;
226 				break;
227 			}
228 
229 		case IPMI_SDR_TYPE_FRU_LOCATOR:
230 			{
231 				ipmi_sdr_fru_locator_t *flp =
232 				    (ipmi_sdr_fru_locator_t *)
233 				    sdr->is_record;
234 				namelen = flp->is_fl_idlen;
235 				name = flp->is_fl_idstring;
236 				type = flp->is_fl_idtype;
237 				break;
238 			}
239 
240 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
241 			{
242 				ipmi_sdr_compact_sensor_t *csp =
243 				    (ipmi_sdr_compact_sensor_t *)
244 				    sdr->is_record;
245 				uint16_t tmp;
246 
247 				namelen = csp->is_cs_idlen;
248 				type = csp->is_cs_idtype;
249 				name = csp->is_cs_idstring;
250 
251 				tmp = LE_IN16(&csp->is_cs_assert_mask);
252 				(void) memcpy(&csp->is_cs_assert_mask, &tmp,
253 				    sizeof (tmp));
254 
255 				tmp = LE_IN16(&csp->is_cs_deassert_mask);
256 				(void) memcpy(&csp->is_cs_deassert_mask, &tmp,
257 				    sizeof (tmp));
258 
259 				tmp = LE_IN16(&csp->is_cs_reading_mask);
260 				(void) memcpy(&csp->is_cs_reading_mask, &tmp,
261 				    sizeof (tmp));
262 				break;
263 			}
264 
265 		case IPMI_SDR_TYPE_FULL_SENSOR:
266 			{
267 				ipmi_sdr_full_sensor_t *fsp =
268 				    (ipmi_sdr_full_sensor_t *)
269 				    sdr->is_record;
270 				uint16_t tmp;
271 
272 				namelen = fsp->is_fs_idlen;
273 				type = fsp->is_fs_idtype;
274 				name = fsp->is_fs_idstring;
275 
276 				tmp = LE_IN16(&fsp->is_fs_assert_mask);
277 				(void) memcpy(&fsp->is_fs_assert_mask, &tmp,
278 				    sizeof (tmp));
279 
280 				tmp = LE_IN16(&fsp->is_fs_deassert_mask);
281 				(void) memcpy(&fsp->is_fs_deassert_mask, &tmp,
282 				    sizeof (tmp));
283 
284 				tmp = LE_IN16(&fsp->is_fs_reading_mask);
285 				(void) memcpy(&fsp->is_fs_reading_mask, &tmp,
286 				    sizeof (tmp));
287 				break;
288 			}
289 
290 		case IPMI_SDR_TYPE_EVENT_ONLY:
291 			{
292 				ipmi_sdr_event_only_t *esp =
293 				    (ipmi_sdr_event_only_t *)
294 				    sdr->is_record;
295 				namelen = esp->is_eo_idlen;
296 				type = esp->is_eo_idtype;
297 				name = esp->is_eo_idstring;
298 				break;
299 			}
300 
301 		case IPMI_SDR_TYPE_MANAGEMENT_LOCATOR:
302 			{
303 				ipmi_sdr_management_locator_t *msp =
304 				    (ipmi_sdr_management_locator_t *)
305 				    sdr->is_record;
306 				namelen = msp->is_ml_idlen;
307 				type = msp->is_ml_idtype;
308 				name = msp->is_ml_idstring;
309 				break;
310 			}
311 
312 		case IPMI_SDR_TYPE_MANAGEMENT_CONFIRMATION:
313 			{
314 				ipmi_sdr_management_confirmation_t *mcp =
315 				    (ipmi_sdr_management_confirmation_t *)
316 				    sdr->is_record;
317 				uint16_t tmp;
318 
319 				name = NULL;
320 				tmp = LE_IN16(&mcp->is_mc_product);
321 				(void) memcpy(&mcp->is_mc_product, &tmp,
322 				    sizeof (tmp));
323 				break;
324 			}
325 
326 		default:
327 			name = NULL;
328 		}
329 
330 		if ((ent = ipmi_zalloc(ihp,
331 		    sizeof (ipmi_sdr_cache_ent_t))) == NULL) {
332 			free(sdr);
333 			goto error;
334 		}
335 
336 		ent->isc_sdr = sdr;
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 				goto error;
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 error:
369 	ipmi_sdr_clear(ihp);
370 	ipmi_entity_clear(ihp);
371 	return (-1);
372 }
373 
374 /*
375  * Hash routines.  We allow lookup by name, but since not all entries have
376  * names, we fall back to the entry pointer, which is guaranteed to be unique.
377  * The end result is that entities without names cannot be looked up, but will
378  * show up during iteration.
379  */
380 static const void *
381 ipmi_sdr_hash_convert(const void *p)
382 {
383 	return (p);
384 }
385 
386 static ulong_t
387 ipmi_sdr_hash_compute(const void *p)
388 {
389 	const ipmi_sdr_cache_ent_t *ep = p;
390 
391 	if (ep->isc_name)
392 		return (ipmi_hash_strhash(ep->isc_name));
393 	else
394 		return (ipmi_hash_ptrhash(ep));
395 }
396 
397 static int
398 ipmi_sdr_hash_compare(const void *a, const void *b)
399 {
400 	const ipmi_sdr_cache_ent_t *ap = a;
401 	const ipmi_sdr_cache_ent_t *bp = b;
402 
403 	if (ap->isc_name == NULL || bp->isc_name == NULL)
404 		return (-1);
405 
406 	if (strcmp(ap->isc_name, bp->isc_name) != 0)
407 		return (-1);
408 
409 	/*
410 	 * While it is strange for a service processor to report multiple
411 	 * entries with the same name, we allow it by treating the (name, id)
412 	 * as the unique identifier.  When looking up by name, the SDR pointer
413 	 * is NULL, and we return the first matching name.
414 	 */
415 	if (ap->isc_sdr == NULL || bp->isc_sdr == NULL)
416 		return (0);
417 
418 	if (ap->isc_sdr->is_id == bp->isc_sdr->is_id)
419 		return (0);
420 	else
421 		return (-1);
422 }
423 
424 int
425 ipmi_sdr_init(ipmi_handle_t *ihp)
426 {
427 	if ((ihp->ih_sdr_cache = ipmi_hash_create(ihp,
428 	    offsetof(ipmi_sdr_cache_ent_t, isc_link),
429 	    ipmi_sdr_hash_convert, ipmi_sdr_hash_compute,
430 	    ipmi_sdr_hash_compare)) == NULL)
431 		return (-1);
432 
433 	return (0);
434 }
435 
436 void
437 ipmi_sdr_clear(ipmi_handle_t *ihp)
438 {
439 	ipmi_sdr_cache_ent_t *ent;
440 
441 	while ((ent = ipmi_hash_first(ihp->ih_sdr_cache)) != NULL) {
442 		ipmi_hash_remove(ihp->ih_sdr_cache, ent);
443 		ipmi_free(ihp, ent->isc_sdr);
444 		ipmi_free(ihp, ent->isc_name);
445 		ipmi_free(ihp, ent);
446 	}
447 }
448 
449 void
450 ipmi_sdr_fini(ipmi_handle_t *ihp)
451 {
452 	if (ihp->ih_sdr_cache != NULL) {
453 		ipmi_sdr_clear(ihp);
454 		ipmi_hash_destroy(ihp->ih_sdr_cache);
455 	}
456 }
457 
458 ipmi_sdr_t *
459 ipmi_sdr_get(ipmi_handle_t *ihp, uint16_t id, uint16_t *next)
460 {
461 	uint8_t offset = IPMI_SDR_HDR_SZ, count = 0, chunksz = 16, sdr_sz;
462 	ipmi_cmd_t cmd, *rsp;
463 	ipmi_cmd_get_sdr_t req;
464 	ipmi_sdr_t *sdr;
465 	int i = 0;
466 	char *buf;
467 
468 	req.ic_gs_resid = ihp->ih_reservation;
469 	req.ic_gs_recid = id;
470 
471 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
472 	cmd.ic_lun = 0;
473 	cmd.ic_cmd = IPMI_CMD_GET_SDR;
474 	cmd.ic_dlen = sizeof (req);
475 	cmd.ic_data = &req;
476 
477 	/*
478 	 * The size of the SDR is contained in the 5th byte of the SDR header,
479 	 * so we'll read the first 5 bytes to get the size, so we know how big
480 	 * to make the buffer.
481 	 */
482 	req.ic_gs_offset = 0;
483 	req.ic_gs_len = IPMI_SDR_HDR_SZ;
484 	for (i = 0; i < ihp->ih_retries; i++) {
485 		if ((rsp = ipmi_send(ihp, &cmd)) != NULL)
486 			break;
487 
488 		if (ipmi_errno(ihp) != EIPMI_INVALID_RESERVATION)
489 			return (NULL);
490 
491 		if (ipmi_sdr_reserve_repository(ihp) != 0)
492 			return (NULL);
493 		req.ic_gs_resid = ihp->ih_reservation;
494 	}
495 	if (rsp == NULL)
496 		return (NULL);
497 
498 	sdr = (ipmi_sdr_t *)((ipmi_rsp_get_sdr_t *)rsp->ic_data)->ir_gs_record;
499 	sdr_sz = sdr->is_length;
500 
501 	if ((buf = ipmi_zalloc(ihp, sdr_sz + IPMI_SDR_HDR_SZ)) == NULL) {
502 		(void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
503 		return (NULL);
504 	}
505 	(void) memcpy(buf, (void *)sdr, IPMI_SDR_HDR_SZ);
506 
507 	/*
508 	 * Some SDRs can be bigger than the buffer sizes for a given bmc
509 	 * interface.  Therefore we break up the process of reading in an entire
510 	 * SDR into multiple smaller reads.
511 	 */
512 	while (count < sdr_sz) {
513 		req.ic_gs_offset = offset;
514 		if (chunksz > (sdr_sz - count))
515 			chunksz = sdr_sz - count;
516 		req.ic_gs_len = chunksz;
517 		rsp = ipmi_send(ihp, &cmd);
518 
519 		if (rsp != NULL) {
520 			count += chunksz;
521 			sdr = (ipmi_sdr_t *)
522 			    ((ipmi_rsp_get_sdr_t *)rsp->ic_data)->ir_gs_record;
523 			(void) memcpy(buf+offset, (void *)sdr, chunksz);
524 			offset += chunksz;
525 			i = 0;
526 		} else if (ipmi_errno(ihp) == EIPMI_INVALID_RESERVATION) {
527 			if (i >= ihp->ih_retries ||
528 			    ipmi_sdr_reserve_repository(ihp) != 0) {
529 				free(buf);
530 				return (NULL);
531 			}
532 			req.ic_gs_resid = ihp->ih_reservation;
533 			i++;
534 		} else {
535 			free(buf);
536 			return (NULL);
537 		}
538 	}
539 	*next = ((ipmi_rsp_get_sdr_t *)rsp->ic_data)->ir_gs_next;
540 
541 	return ((ipmi_sdr_t *)buf);
542 }
543 
544 int
545 ipmi_sdr_iter(ipmi_handle_t *ihp, int (*func)(ipmi_handle_t *,
546     const char *, ipmi_sdr_t *, void *), void *data)
547 {
548 	ipmi_sdr_cache_ent_t *ent;
549 	int ret;
550 
551 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
552 	    ipmi_sdr_refresh(ihp) != 0)
553 		return (-1);
554 
555 	for (ent = ipmi_hash_first(ihp->ih_sdr_cache); ent != NULL;
556 	    ent = ipmi_hash_next(ihp->ih_sdr_cache, ent)) {
557 		if ((ret = func(ihp, ent->isc_name, ent->isc_sdr, data)) != 0)
558 			return (ret);
559 	}
560 
561 	return (0);
562 }
563 
564 ipmi_sdr_t *
565 ipmi_sdr_lookup(ipmi_handle_t *ihp, const char *idstr)
566 {
567 	ipmi_sdr_cache_ent_t *ent, search;
568 
569 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
570 	    ipmi_sdr_refresh(ihp) != 0)
571 		return (NULL);
572 
573 	search.isc_name = (char *)idstr;
574 	search.isc_sdr = NULL;
575 	if ((ent = ipmi_hash_lookup(ihp->ih_sdr_cache, &search)) == NULL) {
576 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
577 		return (NULL);
578 	}
579 
580 	return (ent->isc_sdr);
581 }
582 
583 static void *
584 ipmi_sdr_lookup_common(ipmi_handle_t *ihp, const char *idstr,
585     uint8_t type)
586 {
587 	ipmi_sdr_t *sdrp;
588 
589 	if ((sdrp = ipmi_sdr_lookup(ihp, idstr)) == NULL)
590 		return (NULL);
591 
592 	if (sdrp->is_type != type) {
593 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
594 		return (NULL);
595 	}
596 
597 	return (sdrp->is_record);
598 }
599 
600 ipmi_sdr_fru_locator_t *
601 ipmi_sdr_lookup_fru(ipmi_handle_t *ihp, const char *idstr)
602 {
603 	return (ipmi_sdr_lookup_common(ihp, idstr,
604 	    IPMI_SDR_TYPE_FRU_LOCATOR));
605 }
606 
607 ipmi_sdr_generic_locator_t *
608 ipmi_sdr_lookup_generic(ipmi_handle_t *ihp, const char *idstr)
609 {
610 	return (ipmi_sdr_lookup_common(ihp, idstr,
611 	    IPMI_SDR_TYPE_GENERIC_LOCATOR));
612 }
613 
614 ipmi_sdr_compact_sensor_t *
615 ipmi_sdr_lookup_compact_sensor(ipmi_handle_t *ihp, const char *idstr)
616 {
617 	return (ipmi_sdr_lookup_common(ihp, idstr,
618 	    IPMI_SDR_TYPE_COMPACT_SENSOR));
619 }
620 
621 ipmi_sdr_full_sensor_t *
622 ipmi_sdr_lookup_full_sensor(ipmi_handle_t *ihp, const char *idstr)
623 {
624 	return (ipmi_sdr_lookup_common(ihp, idstr,
625 	    IPMI_SDR_TYPE_FULL_SENSOR));
626 }
627 
628 /*
629  * Mostly taken from ipmitool source v1.88
630  *
631  * This function converts the raw sensor reading returned by
632  * ipmi_get_sensor_reading to a unit-based value of type double.
633  */
634 int
635 ipmi_sdr_conv_reading(ipmi_sdr_full_sensor_t *sensor, uint8_t val,
636     double *result)
637 {
638 	int m, b, k1, k2;
639 
640 	m = __TO_M(sensor->is_fs_mtol);
641 	b = __TO_B(sensor->is_fs_bacc);
642 	k1 = __TO_B_EXP(sensor->is_fs_bacc);
643 	k2 = __TO_R_EXP(sensor->is_fs_bacc);
644 
645 	switch (sensor->is_fs_analog_fmt) {
646 	case IPMI_DATA_FMT_UNSIGNED:
647 		*result = (double)(((m * val) +
648 		    (b * pow(10, k1))) * pow(10, k2));
649 		break;
650 	case IPMI_DATA_FMT_ONESCOMP:
651 		if (val & 0x80)
652 			val++;
653 		/* FALLTHRU */
654 	case IPMI_DATA_FMT_TWOSCOMP:
655 		*result = (double)(((m * (int8_t)val) +
656 		    (b * pow(10, k1))) * pow(10, k2));
657 		break;
658 	default:
659 		/* This sensor does not return a numeric reading */
660 		return (-1);
661 	}
662 
663 	switch (sensor->is_fs_sensor_linear_type) {
664 	case SDR_SENSOR_L_LN:
665 		*result = log(*result);
666 		break;
667 	case SDR_SENSOR_L_LOG10:
668 		*result = log10(*result);
669 		break;
670 	case SDR_SENSOR_L_LOG2:
671 		*result = (double)(log(*result) / log(2.0));
672 		break;
673 	case SDR_SENSOR_L_E:
674 		*result = exp(*result);
675 		break;
676 	case SDR_SENSOR_L_EXP10:
677 		*result = pow(10.0, *result);
678 		break;
679 	case SDR_SENSOR_L_EXP2:
680 		*result = pow(2.0, *result);
681 		break;
682 	case SDR_SENSOR_L_1_X:
683 		*result = pow(*result, -1.0);	/* 1/x w/o exception */
684 		break;
685 	case SDR_SENSOR_L_SQR:
686 		*result = pow(*result, 2.0);
687 		break;
688 	case SDR_SENSOR_L_CUBE:
689 		*result = pow(*result, 3.0);
690 		break;
691 	case SDR_SENSOR_L_SQRT:
692 		*result = sqrt(*result);
693 		break;
694 	case SDR_SENSOR_L_CUBERT:
695 		*result = cbrt(*result);
696 		break;
697 	case SDR_SENSOR_L_LINEAR:
698 	default:
699 		break;
700 	}
701 	return (0);
702 }
703