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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 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/*
30 * These routines in this file are used to interact with SMC driver to
31 * read and write FRUID data
32 */
33
34#include <stdio.h>
35#include <unistd.h>
36#include <string.h>
37#include <strings.h>
38#include <stdarg.h>
39#include <synch.h>
40#include <thread.h>
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <fcntl.h>
44#include <syslog.h>
45#include <stropts.h>
46#include <poll.h>
47#include <smclib.h>
48#include "fru_access_impl.h"
49
50#define	POLL_TIMEOUT			10000
51#define	FRUID_CHECK_POLL_TIMEOUT	5000
52#define	SIZE_TO_READ_WRITE		20
53
54/* IPMI fru spec Storage definition version 1.0, rev 1.1 */
55#define	IPMI_COMMON_HEADER_SIZE		8
56#define	IPMI_VERSION			1
57#define	CMN_HDR_VERSION_MASK		0x0
58#define	CMN_HDR_OFFSET			0x0
59#define	BD_MFR_OFFSET			6
60#define	BD_FIELDS_SIZE			6
61#define	AREA_TERMINATION_INDICATOR	0xc1
62
63/* type encoding */
64#define	BINARY_TYPE			0x0
65#define	BCDPLUS_TYPE			0x1
66#define	SIX_BITASCII_TYPE		0x2
67#define	UNICODE_TYPE			0x3
68
69/* for ascii conversion */
70#define	ASCII_MAP			0x20
71#define	BIT_MASK1			0x3f
72#define	BIT_MASK2			0x0f
73#define	BIT_MASK3			0x03
74
75#define	SUN_NAME			"SUN MICROSYSTEMS, INC."
76#define	SUN_JEDEC_CODE			0x3e
77#define	MANR_MAX_LENGTH	80
78#define	FRU_DATA_MAX_SIZE		100
79
80/* IPMI commands */
81#define	IPMI_GET_DEVICE_ID		0x1
82#define	FRU_DEVICE_ID			0x0
83#define	READ_FRU_INVENTORY_INFO		0x10
84#define	READ_FRU_INVENTORY_DATA		0x11
85#define	WRITE_FRU_INVENTORY_DATA	0x12
86
87#define	TMP_BUFFER_SIZE			10
88#define	BYTE_TO_READ_SUN_CHK		5
89
90typedef struct {
91	uint8_t	internal;	/* internal use area */
92	uint8_t chassis;	/* chassis info area */
93	uint8_t board;		/* board area */
94	uint8_t product;	/* product info area */
95	uint8_t records;	/* multirecord area */
96} fruid_offset_t;
97
98extern void get_fru_data_info(int, int, format_t *);
99static void convert_to_ascii(uint8_t [], uint8_t [], int, int);
100static void bcdplus_to_ascii(uint8_t [], uint8_t [], int);
101static time_t get_utc_time(uint8_t  []);
102static uint8_t	cpu_no = 0;
103
104/*
105 * Routine to read FRUID information from BMC
106 */
107static int
108get_alarm_fru_data(int offset, int size, void *buffer, format_t *format)
109{
110	uint8_t	datap[5];
111	sc_reqmsg_t req_pkt;
112	sc_rspmsg_t res_pkt;
113
114	if (buffer == NULL) {
115		return (-1);
116	}
117	bzero(buffer, size);
118
119	datap[0] = 0x7;			/* bus id */
120	datap[1] = 0xa0;		/* slave address */
121	datap[2] = size;		/* count */
122	datap[3] = offset >> 8;		/* MSB */
123	datap[4] = (uint8_t)offset;	/* LSB */
124
125	(void) smc_init_ipmi_msg(&req_pkt, SMC_MASTER_WR_RD_I2C,
126		FRUACCESS_MSG_ID, 5, datap, DEFAULT_SEQN, format->dest,
127		SMC_NETFN_APP_REQ, SMC_BMC_LUN);
128
129	if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
130		POLL_TIMEOUT) != SMC_SUCCESS) {
131		return (-1);
132	}
133	/* check the completion code */
134	if (res_pkt.data[7] != 0) {
135		return (-1);
136	}
137
138	(void) memcpy(buffer, &(res_pkt.data[8]), size);
139	return (0);
140}
141
142/*
143 * Routine to read FRUID information from other boards
144 */
145static int
146get_fru_data(int offset, int size, void *buffer, format_t *format)
147{
148	sc_reqmsg_t req_pkt;
149	sc_rspmsg_t res_pkt;
150	uint8_t datap[4];
151	int ipmi = 0;
152
153	if (buffer == NULL) {
154		return (-1);
155	}
156
157	/* figure out if onboard access or ipmb access */
158	if (format->src == format->dest) {
159		ipmi = 0;
160	} else {
161		ipmi = 1;
162	}
163
164	switch (ipmi) {
165
166	case 0: /* on board info (local i2c) */
167
168	SC_MSG_CMD(&req_pkt) = SMC_EEPROM_READ;
169	SC_MSG_LEN(&req_pkt) = 4;
170	SC_MSG_ID(&req_pkt) = FRUACCESS_MSG_ID;
171
172	/* data field for request */
173	req_pkt.data[0] = format->sun_device_id;	/* device id */
174	req_pkt.data[1] = (uint8_t)offset; /* (LSB) */
175	req_pkt.data[3] = size;
176
177	if (format->format == SUN_FORMAT) {
178		req_pkt.data[2] = offset >> 8;
179	} else {
180		req_pkt.data[2] = 0x0;	/* (MSB) always 0x0 for IPMI */
181	}
182
183	/* make a call to smc library to send cmd */
184	if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
185		POLL_TIMEOUT) != SMC_SUCCESS) {
186		return (-1);
187	}
188
189	if (SC_MSG_LEN(&res_pkt) != size) {
190		return (-1);
191	}
192	(void) memcpy(buffer, res_pkt.data, size);
193	return (0);
194
195	default:
196
197	/* data for request packet */
198	datap[0] = format->sun_device_id;	/* device id */
199	datap[1] = (uint8_t)offset;		/* LSB */
200	datap[3] = size;			/* bytes to read */
201	if (format->format == SUN_FORMAT) {
202		datap[2] = offset >> 8;
203	} else {
204		datap[2] = 0x0;			/* (MSB) always 0x0 for IPMI */
205	}
206
207	(void) smc_init_ipmi_msg(&req_pkt, READ_FRU_INVENTORY_DATA,
208		FRUACCESS_MSG_ID, 4, datap, DEFAULT_SEQN,
209		format->dest, SMC_NETFN_STORAGE_REQ, format->sun_lun);
210
211	if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
212		POLL_TIMEOUT) != SMC_SUCCESS) {
213		return (-1);
214	}
215	/* check the completion code */
216	if (res_pkt.data[7] != 0) {
217		return (-1);
218	}
219
220	/* check the size */
221	if (res_pkt.data[8] != size) {
222		return (-1);
223	}
224
225	(void) memcpy(buffer, &(res_pkt.data[9]), size);
226	return (0);
227	}
228}
229
230/*
231 * routine to read the IPMI common header field
232 */
233static int
234read_common_header(fruid_offset_t *offset, format_t *format)
235{
236	int ret = 0;
237	uint8_t data[FRU_DATA_MAX_SIZE];
238
239	ret = get_fru_data(CMN_HDR_OFFSET, IPMI_COMMON_HEADER_SIZE, data,
240		format);
241	if (ret < 0) {
242		return (-1);
243	}
244
245	/* version check */
246	if ((data[0] | CMN_HDR_VERSION_MASK) != 1) {
247		return (-1);
248	}
249
250	offset->internal = data[1] * 8;
251	offset->chassis  = data[2] * 8;
252	offset->board    = data[3] * 8;
253	offset->product  = data[4] * 8;
254	offset->records  = data[5] * 8;
255
256	return (0);
257}
258
259/*
260 * Read the values of each field based on FORMAT
261 */
262/* ARGSUSED */
263static int
264read_bd_fields(uint8_t *field, int offset, format_t *format)
265{
266
267	int ret, encode_type = 0x0, len, length, extra_bytes, alloc_size;
268	uint8_t *store;
269	uint8_t data[FRU_DATA_MAX_SIZE];
270
271	bzero(field, MANR_MAX_LENGTH);
272
273	ret = get_fru_data(offset, BD_FIELDS_SIZE, data, format);
274	if (ret < 0) {
275		return (-1);
276	}
277
278	if (data[0] == AREA_TERMINATION_INDICATOR) {
279		return (0);
280	}
281
282	encode_type = data[0] >> 6;
283	len = data[0] & 0x3f;
284	if (len <= 0) {
285		return (0);
286	}
287
288	ret = get_fru_data(offset+1, len, data, format);
289	if (ret < 0) {
290		return (-1);
291	}
292
293	switch (encode_type) {
294
295	case SIX_BITASCII_TYPE:
296
297		length  = len - (len % 3);
298		extra_bytes = len % 3;
299		alloc_size = ((length/3) * 4) + extra_bytes;
300		store = (uint8_t *)malloc(sizeof (uint8_t) * alloc_size);
301		if (store == NULL) {
302			return (-1);
303		}
304		convert_to_ascii(data, store, len, extra_bytes);
305		break;
306
307	case BCDPLUS_TYPE:
308
309		alloc_size = len * 2;
310		store = (uint8_t *)malloc(sizeof (uint8_t) * alloc_size);
311		if (store == NULL) {
312			return (-1);
313		}
314
315		bcdplus_to_ascii(data, store, len);
316		break;
317
318	case BINARY_TYPE:
319	case UNICODE_TYPE:
320	default:
321		return (-1);
322	}
323
324	(void) memcpy(field, store, alloc_size);
325	free(store);
326	return (len);
327}
328
329static int
330read_board_info(uint8_t board_offset, payload_t *manr, format_t *format)
331{
332	time_t time;
333	uint8_t *buffer;
334	uint8_t mfg_time[4];
335	uint8_t data[FRU_DATA_MAX_SIZE];
336	int ret = 0, current_offset = 0x0;
337	int bd_area_len = 0;
338
339	/* read version, length, lang code, mfg. time */
340	ret = get_fru_data(board_offset, BD_FIELDS_SIZE, data, format);
341
342	if (ret < 0) {
343		return (-1);
344	}
345
346	/* version check */
347	if ((data[0] | CMN_HDR_VERSION_MASK) != 1) {
348		return (-1);
349	}
350
351	/* byte 2 is lang code */
352	bd_area_len = data[1] * 8;
353	mfg_time[3] = data[3];
354	mfg_time[2] = data[4];
355	mfg_time[1] = data[5];
356	mfg_time[0] = 0x0;
357	time = get_utc_time(mfg_time);
358
359	/* fill the timestamp into manr */
360	(void) memcpy(manr->timestamp, &time, MANR_TIME_LEN);
361
362	if (bd_area_len < BD_MFR_OFFSET) {
363		return (-1);
364	}
365	buffer = (uint8_t *)malloc(sizeof (uint8_t) * MANR_MAX_LENGTH);
366	if (buffer == NULL) {
367		return (-1);
368	}
369
370	/* read the  board info  */
371	current_offset += board_offset + BD_MFR_OFFSET;
372	current_offset += read_bd_fields(buffer, current_offset, format);
373
374	if (strncmp(SUN_NAME, (char *)buffer, sizeof (SUN_NAME)) == 0) {
375		manr->vendor_name[0] = 0x00;
376		manr->vendor_name[1] = 0x3e;
377	} else {
378		manr->vendor_name[0] = 0x00;
379		manr->vendor_name[1] = 0x00;
380	}
381
382	current_offset += 1;	/* for length/type field */
383
384	current_offset += read_bd_fields(buffer, current_offset, format);
385	current_offset += 1;	/* for length/type field */
386	(void) memcpy(manr->fru_short_name, buffer, MANR_FRUNAME_LEN);
387
388	current_offset += read_bd_fields(buffer, current_offset, format);
389	current_offset += 1;	/* for length/type field */
390	(void) memcpy(manr->sun_serial_no, buffer, MANR_SERIALNUM_LEN);
391
392	current_offset += read_bd_fields(buffer, current_offset, format);
393	current_offset += 1;	/* for length/type field */
394	(void) memcpy(manr->sun_part_no, buffer, MANR_PARTNUM_LEN);
395
396	/*
397	 * We dont need the FRU FILE ID, so just skip the field
398	 * and get the offset to read the custom MFG. info fields
399	 */
400	current_offset += read_bd_fields(buffer, current_offset, format);
401	current_offset += 1;	/* for length/type field */
402
403	current_offset += read_bd_fields(buffer, current_offset, format);
404	current_offset += 1;	/* for length/type field */
405
406	/* read the custom mfg. info fields */
407	current_offset += read_bd_fields(buffer, current_offset, format);
408	current_offset += 1;	/* for length/type field */
409	(void) memcpy(manr->manufacture_loc, buffer, MANR_MFRLOC_LEN);
410
411	current_offset += read_bd_fields(buffer, current_offset, format);
412	(void) memcpy(manr->fru_descr, buffer, MANR_FRUDESCR_LEN);
413
414	free(buffer);
415	return (0);
416}
417
418/*
419 * Read the IPMI information from hardware and translate it into
420 * MANR(SUN format)
421 */
422int
423get_manr(format_t *format, payload_t *manr)
424{
425	int ret = 0;
426	fruid_offset_t *offset = NULL;
427
428	offset = (fruid_offset_t *)malloc(sizeof (fruid_offset_t));
429	if (offset == NULL) {
430		return (-1);
431	}
432
433	ret  = read_common_header(offset, format);
434	if (ret != 0) {
435		free(offset);
436		return (-1);
437	}
438
439	if (offset->board != 0) {
440		ret  = read_board_info(offset->board, manr, format);
441	}
442
443	free(offset);
444	return (ret);
445}
446
447static void
448convert_to_ascii(uint8_t  data [], uint8_t store[],
449				int length, int extra_bytes)
450{
451	uint8_t x, y;
452	int index = 0;
453	int i, idx = length - (length % 3);
454
455	for (i = 0; ; i += 3) {
456
457		x = 0x0;
458		y = 0x0;
459
460		if (i == idx && extra_bytes == 0) {
461			break;
462		}
463
464		/* get the first six bits */
465		x = (data[i] & BIT_MASK1);
466		x +=  ASCII_MAP;
467		store[index] = x;
468
469		if (i == idx && extra_bytes == 1) {
470			break;
471		}
472
473		/*
474		 * get last 2 bits of first byte and first
475		 * 4 bits of second byte
476		 */
477
478		x = (data[i] >> 6);
479		y = (data[i + 1] & BIT_MASK2) << 2;
480		x |= y  + ASCII_MAP;
481		store[index+1] = x;
482
483		if (i == idx) {
484			break;
485		}
486
487		/* get last 4 bits of second byte and 2 bits of last byte */
488		x = data[i + 1] >> 4;
489		y = (data[i + 2] & BIT_MASK3) << 4;
490		x |= y + ASCII_MAP;
491		store[index+2] = x;
492
493		/* get last six bits of third byte */
494		store[index + 3] = (data[i + 2] >> 2) + ASCII_MAP;
495		index += 4;
496	}
497}
498
499static void
500bcdplus_to_ascii(uint8_t data[], uint8_t store[], int len)
501{
502	int i, j, index = 0;
503	uint8_t tmp = 0;
504
505	struct {
506		int a:4;
507		int b:4;
508	} val;
509
510	for (i = 0; i < len; i++) {
511		(void) memcpy(&val, &data[i], 1);
512		for (j = 0; j < 2; j++) {
513			if (j == 0) {
514				tmp = val.a;
515			} else
516				tmp = val.b;
517
518			if (tmp <= 9) {
519				/* ascii conversion */
520				store[index++] = tmp + 48;
521				continue;
522			}
523
524			switch (tmp) {
525
526			case 0xa:
527				store[index++] = ' ';
528				break;
529			case 0xb:
530				store[index++] = '-';
531				break;
532			case 0xc:
533				store[index++] = '.';
534				break;
535			default:
536				store[index++] = ' ';
537			}
538		}
539	}
540}
541
542/* converts ipmi format time to UTC time (unix 32 bit timestamp) */
543static time_t
544get_utc_time(uint8_t data [])
545{
546	time_t time;
547	struct tm tm1;
548	uint32_t ipmi_time;
549
550	(void) memcpy(&ipmi_time, data, 4);
551
552	ipmi_time *= 60;	/* convert into seconds */
553
554	/* get UTC time for 0:00 1/1/96 (ipmi epoch) */
555	tm1.tm_sec 	= 0;
556	tm1.tm_min 	= 0;
557	tm1.tm_hour 	= 0;
558	tm1.tm_mday 	= 1;
559	tm1.tm_mon 	= 0;
560	tm1.tm_year 	= 96;
561
562	time = mktime(&tm1);
563	time += ipmi_time;
564
565	return (time);
566}
567
568/*
569 * routine to write information to BMC
570 */
571static int
572write_alarm_fru_data(const void  *buffer, size_t size,
573		off_t offset, format_t *format)
574{
575	sc_reqmsg_t req_pkt;
576	sc_rspmsg_t res_pkt;
577	uint8_t	*datap = NULL;
578
579	if (buffer == NULL) {
580		return (-1);
581	}
582	datap = (uint8_t *)malloc(sizeof (uint8_t) * (size  + 5));
583	if (datap == NULL) {
584		return (-1);
585	}
586
587	datap[0] = 0x7;		/* bus id */
588	datap[1] = 0xa0;	/* slave address */
589	datap[2] = 0;		/* count */
590	datap[3] = offset >> 8;	/* MSB */
591	datap[4] = (uint8_t)offset;	/* LSB */
592	(void) memcpy((void *)&(datap[5]), buffer, size);
593
594	/* initialize ipmi request packet */
595	(void) smc_init_ipmi_msg(&req_pkt, SMC_MASTER_WR_RD_I2C,
596		FRUACCESS_MSG_ID, (5 + size), datap, DEFAULT_SEQN,
597		format->dest, SMC_NETFN_APP_REQ, SMC_BMC_LUN);
598	free(datap);
599
600	/* send ipmi request packet */
601	if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
602		POLL_TIMEOUT) != SMC_SUCCESS) {
603		return (-1);
604	}
605	/* check the completion code */
606	if (res_pkt.data[7] != 0) {
607		return (-1);
608	}
609	return (0);
610}
611
612static int
613write_fru_data(const void  *buffer, size_t size,
614	off_t offset, format_t *format)
615{
616	int ipmi = 0;
617	sc_reqmsg_t req_pkt;
618	sc_rspmsg_t res_pkt;
619	uint8_t	*datap = NULL;
620
621	if (buffer == NULL) {
622		return (-1);
623	}
624
625	if (format->src == format->dest) {
626		ipmi = 0;
627	} else {
628		ipmi = 1;
629	}
630
631	switch (ipmi) {
632
633	case 0: /* on board info (local i2c) */
634
635	SC_MSG_CMD(&req_pkt) = SMC_EEPROM_WRITE;
636	SC_MSG_LEN(&req_pkt) = 4 + size;
637	SC_MSG_ID(&req_pkt) = FRUACCESS_MSG_ID;
638
639	/* data field for request */
640	req_pkt.data[0] = format->sun_device_id;	/* device id */
641	req_pkt.data[1] = offset; /* (LSB) */
642	req_pkt.data[3] = size;
643	if (format->format == SUN_FORMAT) {
644		req_pkt.data[2] = offset >> 8;
645	} else {
646		req_pkt.data[2] = 0x0;  /* (MSB) always 0x0 for IPMI */
647	}
648	(void) memcpy((void *)&(req_pkt.data[4]), buffer, size);
649
650	/* make a call to smc library to send cmd */
651	if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
652		POLL_TIMEOUT) != SMC_SUCCESS) {
653		return (-1);
654	}
655	break;
656
657	default: /* read data from remote device (ipmi) */
658	datap = (uint8_t *)malloc(sizeof (uint8_t) * (size  + 4));
659	if (datap == NULL) {
660		return (-1);
661	}
662
663	datap[0] = format->sun_device_id;	/* device id */
664	datap[1] = offset;			/* LSB */
665	datap[3] = size;			/* nbytes */
666	if (format->format == SUN_FORMAT) {
667		datap[2] = offset >> 8;
668	} else {
669		datap[2] = 0x0;	/* (MSB) always 0x0 for IPMI */
670	}
671	(void) memcpy((void *)&(datap[4]), buffer, size);
672
673	(void) smc_init_ipmi_msg(&req_pkt, WRITE_FRU_INVENTORY_DATA,
674		FRUACCESS_MSG_ID, (4 + size), datap, DEFAULT_SEQN,
675		format->dest, SMC_NETFN_STORAGE_REQ, format->sun_lun);
676	free(datap);
677
678	if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
679		POLL_TIMEOUT) != SMC_SUCCESS) {
680		return (-1);
681	}
682	/* check the completion code */
683	if (res_pkt.data[7] != 0) {
684		return (-1);
685	}
686	break;
687	} /* end of switch */
688	return (0);
689}
690
691/*
692 * This routine splits the data to write into smaller chunks and
693 * write it to FRUID chip using SMC drv APIs
694 */
695
696/* ARGSUSED */
697ssize_t
698pwrite_new(int fd, const void  *buffer, size_t size,
699		off_t offset, format_t *format)
700{
701	int ret;
702	int index = 0;
703	size_t bytes = 0;
704	off_t next_offset = 0x0;
705	off_t curr_offset = offset;
706	size_t bytes_to_write = size;
707	uint8_t *data;
708	int retry = 3;
709	int (* func_ptr)(const void  *, size_t, off_t, format_t *);
710
711	if (format->dest == 0x20) {
712		func_ptr = write_alarm_fru_data;
713	} else {
714		func_ptr = write_fru_data;
715	}
716
717	data = (uint8_t *)buffer;
718	while (bytes_to_write != 0) {
719
720		retry = 3;
721		ret = 1;
722
723		if (bytes_to_write > SIZE_TO_READ_WRITE) {
724			bytes = SIZE_TO_READ_WRITE;
725			next_offset = curr_offset + SIZE_TO_READ_WRITE;
726		} else {
727			bytes = bytes_to_write;
728		}
729
730		bytes_to_write = bytes_to_write - bytes;
731		while ((ret != 0) && (retry != 0)) {
732			ret = (*func_ptr)((void *)&data[index],
733				bytes, curr_offset, format);
734			retry--;
735		}
736		if (ret != 0) {
737			return (ret);
738		}
739		index = index + bytes;
740		curr_offset = next_offset;
741	}
742	return (size);
743}
744
745/*
746 * This routine reads the data in smaller chunks and
747 * sends it to upper layer(frudata plugin) in the sw stack
748 */
749/* ARGSUSED */
750ssize_t
751pread_new(int fd, void  *buffer, size_t size,
752		off_t offset, format_t *format)
753{
754	int ret;
755	int index = 0;
756	size_t bytes = 0;
757	off_t next_offset = 0x0;
758	off_t curr_offset = offset;
759	size_t bytes_to_read = size;
760	uint8_t *data;
761	int retry = 3;
762	int (* func_ptr)(int, int, void *, format_t *);
763
764	if (format->dest == 0x20) {
765		func_ptr = get_alarm_fru_data;
766	} else {
767		func_ptr = get_fru_data;
768	}
769
770	data = (uint8_t *)buffer;
771
772	while (bytes_to_read != 0) {
773
774		retry = 3;
775		ret = 1;
776
777		if (bytes_to_read > SIZE_TO_READ_WRITE) {
778			bytes = SIZE_TO_READ_WRITE;
779			next_offset = curr_offset + SIZE_TO_READ_WRITE;
780		} else {
781			bytes = bytes_to_read;
782		}
783
784		bytes_to_read = bytes_to_read - bytes;
785
786		while ((ret != 0) && (retry != 0)) {
787			ret = (* func_ptr)(curr_offset, bytes,
788				(void *) &data[index], format);
789			retry--;
790		}
791		if (ret != 0) {
792			return (ret);
793		}
794		index = index + bytes;
795		curr_offset = next_offset;
796	}
797	return (size);
798}
799
800/*
801 * routine to check if IPMI fruid info is available,
802 * return 0: IPMI fruid not present
803 * return 1: IPMI fruid present
804 */
805static int
806is_ipmi_fru_data_available(int src, int dest)
807{
808	sc_reqmsg_t req_pkt;
809	sc_rspmsg_t res_pkt;
810	uint8_t datap[5];
811
812	/* on board access */
813	if (src == dest) {
814
815		SC_MSG_CMD(&req_pkt) = SMC_EEPROM_READ;
816		SC_MSG_LEN(&req_pkt) = 4;
817		SC_MSG_ID(&req_pkt) = FRUACCESS_MSG_ID;
818
819		/* data field for request */
820		req_pkt.data[0] = 0x0;	/* eeprom number (ipmi format) */
821		req_pkt.data[1] = CMN_HDR_OFFSET; /* (LSB) */
822		req_pkt.data[2] = 0x0;	/* (MSB) always 0x0 for IPMI */
823		req_pkt.data[3] = IPMI_COMMON_HEADER_SIZE;
824
825		/* make a call to smc library to send cmd */
826		if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
827			POLL_TIMEOUT) != SMC_SUCCESS) {
828			return (0);
829		}
830
831		/* version check */
832		if (res_pkt.data[0] != IPMI_VERSION) {
833			return (0);
834		} else {
835			return (1);
836		}
837	}
838
839	/* ipmi access */
840	datap[0] = FRU_DEVICE_ID;	/*  fru device id - always */
841	datap[1] = 0x0;		/* LSB */
842	datap[2] = 0x0;		/* MSB */
843	datap[3] = 8;		/* bytes to read */
844
845	(void) smc_init_ipmi_msg(&req_pkt, READ_FRU_INVENTORY_DATA,
846		FRUACCESS_MSG_ID, 4, datap, DEFAULT_SEQN,
847		IPMB_ADDR(dest), SMC_NETFN_STORAGE_REQ, SMC_BMC_LUN);
848
849	if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
850		FRUID_CHECK_POLL_TIMEOUT) != SMC_SUCCESS) {
851		return (0);
852	}
853
854	if (res_pkt.data[9] == IPMI_VERSION) {
855		return (1);
856	} else {
857		return (0);
858	}
859}
860
861/*
862 * routine to check if fruid info is available on BMC,
863 * return 0: fruid not present
864 * return 1: fruid present
865 */
866static int
867is_alarm_frudata_available(format_t *fru_format)
868{
869	int ret;
870	char buffer[TMP_BUFFER_SIZE];
871	int fd = -1;
872	format_t format;
873
874	bzero(buffer, sizeof (buffer));
875	format.src = fru_format->src;
876	format.dest = fru_format->dest;
877	format.sun_device_id = 0x0;
878	format.sun_lun = 0x0;
879	format.format |= SUN_FORMAT;
880
881	/* passing dummy fd */
882	/* for now read the first 3 bytes and check the info */
883	ret = pread_new(fd, (void *) buffer, 3, STATIC_OFFSET, &format);
884	if (ret < 0) {
885		return (0);
886	}
887
888	if (buffer[0] != SECTION_HDR_TAG) {
889		fru_format->format  = NO_FRUDATA;
890		return (0);
891	}
892
893	fru_format->format = SUN_FORMAT;
894	fru_format->sun_device_id = 0x0;
895	fru_format->sun_lun = 0x0;
896	return (1);
897}
898
899/*
900 * checks if the remote device intelligent device (IPMI capable) or not
901 * return 0: not ipmi capable
902 * return 1: ipmi capable
903 */
904static int
905is_ipmi_capable(int src, int dest)
906{
907	sc_reqmsg_t req_pkt;
908	sc_rspmsg_t res_pkt;
909
910	if (src == dest) {
911		return (1);
912	}
913
914	(void) smc_init_ipmi_msg(&req_pkt, IPMI_GET_DEVICE_ID,
915		FRUACCESS_MSG_ID, 0, NULL, DEFAULT_SEQN,
916		IPMB_ADDR(dest), SMC_NETFN_APP_REQ, SMC_BMC_LUN);
917
918	if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
919		FRUID_CHECK_POLL_TIMEOUT) != SMC_SUCCESS) {
920		return (0);
921	}
922	return (1);	/* got response */
923}
924
925int
926is_fru_data_available(int precedence, int slot_no, format_t *fru_format)
927{
928	int ret, fd = 0;
929	uint8_t data[TMP_BUFFER_SIZE];
930
931	fru_format->format  = NO_FRUDATA;
932	if (fru_format->dest == 0x20) {	/* alarm card */
933		ret = is_alarm_frudata_available(fru_format);
934		return (ret);
935	}
936
937	if (cpu_no == 0) { /* get the geo_addr */
938		sc_reqmsg_t req_pkt;
939		sc_rspmsg_t rsp_pkt;
940		uint8_t size = 0;
941
942		/* initialize the request packet */
943		(void) smc_init_smc_msg(&req_pkt,
944			SMC_GET_GEOGRAPHICAL_ADDRESS, DEFAULT_SEQN, size);
945		/* make a call to smc library to send cmd */
946		if (smc_send_msg(DEFAULT_FD, &req_pkt, &rsp_pkt,
947			POLL_TIMEOUT) != SMC_SUCCESS) {
948			return (0);
949		}
950		if (SC_MSG_LEN(&rsp_pkt) == 0) {
951			return (0);
952		}
953		cpu_no = rsp_pkt.data[0];
954	}
955
956	/* check if it is IPMI intelligent or not */
957	if (slot_no != cpu_no) {
958		ret = is_ipmi_capable(cpu_no, slot_no);
959		if (ret == 0) { /* dumb I/O card */
960			return (0);
961		}
962	}
963
964	/* check if ipmi frudata is present or not */
965	ret = is_ipmi_fru_data_available(cpu_no, slot_no);
966	if (ret == 1) {
967		fru_format->format  |= IPMI_FORMAT;
968		fru_format->sun_device_id = 0x0;
969		fru_format->sun_lun = 0x0;
970
971		/* no need to look for sun format */
972		if (precedence == IPMI_FORMAT) {
973			return (fru_format->format);
974		}
975	}
976
977	/* check if sun fruid is present */
978	get_fru_data_info(cpu_no, slot_no, fru_format);
979	/* check the hdr version */
980	if (fru_format->format & SUN_FORMAT) {
981		ret = pread_new(fd, &data, BYTE_TO_READ_SUN_CHK,
982			STATIC_OFFSET, fru_format);
983		if (ret != BYTE_TO_READ_SUN_CHK) {
984			fru_format->format = fru_format->format &
985				(~ (SUN_FORMAT));
986			fru_format->sun_device_id = 0x0;
987			fru_format->sun_lun = 0x0;
988		}
989		if (data[0] != SECTION_HDR_TAG) {
990			fru_format->format = fru_format->format &
991				(~ (SUN_FORMAT));
992			fru_format->sun_device_id = 0x0;
993			fru_format->sun_lun = 0x0;
994		}
995	}
996	return (fru_format->format);
997}
998