xref: /titanic_44/usr/src/lib/libdiskmgt/common/drive.c (revision 2b4a78020b9c38d1b95e2f3fefa6d6e4be382d1f)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <fcntl.h>
29 #include <libdevinfo.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stropts.h>
34 #include <sys/dkio.h>
35 #include <sys/sunddi.h>
36 #include <sys/types.h>
37 #include <unistd.h>
38 #include <kstat.h>
39 #include <errno.h>
40 #include <devid.h>
41 #include <dirent.h>
42 
43 /* included for uscsi */
44 #include <strings.h>
45 #include <sys/stat.h>
46 #include <sys/scsi/impl/types.h>
47 #include <sys/scsi/impl/uscsi.h>
48 #include <sys/scsi/generic/commands.h>
49 #include <sys/scsi/impl/commands.h>
50 #include <sys/scsi/generic/mode.h>
51 #include <sys/byteorder.h>
52 
53 #include "libdiskmgt.h"
54 #include "disks_private.h"
55 
56 #define	KSTAT_CLASS_DISK	"disk"
57 #define	KSTAT_CLASS_ERROR	"device_error"
58 
59 #define	SCSIBUFLEN		0xffff
60 
61 /* byte get macros */
62 #define	b3(a)			(((a)>>24) & 0xFF)
63 #define	b2(a)			(((a)>>16) & 0xFF)
64 #define	b1(a)			(((a)>>8) & 0xFF)
65 #define	b0(a)			(((a)>>0) & 0xFF)
66 
67 static char *kstat_err_names[] = {
68 	"Soft Errors",
69 	"Hard Errors",
70 	"Transport Errors",
71 	"Media Error",
72 	"Device Not Ready",
73 	"No Device",
74 	"Recoverable",
75 	"Illegal Request",
76 	"Predictive Failure Analysis",
77 	NULL
78 };
79 
80 static char *err_attr_names[] = {
81 	DM_NSOFTERRS,
82 	DM_NHARDERRS,
83 	DM_NTRANSERRS,
84 	DM_NMEDIAERRS,
85 	DM_NDNRERRS,
86 	DM_NNODEVERRS,
87 	DM_NRECOVERRS,
88 	DM_NILLREQERRS,
89 	DM_FAILING,
90 	NULL
91 };
92 
93 /*
94  *	**************** begin uscsi stuff ****************
95  */
96 
97 #if defined(_BIT_FIELDS_LTOH)
98 #elif defined(_BIT_FIELDS_HTOL)
99 #else
100 #error	One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
101 #endif
102 
103 struct conf_feature {
104 	uchar_t feature[2]; /* common to all */
105 #if defined(_BIT_FIELDS_LTOH)
106 	uchar_t current : 1;
107 	uchar_t persist : 1;
108 	uchar_t version : 4;
109 	uchar_t reserved: 2;
110 #else
111 	uchar_t reserved: 2;
112 	uchar_t version : 4;
113 	uchar_t persist : 1;
114 	uchar_t current : 1;
115 #endif	/* _BIT_FIELDS_LTOH */
116 	uchar_t len;
117 	union features {
118 		struct generic {
119 			uchar_t data[1];
120 		} gen;
121 		uchar_t data[1];
122 		struct profile_list {
123 			uchar_t profile[2];
124 #if defined(_BIT_FIELDS_LTOH)
125 			uchar_t current_p : 1;
126 			uchar_t reserved1 : 7;
127 #else
128 			uchar_t reserved1 : 7;
129 			uchar_t current_p : 1;
130 #endif	/* _BIT_FIELDS_LTOH */
131 			uchar_t reserved2;
132 		} plist[1];
133 		struct core {
134 			uchar_t phys[4];
135 		} core;
136 		struct morphing {
137 #if defined(_BIT_FIELDS_LTOH)
138 			uchar_t async		: 1;
139 			uchar_t reserved1	: 7;
140 #else
141 			uchar_t reserved1	: 7;
142 			uchar_t async		: 1;
143 #endif	/* _BIT_FIELDS_LTOH */
144 			uchar_t reserved[3];
145 		} morphing;
146 		struct removable {
147 #if defined(_BIT_FIELDS_LTOH)
148 			uchar_t lock	: 1;
149 			uchar_t	resv1	: 1;
150 			uchar_t	pvnt	: 1;
151 			uchar_t eject	: 1;
152 			uchar_t resv2	: 1;
153 			uchar_t loading : 3;
154 #else
155 			uchar_t loading : 3;
156 			uchar_t resv2	: 1;
157 			uchar_t eject	: 1;
158 			uchar_t	pvnt	: 1;
159 			uchar_t	resv1	: 1;
160 			uchar_t lock	: 1;
161 #endif	/* _BIT_FIELDS_LTOH */
162 			uchar_t reserved[3];
163 		} removable;
164 		struct random_readable {
165 			uchar_t lbsize[4];
166 			uchar_t blocking[2];
167 #if defined(_BIT_FIELDS_LTOH)
168 			uchar_t pp		: 1;
169 			uchar_t reserved1	: 7;
170 #else
171 			uchar_t reserved1	: 7;
172 			uchar_t pp		: 1;
173 #endif	/* _BIT_FIELDS_LTOH */
174 			uchar_t reserved;
175 		} rread;
176 		struct cd_read {
177 #if defined(_BIT_FIELDS_LTOH)
178 			uchar_t cdtext		: 1;
179 			uchar_t c2flag		: 1;
180 			uchar_t reserved1	: 6;
181 #else
182 			uchar_t reserved1	: 6;
183 			uchar_t c2flag		: 1;
184 			uchar_t cdtext		: 1;
185 #endif	/* _BIT_FIELDS_LTOH */
186 		} cdread;
187 		struct cd_audio {
188 #if defined(_BIT_FIELDS_LTOH)
189 			uchar_t sv	: 1;
190 			uchar_t scm	: 1;
191 			uchar_t scan	: 1;
192 			uchar_t resv	: 5;
193 #else
194 			uchar_t resv	: 5;
195 			uchar_t scan	: 1;
196 			uchar_t scm	: 1;
197 			uchar_t sv	: 1;
198 #endif	/* _BIT_FIELDS_LTOH */
199 			uchar_t reserved;
200 			uchar_t numlevels[2];
201 		} audio;
202 		struct dvd_css {
203 			uchar_t reserved[3];
204 			uchar_t version;
205 		} dvdcss;
206 	} features;
207 };
208 
209 #define	PROF_NON_REMOVABLE	0x0001
210 #define	PROF_REMOVABLE		0x0002
211 #define	PROF_MAGNETO_OPTICAL	0x0003
212 #define	PROF_OPTICAL_WO		0x0004
213 #define	PROF_OPTICAL_ASMO	0x0005
214 #define	PROF_CDROM		0x0008
215 #define	PROF_CDR		0x0009
216 #define	PROF_CDRW		0x000a
217 #define	PROF_DVDROM		0x0010
218 #define	PROF_DVDR		0x0011
219 #define	PROF_DVDRAM		0x0012
220 #define	PROF_DVDRW_REST		0x0013
221 #define	PROF_DVDRW_SEQ		0x0014
222 #define	PROF_DVDRW		0x001a
223 #define	PROF_DDCD_ROM		0x0020
224 #define	PROF_DDCD_R		0x0021
225 #define	PROF_DDCD_RW		0x0022
226 #define	PROF_NON_CONFORMING	0xffff
227 
228 struct get_configuration {
229 	uchar_t len[4];
230 	uchar_t reserved[2];
231 	uchar_t curprof[2];
232 	struct conf_feature feature;
233 };
234 
235 struct capabilities {
236 #if defined(_BIT_FIELDS_LTOH)
237 	uchar_t pagecode	: 6;
238 	uchar_t resv1		: 1;
239 	uchar_t ps		: 1;
240 #else
241 	uchar_t ps		: 1;
242 	uchar_t resv1		: 1;
243 	uchar_t pagecode	: 6;
244 #endif	/* _BIT_FIELDS_LTOH */
245 	uchar_t pagelen;
246 #if defined(_BIT_FIELDS_LTOH)
247 	/* read capabilities */
248 	uchar_t	cdr_read	: 1;
249 	uchar_t cdrw_read	: 1;
250 	uchar_t method2		: 1;
251 	uchar_t dvdrom_read	: 1;
252 	uchar_t dvdr_read	: 1;
253 	uchar_t dvdram_read	: 1;
254 	uchar_t resv2		: 2;
255 #else
256 	uchar_t resv2		: 2;
257 	uchar_t dvdram_read	: 1;
258 	uchar_t dvdr_read	: 1;
259 	uchar_t dvdrom_read	: 1;
260 	uchar_t method2		: 1;
261 	uchar_t cdrw_read	: 1;
262 	uchar_t	cdr_read	: 1;
263 #endif	/* _BIT_FIELDS_LTOH */
264 #if defined(_BIT_FIELDS_LTOH)
265 	/* write capabilities */
266 	uchar_t cdr_write	: 1;
267 	uchar_t cdrw_write	: 1;
268 	uchar_t testwrite	: 1;
269 	uchar_t resv3		: 1;
270 	uchar_t dvdr_write	: 1;
271 	uchar_t dvdram_write	: 1;
272 	uchar_t resv4		: 2;
273 #else
274 	/* write capabilities */
275 	uchar_t resv4		: 2;
276 	uchar_t dvdram_write	: 1;
277 	uchar_t dvdr_write	: 1;
278 	uchar_t resv3		: 1;
279 	uchar_t testwrite	: 1;
280 	uchar_t cdrw_write	: 1;
281 	uchar_t cdr_write	: 1;
282 #endif	/* _BIT_FIELDS_LTOH */
283 	uchar_t misc0;
284 	uchar_t misc1;
285 	uchar_t misc2;
286 	uchar_t misc3;
287 	uchar_t obsolete0[2];
288 	uchar_t numvlevels[2];
289 	uchar_t bufsize[2];
290 	uchar_t obsolete1[4];
291 	uchar_t resv5;
292 	uchar_t misc4;
293 	uchar_t obsolete2;
294 	uchar_t copymgt[2];
295 	/* there is more to this page, but nothing we care about */
296 };
297 
298 struct mode_header_g2 {
299 	uchar_t modelen[2];
300 	uchar_t obsolete;
301 	uchar_t reserved[3];
302 	uchar_t desclen[2];
303 };
304 
305 /*
306  * Mode sense/select page header information
307  */
308 struct scsi_ms_header {
309 	struct mode_header	mode_header;
310 	struct block_descriptor	block_descriptor;
311 };
312 
313 #define	MODESENSE_PAGE_LEN(p)	(((int)((struct mode_page *)p)->length) + \
314 				    sizeof (struct mode_page))
315 
316 #define	MODE_SENSE_PC_CURRENT	(0 << 6)
317 #define	MODE_SENSE_PC_DEFAULT	(2 << 6)
318 #define	MODE_SENSE_PC_SAVED	(3 << 6)
319 
320 #define	MAX_MODE_SENSE_SIZE	255
321 #define	IMPOSSIBLE_SCSI_STATUS	0xff
322 
323 /*
324  *	********** end of uscsi stuff ************
325  */
326 
327 static descriptor_t	**apply_filter(descriptor_t **drives, int filter[],
328 			    int *errp);
329 static int		check_atapi(int fd);
330 static int		conv_drive_type(uint_t drive_type);
331 static uint64_t		convnum(uchar_t *nptr, int len);
332 static void		fill_command_g1(struct uscsi_cmd *cmd,
333 			    union scsi_cdb *cdb, caddr_t buff, int blen);
334 static void		fill_general_page_cdb_g1(union scsi_cdb *cdb,
335 			    int command, int lun, uchar_t c0, uchar_t c1);
336 static void		fill_mode_page_cdb(union scsi_cdb *cdb, int page);
337 static descriptor_t	**get_assoc_alias(disk_t *diskp, int *errp);
338 static descriptor_t	**get_assoc_controllers(descriptor_t *dp, int *errp);
339 static descriptor_t	**get_assoc_paths(descriptor_t *dp, int *errp);
340 static int		get_attrs(disk_t *diskp, int fd, char *opath,
341 			    nvlist_t *nvp);
342 static int		get_cdrom_drvtype(int fd);
343 static int		get_disk_kstats(kstat_ctl_t *kc, char *diskname,
344 			    char *classname, nvlist_t *stats);
345 static void		get_drive_type(disk_t *dp, int fd);
346 static int		get_err_kstats(kstat_ctl_t *kc, char *diskname,
347 			    nvlist_t *stats);
348 static int		get_io_kstats(kstat_ctl_t *kc, char *diskname,
349 			    nvlist_t *stats);
350 static int		get_kstat_vals(kstat_t *ksp, nvlist_t *stats);
351 static char		*get_err_attr_name(char *kstat_name);
352 static int		get_rpm(disk_t *dp, int fd);
353 static int		update_stat64(nvlist_t *stats, char *attr,
354 			    uint64_t value);
355 static int		update_stat32(nvlist_t *stats, char *attr,
356 			    uint32_t value);
357 static int		uscsi_mode_sense(int fd, int page_code,
358 			    int page_control, caddr_t page_data, int page_size,
359 			    struct  scsi_ms_header *header);
360 
361 descriptor_t **
362 drive_get_assoc_descriptors(descriptor_t *dp, dm_desc_type_t type,
363     int *errp)
364 {
365 	switch (type) {
366 	case DM_CONTROLLER:
367 	    return (get_assoc_controllers(dp, errp));
368 	case DM_PATH:
369 	    return (get_assoc_paths(dp, errp));
370 	case DM_ALIAS:
371 	    return (get_assoc_alias(dp->p.disk, errp));
372 	case DM_MEDIA:
373 	    return (media_get_assocs(dp, errp));
374 	}
375 
376 	*errp = EINVAL;
377 	return (NULL);
378 }
379 
380 /*
381  * Get the drive descriptors for the given media/alias/devpath.
382  */
383 descriptor_t **
384 drive_get_assocs(descriptor_t *desc, int *errp)
385 {
386 	descriptor_t	**drives;
387 
388 	/* at most one drive is associated with these descriptors */
389 
390 	drives = (descriptor_t **)calloc(2, sizeof (descriptor_t *));
391 	if (drives == NULL) {
392 	    *errp = ENOMEM;
393 	    return (NULL);
394 	}
395 
396 	drives[0] = cache_get_desc(DM_DRIVE, desc->p.disk, NULL, NULL, errp);
397 	if (*errp != 0) {
398 	    cache_free_descriptors(drives);
399 	    return (NULL);
400 	}
401 
402 	drives[1] = NULL;
403 
404 	return (drives);
405 }
406 
407 nvlist_t *
408 drive_get_attributes(descriptor_t *dp, int *errp)
409 {
410 	nvlist_t	*attrs = NULL;
411 	int		fd;
412 	char		opath[MAXPATHLEN];
413 
414 	if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
415 	    *errp = ENOMEM;
416 	    return (NULL);
417 	}
418 
419 	opath[0] = 0;
420 	fd = drive_open_disk(dp->p.disk, opath, sizeof (opath));
421 
422 	if ((*errp = get_attrs(dp->p.disk, fd, opath, attrs)) != 0) {
423 	    nvlist_free(attrs);
424 	    attrs = NULL;
425 	}
426 
427 	if (fd >= 0) {
428 	    (void) close(fd);
429 	}
430 
431 	return (attrs);
432 }
433 
434 /*
435  * Check if we have the drive in our list, based upon the device id.
436  * We got the device id from the dev tree walk.  This is encoded
437  * using devid_str_encode(3DEVID).   In order to check the device ids we need
438  * to use the devid_compare(3DEVID) function, so we need to decode the
439  * string representation of the device id.
440  */
441 descriptor_t *
442 drive_get_descriptor_by_name(char *name, int *errp)
443 {
444 	ddi_devid_t	devid;
445 	descriptor_t	**drives;
446 	descriptor_t	*drive = NULL;
447 	int		i;
448 
449 	if (name == NULL || devid_str_decode(name, &devid, NULL) != 0) {
450 	    *errp = EINVAL;
451 	    return (NULL);
452 	}
453 
454 	drives = cache_get_descriptors(DM_DRIVE, errp);
455 	if (*errp != 0) {
456 	    devid_free(devid);
457 	    return (NULL);
458 	}
459 
460 	/*
461 	 * We have to loop through all of them, freeing the ones we don't
462 	 * want.  Once drive is set, we don't need to compare any more.
463 	 */
464 	for (i = 0; drives[i]; i++) {
465 	    if (drive == NULL && drives[i]->p.disk->devid != NULL &&
466 		devid_compare(devid, drives[i]->p.disk->devid) == 0) {
467 		drive = drives[i];
468 
469 	    } else {
470 		/* clean up the unused descriptor */
471 		cache_free_descriptor(drives[i]);
472 	    }
473 	}
474 	free(drives);
475 	devid_free(devid);
476 
477 	if (drive == NULL) {
478 	    *errp = ENODEV;
479 	}
480 
481 	return (drive);
482 }
483 
484 descriptor_t **
485 drive_get_descriptors(int filter[], int *errp)
486 {
487 	descriptor_t	**drives;
488 
489 	drives = cache_get_descriptors(DM_DRIVE, errp);
490 	if (*errp != 0) {
491 	    return (NULL);
492 	}
493 
494 	if (filter != NULL && filter[0] != DM_FILTER_END) {
495 	    descriptor_t	**found;
496 	    found = apply_filter(drives, filter, errp);
497 	    if (*errp != 0) {
498 		drives = NULL;
499 	    } else {
500 		drives = found;
501 	    }
502 	}
503 
504 	return (drives);
505 }
506 
507 char *
508 drive_get_name(descriptor_t *dp)
509 {
510 	return (dp->p.disk->device_id);
511 }
512 
513 nvlist_t *
514 drive_get_stats(descriptor_t *dp, int stat_type, int *errp)
515 {
516 	disk_t		*diskp;
517 	nvlist_t	*stats;
518 
519 	diskp = dp->p.disk;
520 
521 	if (nvlist_alloc(&stats, NVATTRS, 0) != 0) {
522 	    *errp = ENOMEM;
523 	    return (NULL);
524 	}
525 
526 	if (stat_type == DM_DRV_STAT_PERFORMANCE ||
527 	    stat_type == DM_DRV_STAT_DIAGNOSTIC) {
528 
529 	    alias_t	*ap;
530 	    kstat_ctl_t	*kc;
531 
532 	    ap = diskp->aliases;
533 	    if (ap == NULL || ap->kstat_name == NULL) {
534 		nvlist_free(stats);
535 		*errp = EACCES;
536 		return (NULL);
537 	    }
538 
539 	    if ((kc = kstat_open()) == NULL) {
540 		nvlist_free(stats);
541 		*errp = EACCES;
542 		return (NULL);
543 	    }
544 
545 	    while (ap != NULL) {
546 		int	status;
547 
548 		if (ap->kstat_name == NULL) {
549 		    continue;
550 		}
551 
552 		if (stat_type == DM_DRV_STAT_PERFORMANCE) {
553 		    status = get_io_kstats(kc, ap->kstat_name, stats);
554 		} else {
555 		    status = get_err_kstats(kc, ap->kstat_name, stats);
556 		}
557 
558 		if (status != 0) {
559 		    nvlist_free(stats);
560 		    (void) kstat_close(kc);
561 		    *errp = ENOMEM;
562 		    return (NULL);
563 		}
564 
565 		ap = ap->next;
566 	    }
567 
568 	    (void) kstat_close(kc);
569 
570 	    *errp = 0;
571 	    return (stats);
572 	}
573 
574 	if (stat_type == DM_DRV_STAT_TEMPERATURE) {
575 	    int		fd;
576 
577 	    if ((fd = drive_open_disk(diskp, NULL, 0)) >= 0) {
578 		struct dk_temperature	temp;
579 
580 		if (ioctl(fd, DKIOCGTEMPERATURE, &temp) >= 0) {
581 		    if (nvlist_add_uint32(stats, DM_TEMPERATURE,
582 			temp.dkt_cur_temp) != 0) {
583 			*errp = ENOMEM;
584 			nvlist_free(stats);
585 			return (NULL);
586 		    }
587 		} else {
588 		    *errp = errno;
589 		    nvlist_free(stats);
590 		    return (NULL);
591 		}
592 		(void) close(fd);
593 	    } else {
594 		*errp = errno;
595 		nvlist_free(stats);
596 		return (NULL);
597 	    }
598 
599 	    *errp = 0;
600 	    return (stats);
601 	}
602 
603 	nvlist_free(stats);
604 	*errp = EINVAL;
605 	return (NULL);
606 }
607 
608 int
609 drive_make_descriptors()
610 {
611 	int	error;
612 	disk_t	*dp;
613 
614 	dp = cache_get_disklist();
615 	while (dp != NULL) {
616 	    cache_load_desc(DM_DRIVE, dp, NULL, NULL, &error);
617 	    if (error != 0) {
618 		return (error);
619 	    }
620 	    dp = dp->next;
621 	}
622 
623 	return (0);
624 }
625 
626 /*
627  * This function opens the disk generically (any slice).
628  */
629 int
630 drive_open_disk(disk_t *diskp, char *opath, int len)
631 {
632 	/*
633 	 * Just open the first devpath.
634 	 */
635 	if (diskp->aliases != NULL && diskp->aliases->devpaths != NULL) {
636 	    if (opath != NULL) {
637 		(void) strlcpy(opath, diskp->aliases->devpaths->devpath, len);
638 	    }
639 	    return (open(diskp->aliases->devpaths->devpath, O_RDONLY|O_NDELAY));
640 	}
641 
642 	return (-1);
643 }
644 
645 static descriptor_t **
646 apply_filter(descriptor_t **drives, int filter[], int *errp)
647 {
648 	int		i;
649 	descriptor_t	**found;
650 	int		cnt;
651 	int		pos;
652 
653 	/* count the number of drives in the snapshot */
654 	for (cnt = 0; drives[cnt]; cnt++);
655 
656 	found = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
657 	if (found == NULL) {
658 	    *errp = ENOMEM;
659 	    cache_free_descriptors(drives);
660 	    return (NULL);
661 	}
662 
663 	pos = 0;
664 	for (i = 0; drives[i]; i++) {
665 	    int j;
666 	    int match;
667 
668 	    /* Make sure the drive type is set */
669 	    get_drive_type(drives[i]->p.disk, -1);
670 
671 	    match = 0;
672 	    for (j = 0; filter[j] != DM_FILTER_END; j++) {
673 		if (drives[i]->p.disk->drv_type == filter[j]) {
674 		    found[pos++] = drives[i];
675 		    match = 1;
676 		    break;
677 		}
678 	    }
679 
680 	    if (!match) {
681 		cache_free_descriptor(drives[i]);
682 	    }
683 	}
684 	found[pos] = NULL;
685 	free(drives);
686 
687 	*errp = 0;
688 	return (found);
689 }
690 
691 static int
692 conv_drive_type(uint_t drive_type)
693 {
694 	switch (drive_type) {
695 	case DK_UNKNOWN:
696 	    return (DM_DT_UNKNOWN);
697 	case DK_MO_ERASABLE:
698 	    return (DM_DT_MO_ERASABLE);
699 	case DK_MO_WRITEONCE:
700 	    return (DM_DT_MO_WRITEONCE);
701 	case DK_AS_MO:
702 	    return (DM_DT_AS_MO);
703 	case DK_CDROM:
704 	    return (DM_DT_CDROM);
705 	case DK_CDR:
706 	    return (DM_DT_CDR);
707 	case DK_CDRW:
708 	    return (DM_DT_CDRW);
709 	case DK_DVDROM:
710 	    return (DM_DT_DVDROM);
711 	case DK_DVDR:
712 	    return (DM_DT_DVDR);
713 	case DK_DVDRAM:
714 	    return (DM_DT_DVDRAM);
715 	case DK_FIXED_DISK:
716 	    return (DM_DT_FIXED);
717 	case DK_FLOPPY:
718 	    return (DM_DT_FLOPPY);
719 	case DK_ZIP:
720 	    return (DM_DT_ZIP);
721 	case DK_JAZ:
722 	    return (DM_DT_JAZ);
723 	default:
724 	    return (DM_DT_UNKNOWN);
725 	}
726 }
727 
728 static descriptor_t **
729 get_assoc_alias(disk_t *diskp, int *errp)
730 {
731 	alias_t		*aliasp;
732 	uint_t		cnt;
733 	descriptor_t	**out_array;
734 	int		pos;
735 
736 	*errp = 0;
737 
738 	aliasp = diskp->aliases;
739 	cnt = 0;
740 
741 	while (aliasp != NULL) {
742 	    if (aliasp->alias != NULL) {
743 		cnt++;
744 	    }
745 	    aliasp = aliasp->next;
746 	}
747 
748 	/* set up the new array */
749 	out_array = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t));
750 	if (out_array == NULL) {
751 	    *errp = ENOMEM;
752 	    return (NULL);
753 	}
754 
755 	aliasp = diskp->aliases;
756 	pos = 0;
757 	while (aliasp != NULL) {
758 	    if (aliasp->alias != NULL) {
759 		out_array[pos++] = cache_get_desc(DM_ALIAS, diskp,
760 		    aliasp->alias, NULL, errp);
761 		if (*errp != 0) {
762 		    cache_free_descriptors(out_array);
763 		    return (NULL);
764 		}
765 	    }
766 
767 	    aliasp = aliasp->next;
768 	}
769 
770 	out_array[pos] = NULL;
771 
772 	return (out_array);
773 }
774 
775 static descriptor_t **
776 get_assoc_controllers(descriptor_t *dp, int *errp)
777 {
778 	disk_t		*diskp;
779 	int		cnt;
780 	descriptor_t	**controllers;
781 	int		i;
782 
783 	diskp = dp->p.disk;
784 
785 	/* Count how many we have. */
786 	for (cnt = 0; diskp->controllers[cnt]; cnt++);
787 
788 	/* make the snapshot */
789 	controllers = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
790 	if (controllers == NULL) {
791 	    *errp = ENOMEM;
792 	    return (NULL);
793 	}
794 
795 	for (i = 0; diskp->controllers[i]; i++) {
796 	    controllers[i] = cache_get_desc(DM_CONTROLLER,
797 		diskp->controllers[i], NULL, NULL, errp);
798 	    if (*errp != 0) {
799 		cache_free_descriptors(controllers);
800 		return (NULL);
801 	    }
802 	}
803 
804 	controllers[i] = NULL;
805 
806 	*errp = 0;
807 	return (controllers);
808 }
809 
810 static descriptor_t **
811 get_assoc_paths(descriptor_t *dp, int *errp)
812 {
813 	path_t		**pp;
814 	int		cnt;
815 	descriptor_t	**paths;
816 	int		i;
817 
818 	pp = dp->p.disk->paths;
819 
820 	/* Count how many we have. */
821 	cnt = 0;
822 	if (pp != NULL) {
823 	    for (; pp[cnt]; cnt++);
824 	}
825 
826 	/* make the snapshot */
827 	paths = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
828 	if (paths == NULL) {
829 	    *errp = ENOMEM;
830 	    return (NULL);
831 	}
832 
833 	/*
834 	 * We fill in the name field of the descriptor with the device_id
835 	 * when we deal with path descriptors originating from a drive.
836 	 * In that way we can use the device id within the path code to
837 	 * lookup the path state for this drive.
838 	 */
839 	for (i = 0; i < cnt; i++) {
840 	    paths[i] = cache_get_desc(DM_PATH, pp[i], dp->p.disk->device_id,
841 		NULL, errp);
842 	    if (*errp != 0) {
843 		cache_free_descriptors(paths);
844 		return (NULL);
845 	    }
846 	}
847 
848 	paths[i] = NULL;
849 
850 	*errp = 0;
851 	return (paths);
852 }
853 
854 static int
855 get_attrs(disk_t *diskp, int fd, char *opath, nvlist_t *attrs)
856 {
857 	if (diskp->removable) {
858 	    struct dk_minfo	minfo;
859 
860 	    if (nvlist_add_boolean(attrs, DM_REMOVABLE) != 0) {
861 		return (ENOMEM);
862 	    }
863 
864 	    /* Make sure media is inserted and spun up. */
865 	    if (fd >= 0 && media_read_info(fd, &minfo)) {
866 		if (nvlist_add_boolean(attrs, DM_LOADED) != 0) {
867 		    return (ENOMEM);
868 		}
869 	    }
870 
871 	    /* can't tell diff between dead & no media on removable drives */
872 	    if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_UP) != 0) {
873 		return (ENOMEM);
874 	    }
875 
876 	    get_drive_type(diskp, fd);
877 
878 	} else {
879 	    struct dk_minfo	minfo;
880 
881 	    /* check if the fixed drive is up or not */
882 	    if (fd >= 0 && media_read_info(fd, &minfo)) {
883 		if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_UP) != 0) {
884 		    return (ENOMEM);
885 		}
886 	    } else {
887 		if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_DOWN) != 0) {
888 		    return (ENOMEM);
889 		}
890 	    }
891 
892 	    get_drive_type(diskp, fd);
893 	}
894 
895 	if (nvlist_add_uint32(attrs, DM_DRVTYPE, diskp->drv_type) != 0) {
896 	    return (ENOMEM);
897 	}
898 
899 	if (diskp->product_id != NULL) {
900 	    if (nvlist_add_string(attrs, DM_PRODUCT_ID, diskp->product_id)
901 		!= 0) {
902 		return (ENOMEM);
903 	    }
904 	}
905 	if (diskp->vendor_id != NULL) {
906 	    if (nvlist_add_string(attrs, DM_VENDOR_ID, diskp->vendor_id) != 0) {
907 		return (ENOMEM);
908 	    }
909 	}
910 
911 	if (diskp->sync_speed != -1) {
912 	    if (nvlist_add_uint32(attrs, DM_SYNC_SPEED, diskp->sync_speed)
913 		!= 0) {
914 		return (ENOMEM);
915 	    }
916 	}
917 
918 	if (diskp->wide == 1) {
919 	    if (nvlist_add_boolean(attrs, DM_WIDE) != 0) {
920 		return (ENOMEM);
921 	    }
922 	}
923 
924 	if (diskp->rpm == 0) {
925 	    diskp->rpm = get_rpm(diskp, fd);
926 	}
927 
928 	if (diskp->rpm > 0) {
929 	    if (nvlist_add_uint32(attrs, DM_RPM, diskp->rpm) != 0) {
930 		return (ENOMEM);
931 	    }
932 	}
933 
934 	if (diskp->aliases != NULL && diskp->aliases->cluster) {
935 	    if (nvlist_add_boolean(attrs, DM_CLUSTERED) != 0) {
936 		return (ENOMEM);
937 	    }
938 	}
939 
940 	if (strlen(opath) > 0) {
941 	    if (nvlist_add_string(attrs, DM_OPATH, opath) != 0) {
942 		return (ENOMEM);
943 	    }
944 	}
945 
946 	return (0);
947 }
948 
949 static int
950 get_disk_kstats(kstat_ctl_t *kc, char *diskname, char *classname,
951 	nvlist_t *stats)
952 {
953 	kstat_t		*ksp;
954 	size_t		class_len;
955 	int		err = 0;
956 
957 	class_len = strlen(classname);
958 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
959 	    if (strncmp(ksp->ks_class, classname, class_len) == 0) {
960 		char	kstat_name[KSTAT_STRLEN];
961 		char	*dname = kstat_name;
962 		char	*ename = ksp->ks_name;
963 
964 		/* names are format: "sd0,err" - copy chars up to comma */
965 		while (*ename && *ename != ',') {
966 		    *dname++ = *ename++;
967 		}
968 		*dname = NULL;
969 
970 		if (libdiskmgt_str_eq(diskname, kstat_name)) {
971 		    (void) kstat_read(kc, ksp, NULL);
972 		    err = get_kstat_vals(ksp, stats);
973 		    break;
974 		}
975 	    }
976 	}
977 
978 	return (err);
979 }
980 
981 /*
982  * Getting the drive type depends on if the dev tree walk indicated that the
983  * drive was a CD-ROM or not.  The kernal lumps all of the removable multi-media
984  * drives (e.g. CD, DVD, MO, etc.) together as CD-ROMS, so we need to use
985  * a uscsi cmd to check the drive type.
986  */
987 static void
988 get_drive_type(disk_t *dp, int fd)
989 {
990 	if (dp->drv_type == DM_DT_UNKNOWN) {
991 	    int	opened_here = 0;
992 
993 	    /* We may have already opened the device. */
994 	    if (fd < 0) {
995 		fd = drive_open_disk(dp, NULL, 0);
996 		opened_here = 1;
997 	    }
998 
999 	    if (fd >= 0) {
1000 		if (dp->cd_rom) {
1001 		    /* use uscsi to determine drive type */
1002 		    dp->drv_type = get_cdrom_drvtype(fd);
1003 
1004 		    /* if uscsi fails, just call it a cd-rom */
1005 		    if (dp->drv_type == DM_DT_UNKNOWN) {
1006 			dp->drv_type = DM_DT_CDROM;
1007 		    }
1008 
1009 		} else {
1010 		    struct dk_minfo	minfo;
1011 
1012 		    if (media_read_info(fd, &minfo)) {
1013 			dp->drv_type = conv_drive_type(minfo.dki_media_type);
1014 		    }
1015 		}
1016 
1017 		if (opened_here) {
1018 		    (void) close(fd);
1019 		}
1020 
1021 	    } else {
1022 		/* couldn't open */
1023 		if (dp->cd_rom) {
1024 		    dp->drv_type = DM_DT_CDROM;
1025 		}
1026 	    }
1027 	}
1028 }
1029 
1030 static char *
1031 get_err_attr_name(char *kstat_name)
1032 {
1033 	int	i;
1034 
1035 	for (i = 0; kstat_err_names[i] != NULL; i++) {
1036 	    if (libdiskmgt_str_eq(kstat_name, kstat_err_names[i])) {
1037 		return (err_attr_names[i]);
1038 	    }
1039 	}
1040 
1041 	return (NULL);
1042 }
1043 
1044 static int
1045 get_err_kstats(kstat_ctl_t *kc, char *diskname, nvlist_t *stats)
1046 {
1047 	return (get_disk_kstats(kc, diskname, KSTAT_CLASS_ERROR, stats));
1048 }
1049 
1050 static int
1051 get_io_kstats(kstat_ctl_t *kc, char *diskname, nvlist_t *stats)
1052 {
1053 	return (get_disk_kstats(kc, diskname, KSTAT_CLASS_DISK, stats));
1054 }
1055 
1056 static int
1057 get_kstat_vals(kstat_t *ksp, nvlist_t *stats)
1058 {
1059 	if (ksp->ks_type == KSTAT_TYPE_IO) {
1060 	    kstat_io_t *kiop;
1061 
1062 	    kiop = KSTAT_IO_PTR(ksp);
1063 
1064 	    /* see sys/kstat.h kstat_io_t struct for more fields */
1065 
1066 	    if (update_stat64(stats, DM_NBYTESREAD, kiop->nread) != 0) {
1067 		return (ENOMEM);
1068 	    }
1069 	    if (update_stat64(stats, DM_NBYTESWRITTEN, kiop->nwritten) != 0) {
1070 		return (ENOMEM);
1071 	    }
1072 	    if (update_stat64(stats, DM_NREADOPS, kiop->reads) != 0) {
1073 		return (ENOMEM);
1074 	    }
1075 	    if (update_stat64(stats, DM_NWRITEOPS, kiop->writes) != 0) {
1076 		return (ENOMEM);
1077 	    }
1078 
1079 	} else if (ksp->ks_type == KSTAT_TYPE_NAMED) {
1080 	    kstat_named_t *knp;
1081 	    int		i;
1082 
1083 	    knp = KSTAT_NAMED_PTR(ksp);
1084 	    for (i = 0; i < ksp->ks_ndata; i++) {
1085 		char	*attr_name;
1086 
1087 		if (knp[i].name[0] == 0)
1088 		    continue;
1089 
1090 		if ((attr_name = get_err_attr_name(knp[i].name)) == NULL) {
1091 		    continue;
1092 
1093 		}
1094 
1095 		switch (knp[i].data_type) {
1096 		case KSTAT_DATA_UINT32:
1097 		    if (update_stat32(stats, attr_name, knp[i].value.ui32)
1098 			!= 0) {
1099 			return (ENOMEM);
1100 		    }
1101 		    break;
1102 
1103 		default:
1104 		    /* Right now all of the error types are uint32 */
1105 		    break;
1106 		}
1107 	    }
1108 	}
1109 	return (0);
1110 }
1111 
1112 static int
1113 update_stat32(nvlist_t *stats, char *attr, uint32_t value)
1114 {
1115 	int32_t	currval;
1116 
1117 	if (nvlist_lookup_int32(stats, attr, &currval) == 0) {
1118 	    value += currval;
1119 	}
1120 
1121 	return (nvlist_add_uint32(stats, attr, value));
1122 }
1123 
1124 /*
1125  * There can be more than one kstat value when we have multi-path drives
1126  * that are not under mpxio (since there is more than one kstat name for
1127  * the drive in this case).  So, we may have merge all of the kstat values
1128  * to give an accurate set of stats for the drive.
1129  */
1130 static int
1131 update_stat64(nvlist_t *stats, char *attr, uint64_t value)
1132 {
1133 	int64_t	currval;
1134 
1135 	if (nvlist_lookup_int64(stats, attr, &currval) == 0) {
1136 	    value += currval;
1137 	}
1138 	return (nvlist_add_uint64(stats, attr, value));
1139 }
1140 
1141 /*
1142  * uscsi function to get the rpm of the drive
1143  */
1144 static int
1145 get_rpm(disk_t *dp, int fd)
1146 {
1147 	int	opened_here = 0;
1148 	int	rpm = -1;
1149 
1150 	/* We may have already opened the device. */
1151 	if (fd < 0) {
1152 	    fd = drive_open_disk(dp, NULL, 0);
1153 	    opened_here = 1;
1154 	}
1155 
1156 	if (fd >= 0) {
1157 	    int				status;
1158 	    struct mode_geometry	*page4;
1159 	    struct scsi_ms_header	header;
1160 	    union {
1161 		struct mode_geometry	page4;
1162 		char			rawbuf[MAX_MODE_SENSE_SIZE];
1163 	    } u_page4;
1164 
1165 	    page4 = &u_page4.page4;
1166 	    (void) memset(&u_page4, 0, sizeof (u_page4));
1167 
1168 	    status = uscsi_mode_sense(fd, DAD_MODE_GEOMETRY,
1169 		MODE_SENSE_PC_DEFAULT, (caddr_t)page4, MAX_MODE_SENSE_SIZE,
1170 		&header);
1171 
1172 	    if (status) {
1173 		status = uscsi_mode_sense(fd, DAD_MODE_GEOMETRY,
1174 		    MODE_SENSE_PC_SAVED, (caddr_t)page4, MAX_MODE_SENSE_SIZE,
1175 		    &header);
1176 	    }
1177 
1178 	    if (status) {
1179 		status = uscsi_mode_sense(fd, DAD_MODE_GEOMETRY,
1180 		    MODE_SENSE_PC_CURRENT, (caddr_t)page4, MAX_MODE_SENSE_SIZE,
1181 		    &header);
1182 	    }
1183 
1184 	    if (!status) {
1185 #ifdef _LITTLE_ENDIAN
1186 		page4->rpm = ntohs(page4->rpm);
1187 #endif /* _LITTLE_ENDIAN */
1188 
1189 		rpm = page4->rpm;
1190 	    }
1191 
1192 	    if (opened_here) {
1193 		(void) close(fd);
1194 	    }
1195 	}
1196 
1197 	return (rpm);
1198 }
1199 
1200 /*
1201  *	******** the rest of this is uscsi stuff for the drv type ********
1202  */
1203 
1204 /*
1205  * We try a get_configuration uscsi cmd.  If that fails, try a
1206  * atapi_capabilities cmd.  If both fail then this is an older CD-ROM.
1207  */
1208 static int
1209 get_cdrom_drvtype(int fd)
1210 {
1211 	union scsi_cdb cdb;
1212 	struct uscsi_cmd cmd;
1213 	uchar_t buff[SCSIBUFLEN];
1214 
1215 	fill_general_page_cdb_g1(&cdb, SCMD_GET_CONFIGURATION, 0,
1216 	    b0(sizeof (buff)), b1(sizeof (buff)));
1217 	fill_command_g1(&cmd, &cdb, (caddr_t)buff, sizeof (buff));
1218 
1219 	if (ioctl(fd, USCSICMD, &cmd) >= 0) {
1220 	    struct get_configuration	*config;
1221 	    struct conf_feature		*feature;
1222 	    int				flen;
1223 
1224 	    /* The first profile is the preferred one for the drive. */
1225 	    config = (struct get_configuration *)buff;
1226 	    feature = &config->feature;
1227 	    flen = feature->len / sizeof (struct profile_list);
1228 	    if (flen > 0) {
1229 		int prof_num;
1230 
1231 		prof_num = (int)convnum(feature->features.plist[0].profile, 2);
1232 
1233 		if (dm_debug > 1) {
1234 		    (void) fprintf(stderr, "INFO: uscsi get_configuration %d\n",
1235 			prof_num);
1236 		}
1237 
1238 		switch (prof_num) {
1239 		case PROF_MAGNETO_OPTICAL:
1240 		    return (DM_DT_MO_ERASABLE);
1241 		case PROF_OPTICAL_WO:
1242 		    return (DM_DT_MO_WRITEONCE);
1243 		case PROF_OPTICAL_ASMO:
1244 		    return (DM_DT_AS_MO);
1245 		case PROF_CDROM:
1246 		    return (DM_DT_CDROM);
1247 		case PROF_CDR:
1248 		    return (DM_DT_CDR);
1249 		case PROF_CDRW:
1250 		    return (DM_DT_CDRW);
1251 		case PROF_DVDROM:
1252 		    return (DM_DT_DVDROM);
1253 		case PROF_DVDRAM:
1254 		    return (DM_DT_DVDRAM);
1255 		case PROF_DVDRW_REST:
1256 		    return (DM_DT_DVDRW);
1257 		case PROF_DVDRW_SEQ:
1258 		    return (DM_DT_DVDRW);
1259 		case PROF_DVDRW:
1260 		    return (DM_DT_DVDRW);
1261 		case PROF_DDCD_ROM:
1262 		    return (DM_DT_DDCDROM);
1263 		case PROF_DDCD_R:
1264 		    return (DM_DT_DDCDR);
1265 		case PROF_DDCD_RW:
1266 		    return (DM_DT_DDCDRW);
1267 		}
1268 	    }
1269 	}
1270 
1271 	/* see if the atapi capabilities give anything */
1272 	return (check_atapi(fd));
1273 }
1274 
1275 static int
1276 check_atapi(int fd)
1277 {
1278 	union scsi_cdb cdb;
1279 	struct uscsi_cmd cmd;
1280 	uchar_t buff[SCSIBUFLEN];
1281 
1282 	fill_mode_page_cdb(&cdb, ATAPI_CAPABILITIES);
1283 	fill_command_g1(&cmd, &cdb, (caddr_t)buff, sizeof (buff));
1284 
1285 	if (ioctl(fd, USCSICMD, &cmd) >= 0) {
1286 	    int			bdesclen;
1287 	    struct capabilities	*cap;
1288 	    struct mode_header_g2 *mode;
1289 
1290 	    mode = (struct mode_header_g2 *)buff;
1291 
1292 	    bdesclen = (int)convnum(mode->desclen, 2);
1293 	    cap = (struct capabilities *)
1294 		&buff[sizeof (struct mode_header_g2) + bdesclen];
1295 
1296 	    if (dm_debug > 1) {
1297 		(void) fprintf(stderr, "INFO: uscsi atapi capabilities\n");
1298 	    }
1299 
1300 	    /* These are in order of how we want to report the drv type. */
1301 	    if (cap->dvdram_write) {
1302 		return (DM_DT_DVDRAM);
1303 	    }
1304 	    if (cap->dvdr_write) {
1305 		return (DM_DT_DVDR);
1306 	    }
1307 	    if (cap->dvdrom_read) {
1308 		return (DM_DT_DVDROM);
1309 	    }
1310 	    if (cap->cdrw_write) {
1311 		return (DM_DT_CDRW);
1312 	    }
1313 	    if (cap->cdr_write) {
1314 		return (DM_DT_CDR);
1315 	    }
1316 	    if (cap->cdr_read) {
1317 		return (DM_DT_CDROM);
1318 	    }
1319 	}
1320 
1321 	/* everything failed, so this is an older CD-ROM */
1322 	if (dm_debug > 1) {
1323 	    (void) fprintf(stderr, "INFO: uscsi failed\n");
1324 	}
1325 
1326 	return (DM_DT_CDROM);
1327 }
1328 
1329 static uint64_t
1330 convnum(uchar_t *nptr, int len)
1331 {
1332 	uint64_t value;
1333 
1334 	for (value = 0; len > 0; len--, nptr++)
1335 		value = (value << 8) | *nptr;
1336 	return (value);
1337 }
1338 
1339 static void
1340 fill_command_g1(struct uscsi_cmd *cmd, union scsi_cdb *cdb,
1341 	caddr_t buff, int blen)
1342 {
1343 	bzero((caddr_t)cmd, sizeof (struct uscsi_cmd));
1344 	bzero(buff, blen);
1345 
1346 	cmd->uscsi_cdb = (caddr_t)cdb;
1347 	cmd->uscsi_cdblen = CDB_GROUP1;
1348 
1349 	cmd->uscsi_bufaddr = buff;
1350 	cmd->uscsi_buflen = blen;
1351 
1352 	cmd->uscsi_flags = USCSI_DIAGNOSE|USCSI_ISOLATE|USCSI_READ;
1353 }
1354 
1355 static void
1356 fill_general_page_cdb_g1(union scsi_cdb *cdb, int command, int lun,
1357 	uchar_t c0, uchar_t c1)
1358 {
1359 	bzero((caddr_t)cdb, sizeof (union scsi_cdb));
1360 	cdb->scc_cmd = command;
1361 	cdb->scc_lun = lun;
1362 	cdb->g1_count0 = c0; /* max length for page */
1363 	cdb->g1_count1 = c1; /* max length for page */
1364 }
1365 
1366 static void
1367 fill_mode_page_cdb(union scsi_cdb *cdb, int page)
1368 {
1369 	/* group 1 mode page */
1370 	bzero((caddr_t)cdb, sizeof (union scsi_cdb));
1371 	cdb->scc_cmd = SCMD_MODE_SENSE_G1;
1372 	cdb->g1_count0 = 0xff; /* max length for mode page */
1373 	cdb->g1_count1 = 0xff; /* max length for mode page */
1374 	cdb->g1_addr3 = page;
1375 }
1376 
1377 static int
1378 uscsi_mode_sense(int fd, int page_code, int page_control, caddr_t page_data,
1379 	int page_size, struct  scsi_ms_header *header)
1380 {
1381 	caddr_t			mode_sense_buf;
1382 	struct mode_header	*hdr;
1383 	struct mode_page	*pg;
1384 	int			nbytes;
1385 	struct uscsi_cmd	ucmd;
1386 	union scsi_cdb		cdb;
1387 	int			status;
1388 	int			maximum;
1389 	char			rqbuf[255];
1390 
1391 	/*
1392 	 * Allocate a buffer for the mode sense headers
1393 	 * and mode sense data itself.
1394 	 */
1395 	nbytes = sizeof (struct block_descriptor) +
1396 				sizeof (struct mode_header) + page_size;
1397 	nbytes = page_size;
1398 	if ((mode_sense_buf = malloc((uint_t)nbytes)) == NULL) {
1399 	    return (-1);
1400 	}
1401 
1402 	/*
1403 	 * Build and execute the uscsi ioctl
1404 	 */
1405 	(void) memset(mode_sense_buf, 0, nbytes);
1406 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
1407 	(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1408 
1409 	cdb.scc_cmd = SCMD_MODE_SENSE;
1410 	FORMG0COUNT(&cdb, (uchar_t)nbytes);
1411 	cdb.cdb_opaque[2] = page_control | page_code;
1412 	ucmd.uscsi_cdb = (caddr_t)&cdb;
1413 	ucmd.uscsi_cdblen = CDB_GROUP0;
1414 	ucmd.uscsi_bufaddr = mode_sense_buf;
1415 	ucmd.uscsi_buflen = nbytes;
1416 
1417 	ucmd.uscsi_flags |= USCSI_SILENT;
1418 	ucmd.uscsi_flags |= USCSI_READ;
1419 	ucmd.uscsi_timeout = 30;
1420 	ucmd.uscsi_flags |= USCSI_RQENABLE;
1421 	if (ucmd.uscsi_rqbuf == NULL)  {
1422 	    ucmd.uscsi_rqbuf = rqbuf;
1423 	    ucmd.uscsi_rqlen = sizeof (rqbuf);
1424 	    ucmd.uscsi_rqresid = sizeof (rqbuf);
1425 	}
1426 	ucmd.uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS;
1427 
1428 	status = ioctl(fd, USCSICMD, &ucmd);
1429 
1430 	if (status || ucmd.uscsi_status != 0) {
1431 	    free(mode_sense_buf);
1432 	    return (-1);
1433 	}
1434 
1435 	/*
1436 	 * Verify that the returned data looks reasonabled,
1437 	 * find the actual page data, and copy it into the
1438 	 * user's buffer.  Copy the mode_header and block_descriptor
1439 	 * into the header structure, which can then be used to
1440 	 * return the same data to the drive when issuing a mode select.
1441 	 */
1442 	hdr = (struct mode_header *)mode_sense_buf;
1443 	(void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header));
1444 	if (hdr->bdesc_length != sizeof (struct block_descriptor) &&
1445 	    hdr->bdesc_length != 0) {
1446 	    free(mode_sense_buf);
1447 	    return (-1);
1448 	}
1449 	(void) memcpy((caddr_t)header, mode_sense_buf,
1450 	    (int) (sizeof (struct mode_header) + hdr->bdesc_length));
1451 	pg = (struct mode_page *)((ulong_t)mode_sense_buf +
1452 	    sizeof (struct mode_header) + hdr->bdesc_length);
1453 	if (pg->code != page_code) {
1454 	    free(mode_sense_buf);
1455 	    return (-1);
1456 	}
1457 
1458 	/*
1459 	 * Accept up to "page_size" bytes of mode sense data.
1460 	 * This allows us to accept both CCS and SCSI-2
1461 	 * structures, as long as we request the greater
1462 	 * of the two.
1463 	 */
1464 	maximum = page_size - sizeof (struct mode_page) - hdr->bdesc_length;
1465 	if (((int)pg->length) > maximum) {
1466 	    free(mode_sense_buf);
1467 	    return (-1);
1468 	}
1469 
1470 	(void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
1471 
1472 	free(mode_sense_buf);
1473 	return (0);
1474 }
1475