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