xref: /illumos-gate/usr/src/lib/storage/libg_fc/common/io.c (revision 74e12c43fe52f2c30f36e65a4d0fb0e8dfd7068a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 /*LINTLIBRARY*/
28 
29 /*
30  *
31  *	This module is part of the photon Command Line
32  *	Interface program.
33  *
34  */
35 
36 /*
37  * I18N message number ranges
38  *  This file: 11500 - 11999
39  *  Shared common messages: 1 - 1999
40  */
41 
42 /* #define		_POSIX_SOURCE 1 */
43 
44 /*	Includes	*/
45 #include	<stdlib.h>
46 #include	<stdio.h>
47 #include	<string.h>
48 #include	<sys/file.h>
49 #include	<sys/types.h>
50 #include	<fcntl.h>
51 #include	<sys/sunddi.h>
52 #include	<sys/systm.h>
53 #include	<sys/scsi/scsi.h>
54 #include	<nl_types.h>
55 #include	<unistd.h>
56 #include	<l_common.h>
57 #include	<stgcom.h>
58 #include	<l_error.h>
59 #include	<g_state.h>
60 #include	<errno.h>
61 #include	<devid.h>
62 #include	<libdevinfo.h>
63 
64 
65 /*	Defines		*/
66 /* Because of a bug in Unisys Envsen card,  Bug ID:1266986. */
67 #define	SCSI_ESI_PCV	0x01		/* Page Code Valid */
68 #define	SCSI_ESI_PF	0x10		/* Page Format */
69 #define	ACTION_MASK	0x1f		/* Persistent Reserve In command */
70 #define	IMMED		1		/* make the stop immediate */
71 #define	DAK_PROD_STR	"SUNWGS INT FCBPL"
72 #define	DAK_BOXNAME_LEN	16		/* The length of the daktari boxname */
73 #define	DAK_BOXNAME_OFF	36		/* The offset of the daktari boxname */
74 
75 
76 
77 /*	Global variables	*/
78 extern	nl_catd l_catd;
79 
80 
81 /*	Forward declarations	*/
82 static int scsi_read_capacity_16_cmd(int, struct scsi_capacity_16 *, int);
83 
84 
85 /*	External functions	*/
86 
87 
88 int
89 g_scsi_persistent_reserve_in_cmd(int fd, uchar_t *buf_ptr,
90 	int buf_len, uchar_t action)
91 {
92 struct uscsi_cmd	ucmd;
93 my_cdb_g1	cdb = {SCMD_PERS_RESERV_IN, 0, 0, 0, 0, 0, 0, 0, 0, 0};
94 struct	scsi_extended_sense	sense;
95 
96 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
97 		return (L_INVALID_ARG);
98 	}
99 
100 	(void) memset(buf_ptr, 0, buf_len);
101 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
102 	cdb.byte1 = action & ACTION_MASK;
103 	cdb.byte7 = (buf_len>>8) & 0xff;
104 	cdb.byte8 = buf_len & 0xff;
105 	ucmd.uscsi_cdb = (caddr_t)&cdb;
106 	ucmd.uscsi_cdblen = CDB_GROUP1;
107 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
108 	ucmd.uscsi_buflen = buf_len;
109 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
110 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
111 	ucmd.uscsi_timeout = 60;
112 
113 	if (buf_len & 0x03) {
114 		return (L_PR_INVLD_TRNSFR_LEN);
115 	}
116 	/* Do in SILENT mode as cmd may not be supported. */
117 	return (cmd(fd, &ucmd, USCSI_READ | USCSI_SILENT));
118 }
119 /*
120  *	Send Diagnostic command
121  *
122  *	NOTE: This function includes a delay.
123  */
124 int
125 g_scsi_send_diag_cmd(int fd, uchar_t *buf_ptr, int buf_len)
126 {
127 struct uscsi_cmd	ucmd;
128 uchar_t	cdb[] = {SCMD_SDIAG, SCSI_ESI_PF, 0, 0, 0, 0};
129 struct	scsi_extended_sense	sense;
130 int		err;
131 
132 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
133 		return (L_INVALID_ARG);
134 	}
135 
136 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
137 	cdb[3] = (buf_len>>8) & 0xff;
138 	cdb[4] = buf_len & 0xff;
139 	ucmd.uscsi_cdb = (caddr_t)cdb;
140 	ucmd.uscsi_cdblen = CDB_GROUP0;
141 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
142 	ucmd.uscsi_buflen = buf_len;
143 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
144 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
145 	ucmd.uscsi_timeout = 60;
146 
147 	if (err = cmd(fd, &ucmd, USCSI_WRITE)) {
148 		return (err);
149 	}
150 	/*
151 	 * Allow time for things to stabilize.
152 	 */
153 	sleep(5);
154 	return (0);
155 }
156 
157 /*
158  * Internal routine to allow manipulation of the cdb[1] byte
159  * in receive diag.
160  */
161 static int
162 rec_diag_cmd(int fd, uchar_t *buf_ptr, int buf_len, uchar_t page_code,
163 	uchar_t cdb_one)
164 {
165 struct uscsi_cmd	ucmd;
166 uchar_t	cdb[] = {SCMD_GDIAG, 0, 0, 0, 0, 0};
167 struct	scsi_extended_sense	sense;
168 
169 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
170 		return (L_INVALID_ARG);
171 	}
172 
173 	(void) memset(buf_ptr, 0, buf_len);
174 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
175 	cdb[1] = cdb_one;
176 	cdb[2] = page_code;
177 	cdb[3] = (buf_len>>8) & 0xff;
178 	cdb[4] = buf_len & 0xff;
179 	ucmd.uscsi_cdb = (caddr_t)cdb;
180 	ucmd.uscsi_cdblen = CDB_GROUP0;
181 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
182 	ucmd.uscsi_buflen = buf_len;
183 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
184 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
185 	ucmd.uscsi_timeout = 60;
186 	return (cmd(fd, &ucmd, USCSI_READ));
187 }
188 
189 
190 /*
191  *	Receive Diagnostic command
192  */
193 int
194 g_scsi_rec_diag_cmd(int fd, uchar_t *buf_ptr, int buf_len, uchar_t page_code)
195 {
196 int	status;
197 
198 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
199 		return (L_INVALID_ARG);
200 	}
201 
202 	if (buf_len & 0x03) {
203 		return (L_RD_INVLD_TRNSFR_LEN);
204 	}
205 
206 	/*
207 	 * The a5k and newer enclosures abide by the SCSI spec
208 	 * (SPC-2: 7.15) but the SSA does not.  It requires
209 	 * 0x10 to be present in cdb[1].
210 	 *
211 	 * For enclosures that abide by the spec, the first call
212 	 * will work.  For SSAs the first call will fail, at which
213 	 * point we try again with the SSA specific value.
214 	 */
215 	status = rec_diag_cmd(fd, buf_ptr, buf_len, page_code, SCSI_ESI_PCV);
216 	if (status != 0) {
217 	    status = rec_diag_cmd(fd, buf_ptr, buf_len, page_code, SCSI_ESI_PF);
218 	}
219 	return (status);
220 }
221 
222 /*
223  *		Write buffer command set up to download firmware
224  */
225 int
226 g_scsi_writebuffer_cmd(int fd, int off, uchar_t *buf_ptr, int buf_len,
227 				int sp, int bid)
228 {
229 struct uscsi_cmd	ucmd;
230 my_cdb_g1	cdb = {SCMD_WRITE_BUFFER, 0x4, 0, 0, 0, 0, 0, 0, 0, 0};
231 struct	scsi_extended_sense	sense;
232 
233 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
234 		return (L_INVALID_ARG);
235 	}
236 
237 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
238 	cdb.byte1 |= sp;		/* set the save bit */
239 	cdb.byte2 = (char)(bid & 0xff);
240 	cdb.byte3 = off>>16;	/* bytes 3-5 contain file offset */
241 	cdb.byte4 = (off>>8) & 0xff;
242 	cdb.byte5 = off & 0xff;
243 	cdb.byte6 = buf_len>>16;	/* bytes 6-8 contain file length */
244 	cdb.byte7 = (buf_len>>8) & 0xff;
245 	cdb.byte8 = buf_len & 0xff;
246 	ucmd.uscsi_cdb = (caddr_t)&cdb;
247 	ucmd.uscsi_cdblen = CDB_GROUP1;
248 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
249 	ucmd.uscsi_buflen = buf_len;
250 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
251 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
252 	ucmd.uscsi_timeout = 240;	/* long timeout required */
253 
254 	return (cmd(fd, &ucmd, USCSI_WRITE));
255 }
256 
257 /*
258  *	Read buffer command set up to upload firmware
259  *	Reads from code image starting at offset
260  *	"code_off" for "buf_len" bytes.
261  */
262 int
263 g_scsi_readbuffer_cmd(int fd, uchar_t *buf_ptr, int buf_len, int code_off)
264 {
265 struct uscsi_cmd	ucmd;
266 my_cdb_g1	cdb = {SCMD_READ_BUFFER, 0x5, 0, 0, 0, 0, 0, 0, 0, 0};
267 struct	scsi_extended_sense	sense;
268 
269 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
270 		return (L_INVALID_ARG);
271 	}
272 
273 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
274 	cdb.byte3 = (code_off >> 16) & 0xff;
275 	cdb.byte4 = (code_off >> 8) & 0xff;
276 	cdb.byte5 = code_off & 0xff;
277 	cdb.byte6 = buf_len>>16;	/* bytes 6-8 contain file length */
278 	cdb.byte7 = (buf_len>>8) & 0xff;
279 	cdb.byte8 = buf_len & 0xff;
280 	ucmd.uscsi_cdb = (caddr_t)&cdb;
281 	ucmd.uscsi_cdblen = CDB_GROUP1;
282 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
283 	ucmd.uscsi_buflen = buf_len;
284 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
285 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
286 	ucmd.uscsi_timeout = 120;
287 
288 	return (cmd(fd, &ucmd, USCSI_READ));
289 }
290 
291 int
292 g_scsi_inquiry_cmd(int fd, uchar_t *buf_ptr, int buf_len)
293 {
294 struct uscsi_cmd	ucmd;
295 my_cdb_g0	cdb = {SCMD_INQUIRY, 0, 0, 0, 0, 0};
296 struct	scsi_extended_sense	sense;
297 int	myreturn;
298 
299 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
300 		return (L_INVALID_ARG);
301 	}
302 
303 	(void) memset(buf_ptr, 0, buf_len);
304 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
305 	cdb.count = (uchar_t)buf_len;
306 	ucmd.uscsi_cdb = (caddr_t)&cdb;
307 	ucmd.uscsi_cdblen = CDB_GROUP0;
308 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
309 	ucmd.uscsi_buflen = buf_len;
310 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
311 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
312 	ucmd.uscsi_timeout = 60;
313 
314 	myreturn = cmd(fd, &ucmd, USCSI_READ | USCSI_SILENT);
315 	if (myreturn) {
316 	    return (myreturn);	    /* != 0, error just return */
317 	}
318 
319 	/*
320 	 * This is a work around for the format of Daktari's
321 	 * SCSI inquiry page information.  The name of the enclosure
322 	 * is not in the same place that products like the a5000 place it
323 	 * so we have to copy the string to the expected location.
324 	 */
325 	if (strncmp((char *)&buf_ptr[16], DAK_PROD_STR,
326 			strlen(DAK_PROD_STR)) == 0) {
327 		strncpy((char *)&buf_ptr[96], (char *)&buf_ptr[DAK_BOXNAME_OFF],
328 		    DAK_BOXNAME_LEN);
329 	}
330 
331 	return (myreturn);
332 }
333 
334 int
335 g_scsi_log_sense_cmd(int fd, uchar_t *buf_ptr, int buf_len, uchar_t page_code)
336 {
337 struct uscsi_cmd	ucmd;
338 my_cdb_g1	cdb =  {SCMD_LOG_SENSE, 0, 0x40, 0, 0, 0, 0, 0, 0, 0};
339 struct	scsi_extended_sense	sense;
340 
341 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
342 		return (L_INVALID_ARG);
343 	}
344 
345 	/* clear buffers on cmds that read data */
346 	(void) memset(buf_ptr, 0, buf_len);
347 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
348 	cdb.byte2 |= page_code;			/* requested page */
349 	cdb.byte7 = buf_len>>8;
350 	cdb.byte8 = buf_len & 0xff;
351 	ucmd.uscsi_cdb = (caddr_t)&cdb;
352 	ucmd.uscsi_cdblen = CDB_GROUP1;
353 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
354 	ucmd.uscsi_buflen = buf_len;
355 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
356 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
357 	ucmd.uscsi_timeout = 120;
358 	return (cmd(fd, &ucmd, USCSI_READ));
359 }
360 
361 /*
362  *		MODE SELECT
363  *
364  *		MODE SELECT USCSI command
365  *
366  *		sp is the save pages bit  - Must be bit 0 -
367  *
368  */
369 int
370 g_scsi_mode_select_cmd(int fd, uchar_t *buf_ptr, int buf_len, uchar_t sp)
371 {
372 struct uscsi_cmd	ucmd;
373 /* 10 byte Mode Select cmd */
374 my_cdb_g1	cdb =  {SCMD_MODE_SELECT_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
375 struct	scsi_extended_sense	sense;
376 
377 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
378 		return (L_INVALID_ARG);
379 	}
380 
381 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
382 	cdb.byte1 = (sp & 1) | 0x10;		/* 0x10 is the PF bit  */
383 	cdb.byte7 = buf_len>>8;
384 	cdb.byte8 = buf_len & 0xff;
385 
386 	ucmd.uscsi_cdb = (caddr_t)&cdb;
387 	ucmd.uscsi_cdblen = CDB_GROUP1;
388 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
389 	ucmd.uscsi_buflen = buf_len;
390 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
391 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
392 	ucmd.uscsi_timeout = 120;
393 
394 	return (cmd(fd, &ucmd, USCSI_WRITE));
395 }
396 
397 
398 /*
399  *		MODE SENSE USCSI command
400  *
401  *
402  *		pc = page control field
403  *		page_code = Pages to return
404  */
405 int
406 g_scsi_mode_sense_cmd(int fd,
407 	uchar_t *buf_ptr,
408 	int buf_len,
409 	uchar_t pc,
410 	uchar_t page_code)
411 {
412 struct uscsi_cmd	ucmd;
413 /* 10 byte Mode Select cmd */
414 my_cdb_g1	cdb =  {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
415 struct	scsi_extended_sense	sense;
416 int		status;
417 static	int	uscsi_count;
418 
419 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
420 		return (L_INVALID_ARG);
421 	}
422 
423 	(void) memset(buf_ptr, 0, buf_len);
424 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
425 	/* Just for me  - a sanity check */
426 	if ((page_code > MODEPAGE_ALLPAGES) || (pc > 3) ||
427 		(buf_len > MAX_MODE_SENSE_LEN)) {
428 		return (L_ILLEGAL_MODE_SENSE_PAGE);
429 	}
430 	cdb.byte2 = (pc << 6) + page_code;
431 	cdb.byte7 = buf_len>>8;
432 	cdb.byte8 = buf_len & 0xff;
433 	ucmd.uscsi_cdb = (caddr_t)&cdb;
434 	ucmd.uscsi_cdblen = CDB_GROUP1;
435 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
436 	ucmd.uscsi_buflen = buf_len;
437 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
438 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
439 	ucmd.uscsi_timeout = 120;
440 
441 	status = cmd(fd, &ucmd, USCSI_READ);
442 	/* Bytes actually transfered */
443 	if (status == 0) {
444 		uscsi_count = buf_len - ucmd.uscsi_resid;
445 		S_DPRINTF("  Number of bytes read on "
446 			"Mode Sense 0x%x\n", uscsi_count);
447 		if (getenv("_LUX_D_DEBUG") != NULL) {
448 			(void) g_dump("  Mode Sense data: ", buf_ptr,
449 			uscsi_count, HEX_ASCII);
450 		}
451 	}
452 	return (status);
453 }
454 
455 int
456 g_scsi_read_capacity_cmd(int fd, uchar_t *buf_ptr, int buf_len)
457 {
458 struct uscsi_cmd	ucmd;
459 my_cdb_g1	cdb = {SCMD_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0};
460 struct	scsi_extended_sense	sense;
461 
462 	if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
463 		return (L_INVALID_ARG);
464 	}
465 
466 	/* clear buffers on on cmds that read data */
467 	(void) memset(buf_ptr, 0, buf_len);
468 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
469 
470 	ucmd.uscsi_cdb = (caddr_t)&cdb;
471 	ucmd.uscsi_cdblen = CDB_GROUP1;
472 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
473 	ucmd.uscsi_buflen = buf_len;
474 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
475 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
476 	ucmd.uscsi_timeout = 60;
477 	return (cmd(fd, &ucmd, USCSI_READ));
478 }
479 
480 int
481 g_scsi_read_capacity_1016_cmd(int fd,
482 		struct scsi_capacity_16 *cap_ptr, int buf_len)
483 {
484 struct uscsi_cmd	ucmd;
485 my_cdb_g1	cdb = {SCMD_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0};
486 struct scsi_extended_sense	sense;
487 struct scsi_capacity	cap_old;
488 int	ret;
489 
490 	if ((fd < 0) || (cap_ptr == NULL) ||
491 		(buf_len < sizeof (struct scsi_capacity_16))) {
492 		return (L_INVALID_ARG);
493 	}
494 
495 	/* clear buffers on on cmds that read data */
496 	(void) memset((char *)&cap_old, 0, sizeof (cap_old));
497 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
498 
499 	ucmd.uscsi_cdb = (caddr_t)&cdb;
500 	ucmd.uscsi_cdblen = CDB_GROUP1;
501 	ucmd.uscsi_bufaddr = (caddr_t)&cap_old;
502 	ucmd.uscsi_buflen = sizeof (cap_old);
503 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
504 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
505 	ucmd.uscsi_timeout = 60;
506 
507 	ret = cmd(fd, &ucmd, USCSI_READ);
508 	if (cap_old.capacity == 0xffffffff) {
509 		/*
510 		 * A capacity of 0xffffffff in response to a
511 		 * READ CAPACITY 10 indicates that the lun
512 		 * is too large to report the size in a 32 bit
513 		 * value, and a READ CAPACITY 16 is required
514 		 * to get the correct size.
515 		 */
516 		ret = scsi_read_capacity_16_cmd(fd, cap_ptr, buf_len);
517 	} else {
518 		cap_ptr->sc_capacity = cap_old.capacity;
519 		cap_ptr->sc_lbasize = cap_old.lbasize;
520 	}
521 	return (ret);
522 }
523 
524 static int
525 scsi_read_capacity_16_cmd(int fd,
526 		struct scsi_capacity_16 *cap_ptr, int buf_len)
527 {
528 struct uscsi_cmd	ucmd;
529 union scsi_cdb		cdb;
530 struct scsi_extended_sense	sense;
531 
532 	if ((fd < 0) || (cap_ptr == NULL) ||
533 		(buf_len < sizeof (struct scsi_capacity_16))) {
534 		return (L_INVALID_ARG);
535 	}
536 	/* clear buffers on on cmds that read data */
537 	(void) memset((char *)cap_ptr, 0, buf_len);
538 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
539 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
540 
541 	ucmd.uscsi_cdb = (caddr_t)&cdb;
542 	ucmd.uscsi_cdblen = CDB_GROUP4;
543 	ucmd.uscsi_bufaddr = (caddr_t)cap_ptr;
544 	ucmd.uscsi_buflen = buf_len;
545 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
546 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
547 	ucmd.uscsi_timeout = 60;
548 
549 	/*
550 	 * Read Capacity (16) is a Service Action In command.  One
551 	 * command byte (0x9E) is overloaded for multiple operations,
552 	 * with the second CDB byte specifying the desired operation
553 	 */
554 	cdb.scc_cmd = SCMD_SVC_ACTION_IN_G4;
555 	cdb.cdb_opaque[1] = SSVC_ACTION_READ_CAPACITY_G4;
556 
557 	/*
558 	 * Fill in allocation length field
559 	 */
560 	cdb.cdb_opaque[10] =
561 		(uchar_t)((ucmd.uscsi_buflen & 0xff000000) >> 24);
562 	cdb.cdb_opaque[11] =
563 		(uchar_t)((ucmd.uscsi_buflen & 0x00ff0000) >> 16);
564 	cdb.cdb_opaque[12] =
565 		(uchar_t)((ucmd.uscsi_buflen & 0x0000ff00) >> 8);
566 	cdb.cdb_opaque[13] =
567 		(uchar_t)(ucmd.uscsi_buflen & 0x000000ff);
568 
569 	return (cmd(fd, &ucmd, USCSI_READ));
570 }
571 
572 int
573 g_scsi_release_cmd(int fd)
574 {
575 struct uscsi_cmd	ucmd;
576 const my_cdb_g0	cdb = {SCMD_RELEASE, 0, 0, 0, 0, 0};
577 struct	scsi_extended_sense	sense;
578 
579 	if (fd < 0) {
580 		return (L_INVALID_ARG);
581 	}
582 
583 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
584 
585 	ucmd.uscsi_cdb = (caddr_t)&cdb;
586 	ucmd.uscsi_cdblen = CDB_GROUP0;
587 	ucmd.uscsi_bufaddr = NULL;
588 	ucmd.uscsi_buflen = 0;
589 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
590 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
591 	ucmd.uscsi_timeout = 60;
592 	return (cmd(fd, &ucmd, 0));
593 }
594 
595 int
596 g_scsi_reserve_cmd(int fd)
597 {
598 struct uscsi_cmd	ucmd;
599 const my_cdb_g0	cdb = {SCMD_RESERVE, 0, 0, 0, 0, 0};
600 struct	scsi_extended_sense	sense;
601 
602 	if (fd < 0) {
603 		return (L_INVALID_ARG);
604 	}
605 
606 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
607 
608 	ucmd.uscsi_cdb = (caddr_t)&cdb;
609 	ucmd.uscsi_cdblen = CDB_GROUP0;
610 	ucmd.uscsi_bufaddr = NULL;
611 	ucmd.uscsi_buflen = 0;
612 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
613 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
614 	ucmd.uscsi_timeout = 60;
615 	return (cmd(fd, &ucmd, 0));
616 }
617 
618 int
619 g_scsi_start_cmd(int fd)
620 {
621 struct uscsi_cmd	ucmd;
622 /*
623  * Use this to induce a SCSI error
624  *	const my_cdb_g0	cdb = {SCMD_START_STOP, 0, 0xff, 0, 1, 0};
625  */
626 const my_cdb_g0	cdb = {SCMD_START_STOP, 0, 0, 0, 1, 0};
627 struct	scsi_extended_sense	sense;
628 
629 	if (fd < 0) {
630 		return (L_INVALID_ARG);
631 	}
632 
633 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
634 	ucmd.uscsi_cdb = (caddr_t)&cdb;
635 	ucmd.uscsi_cdblen = CDB_GROUP0;
636 	ucmd.uscsi_bufaddr = NULL;
637 	ucmd.uscsi_buflen = 0;
638 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
639 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
640 	ucmd.uscsi_timeout = 240;	/* takes a while to start all */
641 	return (cmd(fd, &ucmd, 0));
642 }
643 
644 int
645 g_scsi_stop_cmd(int fd, int immediate_flag)
646 {
647 struct uscsi_cmd	ucmd;
648 my_cdb_g0	cdb = {SCMD_START_STOP, 0, 0, 0, 0, 0};
649 struct	scsi_extended_sense	sense;
650 
651 	if (fd < 0) {
652 		return (L_INVALID_ARG);
653 	}
654 
655 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
656 	if (immediate_flag) {
657 		cdb.lba_msb = IMMED;
658 	}
659 	ucmd.uscsi_cdb = (caddr_t)&cdb;
660 	ucmd.uscsi_cdblen = CDB_GROUP0;
661 	ucmd.uscsi_bufaddr = NULL;
662 	ucmd.uscsi_buflen = 0;
663 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
664 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
665 	ucmd.uscsi_timeout = 120;
666 	return (cmd(fd, &ucmd, 0));
667 }
668 
669 int
670 g_scsi_tur(int fd)
671 {
672 struct uscsi_cmd	ucmd;
673 const my_cdb_g0	cdb = {SCMD_TEST_UNIT_READY, 0, 0, 0, 0, 0};
674 struct	scsi_extended_sense	sense;
675 
676 	if (fd < 0) {
677 		return (L_INVALID_ARG);
678 	}
679 
680 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
681 
682 	ucmd.uscsi_cdb = (caddr_t)&cdb;
683 	ucmd.uscsi_cdblen = CDB_GROUP0;
684 	ucmd.uscsi_bufaddr = NULL;
685 	ucmd.uscsi_buflen = 0;
686 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
687 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
688 	ucmd.uscsi_timeout = 60;
689 	return (cmd(fd, &ucmd, 0));
690 }
691 
692 /*
693  * NOTE: This function includes a delay.
694  */
695 int
696 g_scsi_reset(int fd)
697 {
698 struct uscsi_cmd	ucmd;
699 struct	scsi_extended_sense	sense;
700 int	err;
701 
702 	if (fd < 0) {
703 		return (L_INVALID_ARG);
704 	}
705 
706 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
707 
708 	ucmd.uscsi_cdb = NULL;
709 	ucmd.uscsi_cdblen = 0;
710 	ucmd.uscsi_bufaddr = NULL;
711 	ucmd.uscsi_buflen = 0;
712 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
713 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
714 	ucmd.uscsi_timeout = 60;
715 	if (err = cmd(fd, &ucmd, USCSI_RESET)) {
716 		return (err);
717 	}
718 	/*
719 	 * Allow time for things to stabilize.
720 	 */
721 	sleep(20);
722 	return (0);
723 }
724 
725 
726 /*
727  * Description:
728  *    Retrieves a devid from a device path.
729  *
730  * Input Values:
731  *
732  *    devpath: Valid block device path.
733  *        Example:/devices/scsi_vhci/ssd@g280000602200416d6257333030303353:c,raw
734  *
735  *    devid: ptr to ddi_devid_t struct
736  *    root: root handle to device tree snapshot
737  *    drvr_name: driver name to start the node tree search
738  * On success, devid points to device tree handle to devid
739  * di_fini on root will invalidate devid pointer
740  *
741  * Return Value:
742  *    0 on success
743  *    non-zero on failure
744  */
745 int
746 g_devid_get(char *devpath, ddi_devid_t *devid, di_node_t root,
747 		const char *drvr_name)
748 {
749 char *cptr;
750 char rootpath[MAXPATHLEN];
751 di_node_t node;
752 char *devfs_path = NULL;
753 hrtime_t	start_time, end_time;
754 char *env = NULL;
755 
756 	if (devpath == NULL || devid == NULL || drvr_name == NULL) {
757 		return (L_INVALID_ARG);
758 	}
759 
760 	if ((env = getenv("_LUX_T_DEBUG")) != NULL) {
761 		start_time = gethrtime();
762 	}
763 
764 	*devid = NULL;
765 	rootpath[0] = '\0';
766 
767 	/*
768 	 * Form a valid root path by stripping off the /devices/ mount point
769 	 * prefix and the minor name (:a[,raw]).
770 	 */
771 	if (strstr(devpath, DEV_PREFIX)) {
772 		strcat(rootpath, devpath + strlen(DEV_PREFIX) - 1);
773 		if (strchr(devpath, ':')) {
774 			cptr = strrchr(rootpath, ':');
775 			*cptr = '\0';
776 		} else {
777 			return (L_INVALID_PATH);
778 		}
779 	} else {
780 		return (L_INVALID_PATH);
781 	}
782 
783 	/* point to first node which matches portdrvr */
784 	node = di_drv_first_node(drvr_name, root);
785 	if (node == DI_NODE_NIL) {
786 		/*
787 		 * Could not find driver node
788 		 */
789 		return (L_NO_DEVID);
790 	}
791 
792 	while (node != DI_NODE_NIL) {
793 		if ((devfs_path = di_devfs_path(node)) != NULL) {
794 			if (strcmp(rootpath, devfs_path) == 0) {
795 				*devid = di_devid(node);
796 				di_devfs_path_free(devfs_path);
797 				break;
798 			}
799 			di_devfs_path_free(devfs_path);
800 		}
801 		node = di_drv_next_node(node);
802 	}
803 
804 	if (env != NULL) {
805 		end_time = gethrtime();
806 		(void) fprintf(stdout,
807 		"      g_devid_get: "
808 		"\t\tTime = %lld millisec\n",
809 		(end_time - start_time)/1000000);
810 	}
811 	/* Did we get back a handle? */
812 	if (*devid != NULL) {
813 		return (0);
814 	} else { /* Couldn't get a devid. */
815 		return (L_NO_DEVID);
816 	}
817 }
818