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