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