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 */
51static int
52open_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 */
66static void
67construct_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 */
77static void
78set_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 */
88static void
89set_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 */
100static void
101set_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 */
115static int
116do_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
140static int
141read_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
223static int
224save_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_code[ENTRY_MAX_SIZE];
233	char log_level[ENTRY_MAX_SIZE];
234	nvlist_t *entry;
235	char entry_num[15];
236	int match_found = 0;
237	char save_buffer[MAX_LOG_ENTRY_SZ];
238	char entry_added = 0;
239	int all_log_data_len;
240
241	/*
242	 * Bytes 2 and 3 of response buffer contain the page length of
243	 * the log entries returned.
244	 */
245	all_log_data_len = SCSI_READ16(&resp[2]);
246
247	/*
248	 * Initialize log parameter pointer to point to first log entry.
249	 * The resp includes 4 bytes of header info and then log entries
250	 */
251	log_param_ptr = &resp[0] + 4;
252
253	/*
254	 * If multiple heads are reading the logs, it is possible that we
255	 * could be re-reading some of the same log entries plus some
256	 * new additional entries. Check to see if any entries in this read
257	 * contain the same log entry as the last entry we read last time.
258	 */
259	if (data->last_log_entry != NULL &&
260	    (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE)) {
261		/*
262		 * We have a valid log entry from a previous read log
263		 * operation.
264		 */
265
266
267		/*
268		 * Start walking each log entry in response buffer looking for
269		 * a duplicate entry.
270		 */
271		for (k = 0; k < all_log_data_len; k += param_len) {
272			/*
273			 * Calculate log entry length
274			 * Log param ptr [3] contains the log length minus the
275			 * header info which is 4 bytes so add that in.
276			 */
277			param_len = log_param_ptr[3] + 4;
278
279			if (param_len <= 4) {
280				/*
281				 * Only header information in this entry
282				 * process next log entry
283				 */
284				log_param_ptr += param_len;
285				continue;
286			}
287
288
289			/*
290			 * initialize log_str_ptr to point to string info
291			 * returned by expander
292			 * first 4 bytes of log parameter contains
293			 * 2 bytes of parameter code, 1 byte of Control data
294			 * and 1 byte for parameter length. Log string begins
295			 * after that so add 4 to log param ptr.
296			 */
297			log_str_ptr = log_param_ptr + 4;
298
299			/*
300			 * Check to see if this is the
301			 * same line
302			 */
303			if (strncmp((char *)log_str_ptr, data->last_log_entry,
304			    SES_LOG_VALID_LOG_SIZE) == 0) {
305				/* Found an exact match */
306				log_param_ptr += param_len;
307				k += param_len;
308				match_found = 1;
309				break;
310			}
311			log_param_ptr += param_len;
312		}
313	}
314	if (!match_found) {
315		log_param_ptr = &resp[0] + 4;
316		k = 0;
317	}
318	if (k == all_log_data_len) {
319		/*
320		 * Either there was no log data or we have
321		 * already read these log entries.
322		 * Just return.
323		 */
324		return (0);
325	}
326
327	/* Grab memory to return logs with */
328	if (nvlist_alloc(&data->log_data, NV_UNIQUE_NAME, 0) != 0) {
329		/* Couldn't alloc memory for nvlist */
330		return (SES_LOG_FAILED_NVLIST_CREATE);
331	}
332
333	(void) memset(log_code,		0, sizeof (log_code));
334	(void) memset(save_buffer,	0, sizeof (save_buffer));
335	(void) memset(log_level,	0, sizeof (log_level));
336
337	/*
338	 * Start saving new log entries
339	 * Walk the log data adding any new entries
340	 */
341
342	for (; k < all_log_data_len; k += param_len) {
343		/*
344		 * Calculate log entry length
345		 * Log ptr [3] contains the log length minus the header info
346		 * which is 4 bytes so add that in
347		 */
348		param_len = log_param_ptr[3] + 4;
349
350		if (param_len <= 4) {
351			/* Only header information in this entry */
352			/* process next log entry */
353			log_param_ptr += param_len;
354			continue;
355		}
356
357		/*
358		 * initialize log_str_ptr to point to string info of the log
359		 * entry. First 4 bytes of log entry contains param code,
360		 * control byte, and length. Log string starts after that.
361		 */
362		log_str_ptr = log_param_ptr + 4;
363
364		/*
365		 * Format of log str is as follows
366		 * "%8x %8x %8x %8x %8x %8x %8x %8x",
367		 * log_entry.log_word0, log_entry.ts_u, log_entry.ts_l,
368		 * log_entry.seq_num, log_entry.log_code, log_entry.log_word2,
369		 * log_entry.log_word3, log_entry.log_word4
370		 * following example has extra spaces removed to fit in 80 char
371		 * 40004 0 42d5f5fe 185b 630002 fd0800 50800207 e482813
372		 */
373
374		(void) strncpy(save_buffer,
375		    (const char *)log_str_ptr,
376		    SES_LOG_VALID_LOG_SIZE);
377
378		(void) strncpy(log_code,
379		    (const char *)log_str_ptr+SES_LOG_CODE_START,
380		    SES_LOG_SPECIFIC_ENTRY_SIZE);
381
382		(void) strncpy(log_level,
383		    (const char *) log_str_ptr +
384		    SES_LOG_LEVEL_START, 1);
385
386
387		/* Add this entry to the nvlist log data */
388		if (nvlist_alloc(&entry, NV_UNIQUE_NAME, 0) != 0) {
389			/* Couldn't alloc space, return error */
390			return (SES_LOG_FAILED_NV_UNIQUE);
391		}
392
393
394		if (nvlist_add_string(entry, ENTRY_LOG, save_buffer) != 0) {
395			/* Error adding string, return error */
396			nvlist_free(entry);
397			return (SES_LOG_FAILED_NV_LOG);
398		}
399
400		if (nvlist_add_string(entry, ENTRY_CODE, log_code) != 0) {
401			/* Error adding string, return error */
402			nvlist_free(entry);
403			return (SES_LOG_FAILED_NV_CODE);
404		}
405		if (nvlist_add_string(entry, ENTRY_SEVERITY, log_level) != 0) {
406			/* Error adding srtring, return error */
407			nvlist_free(entry);
408			return (SES_LOG_FAILED_NV_SEV);
409		}
410
411		param_code = SCSI_READ16(&log_param_ptr[0]);
412
413		(void) snprintf(entry_num, sizeof (entry_num),
414		    "%s%d", ENTRY_PREFIX, param_code);
415
416		if (nvlist_add_nvlist(data->log_data, entry_num, entry) != 0) {
417			/* Error adding nvlist, return error */
418			nvlist_free(entry);
419			return (SES_LOG_FAILED_NV_ENTRY);
420		}
421		nvlist_free(entry);
422
423		entry_added = 1;
424		(data->number_log_entries)++;
425
426		log_param_ptr += param_len;
427
428	}
429	if (entry_added) {
430		/* Update the last log entry string with last one read */
431		(void) strncpy(data->last_log_entry, save_buffer, MAXNAMELEN);
432	}
433	return (0);
434}
435
436
437
438/* Setup struct to send command to device */
439static void
440set_scsi_pt_data_out(struct uscsi_cmd *uscsi, const unsigned char *dxferp,
441    int dxfer_len)
442{
443	if (dxfer_len > 0) {
444		uscsi->uscsi_bufaddr = (char *)dxferp;
445		uscsi->uscsi_buflen = dxfer_len;
446		uscsi->uscsi_flags = USCSI_WRITE | USCSI_ISOLATE |
447		    USCSI_RQENABLE;
448	}
449}
450
451/*
452 * Invokes a SCSI MODE SENSE(10) command.
453 * Return:
454 * 0 for success
455 * SG_LIB_CAT_INVALID_OP -> invalid opcode
456 * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb
457 * SG_LIB_CAT_NOT_READY -> device not ready
458 * -1 -> other failure
459 */
460
461static int
462sg_ll_mode_sense10(int sg_fd, void * resp, int mx_resp_len)
463{
464	int res, ret;
465	unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] =
466	    {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
467	unsigned char sense_b[SENSE_BUFF_LEN];
468	struct uscsi_cmd uscsi;
469
470	modesCmdBlk[1] = 0;
471	modesCmdBlk[2] = 0; /* page code 0 vendor specific */
472	modesCmdBlk[3] = 0;
473	modesCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff);
474	modesCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff);
475
476	construct_scsi_pt_obj(&uscsi);
477	set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk));
478	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
479	set_scsi_pt_data_in(&uscsi, (unsigned char *) resp, mx_resp_len);
480	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
481	if (res) {
482		ret = res;
483	} else {
484		ret = uscsi.uscsi_status;
485	}
486	return (ret);
487}
488
489/*
490 * Invokes a SCSI MODE SELECT(10) command.
491 * Return:
492 * 0 for success.
493 * SG_LIB_CAT_INVALID_OP for invalid opcode
494 * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
495 * SG_LIB_CAT_NOT_READY -> device not ready,
496 * -1 -> other failure
497 */
498static int
499sg_ll_mode_select10(int sg_fd, void * paramp, int param_len)
500{
501	int res, ret;
502	unsigned char modesCmdBlk[MODE_SELECT10_CMDLEN] =
503	    {SCMD_MODE_SELECT_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
504	unsigned char sense_b[SENSE_BUFF_LEN];
505	struct uscsi_cmd uscsi;
506
507
508	modesCmdBlk[1] = 0;
509	/*
510	 * modesCmdBlk 2 equal 0 PC 0 return current page code 0 return
511	 * vendor specific
512	 */
513
514	modesCmdBlk[7] = (unsigned char)((param_len >> 8) & 0xff);
515	modesCmdBlk[8] = (unsigned char)(param_len & 0xff);
516
517	construct_scsi_pt_obj(&uscsi);
518
519	set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk));
520	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
521	set_scsi_pt_data_out(&uscsi, (unsigned char *) paramp, param_len);
522	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
523	if (res) {
524		ret = res;
525	} else {
526		ret = uscsi.uscsi_status;
527	}
528	return (ret);
529}
530
531
532
533/*
534 * MODE SENSE 10 commands yield a response that has block descriptors followed
535 * by mode pages. In most cases users are interested in the first mode page.
536 * This function returns the(byte) offset of the start of the first mode page.
537 * Returns >= 0 is successful or -1 if failure. If there is a failure
538 * a message is written to err_buff.
539 */
540
541/*
542 * return data looks like:
543 * Table 92 - Mode parameter header(10)
544 * Bit
545 * Byte
546 *	7 	6 	5 	4 	3 	2 	1 	0
547 *	----------------------------------------------------------
548 * 0	MSB Data length
549 * 1	LSB Data length
550 *	----------------------------------------------------------
551 * 2	Medium type
552 *	----------------------------------------------------------
553 * 3 	Device-specific parameter
554 *	----------------------------------------------------------
555 * 4 	Reserved
556 *	----------------------------------------------------------
557 * 5	Reserved
558 *	----------------------------------------------------------
559 * 6	MSB block descriptor length
560 * 7	LSB block descriptor length
561 *	----------------------------------------------------------
562 *	block desciptors....
563 *	-----------------------
564 *	mode sense page:
565 *	0 : ps Reserved : page Code
566 *	1 : Page Length(n-1)
567 *	2-N  Mode parameters
568 */
569static int
570sg_mode_page_offset(const unsigned char *resp, int resp_len)
571{
572	int bd_len;
573	int calc_len;
574	int offset;
575
576	if ((NULL == resp) || (resp_len < 8)) {
577		/* Too short of a response buffer */
578		return (-1);
579	}
580
581	calc_len = (resp[0] << 8) + resp[1] + 2;
582	bd_len = (resp[6] << 8) + resp[7];
583
584	/* LongLBA doesn't change this calculation */
585	offset = bd_len + MODE10_RESP_HDR_LEN;
586
587	if ((offset + 2) > resp_len) {
588		/* Given response length to small */
589		offset = -1;
590	} else if ((offset + 2) > calc_len) {
591		/* Calculated response length too small */
592		offset = -1;
593	}
594	return (offset);
595}
596
597/*
598 * Clear logs
599 */
600static int
601clear_log(int sg_fd, ses_log_call_t *data)
602{
603
604	int res, alloc_len, off;
605	int md_len;
606	int read_in_len = 0;
607	unsigned char ref_md[MAX_ALLOC_LEN];
608	struct log_clear_control_struct clear_data;
609	long myhostid;
610	int error = 0;
611	long poll_time;
612	char seq_num_str[10];
613	unsigned long seq_num = 0;
614
615	(void) memset(&clear_data, 0, sizeof (clear_data));
616
617	clear_data.pageControls = 0x40;
618	clear_data.subpage_code = 0;
619	clear_data.page_lengthLower = 0x16;
620
621	myhostid = gethostid();
622	/* 0 -> 11 are memset to 0 */
623	clear_data.host_id[12] = (myhostid & 0xff000000) >> 24;
624	clear_data.host_id[13] = (myhostid & 0xff0000) >> 16;
625	clear_data.host_id[14] = (myhostid & 0xff00) >> 8;
626	clear_data.host_id[15] = myhostid & 0xff;
627
628	/*
629	 * convert nanosecond time to seconds
630	 */
631	poll_time = data->poll_time / 1000000000;
632	/* Add 5 minutes to poll time to allow for data retrieval time */
633	poll_time = poll_time + 300;
634	clear_data.timeout[0] = (poll_time & 0xff00) >> 8;
635	clear_data.timeout[1] = poll_time & 0xff;
636
637	/*
638	 * retrieve the last read sequence number from the last
639	 * log entry read.
640	 */
641	if (data->last_log_entry != NULL &&
642	    (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE)) {
643		/*
644		 * We have a valid log entry from a previous read log
645		 * operation.
646		 */
647		(void) strncpy(seq_num_str,
648		    (const char *) data->last_log_entry +
649		    SES_LOG_SEQ_NUM_START, 8);
650		seq_num = strtoul(seq_num_str, 0, 16);
651	}
652	clear_data.seq_clear[0] = (seq_num & 0xff000000) >> 24;
653	clear_data.seq_clear[1] = (seq_num & 0xff0000) >> 16;
654	clear_data.seq_clear[2] = (seq_num & 0xff00) >> 8;
655	clear_data.seq_clear[3] = (seq_num & 0xff);
656
657	read_in_len = sizeof (clear_data);
658
659
660	/* do MODE SENSE to fetch current values */
661	(void) memset(ref_md, 0, MAX_ALLOC_LEN);
662	alloc_len = MAX_ALLOC_LEN;
663
664
665	res = sg_ll_mode_sense10(sg_fd, ref_md, alloc_len);
666	if (0 != res) {
667		/* Error during mode sense */
668		error = SES_LOG_FAILED_MODE_SENSE;
669		return (error);
670	}
671
672	/* Setup mode Select to clear logs */
673	off = sg_mode_page_offset(ref_md, alloc_len);
674	if (off < 0) {
675		/* Mode page offset error */
676		error =  SES_LOG_FAILED_MODE_SENSE_OFFSET;
677		return (error);
678	}
679	md_len = (ref_md[0] << 8) + ref_md[1] + 2;
680
681	ref_md[0] = 0;
682	ref_md[1] = 0;
683
684	if (md_len > alloc_len) {
685		/* Data length to large */
686		error = SES_LOG_FAILED_BAD_DATA_LEN;
687		return (error);
688	}
689
690	if ((md_len - off) != read_in_len) {
691		/* Content length not correct */
692		error = SES_LOG_FAILED_BAD_CONTENT_LEN;
693		return (error);
694	}
695
696	if ((clear_data.pageControls & 0x40) != (ref_md[off] & 0x40)) {
697		/* reference model doesn't have use subpage format bit set */
698		/* Even though it should have */
699		/* don't send the command */
700		error = SES_LOG_FAILED_FORMAT_PAGE_ERR;
701		return (error);
702	}
703
704	(void) memcpy(ref_md + off, (const void *) &clear_data,
705	    sizeof (clear_data));
706
707	res = sg_ll_mode_select10(sg_fd, ref_md, md_len);
708	if (res != 0) {
709		error = SES_LOG_FAILED_MODE_SELECT;
710		return (error);
711	}
712
713	return (error);
714}
715/*
716 * Gather data from given device.
717 */
718static int
719gather_data(char *device_name, ses_log_call_t *data)
720{
721	int sg_fd;
722	int resp_len, res;
723	unsigned char rsp_buff[MAX_ALLOC_LEN];
724	int error;
725
726	/* Open device */
727	if ((sg_fd = open_device(device_name)) < 0) {
728		/* Failed to open device */
729		return (SES_LOG_FAILED_TO_OPEN_DEVICE);
730	}
731
732	/* Read the logs */
733	(void) memset(rsp_buff, 0, sizeof (rsp_buff));
734	resp_len = 0x8000; /* Maximum size available to read */
735	res = read_log(sg_fd, rsp_buff, resp_len);
736
737	if (res != 0) {
738		/* Some sort of Error during read of logs */
739		(void) close(sg_fd);
740		return (SES_LOG_FAILED_TO_READ_DEVICE);
741	}
742
743	/* Save the logs */
744	error = save_logs(rsp_buff, data);
745	if (error != 0) {
746		(void) close(sg_fd);
747		return (error);
748	}
749	/* Clear the logs */
750	error = clear_log(sg_fd, data);
751
752	(void) close(sg_fd);
753
754	return (error);
755}
756
757/*
758 * Access the SES target identified by the indicated path.  Read the logs
759 * and return them in a nvlist.
760 */
761int
762access_ses_log(ses_log_call_t *data)
763{
764	char real_path[MAXPATHLEN];
765	struct stat buffer;
766	int error;
767
768	/* Initialize return data */
769	data->log_data = NULL;
770	data->number_log_entries = 0;
771
772	if (data->target_path == NULL) {
773		/* NULL Target path, return error */
774		return (SES_LOG_FAILED_NULL_TARGET_PATH);
775	}
776
777	/* Try to find a valid path */
778	(void) snprintf(real_path, sizeof (real_path), "/devices%s:ses",
779	    data->target_path);
780
781	if (stat(real_path, &buffer) != 0) {
782
783		(void) snprintf(real_path, sizeof (real_path), "/devices%s:0",
784		    data->target_path);
785		if (stat(real_path, &buffer) != 0) {
786			/* Couldn't find a path that exists */
787			return (SES_LOG_FAILED_BAD_TARGET_PATH);
788		}
789	}
790
791	error = gather_data(real_path, data);
792
793	/* Update the size of log entries being returned */
794	data->size_of_log_entries =
795	    data->number_log_entries * SES_LOG_VALID_LOG_SIZE;
796
797	return (error);
798}
799