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