xref: /illumos-gate/usr/src/lib/libipmi/common/ipmi_util.c (revision 86d949f9497332fe19be6b5d711d265eb957439f)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <libipmi.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdarg.h>
31 
32 #include "ipmi_impl.h"
33 
34 /*
35  * Extracts bits between index h (high, inclusive) and l (low, exclusive) from
36  * u, which must be an unsigned integer.
37  */
38 #define	BITX(u, h, l)	(((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
39 
40 /*
41  * Error handling
42  */
43 int
44 ipmi_set_error(ipmi_handle_t *ihp, int error, const char *fmt, ...)
45 {
46 	va_list ap;
47 
48 	va_start(ap, fmt);
49 
50 	ihp->ih_errno = error;
51 	if (fmt == NULL)
52 		ihp->ih_errmsg[0] = '\0';
53 	else
54 		(void) vsnprintf(ihp->ih_errmsg, sizeof (ihp->ih_errmsg),
55 		    fmt, ap);
56 	va_end(ap);
57 
58 	return (-1);
59 }
60 
61 int
62 ipmi_errno(ipmi_handle_t *ihp)
63 {
64 	return (ihp->ih_errno);
65 }
66 
67 /* ARGSUSED */
68 const char *
69 ipmi_errmsg(ipmi_handle_t *ihp)
70 {
71 	int i;
72 	const char *str;
73 
74 	str = NULL;
75 	for (i = 0; ipmi_errno_table[i].int_name != NULL; i++) {
76 		if (ipmi_errno_table[i].int_value == ihp->ih_errno) {
77 			str = ipmi_errno_table[i].int_name;
78 			break;
79 		}
80 	}
81 
82 	if (str == NULL && (str = strerror(ihp->ih_errno)) == NULL)
83 		str = "unknown failure";
84 
85 	if (ihp->ih_errmsg[0] == '\0')
86 		return (str);
87 
88 	(void) snprintf(ihp->ih_errbuf, sizeof (ihp->ih_errbuf),
89 	    "%s: %s", str, ihp->ih_errmsg);
90 	return (ihp->ih_errbuf);
91 }
92 
93 /*
94  * Memory allocation
95  */
96 void *
97 ipmi_alloc(ipmi_handle_t *ihp, size_t size)
98 {
99 	void *ptr;
100 
101 	if ((ptr = malloc(size)) == NULL)
102 		(void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
103 
104 	return (ptr);
105 }
106 
107 void *
108 ipmi_zalloc(ipmi_handle_t *ihp, size_t size)
109 {
110 	void *ptr;
111 
112 	if ((ptr = calloc(size, 1)) == NULL)
113 		(void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
114 
115 	return (ptr);
116 }
117 
118 char *
119 ipmi_strdup(ipmi_handle_t *ihp, const char *str)
120 {
121 	char *ptr;
122 
123 	if ((ptr = strdup(str)) == NULL)
124 		(void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
125 
126 	return (ptr);
127 }
128 
129 /* ARGSUSED */
130 void
131 ipmi_free(ipmi_handle_t *ihp, void *ptr)
132 {
133 	free(ptr);
134 }
135 
136 /*
137  * Translation between #defines and strings.
138  */
139 void
140 ipmi_entity_name(uint8_t id, char *buf, size_t len)
141 {
142 	ipmi_name_trans_t *ntp;
143 
144 	for (ntp = &ipmi_entity_table[0]; ntp->int_name != NULL; ntp++) {
145 		if (ntp->int_value == id) {
146 			(void) strlcpy(buf, ntp->int_name, len);
147 			return;
148 		}
149 	}
150 
151 	(void) snprintf(buf, len, "0x%02x", id);
152 }
153 
154 void
155 ipmi_sensor_type_name(uint8_t type, char *buf, size_t len)
156 {
157 	ipmi_name_trans_t *ntp;
158 
159 	for (ntp = &ipmi_sensor_type_table[0]; ntp->int_name != NULL; ntp++) {
160 		if (ntp->int_value == type) {
161 			(void) strlcpy(buf, ntp->int_name, len);
162 			return;
163 		}
164 	}
165 
166 	(void) snprintf(buf, len, "0x%02x", type);
167 }
168 
169 void
170 ipmi_sensor_units_name(uint8_t type, char *buf, size_t len)
171 {
172 	ipmi_name_trans_t *ntp;
173 
174 	for (ntp = &ipmi_units_type_table[0]; ntp->int_name != NULL; ntp++) {
175 		if (ntp->int_value == type) {
176 			(void) strlcpy(buf, ntp->int_name, len);
177 			return;
178 		}
179 	}
180 
181 	(void) snprintf(buf, len, "0x%02x", type);
182 }
183 
184 void
185 ipmi_sensor_reading_name(uint8_t sensor_type, uint8_t reading_type,
186     char *buf, size_t len)
187 {
188 	uint8_t val;
189 	ipmi_name_trans_t *ntp;
190 
191 	if (reading_type == IPMI_RT_SPECIFIC) {
192 		val = sensor_type;
193 		ntp = &ipmi_sensor_type_table[0];
194 	} else {
195 		val = reading_type;
196 		ntp = &ipmi_reading_type_table[0];
197 	}
198 
199 	for (; ntp->int_name != NULL; ntp++) {
200 		if (ntp->int_value == val) {
201 			(void) strlcpy(buf, ntp->int_name, len);
202 			return;
203 		}
204 	}
205 
206 	if (reading_type == IPMI_RT_SPECIFIC)
207 		(void) snprintf(buf, len, "%02x/%02x", reading_type,
208 		    sensor_type);
209 	else
210 		(void) snprintf(buf, len, "%02x", reading_type);
211 }
212 
213 /*
214  * Converts a BCD decimal value to an integer.
215  */
216 int
217 ipmi_convert_bcd(int value)
218 {
219 	int ret = 0;
220 	int digit;
221 	int i;
222 
223 	for (i = 7; i >= 0; i--) {
224 		digit = ((value & (0xf << (i * 4))) >> (i * 4));
225 		ret += digit * 10 * i;
226 	}
227 
228 	return (ret);
229 }
230 
231 /*
232  * See sections 43.15 and 43.16
233  *
234  * This is a utility function for decoding the strings that are packed into
235  * sensor data records.  If the type is 6-bit packed ASCII, then it converts
236  * the string to an 8-bit ASCII string and copies that into the suuplied buffer.
237  * If it is 8-bit ASCII, it copies the string into the supplied buffer as-is.
238  */
239 void
240 ipmi_decode_string(uint8_t type, uint8_t len, char *data, char *buf)
241 {
242 	int i, j = 0, chunks, leftovers;
243 	uint8_t tmp, lo;
244 
245 	if (len == 0) {
246 		*buf = '\0';
247 		return;
248 	}
249 	/*
250 	 * If the type is 8-bit ASCII, we can simply copy the string and return
251 	 */
252 	if (type == 0x3) {
253 		(void) strncpy(buf, data, len);
254 		*(buf+len) = '\0';
255 		return;
256 	} else if (type == 0x1 || type == 0x0) {
257 		/*
258 		 * Yuck - they either used BCD plus encoding, which we don't
259 		 * currently handle, or they used an unspecified encoding type.
260 		 * In these cases we'll set buf to an empty string.  We still
261 		 * need to return the length so that we can get to the next
262 		 * record.
263 		 */
264 		*buf = '\0';
265 		return;
266 	}
267 
268 	/*
269 	 * Otherwise, it's 6-bit packed ASCII, so we have to convert the
270 	 * data first
271 	 */
272 	chunks = len / 3;
273 	leftovers = len % 3;
274 
275 	/*
276 	 * First we decode the 6-bit string in chunks of 3 bytes as far as
277 	 * possible
278 	 */
279 	for (i = 0; i < chunks; i++) {
280 		tmp = BITX(*(data+j), 5, 0);
281 		*buf++ = (char)(tmp + 32);
282 
283 		lo = BITX(*(data+j++), 7, 6);
284 		tmp = BITX(*(data+j), 3, 0);
285 		tmp = (tmp << 2) | lo;
286 		*buf++ = (char)(tmp + 32);
287 
288 		lo = BITX(*(data+j++), 7, 4);
289 		tmp = BITX(*(data+j), 1, 0);
290 		tmp = (tmp << 4) | lo;
291 		*buf++ = (char)(tmp + 32);
292 
293 		tmp = BITX(*(data+j++), 7, 2);
294 		*buf++ = (char)(tmp + 32);
295 	}
296 	switch (leftovers) {
297 		case 1:
298 			tmp = BITX(*(data+j), 5, 0);
299 			*buf++ = (char)(tmp + 32);
300 			break;
301 		case 2:
302 			tmp = BITX(*(data+j), 5, 0);
303 			*buf++ = (char)(tmp + 32);
304 
305 			lo = BITX(*(data+j++), 7, 6);
306 			tmp = BITX(*(data+j), 3, 0);
307 			tmp = (tmp << 2) | lo;
308 			*buf++ = (char)(tmp + 32);
309 			break;
310 	}
311 	*buf = '\0';
312 }
313