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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  * SES Log reader library
27  *
28  * This library is responsible for accessing the SES log at the target address,
29  * formatting and returning any log entries found.
30  *
31  * The data will be returned in an nvlist_t structure allocated here.
32  */
33 
34 #include <assert.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <sys/param.h>
38 #include <libseslog.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <dirent.h>
44 #include <sys/scsi/generic/commands.h>
45 #include <sys/scsi/generic/status.h>
46 #include <sys/scsi/impl/commands.h>
47 
48 /*
49  * open the device with given device name
50  */
51 static int
52 open_device(const char *device_name)
53 {
54 	int oflags = O_NONBLOCK | O_RDWR;
55 	int fd;
56 
57 	fd = open(device_name, oflags);
58 	if (fd < 0)
59 		fd = -errno;
60 	return (fd);
61 }
62 
63 /*
64  * Initialize scsi struct
65  */
66 static void
67 construct_scsi_pt_obj(struct uscsi_cmd *uscsi)
68 {
69 	(void) memset(uscsi, 0, sizeof (struct uscsi_cmd));
70 	uscsi->uscsi_timeout = DEF_PT_TIMEOUT;
71 	uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE;
72 }
73 
74 /*
75  * set control cdb of scsi structure
76  */
77 static void
78 set_scsi_pt_cdb(struct uscsi_cmd *uscsi, const unsigned char *cdb,
79     int cdb_len)
80 {
81 	uscsi->uscsi_cdb = (char *)cdb;
82 	uscsi->uscsi_cdblen = cdb_len;
83 }
84 
85 /*
86  * initialize sense data
87  */
88 static void
89 set_scsi_pt_sense(struct uscsi_cmd *uscsi, unsigned char *sense,
90     int max_sense_len)
91 {
92 	(void) memset(sense, 0, max_sense_len);
93 	uscsi->uscsi_rqbuf = (char *)sense;
94 	uscsi->uscsi_rqlen = max_sense_len;
95 }
96 
97 /*
98  * Initialize data going to device
99  */
100 static void
101 set_scsi_pt_data_in(struct uscsi_cmd *uscsi, unsigned char *dxferp,
102 		    int dxfer_len)
103 {
104 	if (dxfer_len > 0) {
105 		uscsi->uscsi_bufaddr = (char *)dxferp;
106 		uscsi->uscsi_buflen = dxfer_len;
107 		uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE |
108 		    USCSI_RQENABLE;
109 	}
110 }
111 
112 /*
113  * Executes SCSI command(or at least forwards it to lower layers).
114  */
115 static int
116 do_scsi_pt(struct uscsi_cmd *uscsi, int fd, int time_secs)
117 {
118 	if (time_secs > 0)
119 		uscsi->uscsi_timeout = time_secs;
120 
121 	if (ioctl(fd, USCSICMD, uscsi)) {
122 		/* Took an error */
123 		return (errno);
124 	}
125 	return (0);
126 }
127 
128 
129 /*
130  * Read log from device
131  * Invokes a SCSI LOG SENSE command.
132  * Return:
133  * 0 -> success
134  * SG_LIB_CAT_INVALID_OP -> Log Sense not supported,
135  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
136  * SG_LIB_CAT_NOT_READY -> device not ready,
137  * -1 -> other failure
138  */
139 
140 static int
141 read_log(int sg_fd, unsigned char *resp, int mx_resp_len)
142 {
143 	int res, ret;
144 	unsigned char logsCmdBlk[CDB_GROUP1] =
145 	    {SCMD_LOG_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
146 	unsigned char sense_b[SENSE_BUFF_LEN];
147 	struct uscsi_cmd uscsi;
148 
149 	if (mx_resp_len > 0xffff) {
150 		return (-1);
151 	}
152 	logsCmdBlk[1] = 0;
153 	/* pc = 1, pg_code = 0x7 (logs page) */
154 	/* (((pc << 6) & 0xc0) | (pg_code & 0x3f)) = 0x47; */
155 	logsCmdBlk[2] = 0x47;
156 	/* pc = 1 current values */
157 	logsCmdBlk[3] = 0; /* No subpage code */
158 	logsCmdBlk[5] = 0; /* Want all logs starting from 0 */
159 	logsCmdBlk[6] = 0;
160 	logsCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff);
161 	logsCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff);
162 
163 	construct_scsi_pt_obj(&uscsi);
164 
165 	set_scsi_pt_cdb(&uscsi, logsCmdBlk, sizeof (logsCmdBlk));
166 	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
167 	set_scsi_pt_data_in(&uscsi, resp, mx_resp_len);
168 	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
169 	if (res) {
170 		ret = res;
171 	} else {
172 		ret = uscsi.uscsi_status;
173 	}
174 	return (ret);
175 }
176 
177 /*
178  * Save the logs by walking through the entries in the response buffer.
179  *
180  * resp buffer looks like:
181  *
182  * +=====-========-========-========-========-========-========-========-=====+
183  * |  Bit|   7    |   6    |   5    |   4    |   3    |   2    |   1    |   0 |
184  * |Byte |        |        |        |        |        |        |        |     |
185  * |=====+====================================================================|
186  * | 0   |  reserved       |     page code                                    |
187  * |-----+--------------------------------------------------------------------|
188  * | 1   |                   Reserved                                         |
189  * |-----+--------------------------------------------------------------------|
190  * | 2   |(MSB)                           Page Length(n-3)                    |
191  * | --  |                                                                    |
192  * | 3   |                                                            (LSB)   |
193  * |-----+--------------------------------------------------------------------|
194  * | 4   |                           Log Parameter (First)(Length X)          |
195  * | --  |                                                                    |
196  * | x+3 |                                                                    |
197  * |-----+--------------------------------------------------------------------|
198  * |n-y+1|                           Log Parameter (Last)(Length y)           |
199  * | --  |                                                                    |
200  * | n   |                                                                    |
201  * +==========================================================================+
202  *
203  * Log parameter field looks like:
204  *
205  * +=====-========-========-========-========-========-========-========-=====+
206  * |  Bit|   7    |   6    |   5    |   4    |   3    |   2    |   1    |   0 |
207  * |Byte |        |        |        |        |        |        |        |     |
208  * |=====+====================================================================|
209  * | 0   |(MSB)                           Parameter Code                      |
210  * | --  |                                                                    |
211  * | 1   |                                                            (LSB)   |
212  * |-----+--------------------------------------------------------------------|
213  * | 2   | DU     |  DS    |  TSD    | ETC   |         TMC     |  LBIN  | LP  |
214  * |-----+--------------------------------------------------------------------|
215  * | 3   |                          Parameter Length(n-3)                     |
216  * |-----+--------------------------------------------------------------------|
217  * | 4   |                           Parameter Values                         |
218  * | --  |                                                                    |
219  * | n	 |                                                                    |
220  * |-----+--------------------------------------------------------------------|
221  */
222 
223 static int
224 save_logs(unsigned char *resp, ses_log_call_t *data)
225 {
226 	int k;
227 	int param_code; 	/* Parameter code */
228 	int param_len = 0; 	/* Paramter length */
229 	unsigned char *log_param_ptr; 	/* Log parameter pointer */
230 	unsigned char *log_str_ptr; /* ptr to ascii str returend by expander */
231 
232 	char log_event_type[ENTRY_MAX_SIZE];
233 	char log_code[ENTRY_MAX_SIZE];
234 	char log_level[ENTRY_MAX_SIZE];
235 	nvlist_t *entry;
236 	char entry_num[15];
237 	int type;
238 	int match_found = 0;
239 	char save_buffer[MAX_LOG_ENTRY_SZ];
240 	char entry_added = 0;
241 	int all_log_data_len;
242 
243 	/*
244 	 * Bytes 2 and 3 of response buffer contain the page length of
245 	 * the log entries returned.
246 	 */
247 	all_log_data_len = SCSI_READ16(&resp[2]);
248 
249 	/*
250 	 * Initialize log parameter pointer to point to first log entry.
251 	 * The resp includes 4 bytes of header info and then log entries
252 	 */
253 	log_param_ptr = &resp[0] + 4;
254 
255 	/*
256 	 * If multiple heads are reading the logs, it is possible that we
257 	 * could be re-reading some of the same log entries plus some
258 	 * new additional entries. Check to see if any entries in this read
259 	 * contain the same log entry as the last entry we read last time.
260 	 */
261 	if (data->last_log_entry != NULL &&
262 	    (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE)) {
263 		/*
264 		 * We have a valid log entry from a previous read log
265 		 * operation.
266 		 */
267 
268 
269 		/*
270 		 * Start walking each log entry in response buffer looking for
271 		 * a duplicate entry.
272 		 */
273 		for (k = 0; k < all_log_data_len; k += param_len) {
274 			/*
275 			 * Calculate log entry length
276 			 * Log param ptr [3] contains the log length minus the
277 			 * header info which is 4 bytes so add that in.
278 			 */
279 			param_len = log_param_ptr[3] + 4;
280 
281 			if (param_len <= 4) {
282 				/*
283 				 * Only header information in this entry
284 				 * process next log entry
285 				 */
286 				log_param_ptr += param_len;
287 				continue;
288 			}
289 
290 
291 			/*
292 			 * initialize log_str_ptr to point to string info
293 			 * returned by expander
294 			 * first 4 bytes of log parameter contains
295 			 * 2 bytes of parameter code, 1 byte of Control data
296 			 * and 1 byte for parameter length. Log string begins
297 			 * after that so add 4 to log param ptr.
298 			 */
299 			log_str_ptr = log_param_ptr + 4;
300 
301 			/*
302 			 * Check to see if this is the
303 			 * same line
304 			 */
305 			if (strncmp((char *)log_str_ptr, data->last_log_entry,
306 			    SES_LOG_VALID_LOG_SIZE) == 0) {
307 				/* Found an exact match */
308 				log_param_ptr += param_len;
309 				k += param_len;
310 				match_found = 1;
311 				break;
312 			}
313 			log_param_ptr += param_len;
314 		}
315 	}
316 	if (!match_found) {
317 		log_param_ptr = &resp[0] + 4;
318 		k = 0;
319 	}
320 	if (k == all_log_data_len) {
321 		/*
322 		 * Either there was no log data or we have
323 		 * already read these log entries.
324 		 * Just return.
325 		 */
326 		return (0);
327 	}
328 
329 	/* Grab memory to return logs with */
330 	if (nvlist_alloc(&data->log_data, NV_UNIQUE_NAME, 0) != 0) {
331 		/* Couldn't alloc memory for nvlist */
332 		return (SES_LOG_FAILED_NVLIST_CREATE);
333 	}
334 
335 	(void) memset(log_event_type,	0, sizeof (log_event_type));
336 	(void) memset(log_code,		0, sizeof (log_code));
337 	(void) memset(save_buffer,	0, sizeof (save_buffer));
338 	(void) memset(log_level,	0, sizeof (log_level));
339 
340 	/*
341 	 * Start saving new log entries
342 	 * Walk the log data adding any new entries
343 	 */
344 
345 	for (; k < all_log_data_len; k += param_len) {
346 		/*
347 		 * Calculate log entry length
348 		 * Log ptr [3] contains the log length minus the header info
349 		 * which is 4 bytes so add that in
350 		 */
351 		param_len = log_param_ptr[3] + 4;
352 
353 		if (param_len <= 4) {
354 			/* Only header information in this entry */
355 			/* process next log entry */
356 			log_param_ptr += param_len;
357 			continue;
358 		}
359 
360 		/*
361 		 * initialize log_str_ptr to point to string info of the log
362 		 * entry. First 4 bytes of log entry contains param code,
363 		 * control byte, and length. Log string starts after that.
364 		 */
365 		log_str_ptr = log_param_ptr + 4;
366 
367 		/*
368 		 * Format of log str is as follows
369 		 * "%8x %8x %8x %8x %8x %8x %8x %8x",
370 		 * log_entry.log_word0, log_entry.ts_u, log_entry.ts_l,
371 		 * log_entry.seq_num, log_entry.log_code, log_entry.log_word2,
372 		 * log_entry.log_word3, log_entry.log_word4
373 		 * following example has extra spaces removed to fit in 80 char
374 		 * 40004 0 42d5f5fe 185b 630002 fd0800 50800207 e482813
375 		 */
376 
377 		(void) strncpy(save_buffer,
378 		    (const char *)log_str_ptr,
379 		    SES_LOG_VALID_LOG_SIZE);
380 
381 		(void) strncpy(log_event_type, (const char *)log_str_ptr +
382 		    SES_LOG_EVENT_TYPE_START, SES_LOG_SPECIFIC_ENTRY_SIZE);
383 
384 		(void) strncpy(log_code,
385 		    (const char *)log_str_ptr+SES_LOG_CODE_START,
386 		    SES_LOG_SPECIFIC_ENTRY_SIZE);
387 
388 		(void) strncpy(log_level,
389 		    (const char *) log_str_ptr +
390 		    SES_LOG_LEVEL_START, 1);
391 
392 		/* event type is in log_event_type */
393 		/* 4x004 = looking for x */
394 		type = (strtoul(log_event_type, 0, 16) >> 12) & 0xf;
395 
396 		/*
397 		 * Check type. If type is 1, level needs to be
398 		 * changed to FATAL(4). If type is something other
399 		 * than 0 or 1, they are info only(0).
400 		 */
401 		if (type == 1) {
402 			(void) strcpy(log_level, "4");
403 		} else if (type > 1) {
404 			/* These are not application log */
405 			/* entries */
406 			/* make them info only */
407 			(void) strcpy(log_level, "0");
408 		}
409 
410 		/* Add this entry to the nvlist log data */
411 		if (nvlist_alloc(&entry, NV_UNIQUE_NAME, 0) != 0) {
412 			/* Couldn't alloc space, return error */
413 			return (SES_LOG_FAILED_NV_UNIQUE);
414 		}
415 
416 
417 		if (nvlist_add_string(entry, ENTRY_LOG, save_buffer) != 0) {
418 			/* Error adding string, return error */
419 			nvlist_free(entry);
420 			return (SES_LOG_FAILED_NV_LOG);
421 		}
422 
423 		if (nvlist_add_string(entry, ENTRY_CODE, log_code) != 0) {
424 			/* Error adding string, return error */
425 			nvlist_free(entry);
426 			return (SES_LOG_FAILED_NV_CODE);
427 		}
428 		if (nvlist_add_string(entry, ENTRY_SEVERITY, log_level) != 0) {
429 			/* Error adding srtring, return error */
430 			nvlist_free(entry);
431 			return (SES_LOG_FAILED_NV_SEV);
432 		}
433 
434 		param_code = SCSI_READ16(&log_param_ptr[0]);
435 
436 		(void) snprintf(entry_num, sizeof (entry_num),
437 		    "%s%d", ENTRY_PREFIX, param_code);
438 
439 		if (nvlist_add_nvlist(data->log_data, entry_num, entry) != 0) {
440 			/* Error adding nvlist, return error */
441 			nvlist_free(entry);
442 			return (SES_LOG_FAILED_NV_ENTRY);
443 		}
444 		nvlist_free(entry);
445 
446 		entry_added = 1;
447 		(data->number_log_entries)++;
448 
449 		log_param_ptr += param_len;
450 
451 	}
452 	if (entry_added) {
453 		/* Update the last log entry string with last one read */
454 		(void) strncpy(data->last_log_entry, save_buffer, MAXNAMELEN);
455 	}
456 	return (0);
457 }
458 
459 
460 
461 /* Setup struct to send command to device */
462 static void
463 set_scsi_pt_data_out(struct uscsi_cmd *uscsi, const unsigned char *dxferp,
464     int dxfer_len)
465 {
466 	if (dxfer_len > 0) {
467 		uscsi->uscsi_bufaddr = (char *)dxferp;
468 		uscsi->uscsi_buflen = dxfer_len;
469 		uscsi->uscsi_flags = USCSI_WRITE | USCSI_ISOLATE |
470 		    USCSI_RQENABLE;
471 	}
472 }
473 
474 /*
475  * Invokes a SCSI MODE SENSE(10) command.
476  * Return:
477  * 0 for success
478  * SG_LIB_CAT_INVALID_OP -> invalid opcode
479  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb
480  * SG_LIB_CAT_NOT_READY -> device not ready
481  * -1 -> other failure
482  */
483 
484 static int
485 sg_ll_mode_sense10(int sg_fd, void * resp, int mx_resp_len)
486 {
487 	int res, ret;
488 	unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] =
489 	    {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
490 	unsigned char sense_b[SENSE_BUFF_LEN];
491 	struct uscsi_cmd uscsi;
492 
493 	modesCmdBlk[1] = 0;
494 	modesCmdBlk[2] = 0; /* page code 0 vendor specific */
495 	modesCmdBlk[3] = 0;
496 	modesCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff);
497 	modesCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff);
498 
499 	construct_scsi_pt_obj(&uscsi);
500 	set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk));
501 	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
502 	set_scsi_pt_data_in(&uscsi, (unsigned char *) resp, mx_resp_len);
503 	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
504 	if (res) {
505 		ret = res;
506 	} else {
507 		ret = uscsi.uscsi_status;
508 	}
509 	return (ret);
510 }
511 
512 /*
513  * Invokes a SCSI MODE SELECT(10) command.
514  * Return:
515  * 0 for success.
516  * SG_LIB_CAT_INVALID_OP for invalid opcode
517  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
518  * SG_LIB_CAT_NOT_READY -> device not ready,
519  * -1 -> other failure
520  */
521 static int
522 sg_ll_mode_select10(int sg_fd, void * paramp, int param_len)
523 {
524 	int res, ret;
525 	unsigned char modesCmdBlk[MODE_SELECT10_CMDLEN] =
526 	    {SCMD_MODE_SELECT_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
527 	unsigned char sense_b[SENSE_BUFF_LEN];
528 	struct uscsi_cmd uscsi;
529 
530 
531 	modesCmdBlk[1] = 0;
532 	/*
533 	 * modesCmdBlk 2 equal 0 PC 0 return current page code 0 return
534 	 * vendor specific
535 	 */
536 
537 	modesCmdBlk[7] = (unsigned char)((param_len >> 8) & 0xff);
538 	modesCmdBlk[8] = (unsigned char)(param_len & 0xff);
539 
540 	construct_scsi_pt_obj(&uscsi);
541 
542 	set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk));
543 	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
544 	set_scsi_pt_data_out(&uscsi, (unsigned char *) paramp, param_len);
545 	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
546 	if (res) {
547 		ret = res;
548 	} else {
549 		ret = uscsi.uscsi_status;
550 	}
551 	return (ret);
552 }
553 
554 
555 
556 /*
557  * MODE SENSE 10 commands yield a response that has block descriptors followed
558  * by mode pages. In most cases users are interested in the first mode page.
559  * This function returns the(byte) offset of the start of the first mode page.
560  * Returns >= 0 is successful or -1 if failure. If there is a failure
561  * a message is written to err_buff.
562  */
563 
564 /*
565  * return data looks like:
566  * Table 92 - Mode parameter header(10)
567  * Bit
568  * Byte
569  *	7 	6 	5 	4 	3 	2 	1 	0
570  *	----------------------------------------------------------
571  * 0	MSB Data length
572  * 1	LSB Data length
573  *	----------------------------------------------------------
574  * 2	Medium type
575  *	----------------------------------------------------------
576  * 3 	Device-specific parameter
577  *	----------------------------------------------------------
578  * 4 	Reserved
579  *	----------------------------------------------------------
580  * 5	Reserved
581  *	----------------------------------------------------------
582  * 6	MSB block descriptor length
583  * 7	LSB block descriptor length
584  *	----------------------------------------------------------
585  *	block desciptors....
586  *	-----------------------
587  *	mode sense page:
588  *	0 : ps Reserved : page Code
589  *	1 : Page Length(n-1)
590  *	2-N  Mode parameters
591  */
592 static int
593 sg_mode_page_offset(const unsigned char *resp, int resp_len)
594 {
595 	int bd_len;
596 	int calc_len;
597 	int offset;
598 
599 	if ((NULL == resp) || (resp_len < 8)) {
600 		/* Too short of a response buffer */
601 		return (-1);
602 	}
603 
604 	calc_len = (resp[0] << 8) + resp[1] + 2;
605 	bd_len = (resp[6] << 8) + resp[7];
606 
607 	/* LongLBA doesn't change this calculation */
608 	offset = bd_len + MODE10_RESP_HDR_LEN;
609 
610 	if ((offset + 2) > resp_len) {
611 		/* Given response length to small */
612 		offset = -1;
613 	} else if ((offset + 2) > calc_len) {
614 		/* Calculated response length too small */
615 		offset = -1;
616 	}
617 	return (offset);
618 }
619 
620 /*
621  * Clear logs
622  */
623 static int
624 clear_log(int sg_fd, ses_log_call_t *data)
625 {
626 
627 	int res, alloc_len, off;
628 	int md_len;
629 	int read_in_len = 0;
630 	unsigned char ref_md[MAX_ALLOC_LEN];
631 	struct log_clear_control_struct clear_data;
632 	long myhostid;
633 	int error = 0;
634 	long poll_time;
635 	char seq_num_str[10];
636 	unsigned long seq_num = 0;
637 
638 	(void) memset(&clear_data, 0, sizeof (clear_data));
639 
640 	clear_data.pageControls = 0x40;
641 	clear_data.subpage_code = 0;
642 	clear_data.page_lengthLower = 0x16;
643 
644 	myhostid = gethostid();
645 	/* 0 -> 11 are memset to 0 */
646 	clear_data.host_id[12] = (myhostid & 0xff000000) >> 24;
647 	clear_data.host_id[13] = (myhostid & 0xff0000) >> 16;
648 	clear_data.host_id[14] = (myhostid & 0xff00) >> 8;
649 	clear_data.host_id[15] = myhostid & 0xff;
650 
651 	/*
652 	 * convert nanosecond time to seconds
653 	 */
654 	poll_time = data->poll_time / 1000000000;
655 	/* Add 5 minutes to poll time to allow for data retrieval time */
656 	poll_time = poll_time + 300;
657 	clear_data.timeout[0] = (poll_time & 0xff00) >> 8;
658 	clear_data.timeout[1] = poll_time & 0xff;
659 
660 	/*
661 	 * retrieve the last read sequence number from the last
662 	 * log entry read.
663 	 */
664 	if (data->last_log_entry != NULL &&
665 	    (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE)) {
666 		/*
667 		 * We have a valid log entry from a previous read log
668 		 * operation.
669 		 */
670 		(void) strncpy(seq_num_str,
671 		    (const char *) data->last_log_entry +
672 		    SES_LOG_SEQ_NUM_START, 8);
673 		seq_num = strtoul(seq_num_str, 0, 16);
674 	}
675 	clear_data.seq_clear[0] = (seq_num & 0xff000000) >> 24;
676 	clear_data.seq_clear[1] = (seq_num & 0xff0000) >> 16;
677 	clear_data.seq_clear[2] = (seq_num & 0xff00) >> 8;
678 	clear_data.seq_clear[3] = (seq_num & 0xff);
679 
680 	read_in_len = sizeof (clear_data);
681 
682 
683 	/* do MODE SENSE to fetch current values */
684 	(void) memset(ref_md, 0, MAX_ALLOC_LEN);
685 	alloc_len = MAX_ALLOC_LEN;
686 
687 
688 	res = sg_ll_mode_sense10(sg_fd, ref_md, alloc_len);
689 	if (0 != res) {
690 		/* Error during mode sense */
691 		error = SES_LOG_FAILED_MODE_SENSE;
692 		return (error);
693 	}
694 
695 	/* Setup mode Select to clear logs */
696 	off = sg_mode_page_offset(ref_md, alloc_len);
697 	if (off < 0) {
698 		/* Mode page offset error */
699 		error =  SES_LOG_FAILED_MODE_SENSE_OFFSET;
700 		return (error);
701 	}
702 	md_len = (ref_md[0] << 8) + ref_md[1] + 2;
703 
704 	ref_md[0] = 0;
705 	ref_md[1] = 0;
706 
707 	if (md_len > alloc_len) {
708 		/* Data length to large */
709 		error = SES_LOG_FAILED_BAD_DATA_LEN;
710 		return (error);
711 	}
712 
713 	if ((md_len - off) != read_in_len) {
714 		/* Content length not correct */
715 		error = SES_LOG_FAILED_BAD_CONTENT_LEN;
716 		return (error);
717 	}
718 
719 	if ((clear_data.pageControls & 0x40) != (ref_md[off] & 0x40)) {
720 		/* reference model doesn't have use subpage format bit set */
721 		/* Even though it should have */
722 		/* don't send the command */
723 		error = SES_LOG_FAILED_FORMAT_PAGE_ERR;
724 		return (error);
725 	}
726 
727 	(void) memcpy(ref_md + off, (const void *) &clear_data,
728 	    sizeof (clear_data));
729 
730 	res = sg_ll_mode_select10(sg_fd, ref_md, md_len);
731 	if (res != 0) {
732 		error = SES_LOG_FAILED_MODE_SELECT;
733 		return (error);
734 	}
735 
736 	return (error);
737 }
738 /*
739  * Gather data from given device.
740  */
741 static int
742 gather_data(char *device_name, ses_log_call_t *data)
743 {
744 	int sg_fd;
745 	int resp_len, res;
746 	unsigned char rsp_buff[MAX_ALLOC_LEN];
747 	int error;
748 
749 	/* Open device */
750 	if ((sg_fd = open_device(device_name)) < 0) {
751 		/* Failed to open device */
752 		return (SES_LOG_FAILED_TO_OPEN_DEVICE);
753 	}
754 
755 	/* Read the logs */
756 	(void) memset(rsp_buff, 0, sizeof (rsp_buff));
757 	resp_len = 0x8000; /* Maximum size available to read */
758 	res = read_log(sg_fd, rsp_buff, resp_len);
759 
760 	if (res != 0) {
761 		/* Some sort of Error during read of logs */
762 		(void) close(sg_fd);
763 		return (SES_LOG_FAILED_TO_READ_DEVICE);
764 	}
765 
766 	/* Save the logs */
767 	error = save_logs(rsp_buff, data);
768 	if (error != 0) {
769 		(void) close(sg_fd);
770 		return (error);
771 	}
772 	/* Clear the logs */
773 	error = clear_log(sg_fd, data);
774 
775 	(void) close(sg_fd);
776 
777 	return (error);
778 }
779 
780 /*
781  * Access the SES target identified by the indicated path.  Read the logs
782  * and return them in a nvlist.
783  */
784 int
785 access_ses_log(ses_log_call_t *data)
786 {
787 	char real_path[MAXPATHLEN];
788 	struct stat buffer;
789 	int error;
790 
791 	/* Initialize return data */
792 	data->log_data = NULL;
793 	data->number_log_entries = 0;
794 
795 	if (data->target_path == NULL) {
796 		/* NULL Target path, return error */
797 		return (SES_LOG_FAILED_NULL_TARGET_PATH);
798 	}
799 
800 	/* Try to find a valid path */
801 	(void) snprintf(real_path, sizeof (real_path), "/devices%s:ses",
802 	    data->target_path);
803 
804 	if (stat(real_path, &buffer) != 0) {
805 
806 		(void) snprintf(real_path, sizeof (real_path), "/devices%s:0",
807 		    data->target_path);
808 		if (stat(real_path, &buffer) != 0) {
809 			/* Couldn't find a path that exists */
810 			return (SES_LOG_FAILED_BAD_TARGET_PATH);
811 		}
812 	}
813 
814 	error = gather_data(real_path, data);
815 
816 	/* Update the size of log entries being returned */
817 	data->size_of_log_entries =
818 	    data->number_log_entries * SES_LOG_VALID_LOG_SIZE;
819 
820 	return (error);
821 }
822