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 *
ipmi_sdr_get_info(ipmi_handle_t * ihp)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
ipmi_sdr_reserve_repository(ipmi_handle_t * ihp)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
ipmi_sdr_changed(ipmi_handle_t * ihp)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
ipmi_sdr_refresh(ipmi_handle_t * ihp)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 *
ipmi_sdr_hash_convert(const void * p)381 ipmi_sdr_hash_convert(const void *p)
382 {
383 return (p);
384 }
385
386 static ulong_t
ipmi_sdr_hash_compute(const void * p)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
ipmi_sdr_hash_compare(const void * a,const void * b)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
ipmi_sdr_init(ipmi_handle_t * ihp)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
ipmi_sdr_clear(ipmi_handle_t * ihp)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
ipmi_sdr_fini(ipmi_handle_t * ihp)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 *
ipmi_sdr_get(ipmi_handle_t * ihp,uint16_t id,uint16_t * next)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
ipmi_sdr_iter(ipmi_handle_t * ihp,int (* func)(ipmi_handle_t *,const char *,ipmi_sdr_t *,void *),void * data)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 *
ipmi_sdr_lookup(ipmi_handle_t * ihp,const char * idstr)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 *
ipmi_sdr_lookup_common(ipmi_handle_t * ihp,const char * idstr,uint8_t type)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 *
ipmi_sdr_lookup_fru(ipmi_handle_t * ihp,const char * idstr)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 *
ipmi_sdr_lookup_generic(ipmi_handle_t * ihp,const char * idstr)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 *
ipmi_sdr_lookup_compact_sensor(ipmi_handle_t * ihp,const char * idstr)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 *
ipmi_sdr_lookup_full_sensor(ipmi_handle_t * ihp,const char * idstr)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
ipmi_sdr_conv_reading(ipmi_sdr_full_sensor_t * sensor,uint8_t val,double * result)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