112767SJames.Kremer@Sun.COM /*
212767SJames.Kremer@Sun.COM  * CDDL HEADER START
312767SJames.Kremer@Sun.COM  *
412767SJames.Kremer@Sun.COM  * The contents of this file are subject to the terms of the
512767SJames.Kremer@Sun.COM  * Common Development and Distribution License (the "License").
612767SJames.Kremer@Sun.COM  * You may not use this file except in compliance with the License.
712767SJames.Kremer@Sun.COM  *
812767SJames.Kremer@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
912767SJames.Kremer@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1012767SJames.Kremer@Sun.COM  * See the License for the specific language governing permissions
1112767SJames.Kremer@Sun.COM  * and limitations under the License.
1212767SJames.Kremer@Sun.COM  *
1312767SJames.Kremer@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1412767SJames.Kremer@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1512767SJames.Kremer@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1612767SJames.Kremer@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1712767SJames.Kremer@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1812767SJames.Kremer@Sun.COM  *
1912767SJames.Kremer@Sun.COM  * CDDL HEADER END
2012767SJames.Kremer@Sun.COM  */
2112767SJames.Kremer@Sun.COM /*
2212767SJames.Kremer@Sun.COM  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2312767SJames.Kremer@Sun.COM  */
2412767SJames.Kremer@Sun.COM 
2512767SJames.Kremer@Sun.COM /*
2612767SJames.Kremer@Sun.COM  * SES Log reader library
2712767SJames.Kremer@Sun.COM  *
2812767SJames.Kremer@Sun.COM  * This library is responsible for accessing the SES log at the target address,
2912767SJames.Kremer@Sun.COM  * formatting and returning any log entries found.
3012767SJames.Kremer@Sun.COM  *
3112767SJames.Kremer@Sun.COM  * The data will be returned in an nvlist_t structure allocated here.
3212767SJames.Kremer@Sun.COM  */
3312767SJames.Kremer@Sun.COM 
3412767SJames.Kremer@Sun.COM #include <assert.h>
3512767SJames.Kremer@Sun.COM #include <errno.h>
3612767SJames.Kremer@Sun.COM #include <fcntl.h>
3712767SJames.Kremer@Sun.COM #include <sys/param.h>
3812767SJames.Kremer@Sun.COM #include <libseslog.h>
3912767SJames.Kremer@Sun.COM #include <stdlib.h>
4012767SJames.Kremer@Sun.COM #include <string.h>
4112767SJames.Kremer@Sun.COM #include <sys/stat.h>
4212767SJames.Kremer@Sun.COM #include <unistd.h>
4312767SJames.Kremer@Sun.COM #include <dirent.h>
4412767SJames.Kremer@Sun.COM #include <sys/scsi/generic/commands.h>
4512767SJames.Kremer@Sun.COM #include <sys/scsi/generic/status.h>
46*12843Stodd.mckenney@oracle.com #include <sys/scsi/impl/commands.h>
4712767SJames.Kremer@Sun.COM 
4812767SJames.Kremer@Sun.COM /*
4912767SJames.Kremer@Sun.COM  * open the device with given device name
5012767SJames.Kremer@Sun.COM  */
5112767SJames.Kremer@Sun.COM static int
5212767SJames.Kremer@Sun.COM open_device(const char *device_name)
5312767SJames.Kremer@Sun.COM {
5412767SJames.Kremer@Sun.COM 	int oflags = O_NONBLOCK | O_RDWR;
5512767SJames.Kremer@Sun.COM 	int fd;
5612767SJames.Kremer@Sun.COM 
5712767SJames.Kremer@Sun.COM 	fd = open(device_name, oflags);
5812767SJames.Kremer@Sun.COM 	if (fd < 0)
5912767SJames.Kremer@Sun.COM 		fd = -errno;
6012767SJames.Kremer@Sun.COM 	return (fd);
6112767SJames.Kremer@Sun.COM }
6212767SJames.Kremer@Sun.COM 
6312767SJames.Kremer@Sun.COM /*
6412767SJames.Kremer@Sun.COM  * Initialize scsi struct
6512767SJames.Kremer@Sun.COM  */
6612767SJames.Kremer@Sun.COM static void
6712767SJames.Kremer@Sun.COM construct_scsi_pt_obj(struct uscsi_cmd *uscsi)
6812767SJames.Kremer@Sun.COM {
6912767SJames.Kremer@Sun.COM 	(void) memset(uscsi, 0, sizeof (struct uscsi_cmd));
7012767SJames.Kremer@Sun.COM 	uscsi->uscsi_timeout = DEF_PT_TIMEOUT;
7112767SJames.Kremer@Sun.COM 	uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE;
7212767SJames.Kremer@Sun.COM }
7312767SJames.Kremer@Sun.COM 
7412767SJames.Kremer@Sun.COM /*
7512767SJames.Kremer@Sun.COM  * set control cdb of scsi structure
7612767SJames.Kremer@Sun.COM  */
7712767SJames.Kremer@Sun.COM static void
7812767SJames.Kremer@Sun.COM set_scsi_pt_cdb(struct uscsi_cmd *uscsi, const unsigned char *cdb,
7912767SJames.Kremer@Sun.COM     int cdb_len)
8012767SJames.Kremer@Sun.COM {
8112767SJames.Kremer@Sun.COM 	uscsi->uscsi_cdb = (char *)cdb;
8212767SJames.Kremer@Sun.COM 	uscsi->uscsi_cdblen = cdb_len;
8312767SJames.Kremer@Sun.COM }
8412767SJames.Kremer@Sun.COM 
8512767SJames.Kremer@Sun.COM /*
8612767SJames.Kremer@Sun.COM  * initialize sense data
8712767SJames.Kremer@Sun.COM  */
8812767SJames.Kremer@Sun.COM static void
8912767SJames.Kremer@Sun.COM set_scsi_pt_sense(struct uscsi_cmd *uscsi, unsigned char *sense,
9012767SJames.Kremer@Sun.COM     int max_sense_len)
9112767SJames.Kremer@Sun.COM {
9212767SJames.Kremer@Sun.COM 	(void) memset(sense, 0, max_sense_len);
9312767SJames.Kremer@Sun.COM 	uscsi->uscsi_rqbuf = (char *)sense;
9412767SJames.Kremer@Sun.COM 	uscsi->uscsi_rqlen = max_sense_len;
9512767SJames.Kremer@Sun.COM }
9612767SJames.Kremer@Sun.COM 
9712767SJames.Kremer@Sun.COM /*
9812767SJames.Kremer@Sun.COM  * Initialize data going to device
9912767SJames.Kremer@Sun.COM  */
10012767SJames.Kremer@Sun.COM static void
10112767SJames.Kremer@Sun.COM set_scsi_pt_data_in(struct uscsi_cmd *uscsi, unsigned char *dxferp,
10212767SJames.Kremer@Sun.COM 		    int dxfer_len)
10312767SJames.Kremer@Sun.COM {
10412767SJames.Kremer@Sun.COM 	if (dxfer_len > 0) {
10512767SJames.Kremer@Sun.COM 		uscsi->uscsi_bufaddr = (char *)dxferp;
10612767SJames.Kremer@Sun.COM 		uscsi->uscsi_buflen = dxfer_len;
10712767SJames.Kremer@Sun.COM 		uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE |
10812767SJames.Kremer@Sun.COM 		    USCSI_RQENABLE;
10912767SJames.Kremer@Sun.COM 	}
11012767SJames.Kremer@Sun.COM }
11112767SJames.Kremer@Sun.COM 
11212767SJames.Kremer@Sun.COM /*
11312767SJames.Kremer@Sun.COM  * Executes SCSI command(or at least forwards it to lower layers).
11412767SJames.Kremer@Sun.COM  */
11512767SJames.Kremer@Sun.COM static int
11612767SJames.Kremer@Sun.COM do_scsi_pt(struct uscsi_cmd *uscsi, int fd, int time_secs)
11712767SJames.Kremer@Sun.COM {
11812767SJames.Kremer@Sun.COM 	if (time_secs > 0)
11912767SJames.Kremer@Sun.COM 		uscsi->uscsi_timeout = time_secs;
12012767SJames.Kremer@Sun.COM 
12112767SJames.Kremer@Sun.COM 	if (ioctl(fd, USCSICMD, uscsi)) {
12212767SJames.Kremer@Sun.COM 		/* Took an error */
12312767SJames.Kremer@Sun.COM 		return (errno);
12412767SJames.Kremer@Sun.COM 	}
12512767SJames.Kremer@Sun.COM 	return (0);
12612767SJames.Kremer@Sun.COM }
12712767SJames.Kremer@Sun.COM 
12812767SJames.Kremer@Sun.COM 
12912767SJames.Kremer@Sun.COM /*
13012767SJames.Kremer@Sun.COM  * Read log from device
13112767SJames.Kremer@Sun.COM  * Invokes a SCSI LOG SENSE command.
13212767SJames.Kremer@Sun.COM  * Return:
13312767SJames.Kremer@Sun.COM  * 0 -> success
13412767SJames.Kremer@Sun.COM  * SG_LIB_CAT_INVALID_OP -> Log Sense not supported,
13512767SJames.Kremer@Sun.COM  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
13612767SJames.Kremer@Sun.COM  * SG_LIB_CAT_NOT_READY -> device not ready,
13712767SJames.Kremer@Sun.COM  * -1 -> other failure
13812767SJames.Kremer@Sun.COM  */
13912767SJames.Kremer@Sun.COM 
14012767SJames.Kremer@Sun.COM static int
14112767SJames.Kremer@Sun.COM read_log(int sg_fd, unsigned char *resp, int mx_resp_len)
14212767SJames.Kremer@Sun.COM {
14312767SJames.Kremer@Sun.COM 	int res, ret;
14412767SJames.Kremer@Sun.COM 	unsigned char logsCmdBlk[CDB_GROUP1] =
14512767SJames.Kremer@Sun.COM 	    {SCMD_LOG_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
14612767SJames.Kremer@Sun.COM 	unsigned char sense_b[SENSE_BUFF_LEN];
14712767SJames.Kremer@Sun.COM 	struct uscsi_cmd uscsi;
14812767SJames.Kremer@Sun.COM 
14912767SJames.Kremer@Sun.COM 	if (mx_resp_len > 0xffff) {
15012767SJames.Kremer@Sun.COM 		return (-1);
15112767SJames.Kremer@Sun.COM 	}
15212767SJames.Kremer@Sun.COM 	logsCmdBlk[1] = 0;
15312767SJames.Kremer@Sun.COM 	/* pc = 1, pg_code = 0x7 (logs page) */
15412767SJames.Kremer@Sun.COM 	/* (((pc << 6) & 0xc0) | (pg_code & 0x3f)) = 0x47; */
15512767SJames.Kremer@Sun.COM 	logsCmdBlk[2] = 0x47;
15612767SJames.Kremer@Sun.COM 	/* pc = 1 current values */
15712767SJames.Kremer@Sun.COM 	logsCmdBlk[3] = 0; /* No subpage code */
15812767SJames.Kremer@Sun.COM 	logsCmdBlk[5] = 0; /* Want all logs starting from 0 */
15912767SJames.Kremer@Sun.COM 	logsCmdBlk[6] = 0;
16012767SJames.Kremer@Sun.COM 	logsCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff);
16112767SJames.Kremer@Sun.COM 	logsCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff);
16212767SJames.Kremer@Sun.COM 
16312767SJames.Kremer@Sun.COM 	construct_scsi_pt_obj(&uscsi);
16412767SJames.Kremer@Sun.COM 
16512767SJames.Kremer@Sun.COM 	set_scsi_pt_cdb(&uscsi, logsCmdBlk, sizeof (logsCmdBlk));
16612767SJames.Kremer@Sun.COM 	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
16712767SJames.Kremer@Sun.COM 	set_scsi_pt_data_in(&uscsi, resp, mx_resp_len);
16812767SJames.Kremer@Sun.COM 	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
16912767SJames.Kremer@Sun.COM 	if (res) {
17012767SJames.Kremer@Sun.COM 		ret = res;
17112767SJames.Kremer@Sun.COM 	} else {
17212767SJames.Kremer@Sun.COM 		ret = uscsi.uscsi_status;
17312767SJames.Kremer@Sun.COM 	}
17412767SJames.Kremer@Sun.COM 	return (ret);
17512767SJames.Kremer@Sun.COM }
17612767SJames.Kremer@Sun.COM 
17712767SJames.Kremer@Sun.COM /*
17812767SJames.Kremer@Sun.COM  * Save the logs by walking through the entries in the response buffer.
17912767SJames.Kremer@Sun.COM  *
18012767SJames.Kremer@Sun.COM  * resp buffer looks like:
18112767SJames.Kremer@Sun.COM  *
18212767SJames.Kremer@Sun.COM  * +=====-========-========-========-========-========-========-========-=====+
18312767SJames.Kremer@Sun.COM  * |  Bit|   7    |   6    |   5    |   4    |   3    |   2    |   1    |   0 |
18412767SJames.Kremer@Sun.COM  * |Byte |        |        |        |        |        |        |        |     |
18512767SJames.Kremer@Sun.COM  * |=====+====================================================================|
18612767SJames.Kremer@Sun.COM  * | 0   |  reserved       |     page code                                    |
18712767SJames.Kremer@Sun.COM  * |-----+--------------------------------------------------------------------|
18812767SJames.Kremer@Sun.COM  * | 1   |                   Reserved                                         |
18912767SJames.Kremer@Sun.COM  * |-----+--------------------------------------------------------------------|
19012767SJames.Kremer@Sun.COM  * | 2   |(MSB)                           Page Length(n-3)                    |
19112767SJames.Kremer@Sun.COM  * | --  |                                                                    |
19212767SJames.Kremer@Sun.COM  * | 3   |                                                            (LSB)   |
19312767SJames.Kremer@Sun.COM  * |-----+--------------------------------------------------------------------|
19412767SJames.Kremer@Sun.COM  * | 4   |                           Log Parameter (First)(Length X)          |
19512767SJames.Kremer@Sun.COM  * | --  |                                                                    |
19612767SJames.Kremer@Sun.COM  * | x+3 |                                                                    |
19712767SJames.Kremer@Sun.COM  * |-----+--------------------------------------------------------------------|
19812767SJames.Kremer@Sun.COM  * |n-y+1|                           Log Parameter (Last)(Length y)           |
19912767SJames.Kremer@Sun.COM  * | --  |                                                                    |
20012767SJames.Kremer@Sun.COM  * | n   |                                                                    |
20112767SJames.Kremer@Sun.COM  * +==========================================================================+
20212767SJames.Kremer@Sun.COM  *
20312767SJames.Kremer@Sun.COM  * Log parameter field looks like:
20412767SJames.Kremer@Sun.COM  *
20512767SJames.Kremer@Sun.COM  * +=====-========-========-========-========-========-========-========-=====+
20612767SJames.Kremer@Sun.COM  * |  Bit|   7    |   6    |   5    |   4    |   3    |   2    |   1    |   0 |
20712767SJames.Kremer@Sun.COM  * |Byte |        |        |        |        |        |        |        |     |
20812767SJames.Kremer@Sun.COM  * |=====+====================================================================|
20912767SJames.Kremer@Sun.COM  * | 0   |(MSB)                           Parameter Code                      |
21012767SJames.Kremer@Sun.COM  * | --  |                                                                    |
21112767SJames.Kremer@Sun.COM  * | 1   |                                                            (LSB)   |
21212767SJames.Kremer@Sun.COM  * |-----+--------------------------------------------------------------------|
21312767SJames.Kremer@Sun.COM  * | 2   | DU     |  DS    |  TSD    | ETC   |         TMC     |  LBIN  | LP  |
21412767SJames.Kremer@Sun.COM  * |-----+--------------------------------------------------------------------|
215*12843Stodd.mckenney@oracle.com  * | 3   |                          Parameter Length(n-3)                     |
21612767SJames.Kremer@Sun.COM  * |-----+--------------------------------------------------------------------|
21712767SJames.Kremer@Sun.COM  * | 4   |                           Parameter Values                         |
21812767SJames.Kremer@Sun.COM  * | --  |                                                                    |
21912767SJames.Kremer@Sun.COM  * | n	 |                                                                    |
22012767SJames.Kremer@Sun.COM  * |-----+--------------------------------------------------------------------|
22112767SJames.Kremer@Sun.COM  */
22212767SJames.Kremer@Sun.COM 
22312767SJames.Kremer@Sun.COM static int
224*12843Stodd.mckenney@oracle.com save_logs(unsigned char *resp, ses_log_call_t *data)
22512767SJames.Kremer@Sun.COM {
226*12843Stodd.mckenney@oracle.com 	int k;
227*12843Stodd.mckenney@oracle.com 	int param_code; 	/* Parameter code */
228*12843Stodd.mckenney@oracle.com 	int param_len = 0; 	/* Paramter length */
229*12843Stodd.mckenney@oracle.com 	unsigned char *log_param_ptr; 	/* Log parameter pointer */
23012767SJames.Kremer@Sun.COM 	unsigned char *log_str_ptr; /* ptr to ascii str returend by expander */
23112767SJames.Kremer@Sun.COM 
232*12843Stodd.mckenney@oracle.com 	char log_event_type[ENTRY_MAX_SIZE];
233*12843Stodd.mckenney@oracle.com 	char log_code[ENTRY_MAX_SIZE];
234*12843Stodd.mckenney@oracle.com 	char log_level[ENTRY_MAX_SIZE];
23512767SJames.Kremer@Sun.COM 	nvlist_t *entry;
23612767SJames.Kremer@Sun.COM 	char entry_num[15];
23712767SJames.Kremer@Sun.COM 	int type;
23812767SJames.Kremer@Sun.COM 	int match_found = 0;
239*12843Stodd.mckenney@oracle.com 	char save_buffer[MAX_LOG_ENTRY_SZ];
24012767SJames.Kremer@Sun.COM 	char entry_added = 0;
241*12843Stodd.mckenney@oracle.com 	int all_log_data_len;
242*12843Stodd.mckenney@oracle.com 
243*12843Stodd.mckenney@oracle.com 	/*
244*12843Stodd.mckenney@oracle.com 	 * Bytes 2 and 3 of response buffer contain the page length of
245*12843Stodd.mckenney@oracle.com 	 * the log entries returned.
246*12843Stodd.mckenney@oracle.com 	 */
247*12843Stodd.mckenney@oracle.com 	all_log_data_len = SCSI_READ16(&resp[2]);
248*12843Stodd.mckenney@oracle.com 
249*12843Stodd.mckenney@oracle.com 	/*
250*12843Stodd.mckenney@oracle.com 	 * Initialize log parameter pointer to point to first log entry.
251*12843Stodd.mckenney@oracle.com 	 * The resp includes 4 bytes of header info and then log entries
252*12843Stodd.mckenney@oracle.com 	 */
253*12843Stodd.mckenney@oracle.com 	log_param_ptr = &resp[0] + 4;
254*12843Stodd.mckenney@oracle.com 
255*12843Stodd.mckenney@oracle.com 	/*
256*12843Stodd.mckenney@oracle.com 	 * If multiple heads are reading the logs, it is possible that we
257*12843Stodd.mckenney@oracle.com 	 * could be re-reading some of the same log entries plus some
258*12843Stodd.mckenney@oracle.com 	 * new additional entries. Check to see if any entries in this read
259*12843Stodd.mckenney@oracle.com 	 * contain the same log entry as the last entry we read last time.
260*12843Stodd.mckenney@oracle.com 	 */
261*12843Stodd.mckenney@oracle.com 	if (data->last_log_entry != NULL &&
262*12843Stodd.mckenney@oracle.com 	    (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE)) {
263*12843Stodd.mckenney@oracle.com 		/*
264*12843Stodd.mckenney@oracle.com 		 * We have a valid log entry from a previous read log
265*12843Stodd.mckenney@oracle.com 		 * operation.
266*12843Stodd.mckenney@oracle.com 		 */
26712767SJames.Kremer@Sun.COM 
26812767SJames.Kremer@Sun.COM 
26912767SJames.Kremer@Sun.COM 		/*
270*12843Stodd.mckenney@oracle.com 		 * Start walking each log entry in response buffer looking for
27112767SJames.Kremer@Sun.COM 		 * a duplicate entry.
27212767SJames.Kremer@Sun.COM 		 */
273*12843Stodd.mckenney@oracle.com 		for (k = 0; k < all_log_data_len; k += param_len) {
274*12843Stodd.mckenney@oracle.com 			/*
275*12843Stodd.mckenney@oracle.com 			 * Calculate log entry length
276*12843Stodd.mckenney@oracle.com 			 * Log param ptr [3] contains the log length minus the
277*12843Stodd.mckenney@oracle.com 			 * header info which is 4 bytes so add that in.
278*12843Stodd.mckenney@oracle.com 			 */
279*12843Stodd.mckenney@oracle.com 			param_len = log_param_ptr[3] + 4;
280*12843Stodd.mckenney@oracle.com 
281*12843Stodd.mckenney@oracle.com 			if (param_len <= 4) {
28212767SJames.Kremer@Sun.COM 				/*
283*12843Stodd.mckenney@oracle.com 				 * Only header information in this entry
284*12843Stodd.mckenney@oracle.com 				 * process next log entry
28512767SJames.Kremer@Sun.COM 				 */
286*12843Stodd.mckenney@oracle.com 				log_param_ptr += param_len;
287*12843Stodd.mckenney@oracle.com 				continue;
28812767SJames.Kremer@Sun.COM 			}
28912767SJames.Kremer@Sun.COM 
29012767SJames.Kremer@Sun.COM 
291*12843Stodd.mckenney@oracle.com 			/*
292*12843Stodd.mckenney@oracle.com 			 * initialize log_str_ptr to point to string info
293*12843Stodd.mckenney@oracle.com 			 * returned by expander
294*12843Stodd.mckenney@oracle.com 			 * first 4 bytes of log parameter contains
295*12843Stodd.mckenney@oracle.com 			 * 2 bytes of parameter code, 1 byte of Control data
296*12843Stodd.mckenney@oracle.com 			 * and 1 byte for parameter length. Log string begins
297*12843Stodd.mckenney@oracle.com 			 * after that so add 4 to log param ptr.
298*12843Stodd.mckenney@oracle.com 			 */
299*12843Stodd.mckenney@oracle.com 			log_str_ptr = log_param_ptr + 4;
30012767SJames.Kremer@Sun.COM 
301*12843Stodd.mckenney@oracle.com 			/*
302*12843Stodd.mckenney@oracle.com 			 * Check to see if this is the
303*12843Stodd.mckenney@oracle.com 			 * same line
304*12843Stodd.mckenney@oracle.com 			 */
305*12843Stodd.mckenney@oracle.com 			if (strncmp((char *)log_str_ptr, data->last_log_entry,
306*12843Stodd.mckenney@oracle.com 			    SES_LOG_VALID_LOG_SIZE) == 0) {
307*12843Stodd.mckenney@oracle.com 				/* Found an exact match */
308*12843Stodd.mckenney@oracle.com 				log_param_ptr += param_len;
309*12843Stodd.mckenney@oracle.com 				k += param_len;
310*12843Stodd.mckenney@oracle.com 				match_found = 1;
311*12843Stodd.mckenney@oracle.com 				break;
31212767SJames.Kremer@Sun.COM 			}
313*12843Stodd.mckenney@oracle.com 			log_param_ptr += param_len;
31412767SJames.Kremer@Sun.COM 		}
31512767SJames.Kremer@Sun.COM 	}
31612767SJames.Kremer@Sun.COM 	if (!match_found) {
317*12843Stodd.mckenney@oracle.com 		log_param_ptr = &resp[0] + 4;
318*12843Stodd.mckenney@oracle.com 		k = 0;
319*12843Stodd.mckenney@oracle.com 	}
320*12843Stodd.mckenney@oracle.com 	if (k == all_log_data_len) {
321*12843Stodd.mckenney@oracle.com 		/*
322*12843Stodd.mckenney@oracle.com 		 * Either there was no log data or we have
323*12843Stodd.mckenney@oracle.com 		 * already read these log entries.
324*12843Stodd.mckenney@oracle.com 		 * Just return.
325*12843Stodd.mckenney@oracle.com 		 */
326*12843Stodd.mckenney@oracle.com 		return (0);
327*12843Stodd.mckenney@oracle.com 	}
328*12843Stodd.mckenney@oracle.com 
329*12843Stodd.mckenney@oracle.com 	/* Grab memory to return logs with */
330*12843Stodd.mckenney@oracle.com 	if (nvlist_alloc(&data->log_data, NV_UNIQUE_NAME, 0) != 0) {
331*12843Stodd.mckenney@oracle.com 		/* Couldn't alloc memory for nvlist */
332*12843Stodd.mckenney@oracle.com 		return (SES_LOG_FAILED_NVLIST_CREATE);
33312767SJames.Kremer@Sun.COM 	}
33412767SJames.Kremer@Sun.COM 
335*12843Stodd.mckenney@oracle.com 	(void) memset(log_event_type,	0, sizeof (log_event_type));
336*12843Stodd.mckenney@oracle.com 	(void) memset(log_code,		0, sizeof (log_code));
337*12843Stodd.mckenney@oracle.com 	(void) memset(save_buffer,	0, sizeof (save_buffer));
338*12843Stodd.mckenney@oracle.com 	(void) memset(log_level,	0, sizeof (log_level));
339*12843Stodd.mckenney@oracle.com 
340*12843Stodd.mckenney@oracle.com 	/*
341*12843Stodd.mckenney@oracle.com 	 * Start saving new log entries
342*12843Stodd.mckenney@oracle.com 	 * Walk the log data adding any new entries
343*12843Stodd.mckenney@oracle.com 	 */
34412767SJames.Kremer@Sun.COM 
345*12843Stodd.mckenney@oracle.com 	for (; k < all_log_data_len; k += param_len) {
346*12843Stodd.mckenney@oracle.com 		/*
347*12843Stodd.mckenney@oracle.com 		 * Calculate log entry length
348*12843Stodd.mckenney@oracle.com 		 * Log ptr [3] contains the log length minus the header info
349*12843Stodd.mckenney@oracle.com 		 * which is 4 bytes so add that in
350*12843Stodd.mckenney@oracle.com 		 */
351*12843Stodd.mckenney@oracle.com 		param_len = log_param_ptr[3] + 4;
352*12843Stodd.mckenney@oracle.com 
353*12843Stodd.mckenney@oracle.com 		if (param_len <= 4) {
354*12843Stodd.mckenney@oracle.com 			/* Only header information in this entry */
355*12843Stodd.mckenney@oracle.com 			/* process next log entry */
356*12843Stodd.mckenney@oracle.com 			log_param_ptr += param_len;
357*12843Stodd.mckenney@oracle.com 			continue;
35812767SJames.Kremer@Sun.COM 		}
359*12843Stodd.mckenney@oracle.com 
36012767SJames.Kremer@Sun.COM 		/*
361*12843Stodd.mckenney@oracle.com 		 * initialize log_str_ptr to point to string info of the log
362*12843Stodd.mckenney@oracle.com 		 * entry. First 4 bytes of log entry contains param code,
363*12843Stodd.mckenney@oracle.com 		 * control byte, and length. Log string starts after that.
36412767SJames.Kremer@Sun.COM 		 */
365*12843Stodd.mckenney@oracle.com 		log_str_ptr = log_param_ptr + 4;
36612767SJames.Kremer@Sun.COM 
36712767SJames.Kremer@Sun.COM 		/*
36812767SJames.Kremer@Sun.COM 		 * Format of log str is as follows
36912767SJames.Kremer@Sun.COM 		 * "%8x %8x %8x %8x %8x %8x %8x %8x",
37012767SJames.Kremer@Sun.COM 		 * log_entry.log_word0, log_entry.ts_u, log_entry.ts_l,
37112767SJames.Kremer@Sun.COM 		 * log_entry.seq_num, log_entry.log_code, log_entry.log_word2,
37212767SJames.Kremer@Sun.COM 		 * log_entry.log_word3, log_entry.log_word4
37312767SJames.Kremer@Sun.COM 		 * following example has extra spaces removed to fit in 80 char
37412767SJames.Kremer@Sun.COM 		 * 40004 0 42d5f5fe 185b 630002 fd0800 50800207 e482813
37512767SJames.Kremer@Sun.COM 		 */
376*12843Stodd.mckenney@oracle.com 
377*12843Stodd.mckenney@oracle.com 		(void) strncpy(save_buffer,
378*12843Stodd.mckenney@oracle.com 		    (const char *)log_str_ptr,
379*12843Stodd.mckenney@oracle.com 		    SES_LOG_VALID_LOG_SIZE);
380*12843Stodd.mckenney@oracle.com 
381*12843Stodd.mckenney@oracle.com 		(void) strncpy(log_event_type, (const char *)log_str_ptr +
382*12843Stodd.mckenney@oracle.com 		    SES_LOG_EVENT_TYPE_START, SES_LOG_SPECIFIC_ENTRY_SIZE);
383*12843Stodd.mckenney@oracle.com 
384*12843Stodd.mckenney@oracle.com 		(void) strncpy(log_code,
385*12843Stodd.mckenney@oracle.com 		    (const char *)log_str_ptr+SES_LOG_CODE_START,
386*12843Stodd.mckenney@oracle.com 		    SES_LOG_SPECIFIC_ENTRY_SIZE);
387*12843Stodd.mckenney@oracle.com 
388*12843Stodd.mckenney@oracle.com 		(void) strncpy(log_level,
389*12843Stodd.mckenney@oracle.com 		    (const char *) log_str_ptr +
390*12843Stodd.mckenney@oracle.com 		    SES_LOG_LEVEL_START, 1);
391*12843Stodd.mckenney@oracle.com 
392*12843Stodd.mckenney@oracle.com 		/* event type is in log_event_type */
393*12843Stodd.mckenney@oracle.com 		/* 4x004 = looking for x */
394*12843Stodd.mckenney@oracle.com 		type = (strtoul(log_event_type, 0, 16) >> 12) & 0xf;
39512767SJames.Kremer@Sun.COM 
396*12843Stodd.mckenney@oracle.com 		/*
397*12843Stodd.mckenney@oracle.com 		 * Check type. If type is 1, level needs to be
398*12843Stodd.mckenney@oracle.com 		 * changed to FATAL(4). If type is something other
399*12843Stodd.mckenney@oracle.com 		 * than 0 or 1, they are info only(0).
400*12843Stodd.mckenney@oracle.com 		 */
401*12843Stodd.mckenney@oracle.com 		if (type == 1) {
402*12843Stodd.mckenney@oracle.com 			(void) strcpy(log_level, "4");
403*12843Stodd.mckenney@oracle.com 		} else if (type > 1) {
404*12843Stodd.mckenney@oracle.com 			/* These are not application log */
405*12843Stodd.mckenney@oracle.com 			/* entries */
406*12843Stodd.mckenney@oracle.com 			/* make them info only */
407*12843Stodd.mckenney@oracle.com 			(void) strcpy(log_level, "0");
408*12843Stodd.mckenney@oracle.com 		}
40912767SJames.Kremer@Sun.COM 
410*12843Stodd.mckenney@oracle.com 		/* Add this entry to the nvlist log data */
411*12843Stodd.mckenney@oracle.com 		if (nvlist_alloc(&entry, NV_UNIQUE_NAME, 0) != 0) {
412*12843Stodd.mckenney@oracle.com 			/* Couldn't alloc space, return error */
413*12843Stodd.mckenney@oracle.com 			return (SES_LOG_FAILED_NV_UNIQUE);
414*12843Stodd.mckenney@oracle.com 		}
41512767SJames.Kremer@Sun.COM 
41612767SJames.Kremer@Sun.COM 
417*12843Stodd.mckenney@oracle.com 		if (nvlist_add_string(entry, ENTRY_LOG, save_buffer) != 0) {
418*12843Stodd.mckenney@oracle.com 			/* Error adding string, return error */
419*12843Stodd.mckenney@oracle.com 			nvlist_free(entry);
420*12843Stodd.mckenney@oracle.com 			return (SES_LOG_FAILED_NV_LOG);
421*12843Stodd.mckenney@oracle.com 		}
42212767SJames.Kremer@Sun.COM 
423*12843Stodd.mckenney@oracle.com 		if (nvlist_add_string(entry, ENTRY_CODE, log_code) != 0) {
424*12843Stodd.mckenney@oracle.com 			/* Error adding string, return error */
425*12843Stodd.mckenney@oracle.com 			nvlist_free(entry);
426*12843Stodd.mckenney@oracle.com 			return (SES_LOG_FAILED_NV_CODE);
427*12843Stodd.mckenney@oracle.com 		}
428*12843Stodd.mckenney@oracle.com 		if (nvlist_add_string(entry, ENTRY_SEVERITY, log_level) != 0) {
429*12843Stodd.mckenney@oracle.com 			/* Error adding srtring, return error */
430*12843Stodd.mckenney@oracle.com 			nvlist_free(entry);
431*12843Stodd.mckenney@oracle.com 			return (SES_LOG_FAILED_NV_SEV);
432*12843Stodd.mckenney@oracle.com 		}
43312767SJames.Kremer@Sun.COM 
434*12843Stodd.mckenney@oracle.com 		param_code = SCSI_READ16(&log_param_ptr[0]);
435*12843Stodd.mckenney@oracle.com 
436*12843Stodd.mckenney@oracle.com 		(void) snprintf(entry_num, sizeof (entry_num),
437*12843Stodd.mckenney@oracle.com 		    "%s%d", ENTRY_PREFIX, param_code);
43812767SJames.Kremer@Sun.COM 
439*12843Stodd.mckenney@oracle.com 		if (nvlist_add_nvlist(data->log_data, entry_num, entry) != 0) {
440*12843Stodd.mckenney@oracle.com 			/* Error adding nvlist, return error */
441*12843Stodd.mckenney@oracle.com 			nvlist_free(entry);
442*12843Stodd.mckenney@oracle.com 			return (SES_LOG_FAILED_NV_ENTRY);
443*12843Stodd.mckenney@oracle.com 		}
444*12843Stodd.mckenney@oracle.com 		nvlist_free(entry);
44512767SJames.Kremer@Sun.COM 
446*12843Stodd.mckenney@oracle.com 		entry_added = 1;
447*12843Stodd.mckenney@oracle.com 		(data->number_log_entries)++;
44812767SJames.Kremer@Sun.COM 
449*12843Stodd.mckenney@oracle.com 		log_param_ptr += param_len;
45012767SJames.Kremer@Sun.COM 
45112767SJames.Kremer@Sun.COM 	}
45212767SJames.Kremer@Sun.COM 	if (entry_added) {
45312767SJames.Kremer@Sun.COM 		/* Update the last log entry string with last one read */
454*12843Stodd.mckenney@oracle.com 		(void) strncpy(data->last_log_entry, save_buffer, MAXNAMELEN);
45512767SJames.Kremer@Sun.COM 	}
45612767SJames.Kremer@Sun.COM 	return (0);
45712767SJames.Kremer@Sun.COM }
45812767SJames.Kremer@Sun.COM 
45912767SJames.Kremer@Sun.COM 
46012767SJames.Kremer@Sun.COM 
46112767SJames.Kremer@Sun.COM /* Setup struct to send command to device */
46212767SJames.Kremer@Sun.COM static void
46312767SJames.Kremer@Sun.COM set_scsi_pt_data_out(struct uscsi_cmd *uscsi, const unsigned char *dxferp,
46412767SJames.Kremer@Sun.COM     int dxfer_len)
46512767SJames.Kremer@Sun.COM {
46612767SJames.Kremer@Sun.COM 	if (dxfer_len > 0) {
46712767SJames.Kremer@Sun.COM 		uscsi->uscsi_bufaddr = (char *)dxferp;
46812767SJames.Kremer@Sun.COM 		uscsi->uscsi_buflen = dxfer_len;
46912767SJames.Kremer@Sun.COM 		uscsi->uscsi_flags = USCSI_WRITE | USCSI_ISOLATE |
47012767SJames.Kremer@Sun.COM 		    USCSI_RQENABLE;
47112767SJames.Kremer@Sun.COM 	}
47212767SJames.Kremer@Sun.COM }
47312767SJames.Kremer@Sun.COM 
47412767SJames.Kremer@Sun.COM /*
47512767SJames.Kremer@Sun.COM  * Invokes a SCSI MODE SENSE(10) command.
47612767SJames.Kremer@Sun.COM  * Return:
47712767SJames.Kremer@Sun.COM  * 0 for success
47812767SJames.Kremer@Sun.COM  * SG_LIB_CAT_INVALID_OP -> invalid opcode
47912767SJames.Kremer@Sun.COM  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb
48012767SJames.Kremer@Sun.COM  * SG_LIB_CAT_NOT_READY -> device not ready
48112767SJames.Kremer@Sun.COM  * -1 -> other failure
48212767SJames.Kremer@Sun.COM  */
48312767SJames.Kremer@Sun.COM 
48412767SJames.Kremer@Sun.COM static int
48512767SJames.Kremer@Sun.COM sg_ll_mode_sense10(int sg_fd, void * resp, int mx_resp_len)
48612767SJames.Kremer@Sun.COM {
48712767SJames.Kremer@Sun.COM 	int res, ret;
48812767SJames.Kremer@Sun.COM 	unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] =
48912767SJames.Kremer@Sun.COM 	    {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
49012767SJames.Kremer@Sun.COM 	unsigned char sense_b[SENSE_BUFF_LEN];
49112767SJames.Kremer@Sun.COM 	struct uscsi_cmd uscsi;
49212767SJames.Kremer@Sun.COM 
49312767SJames.Kremer@Sun.COM 	modesCmdBlk[1] = 0;
49412767SJames.Kremer@Sun.COM 	modesCmdBlk[2] = 0; /* page code 0 vendor specific */
49512767SJames.Kremer@Sun.COM 	modesCmdBlk[3] = 0;
49612767SJames.Kremer@Sun.COM 	modesCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff);
49712767SJames.Kremer@Sun.COM 	modesCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff);
49812767SJames.Kremer@Sun.COM 
49912767SJames.Kremer@Sun.COM 	construct_scsi_pt_obj(&uscsi);
50012767SJames.Kremer@Sun.COM 	set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk));
50112767SJames.Kremer@Sun.COM 	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
50212767SJames.Kremer@Sun.COM 	set_scsi_pt_data_in(&uscsi, (unsigned char *) resp, mx_resp_len);
50312767SJames.Kremer@Sun.COM 	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
50412767SJames.Kremer@Sun.COM 	if (res) {
50512767SJames.Kremer@Sun.COM 		ret = res;
50612767SJames.Kremer@Sun.COM 	} else {
50712767SJames.Kremer@Sun.COM 		ret = uscsi.uscsi_status;
50812767SJames.Kremer@Sun.COM 	}
50912767SJames.Kremer@Sun.COM 	return (ret);
51012767SJames.Kremer@Sun.COM }
51112767SJames.Kremer@Sun.COM 
51212767SJames.Kremer@Sun.COM /*
51312767SJames.Kremer@Sun.COM  * Invokes a SCSI MODE SELECT(10) command.
51412767SJames.Kremer@Sun.COM  * Return:
51512767SJames.Kremer@Sun.COM  * 0 for success.
51612767SJames.Kremer@Sun.COM  * SG_LIB_CAT_INVALID_OP for invalid opcode
51712767SJames.Kremer@Sun.COM  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
51812767SJames.Kremer@Sun.COM  * SG_LIB_CAT_NOT_READY -> device not ready,
51912767SJames.Kremer@Sun.COM  * -1 -> other failure
52012767SJames.Kremer@Sun.COM  */
52112767SJames.Kremer@Sun.COM static int
52212767SJames.Kremer@Sun.COM sg_ll_mode_select10(int sg_fd, void * paramp, int param_len)
52312767SJames.Kremer@Sun.COM {
52412767SJames.Kremer@Sun.COM 	int res, ret;
52512767SJames.Kremer@Sun.COM 	unsigned char modesCmdBlk[MODE_SELECT10_CMDLEN] =
52612767SJames.Kremer@Sun.COM 	    {SCMD_MODE_SELECT_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
52712767SJames.Kremer@Sun.COM 	unsigned char sense_b[SENSE_BUFF_LEN];
52812767SJames.Kremer@Sun.COM 	struct uscsi_cmd uscsi;
52912767SJames.Kremer@Sun.COM 
53012767SJames.Kremer@Sun.COM 
53112767SJames.Kremer@Sun.COM 	modesCmdBlk[1] = 0;
53212767SJames.Kremer@Sun.COM 	/*
533*12843Stodd.mckenney@oracle.com 	 * modesCmdBlk 2 equal 0 PC 0 return current page code 0 return
53412767SJames.Kremer@Sun.COM 	 * vendor specific
53512767SJames.Kremer@Sun.COM 	 */
53612767SJames.Kremer@Sun.COM 
53712767SJames.Kremer@Sun.COM 	modesCmdBlk[7] = (unsigned char)((param_len >> 8) & 0xff);
53812767SJames.Kremer@Sun.COM 	modesCmdBlk[8] = (unsigned char)(param_len & 0xff);
53912767SJames.Kremer@Sun.COM 
54012767SJames.Kremer@Sun.COM 	construct_scsi_pt_obj(&uscsi);
54112767SJames.Kremer@Sun.COM 
54212767SJames.Kremer@Sun.COM 	set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk));
54312767SJames.Kremer@Sun.COM 	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
54412767SJames.Kremer@Sun.COM 	set_scsi_pt_data_out(&uscsi, (unsigned char *) paramp, param_len);
54512767SJames.Kremer@Sun.COM 	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
54612767SJames.Kremer@Sun.COM 	if (res) {
54712767SJames.Kremer@Sun.COM 		ret = res;
54812767SJames.Kremer@Sun.COM 	} else {
54912767SJames.Kremer@Sun.COM 		ret = uscsi.uscsi_status;
55012767SJames.Kremer@Sun.COM 	}
55112767SJames.Kremer@Sun.COM 	return (ret);
55212767SJames.Kremer@Sun.COM }
55312767SJames.Kremer@Sun.COM 
55412767SJames.Kremer@Sun.COM 
55512767SJames.Kremer@Sun.COM 
55612767SJames.Kremer@Sun.COM /*
55712767SJames.Kremer@Sun.COM  * MODE SENSE 10 commands yield a response that has block descriptors followed
55812767SJames.Kremer@Sun.COM  * by mode pages. In most cases users are interested in the first mode page.
55912767SJames.Kremer@Sun.COM  * This function returns the(byte) offset of the start of the first mode page.
56012767SJames.Kremer@Sun.COM  * Returns >= 0 is successful or -1 if failure. If there is a failure
56112767SJames.Kremer@Sun.COM  * a message is written to err_buff.
56212767SJames.Kremer@Sun.COM  */
56312767SJames.Kremer@Sun.COM 
56412767SJames.Kremer@Sun.COM /*
56512767SJames.Kremer@Sun.COM  * return data looks like:
56612767SJames.Kremer@Sun.COM  * Table 92 - Mode parameter header(10)
56712767SJames.Kremer@Sun.COM  * Bit
56812767SJames.Kremer@Sun.COM  * Byte
56912767SJames.Kremer@Sun.COM  *	7 	6 	5 	4 	3 	2 	1 	0
57012767SJames.Kremer@Sun.COM  *	----------------------------------------------------------
57112767SJames.Kremer@Sun.COM  * 0	MSB Data length
57212767SJames.Kremer@Sun.COM  * 1	LSB Data length
57312767SJames.Kremer@Sun.COM  *	----------------------------------------------------------
57412767SJames.Kremer@Sun.COM  * 2	Medium type
57512767SJames.Kremer@Sun.COM  *	----------------------------------------------------------
57612767SJames.Kremer@Sun.COM  * 3 	Device-specific parameter
57712767SJames.Kremer@Sun.COM  *	----------------------------------------------------------
57812767SJames.Kremer@Sun.COM  * 4 	Reserved
57912767SJames.Kremer@Sun.COM  *	----------------------------------------------------------
58012767SJames.Kremer@Sun.COM  * 5	Reserved
58112767SJames.Kremer@Sun.COM  *	----------------------------------------------------------
58212767SJames.Kremer@Sun.COM  * 6	MSB block descriptor length
58312767SJames.Kremer@Sun.COM  * 7	LSB block descriptor length
58412767SJames.Kremer@Sun.COM  *	----------------------------------------------------------
58512767SJames.Kremer@Sun.COM  *	block desciptors....
58612767SJames.Kremer@Sun.COM  *	-----------------------
58712767SJames.Kremer@Sun.COM  *	mode sense page:
58812767SJames.Kremer@Sun.COM  *	0 : ps Reserved : page Code
58912767SJames.Kremer@Sun.COM  *	1 : Page Length(n-1)
59012767SJames.Kremer@Sun.COM  *	2-N  Mode parameters
59112767SJames.Kremer@Sun.COM  */
59212767SJames.Kremer@Sun.COM static int
593*12843Stodd.mckenney@oracle.com sg_mode_page_offset(const unsigned char *resp, int resp_len)
59412767SJames.Kremer@Sun.COM {
59512767SJames.Kremer@Sun.COM 	int bd_len;
59612767SJames.Kremer@Sun.COM 	int calc_len;
59712767SJames.Kremer@Sun.COM 	int offset;
59812767SJames.Kremer@Sun.COM 
59912767SJames.Kremer@Sun.COM 	if ((NULL == resp) || (resp_len < 8)) {
60012767SJames.Kremer@Sun.COM 		/* Too short of a response buffer */
60112767SJames.Kremer@Sun.COM 		return (-1);
60212767SJames.Kremer@Sun.COM 	}
60312767SJames.Kremer@Sun.COM 
60412767SJames.Kremer@Sun.COM 	calc_len = (resp[0] << 8) + resp[1] + 2;
60512767SJames.Kremer@Sun.COM 	bd_len = (resp[6] << 8) + resp[7];
60612767SJames.Kremer@Sun.COM 
60712767SJames.Kremer@Sun.COM 	/* LongLBA doesn't change this calculation */
60812767SJames.Kremer@Sun.COM 	offset = bd_len + MODE10_RESP_HDR_LEN;
60912767SJames.Kremer@Sun.COM 
61012767SJames.Kremer@Sun.COM 	if ((offset + 2) > resp_len) {
611*12843Stodd.mckenney@oracle.com 		/* Given response length to small */
61212767SJames.Kremer@Sun.COM 		offset = -1;
61312767SJames.Kremer@Sun.COM 	} else if ((offset + 2) > calc_len) {
614*12843Stodd.mckenney@oracle.com 		/* Calculated response length too small */
61512767SJames.Kremer@Sun.COM 		offset = -1;
61612767SJames.Kremer@Sun.COM 	}
61712767SJames.Kremer@Sun.COM 	return (offset);
61812767SJames.Kremer@Sun.COM }
61912767SJames.Kremer@Sun.COM 
62012767SJames.Kremer@Sun.COM /*
62112767SJames.Kremer@Sun.COM  * Clear logs
62212767SJames.Kremer@Sun.COM  */
62312767SJames.Kremer@Sun.COM static int
624*12843Stodd.mckenney@oracle.com clear_log(int sg_fd, ses_log_call_t *data)
62512767SJames.Kremer@Sun.COM {
62612767SJames.Kremer@Sun.COM 
62712767SJames.Kremer@Sun.COM 	int res, alloc_len, off;
62812767SJames.Kremer@Sun.COM 	int md_len;
62912767SJames.Kremer@Sun.COM 	int read_in_len = 0;
630*12843Stodd.mckenney@oracle.com 	unsigned char ref_md[MAX_ALLOC_LEN];
63112767SJames.Kremer@Sun.COM 	struct log_clear_control_struct clear_data;
63212767SJames.Kremer@Sun.COM 	long myhostid;
63312767SJames.Kremer@Sun.COM 	int error = 0;
634*12843Stodd.mckenney@oracle.com 	long poll_time;
635*12843Stodd.mckenney@oracle.com 	char seq_num_str[10];
636*12843Stodd.mckenney@oracle.com 	unsigned long seq_num = 0;
63712767SJames.Kremer@Sun.COM 
63812767SJames.Kremer@Sun.COM 	(void) memset(&clear_data, 0, sizeof (clear_data));
63912767SJames.Kremer@Sun.COM 
64012767SJames.Kremer@Sun.COM 	clear_data.pageControls = 0x40;
64112767SJames.Kremer@Sun.COM 	clear_data.subpage_code = 0;
64212767SJames.Kremer@Sun.COM 	clear_data.page_lengthLower = 0x16;
64312767SJames.Kremer@Sun.COM 
64412767SJames.Kremer@Sun.COM 	myhostid = gethostid();
64512767SJames.Kremer@Sun.COM 	/* 0 -> 11 are memset to 0 */
64612767SJames.Kremer@Sun.COM 	clear_data.host_id[12] = (myhostid & 0xff000000) >> 24;
64712767SJames.Kremer@Sun.COM 	clear_data.host_id[13] = (myhostid & 0xff0000) >> 16;
64812767SJames.Kremer@Sun.COM 	clear_data.host_id[14] = (myhostid & 0xff00) >> 8;
64912767SJames.Kremer@Sun.COM 	clear_data.host_id[15] = myhostid & 0xff;
65012767SJames.Kremer@Sun.COM 
651*12843Stodd.mckenney@oracle.com 	/*
652*12843Stodd.mckenney@oracle.com 	 * convert nanosecond time to seconds
653*12843Stodd.mckenney@oracle.com 	 */
654*12843Stodd.mckenney@oracle.com 	poll_time = data->poll_time / 1000000000;
655*12843Stodd.mckenney@oracle.com 	/* Add 5 minutes to poll time to allow for data retrieval time */
65612767SJames.Kremer@Sun.COM 	poll_time = poll_time + 300;
65712767SJames.Kremer@Sun.COM 	clear_data.timeout[0] = (poll_time & 0xff00) >> 8;
65812767SJames.Kremer@Sun.COM 	clear_data.timeout[1] = poll_time & 0xff;
65912767SJames.Kremer@Sun.COM 
660*12843Stodd.mckenney@oracle.com 	/*
661*12843Stodd.mckenney@oracle.com 	 * retrieve the last read sequence number from the last
662*12843Stodd.mckenney@oracle.com 	 * log entry read.
663*12843Stodd.mckenney@oracle.com 	 */
664*12843Stodd.mckenney@oracle.com 	if (data->last_log_entry != NULL &&
665*12843Stodd.mckenney@oracle.com 	    (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE)) {
666*12843Stodd.mckenney@oracle.com 		/*
667*12843Stodd.mckenney@oracle.com 		 * We have a valid log entry from a previous read log
668*12843Stodd.mckenney@oracle.com 		 * operation.
669*12843Stodd.mckenney@oracle.com 		 */
670*12843Stodd.mckenney@oracle.com 		(void) strncpy(seq_num_str,
671*12843Stodd.mckenney@oracle.com 		    (const char *) data->last_log_entry +
672*12843Stodd.mckenney@oracle.com 		    SES_LOG_SEQ_NUM_START, 8);
673*12843Stodd.mckenney@oracle.com 		seq_num = strtoul(seq_num_str, 0, 16);
674*12843Stodd.mckenney@oracle.com 	}
67512767SJames.Kremer@Sun.COM 	clear_data.seq_clear[0] = (seq_num & 0xff000000) >> 24;
67612767SJames.Kremer@Sun.COM 	clear_data.seq_clear[1] = (seq_num & 0xff0000) >> 16;
67712767SJames.Kremer@Sun.COM 	clear_data.seq_clear[2] = (seq_num & 0xff00) >> 8;
67812767SJames.Kremer@Sun.COM 	clear_data.seq_clear[3] = (seq_num & 0xff);
67912767SJames.Kremer@Sun.COM 
68012767SJames.Kremer@Sun.COM 	read_in_len = sizeof (clear_data);
68112767SJames.Kremer@Sun.COM 
68212767SJames.Kremer@Sun.COM 
68312767SJames.Kremer@Sun.COM 	/* do MODE SENSE to fetch current values */
684*12843Stodd.mckenney@oracle.com 	(void) memset(ref_md, 0, MAX_ALLOC_LEN);
685*12843Stodd.mckenney@oracle.com 	alloc_len = MAX_ALLOC_LEN;
68612767SJames.Kremer@Sun.COM 
68712767SJames.Kremer@Sun.COM 
68812767SJames.Kremer@Sun.COM 	res = sg_ll_mode_sense10(sg_fd, ref_md, alloc_len);
68912767SJames.Kremer@Sun.COM 	if (0 != res) {
69012767SJames.Kremer@Sun.COM 		/* Error during mode sense */
69112767SJames.Kremer@Sun.COM 		error = SES_LOG_FAILED_MODE_SENSE;
69212767SJames.Kremer@Sun.COM 		return (error);
69312767SJames.Kremer@Sun.COM 	}
69412767SJames.Kremer@Sun.COM 
69512767SJames.Kremer@Sun.COM 	/* Setup mode Select to clear logs */
696*12843Stodd.mckenney@oracle.com 	off = sg_mode_page_offset(ref_md, alloc_len);
69712767SJames.Kremer@Sun.COM 	if (off < 0) {
69812767SJames.Kremer@Sun.COM 		/* Mode page offset error */
69912767SJames.Kremer@Sun.COM 		error =  SES_LOG_FAILED_MODE_SENSE_OFFSET;
70012767SJames.Kremer@Sun.COM 		return (error);
70112767SJames.Kremer@Sun.COM 	}
70212767SJames.Kremer@Sun.COM 	md_len = (ref_md[0] << 8) + ref_md[1] + 2;
70312767SJames.Kremer@Sun.COM 
70412767SJames.Kremer@Sun.COM 	ref_md[0] = 0;
70512767SJames.Kremer@Sun.COM 	ref_md[1] = 0;
70612767SJames.Kremer@Sun.COM 
70712767SJames.Kremer@Sun.COM 	if (md_len > alloc_len) {
70812767SJames.Kremer@Sun.COM 		/* Data length to large */
70912767SJames.Kremer@Sun.COM 		error = SES_LOG_FAILED_BAD_DATA_LEN;
71012767SJames.Kremer@Sun.COM 		return (error);
71112767SJames.Kremer@Sun.COM 	}
71212767SJames.Kremer@Sun.COM 
71312767SJames.Kremer@Sun.COM 	if ((md_len - off) != read_in_len) {
71412767SJames.Kremer@Sun.COM 		/* Content length not correct */
71512767SJames.Kremer@Sun.COM 		error = SES_LOG_FAILED_BAD_CONTENT_LEN;
71612767SJames.Kremer@Sun.COM 		return (error);
71712767SJames.Kremer@Sun.COM 	}
71812767SJames.Kremer@Sun.COM 
71912767SJames.Kremer@Sun.COM 	if ((clear_data.pageControls & 0x40) != (ref_md[off] & 0x40)) {
72012767SJames.Kremer@Sun.COM 		/* reference model doesn't have use subpage format bit set */
72112767SJames.Kremer@Sun.COM 		/* Even though it should have */
72212767SJames.Kremer@Sun.COM 		/* don't send the command */
723*12843Stodd.mckenney@oracle.com 		error = SES_LOG_FAILED_FORMAT_PAGE_ERR;
72412767SJames.Kremer@Sun.COM 		return (error);
72512767SJames.Kremer@Sun.COM 	}
72612767SJames.Kremer@Sun.COM 
727*12843Stodd.mckenney@oracle.com 	(void) memcpy(ref_md + off, (const void *) &clear_data,
72812767SJames.Kremer@Sun.COM 	    sizeof (clear_data));
72912767SJames.Kremer@Sun.COM 
73012767SJames.Kremer@Sun.COM 	res = sg_ll_mode_select10(sg_fd, ref_md, md_len);
73112767SJames.Kremer@Sun.COM 	if (res != 0) {
73212767SJames.Kremer@Sun.COM 		error = SES_LOG_FAILED_MODE_SELECT;
73312767SJames.Kremer@Sun.COM 		return (error);
73412767SJames.Kremer@Sun.COM 	}
73512767SJames.Kremer@Sun.COM 
73612767SJames.Kremer@Sun.COM 	return (error);
73712767SJames.Kremer@Sun.COM }
73812767SJames.Kremer@Sun.COM /*
73912767SJames.Kremer@Sun.COM  * Gather data from given device.
74012767SJames.Kremer@Sun.COM  */
74112767SJames.Kremer@Sun.COM static int
742*12843Stodd.mckenney@oracle.com gather_data(char *device_name, ses_log_call_t *data)
74312767SJames.Kremer@Sun.COM {
74412767SJames.Kremer@Sun.COM 	int sg_fd;
745*12843Stodd.mckenney@oracle.com 	int resp_len, res;
746*12843Stodd.mckenney@oracle.com 	unsigned char rsp_buff[MAX_ALLOC_LEN];
74712767SJames.Kremer@Sun.COM 	int error;
74812767SJames.Kremer@Sun.COM 
74912767SJames.Kremer@Sun.COM 	/* Open device */
75012767SJames.Kremer@Sun.COM 	if ((sg_fd = open_device(device_name)) < 0) {
75112767SJames.Kremer@Sun.COM 		/* Failed to open device */
75212767SJames.Kremer@Sun.COM 		return (SES_LOG_FAILED_TO_OPEN_DEVICE);
75312767SJames.Kremer@Sun.COM 	}
75412767SJames.Kremer@Sun.COM 
75512767SJames.Kremer@Sun.COM 	/* Read the logs */
75612767SJames.Kremer@Sun.COM 	(void) memset(rsp_buff, 0, sizeof (rsp_buff));
75712767SJames.Kremer@Sun.COM 	resp_len = 0x8000; /* Maximum size available to read */
75812767SJames.Kremer@Sun.COM 	res = read_log(sg_fd, rsp_buff, resp_len);
75912767SJames.Kremer@Sun.COM 
760*12843Stodd.mckenney@oracle.com 	if (res != 0) {
76112767SJames.Kremer@Sun.COM 		/* Some sort of Error during read of logs */
762*12843Stodd.mckenney@oracle.com 		(void) close(sg_fd);
76312767SJames.Kremer@Sun.COM 		return (SES_LOG_FAILED_TO_READ_DEVICE);
76412767SJames.Kremer@Sun.COM 	}
76512767SJames.Kremer@Sun.COM 
76612767SJames.Kremer@Sun.COM 	/* Save the logs */
767*12843Stodd.mckenney@oracle.com 	error = save_logs(rsp_buff, data);
76812767SJames.Kremer@Sun.COM 	if (error != 0) {
769*12843Stodd.mckenney@oracle.com 		(void) close(sg_fd);
77012767SJames.Kremer@Sun.COM 		return (error);
77112767SJames.Kremer@Sun.COM 	}
772*12843Stodd.mckenney@oracle.com 	/* Clear the logs */
773*12843Stodd.mckenney@oracle.com 	error = clear_log(sg_fd, data);
77412767SJames.Kremer@Sun.COM 
77512767SJames.Kremer@Sun.COM 	(void) close(sg_fd);
77612767SJames.Kremer@Sun.COM 
77712767SJames.Kremer@Sun.COM 	return (error);
77812767SJames.Kremer@Sun.COM }
77912767SJames.Kremer@Sun.COM 
78012767SJames.Kremer@Sun.COM /*
78112767SJames.Kremer@Sun.COM  * Access the SES target identified by the indicated path.  Read the logs
78212767SJames.Kremer@Sun.COM  * and return them in a nvlist.
78312767SJames.Kremer@Sun.COM  */
78412767SJames.Kremer@Sun.COM int
785*12843Stodd.mckenney@oracle.com access_ses_log(ses_log_call_t *data)
78612767SJames.Kremer@Sun.COM {
78712767SJames.Kremer@Sun.COM 	char real_path[MAXPATHLEN];
78812767SJames.Kremer@Sun.COM 	struct stat buffer;
78912767SJames.Kremer@Sun.COM 	int error;
79012767SJames.Kremer@Sun.COM 
791*12843Stodd.mckenney@oracle.com 	/* Initialize return data */
792*12843Stodd.mckenney@oracle.com 	data->log_data = NULL;
793*12843Stodd.mckenney@oracle.com 	data->number_log_entries = 0;
794*12843Stodd.mckenney@oracle.com 
79512767SJames.Kremer@Sun.COM 	if (data->target_path == NULL) {
79612767SJames.Kremer@Sun.COM 		/* NULL Target path, return error */
79712767SJames.Kremer@Sun.COM 		return (SES_LOG_FAILED_NULL_TARGET_PATH);
79812767SJames.Kremer@Sun.COM 	}
79912767SJames.Kremer@Sun.COM 
80012767SJames.Kremer@Sun.COM 	/* Try to find a valid path */
80112767SJames.Kremer@Sun.COM 	(void) snprintf(real_path, sizeof (real_path), "/devices%s:ses",
80212767SJames.Kremer@Sun.COM 	    data->target_path);
80312767SJames.Kremer@Sun.COM 
80412767SJames.Kremer@Sun.COM 	if (stat(real_path, &buffer) != 0) {
80512767SJames.Kremer@Sun.COM 
80612767SJames.Kremer@Sun.COM 		(void) snprintf(real_path, sizeof (real_path), "/devices%s:0",
80712767SJames.Kremer@Sun.COM 		    data->target_path);
80812767SJames.Kremer@Sun.COM 		if (stat(real_path, &buffer) != 0) {
80912767SJames.Kremer@Sun.COM 			/* Couldn't find a path that exists */
81012767SJames.Kremer@Sun.COM 			return (SES_LOG_FAILED_BAD_TARGET_PATH);
81112767SJames.Kremer@Sun.COM 		}
81212767SJames.Kremer@Sun.COM 	}
81312767SJames.Kremer@Sun.COM 
814*12843Stodd.mckenney@oracle.com 	error = gather_data(real_path, data);
81512767SJames.Kremer@Sun.COM 
81612767SJames.Kremer@Sun.COM 	/* Update the size of log entries being returned */
81712767SJames.Kremer@Sun.COM 	data->size_of_log_entries =
81812767SJames.Kremer@Sun.COM 	    data->number_log_entries * SES_LOG_VALID_LOG_SIZE;
81912767SJames.Kremer@Sun.COM 
82012767SJames.Kremer@Sun.COM 	return (error);
82112767SJames.Kremer@Sun.COM }
822