xref: /openbsd-src/sys/scsi/sd.c (revision 9593dc34da13a12012033a17061c846c208061c2)
1*9593dc34Smglocker /*	$OpenBSD: sd.c,v 1.337 2024/09/04 07:54:53 mglocker Exp $	*/
2cb3ad6e3Sdownsj /*	$NetBSD: sd.c,v 1.111 1997/04/02 02:29:41 mycroft Exp $	*/
3df930be7Sderaadt 
4aca6abd4Scsapuntz /*-
5805cc48eSsf  * Copyright (c) 1998, 2003, 2004 The NetBSD Foundation, Inc.
6aca6abd4Scsapuntz  * All rights reserved.
7aca6abd4Scsapuntz  *
8aca6abd4Scsapuntz  * This code is derived from software contributed to The NetBSD Foundation
9aca6abd4Scsapuntz  * by Charles M. Hannum.
10df930be7Sderaadt  *
11df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
12df930be7Sderaadt  * modification, are permitted provided that the following conditions
13df930be7Sderaadt  * are met:
14df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
15df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
16df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
17df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
18df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
19df930be7Sderaadt  *
20aca6abd4Scsapuntz  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21aca6abd4Scsapuntz  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22aca6abd4Scsapuntz  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23aca6abd4Scsapuntz  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24aca6abd4Scsapuntz  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25aca6abd4Scsapuntz  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26aca6abd4Scsapuntz  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27aca6abd4Scsapuntz  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28aca6abd4Scsapuntz  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29aca6abd4Scsapuntz  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30aca6abd4Scsapuntz  * POSSIBILITY OF SUCH DAMAGE.
31df930be7Sderaadt  */
32df930be7Sderaadt 
33df930be7Sderaadt /*
34df930be7Sderaadt  * Originally written by Julian Elischer (julian@dialix.oz.au)
35df930be7Sderaadt  * for TRW Financial Systems for use under the MACH(2.5) operating system.
36df930be7Sderaadt  *
37df930be7Sderaadt  * TRW Financial Systems, in accordance with their agreement with Carnegie
38df930be7Sderaadt  * Mellon University, makes this software available to CMU to distribute
39df930be7Sderaadt  * or use in any manner that they see fit as long as this message is kept with
40df930be7Sderaadt  * the software. For this reason TFS also grants any other persons or
41df930be7Sderaadt  * organisations permission to use or modify this software.
42df930be7Sderaadt  *
43df930be7Sderaadt  * TFS supplies this software to be publicly redistributed
44df930be7Sderaadt  * on the understanding that TFS is not responsible for the correct
45df930be7Sderaadt  * functioning of this software in any circumstances.
46df930be7Sderaadt  *
47df930be7Sderaadt  * Ported to run under 386BSD by Julian Elischer (julian@dialix.oz.au) Sept 1992
48df930be7Sderaadt  */
49df930be7Sderaadt 
504756a0a1Skrw #include <sys/stdint.h>
51df930be7Sderaadt #include <sys/param.h>
52df930be7Sderaadt #include <sys/systm.h>
53d9f31c94Skrw #include <sys/timeout.h>
54c0cd3489Sguenther #include <sys/fcntl.h>
55df930be7Sderaadt #include <sys/stat.h>
56df930be7Sderaadt #include <sys/ioctl.h>
57c03366deSdownsj #include <sys/mtio.h>
58c158b662Sthib #include <sys/mutex.h>
59df930be7Sderaadt #include <sys/buf.h>
60df930be7Sderaadt #include <sys/uio.h>
61df930be7Sderaadt #include <sys/malloc.h>
625c0b007dSderaadt #include <sys/pool.h>
63df930be7Sderaadt #include <sys/errno.h>
64df930be7Sderaadt #include <sys/device.h>
65df930be7Sderaadt #include <sys/disklabel.h>
66df930be7Sderaadt #include <sys/disk.h>
67d724e01aSderaadt #include <sys/conf.h>
68b5107c5aSpefo #include <sys/scsiio.h>
6991f4f7d8Sdlg #include <sys/dkio.h>
70cf726771Skettenis #include <sys/reboot.h>
71df930be7Sderaadt 
72df930be7Sderaadt #include <scsi/scsi_all.h>
73bba39a67Skrw #include <scsi/scsi_debug.h>
74df930be7Sderaadt #include <scsi/scsi_disk.h>
75df930be7Sderaadt #include <scsi/scsiconf.h>
76aca6abd4Scsapuntz #include <scsi/sdvar.h>
77df930be7Sderaadt 
78ff28e22dSmillert #include <ufs/ffs/fs.h>			/* for BBSIZE and SBSIZE */
79ff28e22dSmillert 
801e7162a8Scsapuntz #include <sys/vnode.h>
811e7162a8Scsapuntz 
82c4071fd1Smillert int	sdmatch(struct device *, void *, void *);
83c4071fd1Smillert void	sdattach(struct device *, struct device *, void *);
84e78728c7Spirofti int	sdactivate(struct device *, int);
85c4071fd1Smillert int	sddetach(struct device *, int);
861e7162a8Scsapuntz 
87c4071fd1Smillert void	sdminphys(struct buf *);
88df591ed6Sderaadt int	sdgetdisklabel(dev_t, struct sd_softc *, struct disklabel *, int);
8956cb3956Sdlg void	sdstart(struct scsi_xfer *);
90c4071fd1Smillert int	sd_interpret_sense(struct scsi_xfer *);
917f77a1b5Sdlg int	sd_read_cap_10(struct sd_softc *, int);
927f77a1b5Sdlg int	sd_read_cap_16(struct sd_softc *, int);
9375679190Skrw int	sd_read_cap(struct sd_softc *, int);
94da7312e6Sdlg int	sd_thin_pages(struct sd_softc *, int);
95da7312e6Sdlg int	sd_vpd_block_limits(struct sd_softc *, int);
96da7312e6Sdlg int	sd_vpd_thin(struct sd_softc *, int);
97da7312e6Sdlg int	sd_thin_params(struct sd_softc *, int);
98442ab2acSkrw int	sd_get_parms(struct sd_softc *, int);
99805cc48eSsf int	sd_flush(struct sd_softc *, int);
100aca6abd4Scsapuntz 
101c4071fd1Smillert void	viscpy(u_char *, u_char *, int);
102df930be7Sderaadt 
10329a8ba45Sdlg int	sd_ioctl_inquiry(struct sd_softc *, struct dk_inquiry *);
10462ef6040Sdlg int	sd_ioctl_cache(struct sd_softc *, long, struct dk_cache *);
10529a8ba45Sdlg 
10681e09b69Skrw int	sd_cmd_rw6(struct scsi_generic *, int, u_int64_t, u_int32_t);
10781e09b69Skrw int	sd_cmd_rw10(struct scsi_generic *, int, u_int64_t, u_int32_t);
10881e09b69Skrw int	sd_cmd_rw12(struct scsi_generic *, int, u_int64_t, u_int32_t);
10981e09b69Skrw int	sd_cmd_rw16(struct scsi_generic *, int, u_int64_t, u_int32_t);
110864c175eSdlg 
111864c175eSdlg void	sd_buf_done(struct scsi_xfer *);
112864c175eSdlg 
1139eaf72d1Smpi const struct cfattach sd_ca = {
1141e7162a8Scsapuntz 	sizeof(struct sd_softc), sdmatch, sdattach,
115a23d39c4Smiod 	sddetach, sdactivate
116d724e01aSderaadt };
117d724e01aSderaadt 
118d724e01aSderaadt struct cfdriver sd_cd = {
119d724e01aSderaadt 	NULL, "sd", DV_DISK
120df930be7Sderaadt };
121df930be7Sderaadt 
122697fee68Smickey const struct scsi_inquiry_pattern sd_patterns[] = {
123df930be7Sderaadt 	{T_DIRECT, T_FIXED,
124df930be7Sderaadt 	 "",         "",                 ""},
125df930be7Sderaadt 	{T_DIRECT, T_REMOV,
126df930be7Sderaadt 	 "",         "",                 ""},
127c6e2d171Stdeval 	{T_RDIRECT, T_FIXED,
128c6e2d171Stdeval 	 "",         "",                 ""},
129c6e2d171Stdeval 	{T_RDIRECT, T_REMOV,
130c6e2d171Stdeval 	 "",         "",                 ""},
131df930be7Sderaadt 	{T_OPTICAL, T_FIXED,
132df930be7Sderaadt 	 "",         "",                 ""},
133df930be7Sderaadt 	{T_OPTICAL, T_REMOV,
134df930be7Sderaadt 	 "",         "",                 ""},
135df930be7Sderaadt };
136df930be7Sderaadt 
13731efee77Sjsing #define sdlookup(unit) (struct sd_softc *)disk_lookup(&sd_cd, (unit))
1381e7162a8Scsapuntz 
139df930be7Sderaadt int
1408d3a06f5Sdlg sdmatch(struct device *parent, void *match, void *aux)
141df930be7Sderaadt {
142a0837789Sdlg 	struct scsi_attach_args		*sa = aux;
1430fbd355cSkrw 	struct scsi_inquiry_data	*inq = &sa->sa_sc_link->inqdata;
144df930be7Sderaadt 	int				 priority;
145df930be7Sderaadt 
1460fbd355cSkrw 	(void)scsi_inqmatch(inq, sd_patterns, nitems(sd_patterns),
147df930be7Sderaadt 	    sizeof(sd_patterns[0]), &priority);
1488d3a06f5Sdlg 
14989ea875eSkrw 	return priority;
150df930be7Sderaadt }
151df930be7Sderaadt 
152df930be7Sderaadt /*
153df930be7Sderaadt  * The routine called by the low level scsi routine when it discovers
154df930be7Sderaadt  * a device suitable for this driver.
155df930be7Sderaadt  */
156df930be7Sderaadt void
1578d3a06f5Sdlg sdattach(struct device *parent, struct device *self, void *aux)
158df930be7Sderaadt {
15989ea875eSkrw 	struct dk_cache			 dkc;
16038ea674aSdlg 	struct sd_softc			*sc = (struct sd_softc *)self;
161a0837789Sdlg 	struct scsi_attach_args		*sa = aux;
16238ea674aSdlg 	struct disk_parms		*dp = &sc->params;
1636d4b3493Skrw 	struct scsi_link		*link = sa->sa_sc_link;
16489ea875eSkrw 	int				 error, sd_autoconf;
16589ea875eSkrw 	int				 sortby = BUFQ_DEFAULT;
166df930be7Sderaadt 
1676d4b3493Skrw 	SC_DEBUG(link, SDEV_DB2, ("sdattach:\n"));
168df930be7Sderaadt 
16989ea875eSkrw 	sd_autoconf = scsi_autoconf | SCSI_SILENT |
17089ea875eSkrw 	    SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE;
17189ea875eSkrw 
172df930be7Sderaadt 	/*
17389ea875eSkrw 	 * Store information needed to contact our base driver.
174df930be7Sderaadt 	 */
1756d4b3493Skrw 	sc->sc_link = link;
1766d4b3493Skrw 	link->interpret_sense = sd_interpret_sense;
1776d4b3493Skrw 	link->device_softc = sc;
178df930be7Sderaadt 
17989ea875eSkrw 	if (ISSET(link->flags, SDEV_ATAPI) && ISSET(link->flags,
18089ea875eSkrw 	    SDEV_REMOVABLE))
18138a5deecSkrw 		SET(link->quirks, SDEV_NOSYNCCACHE);
182aca6abd4Scsapuntz 
183df930be7Sderaadt 	/*
18489ea875eSkrw 	 * Use the subdriver to request information regarding the drive. We
18589ea875eSkrw 	 * cannot use interrupts yet, so the request must specify this.
186df930be7Sderaadt 	 */
1870c0430f8Sniklas 	printf("\n");
188cb3ad6e3Sdownsj 
1896d4b3493Skrw 	scsi_xsh_set(&sc->sc_xsh, link, sdstart);
190d9f31c94Skrw 
1915b92815bSkrw 	/* Spin up non-UMASS devices ready or not. */
19271ed4a82Skrw 	if (!ISSET(link->flags, SDEV_UMASS))
1936d4b3493Skrw 		scsi_start(link, SSS_START, sd_autoconf);
1948d506501Skrw 
19576a8859eSkrw 	/*
19676fa4bccSjasper 	 * Some devices (e.g. BlackBerry Pearl) won't admit they have
19776a8859eSkrw 	 * media loaded unless its been locked in.
19876a8859eSkrw 	 */
1999dbfff47Skrw 	if (ISSET(link->flags, SDEV_REMOVABLE))
2006d4b3493Skrw 		scsi_prevent(link, PR_PREVENT, sd_autoconf);
20176a8859eSkrw 
2028d506501Skrw 	/* Check that it is still responding and ok. */
20338ea674aSdlg 	error = scsi_test_unit_ready(sc->sc_link, TEST_READY_RETRIES * 3,
20451a98c34Sdlg 	    sd_autoconf);
205340f3a7eSkrw 	if (error == 0)
206340f3a7eSkrw 		error = sd_get_parms(sc, sd_autoconf);
207aca6abd4Scsapuntz 
2089dbfff47Skrw 	if (ISSET(link->flags, SDEV_REMOVABLE))
2096d4b3493Skrw 		scsi_prevent(link, PR_ALLOW, sd_autoconf);
21076a8859eSkrw 
211340f3a7eSkrw 	if (error == 0) {
2126e49e787Skrw 		printf("%s: %lluMB, %u bytes/sector, %llu sectors",
213d4dbf0e5Skrw 		    sc->sc_dev.dv_xname,
214006d5239Skrw 		    dp->disksize / (1048576 / dp->secsize), dp->secsize,
2159625671fSdlg 		    dp->disksize);
2165149c3a5Sdlg 		if (ISSET(sc->flags, SDF_THIN)) {
2175149c3a5Sdlg 			sortby = BUFQ_FIFO;
2187f77a1b5Sdlg 			printf(", thin");
2195149c3a5Sdlg 		}
220340f3a7eSkrw 		if (ISSET(link->flags, SDEV_READONLY))
2213683b99eShalex 			printf(", readonly");
2227f77a1b5Sdlg 		printf("\n");
223df930be7Sderaadt 	}
224aca6abd4Scsapuntz 
2255149c3a5Sdlg 	/*
2265149c3a5Sdlg 	 * Initialize disk structures.
2275149c3a5Sdlg 	 */
2285149c3a5Sdlg 	sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
2295149c3a5Sdlg 	bufq_init(&sc->sc_bufq, sortby);
2305149c3a5Sdlg 
2315149c3a5Sdlg 	/*
2325149c3a5Sdlg 	 * Enable write cache by default.
2335149c3a5Sdlg 	 */
23463cdeb7cSdlg 	memset(&dkc, 0, sizeof(dkc));
23563cdeb7cSdlg 	if (sd_ioctl_cache(sc, DIOCGCACHE, &dkc) == 0 && dkc.wrcache == 0) {
23663cdeb7cSdlg 		dkc.wrcache = 1;
23763cdeb7cSdlg 		sd_ioctl_cache(sc, DIOCSCACHE, &dkc);
23863cdeb7cSdlg 	}
23963cdeb7cSdlg 
24073b308c6Sjsing 	/* Attach disk. */
2412690bc4bSjsing 	disk_attach(&sc->sc_dev, &sc->sc_dk);
242742a6f35Sderaadt }
243df930be7Sderaadt 
244df930be7Sderaadt int
245e78728c7Spirofti sdactivate(struct device *self, int act)
246df930be7Sderaadt {
247805c29feSbluhm 	struct scsi_link		*link;
24838ea674aSdlg 	struct sd_softc			*sc = (struct sd_softc *)self;
249805c29feSbluhm 
250d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING))
25189ea875eSkrw 		return ENXIO;
252805c29feSbluhm 	link = sc->sc_link;
253df930be7Sderaadt 
2541e7162a8Scsapuntz 	switch (act) {
255550fe890Skettenis 	case DVACT_SUSPEND:
25670276654Sderaadt 		/*
25770276654Sderaadt 		 * We flush the cache, since we our next step before
25870276654Sderaadt 		 * DVACT_POWERDOWN might be a hibernate operation.
25970276654Sderaadt 		 */
2609dbfff47Skrw 		if (ISSET(sc->flags, SDF_DIRTY))
26170276654Sderaadt 			sd_flush(sc, SCSI_AUTOCONF);
2621e4b376cSderaadt 		break;
2631e4b376cSderaadt 	case DVACT_POWERDOWN:
264550fe890Skettenis 		/*
2659d96ede2Skettenis 		 * Stop the disk.  Stopping the disk should flush the
2669d96ede2Skettenis 		 * cache, but we are paranoid so we flush the cache
26743a2a29aSmpi 		 * first.  We're cold at this point, so we poll for
26843a2a29aSmpi 		 * completion.
269550fe890Skettenis 		 */
2709dbfff47Skrw 		if (ISSET(sc->flags, SDF_DIRTY))
271550fe890Skettenis 			sd_flush(sc, SCSI_AUTOCONF);
2729dbfff47Skrw 		if (ISSET(boothowto, RB_POWERDOWN))
273805c29feSbluhm 			scsi_start(link, SSS_STOP,
274cfe99e7eSkettenis 			    SCSI_IGNORE_ILLEGAL_REQUEST |
275cfe99e7eSkettenis 			    SCSI_IGNORE_NOT_READY | SCSI_AUTOCONF);
276550fe890Skettenis 		break;
277550fe890Skettenis 	case DVACT_RESUME:
278805c29feSbluhm 		scsi_start(link, SSS_START,
2799d96ede2Skettenis 		    SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_AUTOCONF);
280550fe890Skettenis 		break;
281a1e05643Sderaadt 	case DVACT_DEACTIVATE:
282d35ed37dSkrw 		SET(sc->flags, SDF_DYING);
2832613c172Sdlg 		scsi_xsh_del(&sc->sc_xsh);
284a1e05643Sderaadt 		break;
285df930be7Sderaadt 	}
28689ea875eSkrw 	return 0;
2871e7162a8Scsapuntz }
2881e7162a8Scsapuntz 
2891e7162a8Scsapuntz int
2908d3a06f5Sdlg sddetach(struct device *self, int flags)
2911e7162a8Scsapuntz {
29238ea674aSdlg 	struct sd_softc *sc = (struct sd_softc *)self;
2931e7162a8Scsapuntz 
294c2235760Sdlg 	bufq_drain(&sc->sc_bufq);
2951e7162a8Scsapuntz 
296e1d64023Smatthew 	disk_gone(sdopen, self->dv_unit);
2971e7162a8Scsapuntz 
298a23d39c4Smiod 	/* Detach disk. */
299c2235760Sdlg 	bufq_destroy(&sc->sc_bufq);
30038ea674aSdlg 	disk_detach(&sc->sc_dk);
301a23d39c4Smiod 
30289ea875eSkrw 	return 0;
3031e7162a8Scsapuntz }
3041e7162a8Scsapuntz 
305df930be7Sderaadt /*
3063cc5dd17Skrw  * Open the device. Make sure the partition info is as up-to-date as can be.
307df930be7Sderaadt  */
308df930be7Sderaadt int
3098d3a06f5Sdlg sdopen(dev_t dev, int flag, int fmt, struct proc *p)
310df930be7Sderaadt {
3116d4b3493Skrw 	struct scsi_link		*link;
31238ea674aSdlg 	struct sd_softc			*sc;
3132518ec81Skrw 	int				 error = 0, part, rawopen, unit;
314df930be7Sderaadt 
3152fb2e161Skrw 	unit = DISKUNIT(dev);
3162fb2e161Skrw 	part = DISKPART(dev);
31740e23e2fSkrw 
3182518ec81Skrw 	rawopen = (part == RAW_PART) && (fmt == S_IFCHR);
3192518ec81Skrw 
32038ea674aSdlg 	sc = sdlookup(unit);
32138ea674aSdlg 	if (sc == NULL)
32289ea875eSkrw 		return ENXIO;
323d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
32438ea674aSdlg 		device_unref(&sc->sc_dev);
32589ea875eSkrw 		return ENXIO;
326396a99aeSbluhm 	}
3276d4b3493Skrw 	link = sc->sc_link;
32839f5c4b5Sbluhm 
329dd2bb779Skrw 	SC_DEBUG(link, SDEV_DB1,
330dd2bb779Skrw 	    ("sdopen: dev=0x%x (unit %d (of %d), partition %d)\n", dev, unit,
331dd2bb779Skrw 	    sd_cd.cd_ndevs, part));
332dd2bb779Skrw 
3336d4b3493Skrw 	if (ISSET(flag, FWRITE) && ISSET(link->flags, SDEV_READONLY)) {
334427418b6Skrw 		device_unref(&sc->sc_dev);
33589ea875eSkrw 		return EACCES;
336427418b6Skrw 	}
33785bbddc9Sderaadt 	if ((error = disk_lock(&sc->sc_dk)) != 0) {
33838ea674aSdlg 		device_unref(&sc->sc_dev);
33989ea875eSkrw 		return error;
3401e7162a8Scsapuntz 	}
34137656c4cSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
34237656c4cSkrw 		error = ENXIO;
34337656c4cSkrw 		goto die;
34437656c4cSkrw 	}
345df930be7Sderaadt 
34638ea674aSdlg 	if (sc->sc_dk.dk_openmask != 0) {
347df930be7Sderaadt 		/*
348df930be7Sderaadt 		 * If any partition is open, but the disk has been invalidated,
3496853657aSkrw 		 * disallow further opens of non-raw partition.
350df930be7Sderaadt 		 */
35171ed4a82Skrw 		if (!ISSET(link->flags, SDEV_MEDIA_LOADED)) {
3522518ec81Skrw 			if (rawopen)
3536853657aSkrw 				goto out;
354df930be7Sderaadt 			error = EIO;
35540e23e2fSkrw 			goto bad;
356df930be7Sderaadt 		}
357df930be7Sderaadt 	} else {
3585b92815bSkrw 		/* Spin up non-UMASS devices ready or not. */
35971ed4a82Skrw 		if (!ISSET(link->flags, SDEV_UMASS))
3606d4b3493Skrw 			scsi_start(link, SSS_START, (rawopen ? SCSI_SILENT :
3615b92815bSkrw 			    0) | SCSI_IGNORE_ILLEGAL_REQUEST |
3625b92815bSkrw 			    SCSI_IGNORE_MEDIA_CHANGE);
3638d506501Skrw 
36489ea875eSkrw 		/*
36589ea875eSkrw 		 * Use sd_interpret_sense() for sense errors.
3668d506501Skrw 		 *
3678d506501Skrw 		 * But only after spinning the disk up! Just in case a broken
3688d506501Skrw 		 * device returns "Initialization command required." and causes
3698d506501Skrw 		 * a loop of scsi_start() calls.
3708d506501Skrw 		 */
371d35ed37dSkrw 		if (ISSET(sc->flags, SDF_DYING)) {
372eba6e6fbSbluhm 			error = ENXIO;
373eba6e6fbSbluhm 			goto die;
374eba6e6fbSbluhm 		}
37538a5deecSkrw 		SET(link->flags, SDEV_OPEN);
37640e23e2fSkrw 
37776a8859eSkrw 		/*
37876a8859eSkrw 		 * Try to prevent the unloading of a removable device while
37976a8859eSkrw 		 * it's open. But allow the open to proceed if the device can't
38076a8859eSkrw 		 * be locked in.
38176a8859eSkrw 		 */
3829dbfff47Skrw 		if (ISSET(link->flags, SDEV_REMOVABLE)) {
3836d4b3493Skrw 			scsi_prevent(link, PR_PREVENT, SCSI_SILENT |
38476a8859eSkrw 			    SCSI_IGNORE_ILLEGAL_REQUEST |
38576a8859eSkrw 			    SCSI_IGNORE_MEDIA_CHANGE);
3861ebc558bSdlg 		}
38776a8859eSkrw 
388df930be7Sderaadt 		/* Check that it is still responding and ok. */
389d35ed37dSkrw 		if (ISSET(sc->flags, SDF_DYING)) {
390eba6e6fbSbluhm 			error = ENXIO;
391eba6e6fbSbluhm 			goto die;
392eba6e6fbSbluhm 		}
3936d4b3493Skrw 		error = scsi_test_unit_ready(link,
3941ebc558bSdlg 		    TEST_READY_RETRIES, SCSI_SILENT |
39540e23e2fSkrw 		    SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE);
3966853657aSkrw 		if (error) {
3972518ec81Skrw 			if (rawopen) {
3986853657aSkrw 				error = 0;
3996853657aSkrw 				goto out;
4006853657aSkrw 			} else
40140e23e2fSkrw 				goto bad;
4026853657aSkrw 		}
403df930be7Sderaadt 
404df930be7Sderaadt 		/* Load the physical device parameters. */
405d35ed37dSkrw 		if (ISSET(sc->flags, SDF_DYING)) {
406eba6e6fbSbluhm 			error = ENXIO;
407eba6e6fbSbluhm 			goto die;
408eba6e6fbSbluhm 		}
40938a5deecSkrw 		SET(link->flags, SDEV_MEDIA_LOADED);
410340f3a7eSkrw 		if (sd_get_parms(sc, (rawopen ? SCSI_SILENT : 0)) == -1) {
411d35ed37dSkrw 			if (ISSET(sc->flags, SDF_DYING)) {
412eba6e6fbSbluhm 				error = ENXIO;
413eba6e6fbSbluhm 				goto die;
414eba6e6fbSbluhm 			}
4155b102313Skrw 			CLR(link->flags, SDEV_MEDIA_LOADED);
416df930be7Sderaadt 			error = ENXIO;
41740e23e2fSkrw 			goto bad;
418df930be7Sderaadt 		}
4196d4b3493Skrw 		SC_DEBUG(link, SDEV_DB3, ("Params loaded\n"));
420df930be7Sderaadt 
421df930be7Sderaadt 		/* Load the partition info if not already loaded. */
42201e31546Sbluhm 		error = sdgetdisklabel(dev, sc, sc->sc_dk.dk_label, 0);
42301e31546Sbluhm 		if (error == EIO || error == ENXIO)
424df591ed6Sderaadt 			goto bad;
4256d4b3493Skrw 		SC_DEBUG(link, SDEV_DB3, ("Disklabel loaded\n"));
426df930be7Sderaadt 	}
427df930be7Sderaadt 
428e1d64023Smatthew out:
429e1d64023Smatthew 	if ((error = disk_openpart(&sc->sc_dk, part, fmt, 1)) != 0)
430df930be7Sderaadt 		goto bad;
431df930be7Sderaadt 
4326d4b3493Skrw 	SC_DEBUG(link, SDEV_DB3, ("open complete\n"));
433df930be7Sderaadt 
43440e23e2fSkrw 	/* It's OK to fall through because dk_openmask is now non-zero. */
435df930be7Sderaadt bad:
43638ea674aSdlg 	if (sc->sc_dk.dk_openmask == 0) {
437d35ed37dSkrw 		if (ISSET(sc->flags, SDF_DYING)) {
438eba6e6fbSbluhm 			error = ENXIO;
439eba6e6fbSbluhm 			goto die;
440eba6e6fbSbluhm 		}
4419dbfff47Skrw 		if (ISSET(link->flags, SDEV_REMOVABLE))
4426d4b3493Skrw 			scsi_prevent(link, PR_ALLOW, SCSI_SILENT |
443dec5b8f4Skrw 			    SCSI_IGNORE_ILLEGAL_REQUEST |
444dec5b8f4Skrw 			    SCSI_IGNORE_MEDIA_CHANGE);
445d35ed37dSkrw 		if (ISSET(sc->flags, SDF_DYING)) {
446eba6e6fbSbluhm 			error = ENXIO;
447eba6e6fbSbluhm 			goto die;
448eba6e6fbSbluhm 		}
4495b102313Skrw 		CLR(link->flags, SDEV_OPEN | SDEV_MEDIA_LOADED);
450df930be7Sderaadt 	}
451df930be7Sderaadt 
452eba6e6fbSbluhm die:
45385bbddc9Sderaadt 	disk_unlock(&sc->sc_dk);
45438ea674aSdlg 	device_unref(&sc->sc_dev);
45589ea875eSkrw 	return error;
456df930be7Sderaadt }
457df930be7Sderaadt 
458df930be7Sderaadt /*
45940e23e2fSkrw  * Close the device. Only called if we are the last occurrence of an open
460df930be7Sderaadt  * device.  Convenient now but usually a pain.
461df930be7Sderaadt  */
462df930be7Sderaadt int
4638d3a06f5Sdlg sdclose(dev_t dev, int flag, int fmt, struct proc *p)
464df930be7Sderaadt {
4656d4b3493Skrw 	struct scsi_link		*link;
46638ea674aSdlg 	struct sd_softc			*sc;
4672fb2e161Skrw 	int				 part = DISKPART(dev);
468b306a45eSbluhm 	int				 error = 0;
469df930be7Sderaadt 
47038ea674aSdlg 	sc = sdlookup(DISKUNIT(dev));
47138ea674aSdlg 	if (sc == NULL)
47289ea875eSkrw 		return ENXIO;
473d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
47438ea674aSdlg 		device_unref(&sc->sc_dev);
47589ea875eSkrw 		return ENXIO;
476396a99aeSbluhm 	}
4776d4b3493Skrw 	link = sc->sc_link;
4781e7162a8Scsapuntz 
4795f30bcfcSderaadt 	disk_lock_nointr(&sc->sc_dk);
480df930be7Sderaadt 
481e1d64023Smatthew 	disk_closepart(&sc->sc_dk, part, fmt);
482df930be7Sderaadt 
483c3094a8aSkrw 	if ((ISSET(flag, FWRITE) || sc->sc_dk.dk_openmask == 0) &&
4849dbfff47Skrw 	    ISSET(sc->flags, SDF_DIRTY))
48538ea674aSdlg 		sd_flush(sc, 0);
486df930be7Sderaadt 
4873c102e39Ssf 	if (sc->sc_dk.dk_openmask == 0) {
488d35ed37dSkrw 		if (ISSET(sc->flags, SDF_DYING)) {
489b306a45eSbluhm 			error = ENXIO;
490b306a45eSbluhm 			goto die;
491b306a45eSbluhm 		}
4929dbfff47Skrw 		if (ISSET(link->flags, SDEV_REMOVABLE))
4936d4b3493Skrw 			scsi_prevent(link, PR_ALLOW,
49440e23e2fSkrw 			    SCSI_IGNORE_ILLEGAL_REQUEST |
4951ebc558bSdlg 			    SCSI_IGNORE_NOT_READY | SCSI_SILENT);
496d35ed37dSkrw 		if (ISSET(sc->flags, SDF_DYING)) {
497b306a45eSbluhm 			error = ENXIO;
498b306a45eSbluhm 			goto die;
499b306a45eSbluhm 		}
5003cc799f1Skrw 		CLR(link->flags, SDEV_OPEN | SDEV_MEDIA_LOADED);
501c03366deSdownsj 
5029dbfff47Skrw 		if (ISSET(link->flags, SDEV_EJECTING)) {
5036d4b3493Skrw 			scsi_start(link, SSS_STOP|SSS_LOEJ, 0);
504d35ed37dSkrw 			if (ISSET(sc->flags, SDF_DYING)) {
505b306a45eSbluhm 				error = ENXIO;
506b306a45eSbluhm 				goto die;
507b306a45eSbluhm 			}
5085b102313Skrw 			CLR(link->flags, SDEV_EJECTING);
509c03366deSdownsj 		}
510d9f31c94Skrw 
51156cb3956Sdlg 		scsi_xsh_del(&sc->sc_xsh);
512df930be7Sderaadt 	}
513df930be7Sderaadt 
514b306a45eSbluhm die:
51585bbddc9Sderaadt 	disk_unlock(&sc->sc_dk);
51638ea674aSdlg 	device_unref(&sc->sc_dev);
51789ea875eSkrw 	return error;
518df930be7Sderaadt }
519df930be7Sderaadt 
520df930be7Sderaadt /*
521df930be7Sderaadt  * Actually translate the requested transfer into one the physical driver
522df930be7Sderaadt  * can understand.  The transfer is described by a buf and will include
523df930be7Sderaadt  * only one physical transfer.
524df930be7Sderaadt  */
525df930be7Sderaadt void
5268d3a06f5Sdlg sdstrategy(struct buf *bp)
527df930be7Sderaadt {
5286d4b3493Skrw 	struct scsi_link		*link;
52938ea674aSdlg 	struct sd_softc			*sc;
530df930be7Sderaadt 	int				 s;
531df930be7Sderaadt 
53238ea674aSdlg 	sc = sdlookup(DISKUNIT(bp->b_dev));
53338ea674aSdlg 	if (sc == NULL) {
5341e7162a8Scsapuntz 		bp->b_error = ENXIO;
5351e7162a8Scsapuntz 		goto bad;
5361e7162a8Scsapuntz 	}
537d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
538396a99aeSbluhm 		bp->b_error = ENXIO;
539396a99aeSbluhm 		goto bad;
540396a99aeSbluhm 	}
5416d4b3493Skrw 	link = sc->sc_link;
5421e7162a8Scsapuntz 
5436d4b3493Skrw 	SC_DEBUG(link, SDEV_DB2, ("sdstrategy: %ld bytes @ blk %lld\n",
544bb4f4faeSkrw 	    bp->b_bcount, (long long)bp->b_blkno));
545df930be7Sderaadt 	/*
54689ea875eSkrw 	 * If the device has been made invalid, error out.
547df930be7Sderaadt 	 */
54871ed4a82Skrw 	if (!ISSET(link->flags, SDEV_MEDIA_LOADED)) {
5499dbfff47Skrw 		if (ISSET(link->flags, SDEV_OPEN))
550df930be7Sderaadt 			bp->b_error = EIO;
551aca6abd4Scsapuntz 		else
552aca6abd4Scsapuntz 			bp->b_error = ENODEV;
553df930be7Sderaadt 		goto bad;
554df930be7Sderaadt 	}
555df930be7Sderaadt 
556d1e5b14cSmatthew 	/* Validate the request. */
557d1e5b14cSmatthew 	if (bounds_check_with_label(bp, sc->sc_dk.dk_label) == -1)
558df930be7Sderaadt 		goto done;
559df930be7Sderaadt 
560c158b662Sthib 	/* Place it in the queue of disk activities for this disk. */
561c2235760Sdlg 	bufq_queue(&sc->sc_bufq, bp);
562df930be7Sderaadt 
563df930be7Sderaadt 	/*
564df930be7Sderaadt 	 * Tell the device to get going on the transfer if it's
565df930be7Sderaadt 	 * not doing anything, otherwise just wait for completion
566df930be7Sderaadt 	 */
56756cb3956Sdlg 	scsi_xsh_add(&sc->sc_xsh);
568df930be7Sderaadt 
56938ea674aSdlg 	device_unref(&sc->sc_dev);
570df930be7Sderaadt 	return;
571df930be7Sderaadt 
572df930be7Sderaadt bad:
5737e5f2394Skrw 	SET(bp->b_flags, B_ERROR);
574df930be7Sderaadt 	bp->b_resid = bp->b_bcount;
575d1e5b14cSmatthew done:
576bac96729Sart 	s = splbio();
577df930be7Sderaadt 	biodone(bp);
578bac96729Sart 	splx(s);
57938ea674aSdlg 	if (sc != NULL)
58038ea674aSdlg 		device_unref(&sc->sc_dev);
581df930be7Sderaadt }
582df930be7Sderaadt 
5834459c984Skrw int
5843da91bb4Skrw sd_cmd_rw6(struct scsi_generic *generic, int read, u_int64_t secno,
5853da91bb4Skrw     u_int32_t nsecs)
586864c175eSdlg {
5874459c984Skrw 	struct scsi_rw *cmd = (struct scsi_rw *)generic;
588864c175eSdlg 
589864c175eSdlg 	cmd->opcode = read ? READ_COMMAND : WRITE_COMMAND;
590006d5239Skrw 	_lto3b(secno, cmd->addr);
591006d5239Skrw 	cmd->length = nsecs;
592864c175eSdlg 
5934459c984Skrw 	return sizeof(*cmd);
594864c175eSdlg }
595864c175eSdlg 
5964459c984Skrw int
5973da91bb4Skrw sd_cmd_rw10(struct scsi_generic *generic, int read, u_int64_t secno,
5983da91bb4Skrw     u_int32_t nsecs)
599864c175eSdlg {
600eccd596dSkrw 	struct scsi_rw_10 *cmd = (struct scsi_rw_10 *)generic;
601864c175eSdlg 
602eccd596dSkrw 	cmd->opcode = read ? READ_10 : WRITE_10;
603006d5239Skrw 	_lto4b(secno, cmd->addr);
604006d5239Skrw 	_lto2b(nsecs, cmd->length);
605864c175eSdlg 
6064459c984Skrw 	return sizeof(*cmd);
607864c175eSdlg }
608864c175eSdlg 
6094459c984Skrw int
6103da91bb4Skrw sd_cmd_rw12(struct scsi_generic *generic, int read, u_int64_t secno,
6113da91bb4Skrw     u_int32_t nsecs)
612864c175eSdlg {
6134459c984Skrw 	struct scsi_rw_12 *cmd = (struct scsi_rw_12 *)generic;
614864c175eSdlg 
615864c175eSdlg 	cmd->opcode = read ? READ_12 : WRITE_12;
616006d5239Skrw 	_lto4b(secno, cmd->addr);
617006d5239Skrw 	_lto4b(nsecs, cmd->length);
618864c175eSdlg 
6194459c984Skrw 	return sizeof(*cmd);
620864c175eSdlg }
621864c175eSdlg 
6224459c984Skrw int
6233da91bb4Skrw sd_cmd_rw16(struct scsi_generic *generic, int read, u_int64_t secno,
6243da91bb4Skrw     u_int32_t nsecs)
625864c175eSdlg {
6264459c984Skrw 	struct scsi_rw_16 *cmd = (struct scsi_rw_16 *)generic;
627864c175eSdlg 
628864c175eSdlg 	cmd->opcode = read ? READ_16 : WRITE_16;
629006d5239Skrw 	_lto8b(secno, cmd->addr);
630006d5239Skrw 	_lto4b(nsecs, cmd->length);
631864c175eSdlg 
6324459c984Skrw 	return sizeof(*cmd);
633864c175eSdlg }
634864c175eSdlg 
635df930be7Sderaadt /*
636df930be7Sderaadt  * sdstart looks to see if there is a buf waiting for the device
637df930be7Sderaadt  * and that the device is not already busy. If both are true,
638df930be7Sderaadt  * It dequeues the buf and creates a scsi command to perform the
639df930be7Sderaadt  * transfer in the buf. The transfer request will call scsi_done
640df930be7Sderaadt  * on completion, which will in turn call this routine again
641df930be7Sderaadt  * so that the next queued transfer is performed.
642df930be7Sderaadt  * The bufs are queued by the strategy routine (sdstrategy)
643df930be7Sderaadt  *
644df930be7Sderaadt  * This routine is also called after other non-queued requests
645df930be7Sderaadt  * have been made of the scsi driver, to ensure that the queue
646df930be7Sderaadt  * continues to be drained.
647df930be7Sderaadt  */
648df930be7Sderaadt void
64956cb3956Sdlg sdstart(struct scsi_xfer *xs)
650df930be7Sderaadt {
65156cb3956Sdlg 	struct scsi_link		*link = xs->sc_link;
65256cb3956Sdlg 	struct sd_softc			*sc = link->device_softc;
653864c175eSdlg 	struct buf			*bp;
654df930be7Sderaadt 	struct partition		*p;
65589ea875eSkrw 	u_int64_t			 secno;
65681e09b69Skrw 	u_int32_t			 nsecs;
65781e09b69Skrw 	int				 read;
658df930be7Sderaadt 
659d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
66056cb3956Sdlg 		scsi_xs_put(xs);
661133f9f6dSbeck 		return;
662133f9f6dSbeck 	}
66371ed4a82Skrw 	if (!ISSET(link->flags, SDEV_MEDIA_LOADED)) {
664c2235760Sdlg 		bufq_drain(&sc->sc_bufq);
66556cb3956Sdlg 		scsi_xs_put(xs);
66656cb3956Sdlg 		return;
667df930be7Sderaadt 	}
668df930be7Sderaadt 
669c2235760Sdlg 	bp = bufq_dequeue(&sc->sc_bufq);
67056cb3956Sdlg 	if (bp == NULL) {
67156cb3956Sdlg 		scsi_xs_put(xs);
67256cb3956Sdlg 		return;
673864c175eSdlg 	}
67457a0c0c6Skrw 	read = ISSET(bp->b_flags, B_READ);
675864c175eSdlg 
676257244d2Skrw 	SET(xs->flags, (read ? SCSI_DATA_IN : SCSI_DATA_OUT));
677257244d2Skrw 	xs->timeout = 60000;
678257244d2Skrw 	xs->data = bp->b_data;
679257244d2Skrw 	xs->datalen = bp->b_bcount;
680257244d2Skrw 	xs->done = sd_buf_done;
681257244d2Skrw 	xs->cookie = bp;
682257244d2Skrw 	xs->bp = bp;
6838d28cd76Skrw 
684864c175eSdlg 	p = &sc->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
685257244d2Skrw 	secno = DL_GETPOFFSET(p) + DL_BLKTOSEC(sc->sc_dk.dk_label, bp->b_blkno);
686006d5239Skrw 	nsecs = howmany(bp->b_bcount, sc->sc_dk.dk_label->d_secsize);
687df930be7Sderaadt 
688fc548c49Skrw 	if (!ISSET(link->flags, SDEV_ATAPI | SDEV_UMASS) &&
689b9499433Skrw 	    (SID_ANSII_REV(&link->inqdata) < SCSI_REV_2) &&
690006d5239Skrw 	    ((secno & 0x1fffff) == secno) &&
691006d5239Skrw 	    ((nsecs & 0xff) == nsecs))
692664c6166Skrw 		xs->cmdlen = sd_cmd_rw6(&xs->cmd, read, secno, nsecs);
6933e16148dSkrw 
6943e16148dSkrw 	else if (sc->params.disksize > UINT32_MAX)
695664c6166Skrw 		xs->cmdlen = sd_cmd_rw16(&xs->cmd, read, secno, nsecs);
696864c175eSdlg 
6973e16148dSkrw 	else if (nsecs <= UINT16_MAX)
698664c6166Skrw 		xs->cmdlen = sd_cmd_rw10(&xs->cmd, read, secno, nsecs);
6993e16148dSkrw 
7003e16148dSkrw 	else
701664c6166Skrw 		xs->cmdlen = sd_cmd_rw12(&xs->cmd, read, secno, nsecs);
7023e16148dSkrw 
703864c175eSdlg 	disk_busy(&sc->sc_dk);
70456cb3956Sdlg 	if (!read)
70538a5deecSkrw 		SET(sc->flags, SDF_DIRTY);
706864c175eSdlg 	scsi_xs_exec(xs);
70756cb3956Sdlg 
70889ea875eSkrw 	/* Move onto the next io. */
70966dcdfe8Skrw 	if (bufq_peek(&sc->sc_bufq))
71056cb3956Sdlg 		scsi_xsh_add(&sc->sc_xsh);
711864c175eSdlg }
712dc94813fSdlg 
713864c175eSdlg void
714864c175eSdlg sd_buf_done(struct scsi_xfer *xs)
715864c175eSdlg {
716864c175eSdlg 	struct sd_softc			*sc = xs->sc_link->device_softc;
717864c175eSdlg 	struct buf			*bp = xs->cookie;
7187666de72Skrw 	int				 error, s;
719864c175eSdlg 
720864c175eSdlg 	switch (xs->error) {
721864c175eSdlg 	case XS_NOERROR:
722864c175eSdlg 		bp->b_error = 0;
7237e5f2394Skrw 		CLR(bp->b_flags, B_ERROR);
724864c175eSdlg 		bp->b_resid = xs->resid;
725dc94813fSdlg 		break;
726864c175eSdlg 
727519eb678Sdlg 	case XS_SENSE:
728519eb678Sdlg 	case XS_SHORTSENSE:
729f6e3733cSkrw 		SC_DEBUG_SENSE(xs);
73077877975Skrw 		error = sd_interpret_sense(xs);
7317666de72Skrw 		if (error == 0) {
7327666de72Skrw 			bp->b_error = 0;
7337e5f2394Skrw 			CLR(bp->b_flags, B_ERROR);
7347666de72Skrw 			bp->b_resid = xs->resid;
7357666de72Skrw 			break;
7367666de72Skrw 		}
7370d5fb99dSkrw 		if (error != ERESTART) {
7380d5fb99dSkrw 			bp->b_error = error;
7397e5f2394Skrw 			SET(bp->b_flags, B_ERROR);
740519eb678Sdlg 			xs->retries = 0;
7410d5fb99dSkrw 		}
7422fb0de7bSkrw 		goto retry;
743519eb678Sdlg 
744519eb678Sdlg 	case XS_BUSY:
7452fb0de7bSkrw 		if (xs->retries) {
7462fb0de7bSkrw 			if (scsi_delay(xs, 1) != ERESTART)
7472fb0de7bSkrw 				xs->retries = 0;
7482fb0de7bSkrw 		}
7492fb0de7bSkrw 		goto retry;
7502fb0de7bSkrw 
751519eb678Sdlg 	case XS_TIMEOUT:
7522fb0de7bSkrw retry:
753519eb678Sdlg 		if (xs->retries--) {
754519eb678Sdlg 			scsi_xs_exec(xs);
755519eb678Sdlg 			return;
756519eb678Sdlg 		}
757519eb678Sdlg 		/* FALLTHROUGH */
7582fb0de7bSkrw 
759dc94813fSdlg 	default:
7600d5fb99dSkrw 		if (bp->b_error == 0)
761864c175eSdlg 			bp->b_error = EIO;
7627e5f2394Skrw 		SET(bp->b_flags, B_ERROR);
763864c175eSdlg 		bp->b_resid = bp->b_bcount;
764dc94813fSdlg 		break;
765dc94813fSdlg 	}
766df930be7Sderaadt 
767d40269afSderaadt 	disk_unbusy(&sc->sc_dk, bp->b_bcount - xs->resid, bp->b_blkno,
7689e07d8c2Sdlg 	    bp->b_flags & B_READ);
7699e07d8c2Sdlg 
770ed6e1f72Sdlg 	s = splbio();
771864c175eSdlg 	biodone(bp);
772ed6e1f72Sdlg 	splx(s);
773864c175eSdlg 	scsi_xs_put(xs);
7743dbef52bSderaadt }
7753dbef52bSderaadt 
7766899be97Sderaadt void
7778d3a06f5Sdlg sdminphys(struct buf *bp)
7786899be97Sderaadt {
7796d4b3493Skrw 	struct scsi_link		*link;
78038ea674aSdlg 	struct sd_softc			*sc;
7816899be97Sderaadt 	long				 max;
7826899be97Sderaadt 
78338ea674aSdlg 	sc = sdlookup(DISKUNIT(bp->b_dev));
78438ea674aSdlg 	if (sc == NULL)
7851e7162a8Scsapuntz 		return;  /* XXX - right way to fail this? */
786d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
78739f5c4b5Sbluhm 		device_unref(&sc->sc_dev);
78839f5c4b5Sbluhm 		return;
78939f5c4b5Sbluhm 	}
7906d4b3493Skrw 	link = sc->sc_link;
7911e7162a8Scsapuntz 
7926899be97Sderaadt 	/*
7936899be97Sderaadt 	 * If the device is ancient, we want to make sure that
7946899be97Sderaadt 	 * the transfer fits into a 6-byte cdb.
7956899be97Sderaadt 	 *
7966899be97Sderaadt 	 * XXX Note that the SCSI-I spec says that 256-block transfers
7976899be97Sderaadt 	 * are allowed in a 6-byte read/write, and are specified
7989244ce73Smiod 	 * by setting the "length" to 0.  However, we're conservative
7996899be97Sderaadt 	 * here, allowing only 255-block transfers in case an
8006899be97Sderaadt 	 * ancient device gets confused by length == 0.  A length of 0
8016899be97Sderaadt 	 * in a 10-byte read/write actually means 0 blocks.
8026899be97Sderaadt 	 */
803b9499433Skrw 	if (!ISSET(link->flags, SDEV_ATAPI | SDEV_UMASS) &&
804b9499433Skrw 	    SID_ANSII_REV(&link->inqdata) < SCSI_REV_2) {
80538ea674aSdlg 		max = sc->sc_dk.dk_label->d_secsize * 0xff;
8066899be97Sderaadt 
8076899be97Sderaadt 		if (bp->b_bcount > max)
8086899be97Sderaadt 			bp->b_bcount = max;
8096899be97Sderaadt 	}
8106899be97Sderaadt 
81167c3123bSkrw 	if (link->bus->sb_adapter->dev_minphys != NULL)
81267c3123bSkrw 		(*link->bus->sb_adapter->dev_minphys)(bp, link);
8137f649021Skrw 	else
81421ceeee0Skrw 		minphys(bp);
8151e7162a8Scsapuntz 
81638ea674aSdlg 	device_unref(&sc->sc_dev);
8176899be97Sderaadt }
8186899be97Sderaadt 
819df930be7Sderaadt int
8208d3a06f5Sdlg sdread(dev_t dev, struct uio *uio, int ioflag)
821df930be7Sderaadt {
82289ea875eSkrw 	return physio(sdstrategy, dev, B_READ, sdminphys, uio);
823df930be7Sderaadt }
824df930be7Sderaadt 
825df930be7Sderaadt int
8268d3a06f5Sdlg sdwrite(dev_t dev, struct uio *uio, int ioflag)
827df930be7Sderaadt {
82889ea875eSkrw 	return physio(sdstrategy, dev, B_WRITE, sdminphys, uio);
829df930be7Sderaadt }
830df930be7Sderaadt 
831df930be7Sderaadt /*
83289ea875eSkrw  * Perform special action on behalf of the user. Knows about the internals of
83389ea875eSkrw  * this device
834df930be7Sderaadt  */
835df930be7Sderaadt int
8368d3a06f5Sdlg sdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
837df930be7Sderaadt {
8386d4b3493Skrw 	struct scsi_link		*link;
83938ea674aSdlg 	struct sd_softc			*sc;
840ce578d69Sderaadt 	struct disklabel		*lp;
8411e7162a8Scsapuntz 	int				 error = 0;
8422fb2e161Skrw 	int				 part = DISKPART(dev);
843df930be7Sderaadt 
84438ea674aSdlg 	sc = sdlookup(DISKUNIT(dev));
84538ea674aSdlg 	if (sc == NULL)
84689ea875eSkrw 		return ENXIO;
847d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
84838ea674aSdlg 		device_unref(&sc->sc_dev);
84989ea875eSkrw 		return ENXIO;
850396a99aeSbluhm 	}
8516d4b3493Skrw 	link = sc->sc_link;
8521e7162a8Scsapuntz 
8536d4b3493Skrw 	SC_DEBUG(link, SDEV_DB2, ("sdioctl 0x%lx\n", cmd));
854df930be7Sderaadt 
855df930be7Sderaadt 	/*
85689ea875eSkrw 	 * If the device is not valid, abandon ship.
857df930be7Sderaadt 	 */
85871ed4a82Skrw 	if (!ISSET(link->flags, SDEV_MEDIA_LOADED)) {
859aca6abd4Scsapuntz 		switch (cmd) {
860aca6abd4Scsapuntz 		case DIOCLOCK:
861aca6abd4Scsapuntz 		case DIOCEJECT:
862aca6abd4Scsapuntz 		case SCIOCIDENTIFY:
863aca6abd4Scsapuntz 		case SCIOCCOMMAND:
864aca6abd4Scsapuntz 		case SCIOCDEBUG:
865aca6abd4Scsapuntz 			if (part == RAW_PART)
866aca6abd4Scsapuntz 				break;
867aca6abd4Scsapuntz 		/* FALLTHROUGH */
868aca6abd4Scsapuntz 		default:
86971ed4a82Skrw 			if (!ISSET(link->flags, SDEV_OPEN)) {
8701e7162a8Scsapuntz 				error = ENODEV;
8711e7162a8Scsapuntz 				goto exit;
8721e7162a8Scsapuntz 			} else {
8731e7162a8Scsapuntz 				error = EIO;
8741e7162a8Scsapuntz 				goto exit;
8751e7162a8Scsapuntz 			}
876aca6abd4Scsapuntz 		}
877aca6abd4Scsapuntz 	}
878df930be7Sderaadt 
879df930be7Sderaadt 	switch (cmd) {
8801dfe2d86Sderaadt 	case DIOCRLDINFO:
881ce578d69Sderaadt 		lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
88238ea674aSdlg 		sdgetdisklabel(dev, sc, lp, 0);
8839e4f07dcStedu 		memcpy(sc->sc_dk.dk_label, lp, sizeof(*lp));
8849e4f07dcStedu 		free(lp, M_TEMP, sizeof(*lp));
8851e7162a8Scsapuntz 		goto exit;
8865645b8b9Skrw 
887c326f117Sderaadt 	case DIOCGPDINFO:
88838ea674aSdlg 		sdgetdisklabel(dev, sc, (struct disklabel *)addr, 1);
8891e7162a8Scsapuntz 		goto exit;
89075ff5a4cSmillert 
891df930be7Sderaadt 	case DIOCGDINFO:
89238ea674aSdlg 		*(struct disklabel *)addr = *(sc->sc_dk.dk_label);
8931e7162a8Scsapuntz 		goto exit;
894df930be7Sderaadt 
895df930be7Sderaadt 	case DIOCGPART:
89638ea674aSdlg 		((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
897df930be7Sderaadt 		((struct partinfo *)addr)->part =
89838ea674aSdlg 		    &sc->sc_dk.dk_label->d_partitions[DISKPART(dev)];
8991e7162a8Scsapuntz 		goto exit;
900df930be7Sderaadt 
901df930be7Sderaadt 	case DIOCWDINFO:
902df930be7Sderaadt 	case DIOCSDINFO:
90371ed4a82Skrw 		if (!ISSET(flag, FWRITE)) {
9041e7162a8Scsapuntz 			error = EBADF;
9051e7162a8Scsapuntz 			goto exit;
9061e7162a8Scsapuntz 		}
907df930be7Sderaadt 
90885bbddc9Sderaadt 		if ((error = disk_lock(&sc->sc_dk)) != 0)
9091e7162a8Scsapuntz 			goto exit;
910df930be7Sderaadt 
91138ea674aSdlg 		error = setdisklabel(sc->sc_dk.dk_label,
912a2f17f2eSmatthew 		    (struct disklabel *)addr, sc->sc_dk.dk_openmask);
913df930be7Sderaadt 		if (error == 0) {
914df930be7Sderaadt 			if (cmd == DIOCWDINFO)
9152fb2e161Skrw 				error = writedisklabel(DISKLABELDEV(dev),
91638ea674aSdlg 				    sdstrategy, sc->sc_dk.dk_label);
917df930be7Sderaadt 		}
918df930be7Sderaadt 
91985bbddc9Sderaadt 		disk_unlock(&sc->sc_dk);
9201e7162a8Scsapuntz 		goto exit;
921df930be7Sderaadt 
922ddd0d9dfSbriggs 	case DIOCLOCK:
9236d4b3493Skrw 		error = scsi_prevent(link,
924ddd0d9dfSbriggs 		    (*(int *)addr) ? PR_PREVENT : PR_ALLOW, 0);
9251e7162a8Scsapuntz 		goto exit;
926ddd0d9dfSbriggs 
927c03366deSdownsj 	case MTIOCTOP:
9281e7162a8Scsapuntz 		if (((struct mtop *)addr)->mt_op != MTOFFL) {
9291e7162a8Scsapuntz 			error = EIO;
9301e7162a8Scsapuntz 			goto exit;
9311e7162a8Scsapuntz 		}
932c03366deSdownsj 		/* FALLTHROUGH */
933ddd0d9dfSbriggs 	case DIOCEJECT:
93471ed4a82Skrw 		if (!ISSET(link->flags, SDEV_REMOVABLE)) {
9351e7162a8Scsapuntz 			error = ENOTTY;
9361e7162a8Scsapuntz 			goto exit;
9371e7162a8Scsapuntz 		}
93838a5deecSkrw 		SET(link->flags, SDEV_EJECTING);
9391e7162a8Scsapuntz 		goto exit;
940ddd0d9dfSbriggs 
94129a8ba45Sdlg 	case DIOCINQ:
9426d4b3493Skrw 		error = scsi_do_ioctl(link, cmd, addr, flag);
94329a8ba45Sdlg 		if (error == ENOTTY)
94438ea674aSdlg 			error = sd_ioctl_inquiry(sc,
94529a8ba45Sdlg 			    (struct dk_inquiry *)addr);
94629a8ba45Sdlg 		goto exit;
94729a8ba45Sdlg 
94862ef6040Sdlg 	case DIOCSCACHE:
94963cdeb7cSdlg 		if (!ISSET(flag, FWRITE)) {
95063cdeb7cSdlg 			error = EBADF;
95163cdeb7cSdlg 			goto exit;
95263cdeb7cSdlg 		}
95363cdeb7cSdlg 		/* FALLTHROUGH */
95463cdeb7cSdlg 	case DIOCGCACHE:
95563cdeb7cSdlg 		error = sd_ioctl_cache(sc, cmd, (struct dk_cache *)addr);
95662ef6040Sdlg 		goto exit;
95762ef6040Sdlg 
958805cc48eSsf 	case DIOCCACHESYNC:
959805cc48eSsf 		if (!ISSET(flag, FWRITE)) {
960805cc48eSsf 			error = EBADF;
961805cc48eSsf 			goto exit;
962805cc48eSsf 		}
9639dbfff47Skrw 		if (ISSET(sc->flags, SDF_DIRTY) || *(int *)addr != 0)
964805cc48eSsf 			error = sd_flush(sc, 0);
965805cc48eSsf 		goto exit;
966805cc48eSsf 
967df930be7Sderaadt 	default:
9681e7162a8Scsapuntz 		if (part != RAW_PART) {
9691e7162a8Scsapuntz 			error = ENOTTY;
9701e7162a8Scsapuntz 			goto exit;
9711e7162a8Scsapuntz 		}
9726d4b3493Skrw 		error = scsi_do_ioctl(link, cmd, addr, flag);
973df930be7Sderaadt 	}
974df930be7Sderaadt 
9751e7162a8Scsapuntz  exit:
97638ea674aSdlg 	device_unref(&sc->sc_dev);
97789ea875eSkrw 	return error;
978df930be7Sderaadt }
979df930be7Sderaadt 
98029a8ba45Sdlg int
98138ea674aSdlg sd_ioctl_inquiry(struct sd_softc *sc, struct dk_inquiry *di)
98229a8ba45Sdlg {
9836d4b3493Skrw 	struct scsi_link		*link;
9845c0b007dSderaadt 	struct scsi_vpd_serial		*vpd;
9855c0b007dSderaadt 
9865c0b007dSderaadt 	vpd = dma_alloc(sizeof(*vpd), PR_WAITOK | PR_ZERO);
98729a8ba45Sdlg 
988d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
9890e5ef83fSbluhm 		dma_free(vpd, sizeof(*vpd));
99089ea875eSkrw 		return ENXIO;
9910e5ef83fSbluhm 	}
9926d4b3493Skrw 	link = sc->sc_link;
993008aca0dSbluhm 
99429a8ba45Sdlg 	bzero(di, sizeof(struct dk_inquiry));
9956d4b3493Skrw 	scsi_strvis(di->vendor, link->inqdata.vendor,
9966d4b3493Skrw 	    sizeof(link->inqdata.vendor));
9976d4b3493Skrw 	scsi_strvis(di->product, link->inqdata.product,
9986d4b3493Skrw 	    sizeof(link->inqdata.product));
9996d4b3493Skrw 	scsi_strvis(di->revision, link->inqdata.revision,
10006d4b3493Skrw 	    sizeof(link->inqdata.revision));
100129a8ba45Sdlg 
100229a8ba45Sdlg 	/* the serial vpd page is optional */
10036d4b3493Skrw 	if (scsi_inquire_vpd(link, vpd, sizeof(*vpd), SI_PG_SERIAL, 0) == 0)
10045c0b007dSderaadt 		scsi_strvis(di->serial, vpd->serial, sizeof(vpd->serial));
100564c01044Sdlg 	else
10065c0b007dSderaadt 		strlcpy(di->serial, "(unknown)", sizeof(vpd->serial));
100729a8ba45Sdlg 
10085c0b007dSderaadt 	dma_free(vpd, sizeof(*vpd));
100989ea875eSkrw 	return 0;
101029a8ba45Sdlg }
101129a8ba45Sdlg 
101262ef6040Sdlg int
101362ef6040Sdlg sd_ioctl_cache(struct sd_softc *sc, long cmd, struct dk_cache *dkc)
101462ef6040Sdlg {
10156d4b3493Skrw 	struct scsi_link		*link;
101662ef6040Sdlg 	union scsi_mode_sense_buf	*buf;
101762ef6040Sdlg 	struct page_caching_mode	*mode = NULL;
101895432a5dSkrw 	u_int				 wrcache, rdcache;
101989ea875eSkrw 	int				 big, rv;
102062ef6040Sdlg 
1021d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING))
102289ea875eSkrw 		return ENXIO;
10236d4b3493Skrw 	link = sc->sc_link;
1024008aca0dSbluhm 
10256d4b3493Skrw 	if (ISSET(link->flags, SDEV_UMASS))
102689ea875eSkrw 		return EOPNOTSUPP;
1027cece5972Skrw 
102889ea875eSkrw 	/* See if the adapter has special handling. */
10296d4b3493Skrw 	rv = scsi_do_ioctl(link, cmd, (caddr_t)dkc, 0);
10305c0b007dSderaadt 	if (rv != ENOTTY)
103189ea875eSkrw 		return rv;
103263cdeb7cSdlg 
10335c0b007dSderaadt 	buf = dma_alloc(sizeof(*buf), PR_WAITOK);
103462ef6040Sdlg 	if (buf == NULL)
103589ea875eSkrw 		return ENOMEM;
103662ef6040Sdlg 
1037d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
10380e5ef83fSbluhm 		rv = ENXIO;
10390e5ef83fSbluhm 		goto done;
10400e5ef83fSbluhm 	}
104185f9847cSkrw 	rv = scsi_do_mode_sense(link, PAGE_CACHING_MODE, buf, (void **)&mode,
104263cdeb7cSdlg 	    sizeof(*mode) - 4, scsi_autoconf | SCSI_SILENT, &big);
1043724ba685Skrw 	if (rv == 0 && mode == NULL)
1044724ba685Skrw 		rv = EIO;
104562ef6040Sdlg 	if (rv != 0)
104662ef6040Sdlg 		goto done;
104762ef6040Sdlg 
104895432a5dSkrw 	wrcache = (ISSET(mode->flags, PG_CACHE_FL_WCE) ? 1 : 0);
104995432a5dSkrw 	rdcache = (ISSET(mode->flags, PG_CACHE_FL_RCD) ? 0 : 1);
105095432a5dSkrw 
105162ef6040Sdlg 	switch (cmd) {
105262ef6040Sdlg 	case DIOCGCACHE:
105395432a5dSkrw 		dkc->wrcache = wrcache;
105495432a5dSkrw 		dkc->rdcache = rdcache;
105562ef6040Sdlg 		break;
105662ef6040Sdlg 
105762ef6040Sdlg 	case DIOCSCACHE:
105895432a5dSkrw 		if (dkc->wrcache == wrcache && dkc->rdcache == rdcache)
105995432a5dSkrw 			break;
106095432a5dSkrw 
106162ef6040Sdlg 		if (dkc->wrcache)
106262ef6040Sdlg 			SET(mode->flags, PG_CACHE_FL_WCE);
106362ef6040Sdlg 		else
106462ef6040Sdlg 			CLR(mode->flags, PG_CACHE_FL_WCE);
106562ef6040Sdlg 
106662ef6040Sdlg 		if (dkc->rdcache)
106762ef6040Sdlg 			CLR(mode->flags, PG_CACHE_FL_RCD);
106862ef6040Sdlg 		else
106962ef6040Sdlg 			SET(mode->flags, PG_CACHE_FL_RCD);
107062ef6040Sdlg 
1071d35ed37dSkrw 		if (ISSET(sc->flags, SDF_DYING)) {
10720e5ef83fSbluhm 			rv = ENXIO;
10730e5ef83fSbluhm 			goto done;
10740e5ef83fSbluhm 		}
107562ef6040Sdlg 		if (big) {
10766d4b3493Skrw 			rv = scsi_mode_select_big(link, SMS_PF,
107763cdeb7cSdlg 			    &buf->hdr_big, scsi_autoconf | SCSI_SILENT, 20000);
107862ef6040Sdlg 		} else {
10796d4b3493Skrw 			rv = scsi_mode_select(link, SMS_PF,
108063cdeb7cSdlg 			    &buf->hdr, scsi_autoconf | SCSI_SILENT, 20000);
108162ef6040Sdlg 		}
108262ef6040Sdlg 		break;
108362ef6040Sdlg 	}
108462ef6040Sdlg 
108562ef6040Sdlg done:
10865c0b007dSderaadt 	dma_free(buf, sizeof(*buf));
108789ea875eSkrw 	return rv;
108862ef6040Sdlg }
108962ef6040Sdlg 
1090df930be7Sderaadt /*
109189ea875eSkrw  * Load the label information on the named device.
1092df930be7Sderaadt  */
1093df591ed6Sderaadt int
109438ea674aSdlg sdgetdisklabel(dev_t dev, struct sd_softc *sc, struct disklabel *lp,
1095c326f117Sderaadt     int spoofonly)
1096df930be7Sderaadt {
1097df591ed6Sderaadt 	char				 packname[sizeof(lp->d_packname) + 1];
1098b5a79f65Skrw 	char				 product[17], vendor[9];
109989ea875eSkrw 	struct scsi_link		*link;
110089ea875eSkrw 	size_t				 len;
1101df930be7Sderaadt 
1102d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING))
110389ea875eSkrw 		return ENXIO;
11046d4b3493Skrw 	link = sc->sc_link;
1105008aca0dSbluhm 
1106df930be7Sderaadt 	bzero(lp, sizeof(struct disklabel));
1107df930be7Sderaadt 
1108006d5239Skrw 	lp->d_secsize = sc->params.secsize;
110938ea674aSdlg 	lp->d_ntracks = sc->params.heads;
111038ea674aSdlg 	lp->d_nsectors = sc->params.sectors;
111138ea674aSdlg 	lp->d_ncylinders = sc->params.cyls;
1112df930be7Sderaadt 	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
1113df930be7Sderaadt 	if (lp->d_secpercyl == 0) {
1114df930be7Sderaadt 		lp->d_secpercyl = 100;
111589ea875eSkrw 		/* As long as it's not 0 - readdisklabel divides by it. */
1116df930be7Sderaadt 	}
1117df930be7Sderaadt 
1118176eada0Skrw 	if (ISSET(link->flags, SDEV_UFI)) {
1119176eada0Skrw 		lp->d_type = DTYPE_FLOPPY;
1120176eada0Skrw 		strncpy(lp->d_typename, "USB floppy disk",
1121176eada0Skrw 		    sizeof(lp->d_typename));
1122176eada0Skrw 	} else {
1123df930be7Sderaadt 		lp->d_type = DTYPE_SCSI;
11246d4b3493Skrw 		if ((link->inqdata.device & SID_TYPE) == T_OPTICAL)
11254ec8a6d9Smillert 			strncpy(lp->d_typename, "SCSI optical",
1126806a74b9Skrw 			    sizeof(lp->d_typename));
11274ec8a6d9Smillert 		else
11284ec8a6d9Smillert 			strncpy(lp->d_typename, "SCSI disk",
1129806a74b9Skrw 			    sizeof(lp->d_typename));
1130176eada0Skrw 	}
113154eb5b4eSmillert 
113237ab02fdSkrw 	/*
113337ab02fdSkrw 	 * Try to fit '<vendor> <product>' into d_packname. If that doesn't fit
113437ab02fdSkrw 	 * then leave out '<vendor> ' and use only as much of '<product>' as
113537ab02fdSkrw 	 * does fit.
113637ab02fdSkrw 	 */
11376d4b3493Skrw 	viscpy(vendor, link->inqdata.vendor, 8);
11386d4b3493Skrw 	viscpy(product, link->inqdata.product, 16);
1139b5a79f65Skrw 	len = snprintf(packname, sizeof(packname), "%s %s", vendor, product);
114037ab02fdSkrw 	if (len > sizeof(lp->d_packname)) {
1141b5a79f65Skrw 		strlcpy(packname, product, sizeof(packname));
114237ab02fdSkrw 		len = strlen(packname);
114337ab02fdSkrw 	}
114437ab02fdSkrw 	/*
114537ab02fdSkrw 	 * It is safe to use len as the count of characters to copy because
114637ab02fdSkrw 	 * packname is sizeof(lp->d_packname)+1, the string in packname is
114737ab02fdSkrw 	 * always null terminated and len does not count the terminating null.
114837ab02fdSkrw 	 * d_packname is not a null terminated string.
114937ab02fdSkrw 	 */
11509e4f07dcStedu 	memcpy(lp->d_packname, packname, len);
115154eb5b4eSmillert 
115238ea674aSdlg 	DL_SETDSIZE(lp, sc->params.disksize);
115322230921Sderaadt 	lp->d_version = 1;
1154df930be7Sderaadt 
1155df930be7Sderaadt 	lp->d_magic = DISKMAGIC;
1156df930be7Sderaadt 	lp->d_magic2 = DISKMAGIC;
1157df930be7Sderaadt 	lp->d_checksum = dkcksum(lp);
1158df930be7Sderaadt 
1159df930be7Sderaadt 	/*
116089ea875eSkrw 	 * Call the generic disklabel extraction routine.
1161df930be7Sderaadt 	 */
1162df591ed6Sderaadt 	return readdisklabel(DISKLABELDEV(dev), sdstrategy, lp, spoofonly);
1163df930be7Sderaadt }
1164df930be7Sderaadt 
11653855e719Skettenis 
1166df930be7Sderaadt /*
116789ea875eSkrw  * Check Errors.
1168df930be7Sderaadt  */
1169df930be7Sderaadt int
11708d3a06f5Sdlg sd_interpret_sense(struct scsi_xfer *xs)
1171df930be7Sderaadt {
1172aca6abd4Scsapuntz 	struct scsi_sense_data		*sense = &xs->sense;
1173008aca0dSbluhm 	struct scsi_link		*link = xs->sc_link;
11744e039a66Skrw 	int				 retval;
117589ea875eSkrw 	u_int8_t			 serr = sense->error_code & SSD_ERRCODE;
1176df930be7Sderaadt 
1177df930be7Sderaadt 	/*
11784e039a66Skrw 	 * Let the generic code handle everything except a few categories of
11794e039a66Skrw 	 * LUN not ready errors on open devices.
1180df930be7Sderaadt 	 */
118171ed4a82Skrw 	if ((!ISSET(link->flags, SDEV_OPEN)) ||
11821d951d12Sbeck 	    (serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED) ||
11834e039a66Skrw 	    ((sense->flags & SSD_KEY) != SKEY_NOT_READY) ||
11840d8e1065Sbeck 	    (sense->extra_len < 6))
118589ea875eSkrw 		return scsi_interpret_sense(xs);
1186df930be7Sderaadt 
11879dbfff47Skrw 	if (ISSET(xs->flags, SCSI_IGNORE_NOT_READY))
118889ea875eSkrw 		return 0;
1189cfe99e7eSkettenis 
11900d8e1065Sbeck 	switch (ASC_ASCQ(sense)) {
11910d8e1065Sbeck 	case SENSE_NOT_READY_BECOMING_READY:
1192008aca0dSbluhm 		SC_DEBUG(link, SDEV_DB1, ("becoming ready.\n"));
119357bd680cSkrw 		retval = scsi_delay(xs, 5);
11944e039a66Skrw 		break;
119546530cadSkrw 
11960d8e1065Sbeck 	case SENSE_NOT_READY_INIT_REQUIRED:
1197008aca0dSbluhm 		SC_DEBUG(link, SDEV_DB1, ("spinning up\n"));
1198008aca0dSbluhm 		retval = scsi_start(link, SSS_START,
11998593d9daSkrw 		    SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_NOSLEEP);
120046530cadSkrw 		if (retval == 0)
120189677328Skrw 			retval = ERESTART;
120254ea3f94Skrw 		else if (retval == ENOMEM)
120354ea3f94Skrw 			/* Can't issue the command. Fall back on a delay. */
120454ea3f94Skrw 			retval = scsi_delay(xs, 5);
120546530cadSkrw 		else
1206008aca0dSbluhm 			SC_DEBUG(link, SDEV_DB1, ("spin up failed (%#x)\n",
120746530cadSkrw 			    retval));
12084e039a66Skrw 		break;
120946530cadSkrw 
12104e039a66Skrw 	default:
121177877975Skrw 		retval = scsi_interpret_sense(xs);
12124e039a66Skrw 		break;
1213aca6abd4Scsapuntz 	}
12144e039a66Skrw 
121589ea875eSkrw 	return retval;
1216df930be7Sderaadt }
1217df930be7Sderaadt 
12181abdbfdeSderaadt daddr_t
12198d3a06f5Sdlg sdsize(dev_t dev)
1220df930be7Sderaadt {
12218d28cd76Skrw 	struct disklabel		*lp;
122238ea674aSdlg 	struct sd_softc			*sc;
12238d28cd76Skrw 	daddr_t				 size;
122489ea875eSkrw 	int				 part, omask;
1225df930be7Sderaadt 
122638ea674aSdlg 	sc = sdlookup(DISKUNIT(dev));
122738ea674aSdlg 	if (sc == NULL)
1228921b91cbSweingart 		return -1;
1229d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
1230396a99aeSbluhm 		size = -1;
1231396a99aeSbluhm 		goto exit;
1232396a99aeSbluhm 	}
1233921b91cbSweingart 
12342fb2e161Skrw 	part = DISKPART(dev);
123538ea674aSdlg 	omask = sc->sc_dk.dk_openmask & (1 << part);
1236921b91cbSweingart 
12371e7162a8Scsapuntz 	if (omask == 0 && sdopen(dev, 0, S_IFBLK, NULL) != 0) {
12381e7162a8Scsapuntz 		size = -1;
12391e7162a8Scsapuntz 		goto exit;
12401e7162a8Scsapuntz 	}
12418d28cd76Skrw 
12428d28cd76Skrw 	lp = sc->sc_dk.dk_label;
1243d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
12440e5ef83fSbluhm 		size = -1;
12450e5ef83fSbluhm 		goto exit;
12460e5ef83fSbluhm 	}
124771ed4a82Skrw 	if (!ISSET(sc->sc_link->flags, SDEV_MEDIA_LOADED))
1248921b91cbSweingart 		size = -1;
12498d28cd76Skrw 	else if (lp->d_partitions[part].p_fstype != FS_SWAP)
1250df930be7Sderaadt 		size = -1;
1251df930be7Sderaadt 	else
12528d28cd76Skrw 		size = DL_SECTOBLK(lp, DL_GETPSIZE(&lp->d_partitions[part]));
1253921b91cbSweingart 	if (omask == 0 && sdclose(dev, 0, S_IFBLK, NULL) != 0)
12541e7162a8Scsapuntz 		size = -1;
12551e7162a8Scsapuntz 
12561e7162a8Scsapuntz  exit:
125738ea674aSdlg 	device_unref(&sc->sc_dev);
1258df930be7Sderaadt 	return size;
1259df930be7Sderaadt }
1260df930be7Sderaadt 
126189ea875eSkrw /* #define SD_DUMP_NOT_TRUSTED if you just want to watch. */
1262df930be7Sderaadt static int sddoingadump;
1263df930be7Sderaadt 
1264df930be7Sderaadt /*
126589ea875eSkrw  * Dump all of physical memory into the partition specified, starting
1266df930be7Sderaadt  * at offset 'dumplo' into the partition.
1267df930be7Sderaadt  */
1268df930be7Sderaadt int
12691abdbfdeSderaadt sddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
1270df930be7Sderaadt {
127189ea875eSkrw 	struct sd_softc			*sc;
127289ea875eSkrw 	struct disklabel		*lp;
127389ea875eSkrw 	struct scsi_xfer		*xs;
127489ea875eSkrw 	u_int64_t			 nsects;	/* partition sectors */
127589ea875eSkrw 	u_int64_t			 sectoff;	/* partition offset */
127689ea875eSkrw 	u_int64_t			 totwrt;	/* sectors left */
127789ea875eSkrw 	int				 part, rv, unit;
127889ea875eSkrw 	u_int32_t			 sectorsize;
127989ea875eSkrw 	u_int32_t			 nwrt;		/* sectors to write */
1280df930be7Sderaadt 
1281df930be7Sderaadt 	/* Check if recursive dump; if so, punt. */
1282df930be7Sderaadt 	if (sddoingadump)
1283df930be7Sderaadt 		return EFAULT;
12844756a0a1Skrw 	if (blkno < 0)
12854756a0a1Skrw 		return EINVAL;
1286df930be7Sderaadt 
1287df930be7Sderaadt 	/* Mark as active early. */
1288df930be7Sderaadt 	sddoingadump = 1;
1289df930be7Sderaadt 
12902fb2e161Skrw 	unit = DISKUNIT(dev);	/* Decompose unit & partition. */
12912fb2e161Skrw 	part = DISKPART(dev);
1292df930be7Sderaadt 
1293df930be7Sderaadt 	/* Check for acceptable drive number. */
1294864c175eSdlg 	if (unit >= sd_cd.cd_ndevs || (sc = sd_cd.cd_devs[unit]) == NULL)
1295df930be7Sderaadt 		return ENXIO;
1296df930be7Sderaadt 
1297342446fbSderaadt 	/*
1298342446fbSderaadt 	 * XXX Can't do this check, since the media might have been
1299342446fbSderaadt 	 * XXX marked `invalid' by successful unmounting of all
1300342446fbSderaadt 	 * XXX filesystems.
1301342446fbSderaadt 	 */
1302342446fbSderaadt #if 0
1303df930be7Sderaadt 	/* Make sure it was initialized. */
13041b9e05ceSkrw 	if (!ISSET(sc->sc_link->flags, SDEV_MEDIA_LOADED))
1305df930be7Sderaadt 		return ENXIO;
1306364ebb70Skrw #endif /* 0 */
1307df930be7Sderaadt 
1308df930be7Sderaadt 	/* Convert to disk sectors. Request must be a multiple of size. */
1309864c175eSdlg 	lp = sc->sc_dk.dk_label;
1310df930be7Sderaadt 	sectorsize = lp->d_secsize;
1311df930be7Sderaadt 	if ((size % sectorsize) != 0)
1312df930be7Sderaadt 		return EFAULT;
13134756a0a1Skrw 	if ((blkno % DL_BLKSPERSEC(lp)) != 0)
13144756a0a1Skrw 		return EFAULT;
1315df930be7Sderaadt 	totwrt = size / sectorsize;
13164756a0a1Skrw 	blkno = DL_BLKTOSEC(lp, blkno);
1317df930be7Sderaadt 
1318e16633b4Sderaadt 	nsects = DL_GETPSIZE(&lp->d_partitions[part]);
1319e16633b4Sderaadt 	sectoff = DL_GETPOFFSET(&lp->d_partitions[part]);
1320df930be7Sderaadt 
1321df930be7Sderaadt 	/* Check transfer bounds against partition size. */
13224756a0a1Skrw 	if ((blkno + totwrt) > nsects)
1323df930be7Sderaadt 		return EINVAL;
1324df930be7Sderaadt 
1325df930be7Sderaadt 	/* Offset block number to start of partition. */
1326df930be7Sderaadt 	blkno += sectoff;
1327df930be7Sderaadt 
1328df930be7Sderaadt 	while (totwrt > 0) {
13294756a0a1Skrw 		if (totwrt > UINT32_MAX)
13304756a0a1Skrw 			nwrt = UINT32_MAX;
13314756a0a1Skrw 		else
13324756a0a1Skrw 			nwrt = totwrt;
1333864c175eSdlg 
1334df930be7Sderaadt #ifndef	SD_DUMP_NOT_TRUSTED
1335ddd4a1bcSkrw 		xs = scsi_xs_get(sc->sc_link, SCSI_NOSLEEP | SCSI_DATA_OUT);
1336864c175eSdlg 		if (xs == NULL)
133789ea875eSkrw 			return ENOMEM;
1338864c175eSdlg 
1339864c175eSdlg 		xs->timeout = 10000;
1340df930be7Sderaadt 		xs->data = va;
1341df930be7Sderaadt 		xs->datalen = nwrt * sectorsize;
1342df930be7Sderaadt 
1343664c6166Skrw 		xs->cmdlen = sd_cmd_rw10(&xs->cmd, 0, blkno, nwrt); /* XXX */
1344864c175eSdlg 
13451fa2e30aSdlg 		rv = scsi_xs_sync(xs);
1346864c175eSdlg 		scsi_xs_put(xs);
13471fa2e30aSdlg 		if (rv != 0)
134889ea875eSkrw 			return ENXIO;
1349df930be7Sderaadt #else	/* SD_DUMP_NOT_TRUSTED */
135089ea875eSkrw 		/* Let's just talk about this first. */
1351bb4f4faeSkrw 		printf("sd%d: dump addr 0x%x, blk %lld\n", unit, va,
1352bb4f4faeSkrw 		    (long long)blkno);
135389ea875eSkrw 		delay(500 * 1000);	/* 1/2 a second */
1354364ebb70Skrw #endif	/* ~SD_DUMP_NOT_TRUSTED */
1355df930be7Sderaadt 
135689ea875eSkrw 		/* Update block count. */
1357df930be7Sderaadt 		totwrt -= nwrt;
1358df930be7Sderaadt 		blkno += nwrt;
1359df930be7Sderaadt 		va += sectorsize * nwrt;
1360df930be7Sderaadt 	}
1361864c175eSdlg 
1362df930be7Sderaadt 	sddoingadump = 0;
1363864c175eSdlg 
136489ea875eSkrw 	return 0;
1365df930be7Sderaadt }
136654eb5b4eSmillert 
136754eb5b4eSmillert /*
136854eb5b4eSmillert  * Copy up to len chars from src to dst, ignoring non-printables.
136954eb5b4eSmillert  * Must be room for len+1 chars in dst so we can write the NUL.
137054eb5b4eSmillert  * Does not assume src is NUL-terminated.
137154eb5b4eSmillert  */
137254eb5b4eSmillert void
13738d3a06f5Sdlg viscpy(u_char *dst, u_char *src, int len)
137454eb5b4eSmillert {
137554eb5b4eSmillert 	while (len > 0 && *src != '\0') {
137654eb5b4eSmillert 		if (*src < 0x20 || *src >= 0x80) {
137754eb5b4eSmillert 			src++;
137854eb5b4eSmillert 			continue;
137954eb5b4eSmillert 		}
138054eb5b4eSmillert 		*dst++ = *src++;
138154eb5b4eSmillert 		len--;
138254eb5b4eSmillert 	}
138354eb5b4eSmillert 	*dst = '\0';
138454eb5b4eSmillert }
1385c2e832f7Skrw 
13867f77a1b5Sdlg int
13877f77a1b5Sdlg sd_read_cap_10(struct sd_softc *sc, int flags)
13887f77a1b5Sdlg {
13897f77a1b5Sdlg 	struct scsi_read_cap_data	*rdcap;
139085680492Skrw 	int				 rv;
13917f77a1b5Sdlg 
13927f77a1b5Sdlg 	rdcap = dma_alloc(sizeof(*rdcap), (ISSET(flags, SCSI_NOSLEEP) ?
13937f77a1b5Sdlg 	    PR_NOWAIT : PR_WAITOK) | PR_ZERO);
13947f77a1b5Sdlg 	if (rdcap == NULL)
139585680492Skrw 		return -1;
13967f77a1b5Sdlg 
1397d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
139885680492Skrw 		rv = -1;
13990e5ef83fSbluhm 		goto done;
14000e5ef83fSbluhm 	}
14017f77a1b5Sdlg 
140285680492Skrw 	rv = scsi_read_cap_10(sc->sc_link, rdcap, flags);
14037f77a1b5Sdlg 	if (rv == 0) {
14041d2f13acSkrw 		if (_4btol(rdcap->addr) == 0) {
140585680492Skrw 			rv = -1;
140685680492Skrw 			goto done;
140785680492Skrw 		}
1408537c0d25Smatthew 		sc->params.disksize = _4btol(rdcap->addr) + 1ll;
14097f77a1b5Sdlg 		sc->params.secsize = _4btol(rdcap->length);
14107f77a1b5Sdlg 		CLR(sc->flags, SDF_THIN);
14117f77a1b5Sdlg 	}
14127f77a1b5Sdlg 
14137f77a1b5Sdlg done:
14147f77a1b5Sdlg 	dma_free(rdcap, sizeof(*rdcap));
141585680492Skrw 	return rv;
14167f77a1b5Sdlg }
14177f77a1b5Sdlg 
14187f77a1b5Sdlg int
14197f77a1b5Sdlg sd_read_cap_16(struct sd_softc *sc, int flags)
14207f77a1b5Sdlg {
14217f77a1b5Sdlg 	struct scsi_read_cap_data_16	*rdcap;
142285680492Skrw 	int				 rv;
14237f77a1b5Sdlg 
14247f77a1b5Sdlg 	rdcap = dma_alloc(sizeof(*rdcap), (ISSET(flags, SCSI_NOSLEEP) ?
14257f77a1b5Sdlg 	    PR_NOWAIT : PR_WAITOK) | PR_ZERO);
14267f77a1b5Sdlg 	if (rdcap == NULL)
142785680492Skrw 		return -1;
14287f77a1b5Sdlg 
1429d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
143085680492Skrw 		rv = -1;
14310e5ef83fSbluhm 		goto done;
14320e5ef83fSbluhm 	}
14337f77a1b5Sdlg 
143485680492Skrw 	rv = scsi_read_cap_16(sc->sc_link, rdcap, flags);
14357f77a1b5Sdlg 	if (rv == 0) {
1436f802350aSmpi 		if (_8btol(rdcap->addr) == 0) {
143785680492Skrw 			rv = -1;
1438f802350aSmpi 			goto done;
1439f802350aSmpi 		}
144085680492Skrw 		sc->params.disksize = _8btol(rdcap->addr) + 1ll;
14417f77a1b5Sdlg 		sc->params.secsize = _4btol(rdcap->length);
14427f77a1b5Sdlg 		if (ISSET(_2btol(rdcap->lowest_aligned), READ_CAP_16_TPE))
14437f77a1b5Sdlg 			SET(sc->flags, SDF_THIN);
14447f77a1b5Sdlg 		else
14457f77a1b5Sdlg 			CLR(sc->flags, SDF_THIN);
14467f77a1b5Sdlg 	}
14477f77a1b5Sdlg 
14487f77a1b5Sdlg done:
14497f77a1b5Sdlg 	dma_free(rdcap, sizeof(*rdcap));
145085680492Skrw 	return rv;
14517f77a1b5Sdlg }
14527f77a1b5Sdlg 
14537f77a1b5Sdlg int
145475679190Skrw sd_read_cap(struct sd_softc *sc, int flags)
14557f77a1b5Sdlg {
14567f77a1b5Sdlg 	int rv;
14577f77a1b5Sdlg 
145885680492Skrw 	CLR(flags, SCSI_IGNORE_ILLEGAL_REQUEST);
14597b732f28Skrw 
14607b732f28Skrw 	/*
14614ab68d81Skrw 	 * post-SPC2 (i.e. post-SCSI-3) devices can start with 16 byte
14624ab68d81Skrw 	 * read capacity commands. Older devices start with the 10 byte
14637b732f28Skrw 	 * version and move up to the 16 byte version if the device
14647b732f28Skrw 	 * says it has more sectors than can be reported via the 10 byte
14657b732f28Skrw 	 * read capacity.
14667b732f28Skrw 	 */
14674ab68d81Skrw 	if (SID_ANSII_REV(&sc->sc_link->inqdata) > SCSI_REV_SPC2) {
14687f77a1b5Sdlg 		rv = sd_read_cap_16(sc, flags);
14697f77a1b5Sdlg 		if (rv != 0)
14707f77a1b5Sdlg 			rv = sd_read_cap_10(sc, flags);
14717f77a1b5Sdlg 	} else {
14727f77a1b5Sdlg 		rv = sd_read_cap_10(sc, flags);
1473537c0d25Smatthew 		if (rv == 0 && sc->params.disksize == 0x100000000ll)
14747f77a1b5Sdlg 			rv = sd_read_cap_16(sc, flags);
14757f77a1b5Sdlg 	}
14767f77a1b5Sdlg 
147785680492Skrw 	return rv;
14787f77a1b5Sdlg }
14797f77a1b5Sdlg 
1480da7312e6Sdlg int
1481da7312e6Sdlg sd_thin_pages(struct sd_softc *sc, int flags)
1482da7312e6Sdlg {
1483da7312e6Sdlg 	struct scsi_vpd_hdr		*pg;
1484da7312e6Sdlg 	u_int8_t			*pages;
148589ea875eSkrw 	size_t				 len = 0;
148689ea875eSkrw 	int				 i, rv, score = 0;
1487da7312e6Sdlg 
1488da7312e6Sdlg 	pg = dma_alloc(sizeof(*pg), (ISSET(flags, SCSI_NOSLEEP) ?
1489da7312e6Sdlg 	    PR_NOWAIT : PR_WAITOK) | PR_ZERO);
1490da7312e6Sdlg 	if (pg == NULL)
149189ea875eSkrw 		return ENOMEM;
1492da7312e6Sdlg 
1493d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
14940e5ef83fSbluhm 		rv = ENXIO;
14950e5ef83fSbluhm 		goto done;
14960e5ef83fSbluhm 	}
1497da7312e6Sdlg 	rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg),
1498da7312e6Sdlg 	    SI_PG_SUPPORTED, flags);
1499da7312e6Sdlg 	if (rv != 0)
1500da7312e6Sdlg 		goto done;
1501da7312e6Sdlg 
1502da7312e6Sdlg 	len = _2btol(pg->page_length);
1503da7312e6Sdlg 
1504da7312e6Sdlg 	dma_free(pg, sizeof(*pg));
1505da7312e6Sdlg 	pg = dma_alloc(sizeof(*pg) + len, (ISSET(flags, SCSI_NOSLEEP) ?
1506da7312e6Sdlg 	    PR_NOWAIT : PR_WAITOK) | PR_ZERO);
1507da7312e6Sdlg 	if (pg == NULL)
150889ea875eSkrw 		return ENOMEM;
1509da7312e6Sdlg 
1510d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
15110e5ef83fSbluhm 		rv = ENXIO;
15120e5ef83fSbluhm 		goto done;
15130e5ef83fSbluhm 	}
1514da7312e6Sdlg 	rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg) + len,
1515da7312e6Sdlg 	    SI_PG_SUPPORTED, flags);
1516da7312e6Sdlg 	if (rv != 0)
1517da7312e6Sdlg 		goto done;
1518da7312e6Sdlg 
1519da7312e6Sdlg 	pages = (u_int8_t *)(pg + 1);
1520da7312e6Sdlg 	if (pages[0] != SI_PG_SUPPORTED) {
1521da7312e6Sdlg 		rv = EIO;
1522da7312e6Sdlg 		goto done;
1523da7312e6Sdlg 	}
1524da7312e6Sdlg 
1525da7312e6Sdlg 	for (i = 1; i < len; i++) {
1526da7312e6Sdlg 		switch (pages[i]) {
1527da7312e6Sdlg 		case SI_PG_DISK_LIMITS:
1528da7312e6Sdlg 		case SI_PG_DISK_THIN:
1529da7312e6Sdlg 			score++;
1530da7312e6Sdlg 			break;
1531da7312e6Sdlg 		}
1532da7312e6Sdlg 	}
1533da7312e6Sdlg 
1534da7312e6Sdlg 	if (score < 2)
1535da7312e6Sdlg 		rv = EOPNOTSUPP;
1536da7312e6Sdlg 
1537da7312e6Sdlg done:
1538da7312e6Sdlg 	dma_free(pg, sizeof(*pg) + len);
153989ea875eSkrw 	return rv;
1540da7312e6Sdlg }
1541da7312e6Sdlg 
1542da7312e6Sdlg int
1543da7312e6Sdlg sd_vpd_block_limits(struct sd_softc *sc, int flags)
1544da7312e6Sdlg {
1545da7312e6Sdlg 	struct scsi_vpd_disk_limits	*pg;
1546da7312e6Sdlg 	int				 rv;
1547da7312e6Sdlg 
1548da7312e6Sdlg 	pg = dma_alloc(sizeof(*pg), (ISSET(flags, SCSI_NOSLEEP) ?
1549da7312e6Sdlg 	    PR_NOWAIT : PR_WAITOK) | PR_ZERO);
1550da7312e6Sdlg 	if (pg == NULL)
155189ea875eSkrw 		return ENOMEM;
1552da7312e6Sdlg 
1553d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
15540e5ef83fSbluhm 		rv = ENXIO;
15550e5ef83fSbluhm 		goto done;
15560e5ef83fSbluhm 	}
1557da7312e6Sdlg 	rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg),
1558da7312e6Sdlg 	    SI_PG_DISK_LIMITS, flags);
1559da7312e6Sdlg 	if (rv != 0)
1560da7312e6Sdlg 		goto done;
1561da7312e6Sdlg 
1562da7312e6Sdlg 	if (_2btol(pg->hdr.page_length) == SI_PG_DISK_LIMITS_LEN_THIN) {
1563da7312e6Sdlg 		sc->params.unmap_sectors = _4btol(pg->max_unmap_lba_count);
1564da7312e6Sdlg 		sc->params.unmap_descs = _4btol(pg->max_unmap_desc_count);
1565da7312e6Sdlg 	} else
1566da7312e6Sdlg 		rv = EOPNOTSUPP;
1567da7312e6Sdlg 
1568da7312e6Sdlg done:
1569da7312e6Sdlg 	dma_free(pg, sizeof(*pg));
157089ea875eSkrw 	return rv;
1571da7312e6Sdlg }
1572da7312e6Sdlg 
1573da7312e6Sdlg int
1574da7312e6Sdlg sd_vpd_thin(struct sd_softc *sc, int flags)
1575da7312e6Sdlg {
1576da7312e6Sdlg 	struct scsi_vpd_disk_thin	*pg;
1577da7312e6Sdlg 	int				 rv;
1578da7312e6Sdlg 
1579da7312e6Sdlg 	pg = dma_alloc(sizeof(*pg), (ISSET(flags, SCSI_NOSLEEP) ?
1580da7312e6Sdlg 	    PR_NOWAIT : PR_WAITOK) | PR_ZERO);
1581da7312e6Sdlg 	if (pg == NULL)
158289ea875eSkrw 		return ENOMEM;
1583da7312e6Sdlg 
1584d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING)) {
15850e5ef83fSbluhm 		rv = ENXIO;
15860e5ef83fSbluhm 		goto done;
15870e5ef83fSbluhm 	}
1588da7312e6Sdlg 	rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg),
1589da7312e6Sdlg 	    SI_PG_DISK_THIN, flags);
1590da7312e6Sdlg 	if (rv != 0)
1591da7312e6Sdlg 		goto done;
1592da7312e6Sdlg 
1593da7312e6Sdlg #ifdef notyet
1594a4d5420cSdlg 	if (ISSET(pg->flags, VPD_DISK_THIN_TPU))
1595da7312e6Sdlg 		sc->sc_delete = sd_unmap;
1596a4d5420cSdlg 	else if (ISSET(pg->flags, VPD_DISK_THIN_TPWS)) {
1597da7312e6Sdlg 		sc->sc_delete = sd_write_same_16;
1598da7312e6Sdlg 		sc->params.unmap_descs = 1; /* WRITE SAME 16 only does one */
1599da7312e6Sdlg 	} else
1600da7312e6Sdlg 		rv = EOPNOTSUPP;
1601364ebb70Skrw #endif /* notyet */
1602da7312e6Sdlg 
1603da7312e6Sdlg done:
1604da7312e6Sdlg 	dma_free(pg, sizeof(*pg));
160589ea875eSkrw 	return rv;
1606da7312e6Sdlg }
1607da7312e6Sdlg 
1608da7312e6Sdlg int
1609da7312e6Sdlg sd_thin_params(struct sd_softc *sc, int flags)
1610da7312e6Sdlg {
1611da7312e6Sdlg 	int rv;
1612da7312e6Sdlg 
1613da7312e6Sdlg 	rv = sd_thin_pages(sc, flags);
1614da7312e6Sdlg 	if (rv != 0)
161589ea875eSkrw 		return rv;
1616da7312e6Sdlg 
1617da7312e6Sdlg 	rv = sd_vpd_block_limits(sc, flags);
1618da7312e6Sdlg 	if (rv != 0)
161989ea875eSkrw 		return rv;
1620da7312e6Sdlg 
1621da7312e6Sdlg 	rv = sd_vpd_thin(sc, flags);
1622da7312e6Sdlg 	if (rv != 0)
162389ea875eSkrw 		return rv;
1624da7312e6Sdlg 
162589ea875eSkrw 	return 0;
1626da7312e6Sdlg }
1627da7312e6Sdlg 
1628c2e832f7Skrw /*
1629340f3a7eSkrw  * Fill out the disk parameter structure. Return 0 if the structure is correctly
1630340f3a7eSkrw  * filled in, otherwise return -1.
1631340f3a7eSkrw  *
1632340f3a7eSkrw  * The caller is responsible for clearing the SDEV_MEDIA_LOADED flag if the
1633340f3a7eSkrw  * structure cannot be completed.
1634c2e832f7Skrw  */
1635c2e832f7Skrw int
1636442ab2acSkrw sd_get_parms(struct sd_softc *sc, int flags)
1637c2e832f7Skrw {
16388ed651c0Skrw 	struct disk_parms		 dp;
1639442ab2acSkrw 	struct scsi_link		*link = sc->sc_link;
164024b6d99aSkrw 	union scsi_mode_sense_buf	*buf = NULL;
16416a2df027Skrw 	struct page_rigid_geometry	*rigid = NULL;
16426a2df027Skrw 	struct page_flex_geometry	*flex = NULL;
16436a2df027Skrw 	struct page_reduced_geometry	*reduced = NULL;
1644427418b6Skrw 	u_char				*page0 = NULL;
164589ea875eSkrw 	int				 big, err = 0;
1646c2e832f7Skrw 
164775679190Skrw 	if (sd_read_cap(sc, flags) != 0)
1648340f3a7eSkrw 		return -1;
1649c2e832f7Skrw 
1650da7312e6Sdlg 	if (ISSET(sc->flags, SDF_THIN) && sd_thin_params(sc, flags) != 0) {
1651*9593dc34Smglocker 		/* we don't know the unmap limits, so we can't use this shizz */
1652da7312e6Sdlg 		CLR(sc->flags, SDF_THIN);
1653da7312e6Sdlg 	}
1654da7312e6Sdlg 
16558ed651c0Skrw 	/*
16568ed651c0Skrw 	 * Work on a copy of the values initialized by sd_read_cap() and
16578ed651c0Skrw 	 * sd_thin_params().
16588ed651c0Skrw 	 */
16598ed651c0Skrw 	dp = sc->params;
16608ed651c0Skrw 
16615c0b007dSderaadt 	buf = dma_alloc(sizeof(*buf), PR_NOWAIT);
1662427418b6Skrw 	if (buf == NULL)
1663427418b6Skrw 		goto validate;
1664427418b6Skrw 
1665d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING))
16660e5ef83fSbluhm 		goto die;
1667008aca0dSbluhm 
1668427418b6Skrw 	/*
1669427418b6Skrw 	 * Ask for page 0 (vendor specific) mode sense data to find
1670427418b6Skrw 	 * READONLY info. The only thing USB devices will ask for.
167185f9847cSkrw 	 *
167285f9847cSkrw 	 * page0 == NULL is a valid situation.
1673427418b6Skrw 	 */
167485f9847cSkrw 	err = scsi_do_mode_sense(link, 0, buf, (void **)&page0, 1,
167585f9847cSkrw 	    flags | SCSI_SILENT, &big);
1676d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING))
16770e5ef83fSbluhm 		goto die;
1678427418b6Skrw 	if (err == 0) {
1679427418b6Skrw 		if (big && buf->hdr_big.dev_spec & SMH_DSP_WRITE_PROT)
16806d4b3493Skrw 			SET(link->flags, SDEV_READONLY);
1681427418b6Skrw 		else if (!big && buf->hdr.dev_spec & SMH_DSP_WRITE_PROT)
16826d4b3493Skrw 			SET(link->flags, SDEV_READONLY);
1683427418b6Skrw 		else
16846d4b3493Skrw 			CLR(link->flags, SDEV_READONLY);
1685427418b6Skrw 	}
1686427418b6Skrw 
16877b4feecfSkrw 	/*
16887b4feecfSkrw 	 * Many UMASS devices choke when asked about their geometry. Most
16897b4feecfSkrw 	 * don't have a meaningful geometry anyway, so just fake it if
169075679190Skrw 	 * sd_read_cap() worked.
16917b4feecfSkrw 	 */
16928ed651c0Skrw 	if (ISSET(link->flags, SDEV_UMASS) && dp.disksize > 0)
1693b45f7966Skrw 		goto validate;
1694b45f7966Skrw 
16956d4b3493Skrw 	switch (link->inqdata.device & SID_TYPE) {
1696c2e832f7Skrw 	case T_OPTICAL:
1697c2e832f7Skrw 		/* No more information needed or available. */
1698c2e832f7Skrw 		break;
1699c2e832f7Skrw 
1700c2e832f7Skrw 	case T_RDIRECT:
170178cd80b3Skrw 		/* T_RDIRECT supports only PAGE_REDUCED_GEOMETRY (6). */
170285f9847cSkrw 		err = scsi_do_mode_sense(link, PAGE_REDUCED_GEOMETRY, buf,
170385f9847cSkrw 		    (void **)&reduced, sizeof(*reduced), flags | SCSI_SILENT,
170485f9847cSkrw 		    &big);
170585f9847cSkrw 		if (err == 0) {
170689ea875eSkrw 			scsi_parse_blkdesc(link, buf, big, NULL, NULL,
170789ea875eSkrw 			    &dp.secsize);
170885f9847cSkrw 			if (reduced != NULL) {
17098ed651c0Skrw 				if (dp.disksize == 0)
17108ed651c0Skrw 					dp.disksize = _5btol(reduced->sectors);
17118ed651c0Skrw 				if (dp.secsize == 0)
17128ed651c0Skrw 					dp.secsize = _2btol(reduced->bytes_s);
1713c2e832f7Skrw 			}
171485f9847cSkrw 		}
1715c2e832f7Skrw 		break;
1716c2e832f7Skrw 
1717c2e832f7Skrw 	default:
1718a656d1baSkrw 		/*
1719a656d1baSkrw 		 * NOTE: Some devices leave off the last four bytes of
1720a656d1baSkrw 		 * PAGE_RIGID_GEOMETRY and PAGE_FLEX_GEOMETRY mode sense pages.
1721a656d1baSkrw 		 * The only information in those four bytes is RPM information
1722a656d1baSkrw 		 * so accept the page. The extra bytes will be zero and RPM will
1723a656d1baSkrw 		 * end up with the default value of 3600.
1724a656d1baSkrw 		 */
172556ce827eSkrw 		err = 0;
172671ed4a82Skrw 		if (!ISSET(link->flags, SDEV_ATAPI) ||
172771ed4a82Skrw 		    !ISSET(link->flags, SDEV_REMOVABLE))
172885f9847cSkrw 			err = scsi_do_mode_sense(link, PAGE_RIGID_GEOMETRY, buf,
172985f9847cSkrw 			    (void **)&rigid, sizeof(*rigid) - 4,
1730944c98adSkrw 			    flags | SCSI_SILENT, &big);
173185f9847cSkrw 		if (err == 0) {
173289ea875eSkrw 			scsi_parse_blkdesc(link, buf, big, NULL, NULL,
173389ea875eSkrw 			    &dp.secsize);
173485f9847cSkrw 			if (rigid != NULL) {
17358ed651c0Skrw 				dp.heads = rigid->nheads;
17368ed651c0Skrw 				dp.cyls = _3btol(rigid->ncyl);
17378ed651c0Skrw 				if (dp.heads * dp.cyls > 0)
173889ea875eSkrw 					dp.sectors = dp.disksize / (dp.heads *
173989ea875eSkrw 					    dp.cyls);
174085f9847cSkrw 			}
1741c2e832f7Skrw 		} else {
1742d35ed37dSkrw 			if (ISSET(sc->flags, SDF_DYING))
17430e5ef83fSbluhm 				goto die;
174485f9847cSkrw 			err = scsi_do_mode_sense(link, PAGE_FLEX_GEOMETRY, buf,
174585f9847cSkrw 			    (void **)&flex, sizeof(*flex) - 4,
1746944c98adSkrw 			    flags | SCSI_SILENT, &big);
174785f9847cSkrw 			if (err == 0) {
174889ea875eSkrw 				scsi_parse_blkdesc(link, buf, big, NULL, NULL,
174989ea875eSkrw 				    &dp.secsize);
175085f9847cSkrw 				if (flex != NULL) {
17518ed651c0Skrw 					dp.sectors = flex->ph_sec_tr;
17528ed651c0Skrw 					dp.heads = flex->nheads;
17538ed651c0Skrw 					dp.cyls = _2btol(flex->ncyl);
17548ed651c0Skrw 					if (dp.secsize == 0)
175589ea875eSkrw 						dp.secsize =
175689ea875eSkrw 						    _2btol(flex->bytes_s);
17578ed651c0Skrw 					if (dp.disksize == 0)
175889ea875eSkrw 						dp.disksize =
175989ea875eSkrw 						    (u_int64_t)dp.cyls *
17608ed651c0Skrw 						    dp.heads * dp.sectors;
1761c2e832f7Skrw 				}
1762c2e832f7Skrw 			}
176385f9847cSkrw 		}
1764c2e832f7Skrw 		break;
1765c2e832f7Skrw 	}
1766c2e832f7Skrw 
1767b45f7966Skrw validate:
17688ed651c0Skrw 	if (buf) {
17695c0b007dSderaadt 		dma_free(buf, sizeof(*buf));
17708ed651c0Skrw 		buf = NULL;
17718ed651c0Skrw 	}
1772817e312bSkrw 
17738ed651c0Skrw 	if (dp.disksize == 0)
1774450e503cSkn 		return -1;
17753a00b702Skrw 
1776c2e832f7Skrw 	/*
1777006d5239Skrw 	 * Restrict secsize values to powers of two between 512 and 64k.
1778a78c7a79Skrw 	 */
17798ed651c0Skrw 	switch (dp.secsize) {
17808ed651c0Skrw 	case 0:
17818ed651c0Skrw 		dp.secsize = DEV_BSIZE;
17828ed651c0Skrw 		break;
1783a78c7a79Skrw 	case 0x200:	/* == 512, == DEV_BSIZE on all architectures. */
1784a78c7a79Skrw 	case 0x400:
1785a78c7a79Skrw 	case 0x800:
1786a78c7a79Skrw 	case 0x1000:
1787a78c7a79Skrw 	case 0x2000:
1788a78c7a79Skrw 	case 0x4000:
1789a78c7a79Skrw 	case 0x8000:
1790a78c7a79Skrw 	case 0x10000:
1791a78c7a79Skrw 		break;
1792a78c7a79Skrw 	default:
179338ea674aSdlg 		SC_DEBUG(sc->sc_link, SDEV_DB1,
17948ed651c0Skrw 		    ("sd_get_parms: bad secsize: %#x\n", dp.secsize));
1795340f3a7eSkrw 		return -1;
1796a78c7a79Skrw 	}
1797a78c7a79Skrw 
1798a78c7a79Skrw 	/*
1799c2e832f7Skrw 	 * XXX THINK ABOUT THIS!!  Using values such that sectors * heads *
1800c2e832f7Skrw 	 * cyls is <= disk_size can lead to wasted space. We need a more
1801c2e832f7Skrw 	 * careful calculation/validation to make everything work out
1802c2e832f7Skrw 	 * optimally.
1803c2e832f7Skrw 	 */
18048ed651c0Skrw 	if (dp.disksize > 0xffffffff && (dp.heads * dp.sectors) < 0xffff) {
18058ed651c0Skrw 		dp.heads = 511;
18068ed651c0Skrw 		dp.sectors = 255;
18078ed651c0Skrw 		dp.cyls = 0;
18088ed651c0Skrw 	}
18098ed651c0Skrw 
18109ce3b05fSkrw 	/*
18119ce3b05fSkrw 	 * Use standard geometry values for anything we still don't
18129ce3b05fSkrw 	 * know.
18139ce3b05fSkrw 	 */
18148ed651c0Skrw 	if (dp.heads == 0)
18158ed651c0Skrw 		dp.heads = 255;
18168ed651c0Skrw 	if (dp.sectors == 0)
18178ed651c0Skrw 		dp.sectors = 63;
18188ed651c0Skrw 	if (dp.cyls == 0) {
18198ed651c0Skrw 		dp.cyls = dp.disksize / (dp.heads * dp.sectors);
18208ed651c0Skrw 		if (dp.cyls == 0) {
18218ed651c0Skrw 			/* Put everything into one cylinder. */
18228ed651c0Skrw 			dp.heads = dp.cyls = 1;
18238ed651c0Skrw 			dp.sectors = dp.disksize;
18248ed651c0Skrw 		}
18259ce3b05fSkrw 	}
18269ce3b05fSkrw 
18278ed651c0Skrw #ifdef SCSIDEBUG
18288ed651c0Skrw 	if (dp.disksize != (u_int64_t)dp.cyls * dp.heads * dp.sectors) {
18298ed651c0Skrw 		sc_print_addr(sc->sc_link);
18308ed651c0Skrw 		printf("disksize (%llu) != cyls (%u) * heads (%u) * "
18318ed651c0Skrw 		    "sectors/track (%u) (%llu)\n", dp.disksize, dp.cyls,
18328ed651c0Skrw 		    dp.heads, dp.sectors,
18338ed651c0Skrw 		    (u_int64_t)dp.cyls * dp.heads * dp.sectors);
1834d0f36e27Skrw 	}
18358ed651c0Skrw #endif /* SCSIDEBUG */
1836d0f36e27Skrw 
18378ed651c0Skrw 	sc->params = dp;
1838340f3a7eSkrw 	return 0;
18390e5ef83fSbluhm 
18400e5ef83fSbluhm die:
18410e5ef83fSbluhm 	dma_free(buf, sizeof(*buf));
1842340f3a7eSkrw 	return -1;
1843c2e832f7Skrw }
1844c2e832f7Skrw 
1845805cc48eSsf int
1846864c175eSdlg sd_flush(struct sd_softc *sc, int flags)
1847c2e832f7Skrw {
18486d4b3493Skrw 	struct scsi_link		*link;
1849864c175eSdlg 	struct scsi_xfer		*xs;
1850864c175eSdlg 	struct scsi_synchronize_cache	*cmd;
1851805cc48eSsf 	int				 error;
185205911573Skrw 
1853d35ed37dSkrw 	if (ISSET(sc->flags, SDF_DYING))
185489ea875eSkrw 		return ENXIO;
18556d4b3493Skrw 	link = sc->sc_link;
1856008aca0dSbluhm 
18579dbfff47Skrw 	if (ISSET(link->quirks, SDEV_NOSYNCCACHE))
185889ea875eSkrw 		return 0;
1859c2e832f7Skrw 
1860c2e832f7Skrw 	/*
186105911573Skrw 	 * Issue a SYNCHRONIZE CACHE. Address 0, length 0 means "all remaining
186205911573Skrw 	 * blocks starting at address 0". Ignore ILLEGAL REQUEST in the event
186305911573Skrw 	 * that the command is not supported by the device.
1864c2e832f7Skrw 	 */
1865c2e832f7Skrw 
1866ddd4a1bcSkrw 	xs = scsi_xs_get(link, flags | SCSI_IGNORE_ILLEGAL_REQUEST);
1867864c175eSdlg 	if (xs == NULL) {
18686d4b3493Skrw 		SC_DEBUG(link, SDEV_DB1, ("cache sync failed to get xs\n"));
186989ea875eSkrw 		return EIO;
1870864c175eSdlg 	}
187105911573Skrw 
1872664c6166Skrw 	cmd = (struct scsi_synchronize_cache *)&xs->cmd;
1873864c175eSdlg 	cmd->opcode = SYNCHRONIZE_CACHE;
1874864c175eSdlg 
18756056de39Sdlg 	xs->cmdlen = sizeof(*cmd);
1876864c175eSdlg 	xs->timeout = 100000;
1877864c175eSdlg 
1878805cc48eSsf 	error = scsi_xs_sync(xs);
1879864c175eSdlg 
1880864c175eSdlg 	scsi_xs_put(xs);
1881805cc48eSsf 
1882805cc48eSsf 	if (error)
1883805cc48eSsf 		SC_DEBUG(link, SDEV_DB1, ("cache sync failed\n"));
1884805cc48eSsf 	else
18855b102313Skrw 		CLR(sc->flags, SDF_DIRTY);
1886805cc48eSsf 
188789ea875eSkrw 	return error;
1888864c175eSdlg }
1889