1*fd114b83Sthib /* $OpenBSD: wd.c,v 1.83 2010/05/26 16:16:23 thib Exp $ */ 27481efa2Scsapuntz /* $NetBSD: wd.c,v 1.193 1999/02/28 17:15:27 explorer Exp $ */ 37481efa2Scsapuntz 47481efa2Scsapuntz /* 59a5cc0e7Sgrange * Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved. 67481efa2Scsapuntz * 77481efa2Scsapuntz * Redistribution and use in source and binary forms, with or without 87481efa2Scsapuntz * modification, are permitted provided that the following conditions 97481efa2Scsapuntz * are met: 107481efa2Scsapuntz * 1. Redistributions of source code must retain the above copyright 117481efa2Scsapuntz * notice, this list of conditions and the following disclaimer. 127481efa2Scsapuntz * 2. Redistributions in binary form must reproduce the above copyright 137481efa2Scsapuntz * notice, this list of conditions and the following disclaimer in the 147481efa2Scsapuntz * documentation and/or other materials provided with the distribution. 157481efa2Scsapuntz * 3. All advertising materials mentioning features or use of this software 167481efa2Scsapuntz * must display the following acknowledgement: 177481efa2Scsapuntz * This product includes software developed by Manuel Bouyer. 187481efa2Scsapuntz * 4. The name of the author may not be used to endorse or promote products 197481efa2Scsapuntz * derived from this software without specific prior written permission. 207481efa2Scsapuntz * 217481efa2Scsapuntz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 227481efa2Scsapuntz * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 237481efa2Scsapuntz * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 247481efa2Scsapuntz * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 257481efa2Scsapuntz * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 267481efa2Scsapuntz * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 277481efa2Scsapuntz * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 287481efa2Scsapuntz * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 297481efa2Scsapuntz * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 307481efa2Scsapuntz * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 317481efa2Scsapuntz */ 327481efa2Scsapuntz 337481efa2Scsapuntz /*- 347481efa2Scsapuntz * Copyright (c) 1998 The NetBSD Foundation, Inc. 357481efa2Scsapuntz * All rights reserved. 367481efa2Scsapuntz * 377481efa2Scsapuntz * This code is derived from software contributed to The NetBSD Foundation 387481efa2Scsapuntz * by Charles M. Hannum and by Onno van der Linden. 397481efa2Scsapuntz * 407481efa2Scsapuntz * Redistribution and use in source and binary forms, with or without 417481efa2Scsapuntz * modification, are permitted provided that the following conditions 427481efa2Scsapuntz * are met: 437481efa2Scsapuntz * 1. Redistributions of source code must retain the above copyright 447481efa2Scsapuntz * notice, this list of conditions and the following disclaimer. 457481efa2Scsapuntz * 2. Redistributions in binary form must reproduce the above copyright 467481efa2Scsapuntz * notice, this list of conditions and the following disclaimer in the 477481efa2Scsapuntz * documentation and/or other materials provided with the distribution. 487481efa2Scsapuntz * 497481efa2Scsapuntz * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 507481efa2Scsapuntz * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 517481efa2Scsapuntz * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 527481efa2Scsapuntz * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 537481efa2Scsapuntz * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 547481efa2Scsapuntz * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 557481efa2Scsapuntz * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 567481efa2Scsapuntz * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 577481efa2Scsapuntz * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 587481efa2Scsapuntz * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 597481efa2Scsapuntz * POSSIBILITY OF SUCH DAMAGE. 607481efa2Scsapuntz */ 617481efa2Scsapuntz 627481efa2Scsapuntz #if 0 637481efa2Scsapuntz #include "rnd.h" 647481efa2Scsapuntz #endif 657481efa2Scsapuntz 667481efa2Scsapuntz #include <sys/param.h> 677481efa2Scsapuntz #include <sys/systm.h> 687481efa2Scsapuntz #include <sys/kernel.h> 697481efa2Scsapuntz #include <sys/conf.h> 707481efa2Scsapuntz #include <sys/file.h> 717481efa2Scsapuntz #include <sys/stat.h> 727481efa2Scsapuntz #include <sys/ioctl.h> 73*fd114b83Sthib #include <sys/mutex.h> 747481efa2Scsapuntz #include <sys/buf.h> 757481efa2Scsapuntz #include <sys/uio.h> 767481efa2Scsapuntz #include <sys/malloc.h> 777481efa2Scsapuntz #include <sys/device.h> 787481efa2Scsapuntz #include <sys/disklabel.h> 797481efa2Scsapuntz #include <sys/disk.h> 807481efa2Scsapuntz #include <sys/syslog.h> 817481efa2Scsapuntz #include <sys/proc.h> 82f97ca3e4Scsapuntz #include <sys/vnode.h> 8391f4f7d8Sdlg #include <sys/dkio.h> 847481efa2Scsapuntz 85489e49f9Smiod #include <uvm/uvm_extern.h> 867481efa2Scsapuntz 877481efa2Scsapuntz #include <machine/intr.h> 887481efa2Scsapuntz #include <machine/bus.h> 897481efa2Scsapuntz 907481efa2Scsapuntz #include <dev/ata/atareg.h> 917481efa2Scsapuntz #include <dev/ata/atavar.h> 927481efa2Scsapuntz #include <dev/ata/wdvar.h> 937481efa2Scsapuntz #include <dev/ic/wdcreg.h> 94f5423f8cScsapuntz #include <dev/ic/wdcvar.h> 957481efa2Scsapuntz #if 0 967481efa2Scsapuntz #include "locators.h" 977481efa2Scsapuntz #endif 987481efa2Scsapuntz 9981a81281Saaron #define LBA48_THRESHOLD (0xfffffff) /* 128GB / DEV_BSIZE */ 10081a81281Saaron 1017481efa2Scsapuntz #define WDIORETRIES_SINGLE 4 /* number of retries before single-sector */ 1027481efa2Scsapuntz #define WDIORETRIES 5 /* number of retries before giving up */ 1037481efa2Scsapuntz #define RECOVERYTIME hz/2 /* time to wait before retrying a cmd */ 1047481efa2Scsapuntz 1057481efa2Scsapuntz #define DEBUG_INTR 0x01 1067481efa2Scsapuntz #define DEBUG_XFERS 0x02 1077481efa2Scsapuntz #define DEBUG_STATUS 0x04 1087481efa2Scsapuntz #define DEBUG_FUNCS 0x08 1097481efa2Scsapuntz #define DEBUG_PROBE 0x10 1107481efa2Scsapuntz #ifdef WDCDEBUG 1117481efa2Scsapuntz extern int wdcdebug_wd_mask; /* init'ed in ata_wdc.c */ 112d6986e4fSgrange #define WDCDEBUG_PRINT(args, level) do { \ 113d6986e4fSgrange if ((wdcdebug_wd_mask & (level)) != 0) \ 114d6986e4fSgrange printf args; \ 115d6986e4fSgrange } while (0) 1167481efa2Scsapuntz #else 1177481efa2Scsapuntz #define WDCDEBUG_PRINT(args, level) 1187481efa2Scsapuntz #endif 1197481efa2Scsapuntz 1207481efa2Scsapuntz struct wd_softc { 1217481efa2Scsapuntz /* General disk infos */ 1227481efa2Scsapuntz struct device sc_dev; 1237481efa2Scsapuntz struct disk sc_dk; 124*fd114b83Sthib struct bufq *sc_bufq; 125*fd114b83Sthib 1267481efa2Scsapuntz /* IDE disk soft states */ 1277481efa2Scsapuntz struct ata_bio sc_wdc_bio; /* current transfer */ 128f2b96351Stodd struct buf *sc_bp; /* buf being transferred */ 1297481efa2Scsapuntz struct ata_drive_datas *drvp; /* Our controller's infos */ 1307481efa2Scsapuntz int openings; 13159a52720Sjmc struct ataparams sc_params;/* drive characteristics found */ 1327481efa2Scsapuntz int sc_flags; 1337481efa2Scsapuntz #define WDF_LOCKED 0x01 1347481efa2Scsapuntz #define WDF_WANTED 0x02 1357481efa2Scsapuntz #define WDF_WLABEL 0x04 /* label is writable */ 1367481efa2Scsapuntz #define WDF_LABELLING 0x08 /* writing label */ 1377481efa2Scsapuntz /* 1387481efa2Scsapuntz * XXX Nothing resets this yet, but disk change sensing will when ATA-4 is 1397481efa2Scsapuntz * more fully implemented. 1407481efa2Scsapuntz */ 1417481efa2Scsapuntz #define WDF_LOADED 0x10 /* parameters loaded */ 1427481efa2Scsapuntz #define WDF_WAIT 0x20 /* waiting for resources */ 1437481efa2Scsapuntz #define WDF_LBA 0x40 /* using LBA mode */ 1444b4c6f4eSgluk #define WDF_LBA48 0x80 /* using 48-bit LBA mode */ 145f97ca3e4Scsapuntz 1464b4c6f4eSgluk u_int64_t sc_capacity; 1477481efa2Scsapuntz int cyl; /* actual drive parameters */ 1487481efa2Scsapuntz int heads; 1497481efa2Scsapuntz int sectors; 1507481efa2Scsapuntz int retries; /* number of xfer retry */ 1517c4d2af8Sart struct timeout sc_restart_timeout; 152f97ca3e4Scsapuntz void *sc_sdhook; 1537481efa2Scsapuntz }; 1547481efa2Scsapuntz 1557481efa2Scsapuntz #define sc_drive sc_wdc_bio.drive 1567481efa2Scsapuntz #define sc_mode sc_wdc_bio.mode 1577481efa2Scsapuntz #define sc_multi sc_wdc_bio.multi 1587481efa2Scsapuntz 159c4071fd1Smillert int wdprobe(struct device *, void *, void *); 160c4071fd1Smillert void wdattach(struct device *, struct device *, void *); 161c4071fd1Smillert int wddetach(struct device *, int); 162e78728c7Spirofti int wdactivate(struct device *, int); 163c4071fd1Smillert int wdprint(void *, char *); 1647481efa2Scsapuntz 1657481efa2Scsapuntz struct cfattach wd_ca = { 166f97ca3e4Scsapuntz sizeof(struct wd_softc), wdprobe, wdattach, 167a23d39c4Smiod wddetach, wdactivate 1687481efa2Scsapuntz }; 1697481efa2Scsapuntz 1707481efa2Scsapuntz struct cfdriver wd_cd = { 1717481efa2Scsapuntz NULL, "wd", DV_DISK 1727481efa2Scsapuntz }; 1737481efa2Scsapuntz 174c4071fd1Smillert void wdgetdefaultlabel(struct wd_softc *, struct disklabel *); 175df591ed6Sderaadt int wdgetdisklabel(dev_t dev, struct wd_softc *, struct disklabel *, int); 176c4071fd1Smillert void wdstrategy(struct buf *); 177c4071fd1Smillert void wdstart(void *); 178c4071fd1Smillert void __wdstart(struct wd_softc*, struct buf *); 179c4071fd1Smillert void wdrestart(void *); 180c4071fd1Smillert int wd_get_params(struct wd_softc *, u_int8_t, struct ataparams *); 181c4071fd1Smillert void wd_flushcache(struct wd_softc *, int); 182f8fc1e98Skettenis void wd_standby(struct wd_softc *, int); 183c4071fd1Smillert void wd_shutdown(void *); 1847481efa2Scsapuntz 1857481efa2Scsapuntz struct dkdriver wddkdriver = { wdstrategy }; 1867481efa2Scsapuntz 1877481efa2Scsapuntz /* XXX: these should go elsewhere */ 1887481efa2Scsapuntz cdev_decl(wd); 1897481efa2Scsapuntz bdev_decl(wd); 1907481efa2Scsapuntz 191f97ca3e4Scsapuntz #define wdlock(wd) disk_lock(&(wd)->sc_dk) 192f97ca3e4Scsapuntz #define wdunlock(wd) disk_unlock(&(wd)->sc_dk) 193f97ca3e4Scsapuntz #define wdlookup(unit) (struct wd_softc *)device_lookup(&wd_cd, (unit)) 194f97ca3e4Scsapuntz 1957481efa2Scsapuntz 1967481efa2Scsapuntz int 19706ad70d2Sgrange wdprobe(struct device *parent, void *match_, void *aux) 1987481efa2Scsapuntz { 1997481efa2Scsapuntz struct ata_atapi_attach *aa_link = aux; 2007481efa2Scsapuntz struct cfdata *match = match_; 2017481efa2Scsapuntz 2027481efa2Scsapuntz if (aa_link == NULL) 2037481efa2Scsapuntz return 0; 2047481efa2Scsapuntz if (aa_link->aa_type != T_ATA) 2057481efa2Scsapuntz return 0; 2067481efa2Scsapuntz 2077481efa2Scsapuntz if (match->cf_loc[0] != -1 && 2087481efa2Scsapuntz match->cf_loc[0] != aa_link->aa_channel) 2097481efa2Scsapuntz return 0; 2107481efa2Scsapuntz 2117481efa2Scsapuntz if (match->cf_loc[1] != -1 && 2127481efa2Scsapuntz match->cf_loc[1] != aa_link->aa_drv_data->drive) 2137481efa2Scsapuntz return 0; 2147481efa2Scsapuntz 2157481efa2Scsapuntz return 1; 2167481efa2Scsapuntz } 2177481efa2Scsapuntz 2187481efa2Scsapuntz void 21906ad70d2Sgrange wdattach(struct device *parent, struct device *self, void *aux) 2207481efa2Scsapuntz { 2217481efa2Scsapuntz struct wd_softc *wd = (void *)self; 2227481efa2Scsapuntz struct ata_atapi_attach *aa_link= aux; 22342a17170Sjsg struct wdc_command wdc_c; 2247481efa2Scsapuntz int i, blank; 2257481efa2Scsapuntz char buf[41], c, *p, *q; 2267481efa2Scsapuntz WDCDEBUG_PRINT(("wdattach\n"), DEBUG_FUNCS | DEBUG_PROBE); 2277481efa2Scsapuntz 2287481efa2Scsapuntz wd->openings = aa_link->aa_openings; 229f97ca3e4Scsapuntz wd->drvp = aa_link->aa_drv_data; 230f97ca3e4Scsapuntz 23102acd7cbSray strlcpy(wd->drvp->drive_name, wd->sc_dev.dv_xname, 23202acd7cbSray sizeof(wd->drvp->drive_name)); 233f97ca3e4Scsapuntz wd->drvp->cf_flags = wd->sc_dev.dv_cfdata->cf_flags; 2347481efa2Scsapuntz 235ec8802a4Scsapuntz if ((NERRS_MAX - 2) > 0) 236ec8802a4Scsapuntz wd->drvp->n_dmaerrs = NERRS_MAX - 2; 237ec8802a4Scsapuntz else 238ec8802a4Scsapuntz wd->drvp->n_dmaerrs = 0; 239ec8802a4Scsapuntz 2407481efa2Scsapuntz /* read our drive info */ 241f97ca3e4Scsapuntz if (wd_get_params(wd, at_poll, &wd->sc_params) != 0) { 2427481efa2Scsapuntz printf("%s: IDENTIFY failed\n", wd->sc_dev.dv_xname); 2437481efa2Scsapuntz return; 2447481efa2Scsapuntz } 2457481efa2Scsapuntz 2467481efa2Scsapuntz for (blank = 0, p = wd->sc_params.atap_model, q = buf, i = 0; 2477481efa2Scsapuntz i < sizeof(wd->sc_params.atap_model); i++) { 2487481efa2Scsapuntz c = *p++; 2497481efa2Scsapuntz if (c == '\0') 2507481efa2Scsapuntz break; 2517481efa2Scsapuntz if (c != ' ') { 2527481efa2Scsapuntz if (blank) { 2537481efa2Scsapuntz *q++ = ' '; 2547481efa2Scsapuntz blank = 0; 2557481efa2Scsapuntz } 2567481efa2Scsapuntz *q++ = c; 2577481efa2Scsapuntz } else 2587481efa2Scsapuntz blank = 1; 2597481efa2Scsapuntz } 2607481efa2Scsapuntz *q++ = '\0'; 2617481efa2Scsapuntz 2627481efa2Scsapuntz printf(": <%s>\n", buf); 2637481efa2Scsapuntz 26412dc5567Scsapuntz wdc_probe_caps(wd->drvp, &wd->sc_params); 26512dc5567Scsapuntz wdc_print_caps(wd->drvp); 26612dc5567Scsapuntz 2677481efa2Scsapuntz if ((wd->sc_params.atap_multi & 0xff) > 1) { 2687481efa2Scsapuntz wd->sc_multi = wd->sc_params.atap_multi & 0xff; 2697481efa2Scsapuntz } else { 2707481efa2Scsapuntz wd->sc_multi = 1; 2717481efa2Scsapuntz } 2727481efa2Scsapuntz 273c3a9df52Sderaadt printf("%s: %d-sector PIO,", wd->sc_dev.dv_xname, wd->sc_multi); 2747481efa2Scsapuntz 2754b4c6f4eSgluk /* use 48-bit LBA if enabled */ 2764b4c6f4eSgluk /* XXX: shall we use it if drive capacity < 137Gb? */ 2774b4c6f4eSgluk if ((wd->sc_params.atap_cmd2_en & ATAPI_CMD2_48AD) != 0) 2784b4c6f4eSgluk wd->sc_flags |= WDF_LBA48; 2794b4c6f4eSgluk 2807481efa2Scsapuntz /* Prior to ATA-4, LBA was optional. */ 2817481efa2Scsapuntz if ((wd->sc_params.atap_capabilities1 & WDC_CAP_LBA) != 0) 2827481efa2Scsapuntz wd->sc_flags |= WDF_LBA; 2837481efa2Scsapuntz #if 0 2847481efa2Scsapuntz /* ATA-4 requires LBA. */ 2857481efa2Scsapuntz if (wd->sc_params.atap_ataversion != 0xffff && 2867481efa2Scsapuntz wd->sc_params.atap_ataversion >= WDC_VER_ATA4) 2877481efa2Scsapuntz wd->sc_flags |= WDF_LBA; 2887481efa2Scsapuntz #endif 2897481efa2Scsapuntz 2904b4c6f4eSgluk if ((wd->sc_flags & WDF_LBA48) != 0) { 2914b4c6f4eSgluk wd->sc_capacity = 2924b4c6f4eSgluk (((u_int64_t)wd->sc_params.atap_max_lba[3] << 48) | 2934b4c6f4eSgluk ((u_int64_t)wd->sc_params.atap_max_lba[2] << 32) | 2944b4c6f4eSgluk ((u_int64_t)wd->sc_params.atap_max_lba[1] << 16) | 2954b4c6f4eSgluk (u_int64_t)wd->sc_params.atap_max_lba[0]); 2962a13a35bSgrange printf(" LBA48, %lluMB, %llu sectors\n", 2974b4c6f4eSgluk wd->sc_capacity / (1048576 / DEV_BSIZE), 2984b4c6f4eSgluk wd->sc_capacity); 2994b4c6f4eSgluk } else if ((wd->sc_flags & WDF_LBA) != 0) { 3007481efa2Scsapuntz wd->sc_capacity = 3017481efa2Scsapuntz (wd->sc_params.atap_capacity[1] << 16) | 3027481efa2Scsapuntz wd->sc_params.atap_capacity[0]; 3032a13a35bSgrange printf(" LBA, %lluMB, %llu sectors\n", 3047481efa2Scsapuntz wd->sc_capacity / (1048576 / DEV_BSIZE), 3057481efa2Scsapuntz wd->sc_capacity); 3067481efa2Scsapuntz } else { 3077481efa2Scsapuntz wd->sc_capacity = 3087481efa2Scsapuntz wd->sc_params.atap_cylinders * 3097481efa2Scsapuntz wd->sc_params.atap_heads * 3107481efa2Scsapuntz wd->sc_params.atap_sectors; 3114b4c6f4eSgluk printf(" CHS, %lluMB, %d cyl, %d head, %d sec, %llu sectors\n", 3127481efa2Scsapuntz wd->sc_capacity / (1048576 / DEV_BSIZE), 3137481efa2Scsapuntz wd->sc_params.atap_cylinders, 3147481efa2Scsapuntz wd->sc_params.atap_heads, 3157481efa2Scsapuntz wd->sc_params.atap_sectors, 3167481efa2Scsapuntz wd->sc_capacity); 3177481efa2Scsapuntz } 3187481efa2Scsapuntz WDCDEBUG_PRINT(("%s: atap_dmatiming_mimi=%d, atap_dmatiming_recom=%d\n", 3197481efa2Scsapuntz self->dv_xname, wd->sc_params.atap_dmatiming_mimi, 3207481efa2Scsapuntz wd->sc_params.atap_dmatiming_recom), DEBUG_PROBE); 32142a17170Sjsg 32244323affSjsg /* use read look ahead if supported */ 323119641fdSjsg if (wd->sc_params.atap_cmd_set1 & WDC_CMD1_AHEAD) { 324119641fdSjsg bzero(&wdc_c, sizeof(struct wdc_command)); 325119641fdSjsg wdc_c.r_command = SET_FEATURES; 326119641fdSjsg wdc_c.r_precomp = WDSF_READAHEAD_EN; 327119641fdSjsg wdc_c.timeout = 1000; 328119641fdSjsg wdc_c.flags = at_poll; 329119641fdSjsg 330119641fdSjsg if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 331119641fdSjsg printf("%s: enable look ahead command didn't " 332119641fdSjsg "complete\n", wd->sc_dev.dv_xname); 333119641fdSjsg } 334119641fdSjsg } 335119641fdSjsg 33644323affSjsg /* use write cache if supported */ 337119641fdSjsg if (wd->sc_params.atap_cmd_set1 & WDC_CMD1_CACHE) { 338119641fdSjsg bzero(&wdc_c, sizeof(struct wdc_command)); 339119641fdSjsg wdc_c.r_command = SET_FEATURES; 340119641fdSjsg wdc_c.r_precomp = WDSF_EN_WR_CACHE; 341119641fdSjsg wdc_c.timeout = 1000; 342119641fdSjsg wdc_c.flags = at_poll; 343119641fdSjsg 344119641fdSjsg if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 345119641fdSjsg printf("%s: enable write cache command didn't " 346119641fdSjsg "complete\n", wd->sc_dev.dv_xname); 347119641fdSjsg } 348119641fdSjsg } 34944323affSjsg 35042a17170Sjsg /* 35142a17170Sjsg * FREEZE LOCK the drive so malicous users can't lock it on us. 35242a17170Sjsg * As there is no harm in issuing this to drives that don't 35342a17170Sjsg * support the security feature set we just send it, and don't 35442a17170Sjsg * bother checking if the drive sends a command abort to tell us it 35542a17170Sjsg * doesn't support it. 35642a17170Sjsg */ 35742a17170Sjsg bzero(&wdc_c, sizeof(struct wdc_command)); 35842a17170Sjsg 35942a17170Sjsg wdc_c.r_command = WDCC_SEC_FREEZE_LOCK; 36042a17170Sjsg wdc_c.timeout = 1000; 36142a17170Sjsg wdc_c.flags = at_poll; 36242a17170Sjsg if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 36342a17170Sjsg printf("%s: freeze lock command didn't complete\n", 36442a17170Sjsg wd->sc_dev.dv_xname); 36542a17170Sjsg } 36642a17170Sjsg 3677481efa2Scsapuntz /* 3687481efa2Scsapuntz * Initialize and attach the disk structure. 3697481efa2Scsapuntz */ 3707481efa2Scsapuntz wd->sc_dk.dk_driver = &wddkdriver; 3717481efa2Scsapuntz wd->sc_dk.dk_name = wd->sc_dev.dv_xname; 372*fd114b83Sthib wd->sc_bufq = bufq_init(BUFQ_DEFAULT); 3737481efa2Scsapuntz disk_attach(&wd->sc_dk); 3747481efa2Scsapuntz wd->sc_wdc_bio.lp = wd->sc_dk.dk_label; 375f97ca3e4Scsapuntz wd->sc_sdhook = shutdownhook_establish(wd_shutdown, wd); 376f97ca3e4Scsapuntz if (wd->sc_sdhook == NULL) 3777481efa2Scsapuntz printf("%s: WARNING: unable to establish shutdown hook\n", 3787481efa2Scsapuntz wd->sc_dev.dv_xname); 3797c4d2af8Sart timeout_set(&wd->sc_restart_timeout, wdrestart, wd); 3807481efa2Scsapuntz } 3817481efa2Scsapuntz 382f97ca3e4Scsapuntz int 383e78728c7Spirofti wdactivate(struct device *self, int act) 384f97ca3e4Scsapuntz { 3856753d76aSkettenis struct wd_softc *wd = (void *)self; 386f97ca3e4Scsapuntz int rv = 0; 387f97ca3e4Scsapuntz 388f97ca3e4Scsapuntz switch (act) { 389f97ca3e4Scsapuntz case DVACT_ACTIVATE: 390f97ca3e4Scsapuntz break; 391f97ca3e4Scsapuntz 392f97ca3e4Scsapuntz case DVACT_DEACTIVATE: 393f97ca3e4Scsapuntz /* 394f97ca3e4Scsapuntz * Nothing to do; we key off the device's DVF_ACTIVATE. 395f97ca3e4Scsapuntz */ 396f97ca3e4Scsapuntz break; 3976753d76aSkettenis case DVACT_SUSPEND: 39845326f51Skettenis wd_flushcache(wd, AT_POLL); 399f8fc1e98Skettenis wd_standby(wd, AT_POLL); 4006753d76aSkettenis break; 401f97ca3e4Scsapuntz } 402f97ca3e4Scsapuntz return (rv); 403f97ca3e4Scsapuntz } 404f97ca3e4Scsapuntz 405f97ca3e4Scsapuntz int 40606ad70d2Sgrange wddetach(struct device *self, int flags) 407f97ca3e4Scsapuntz { 408f97ca3e4Scsapuntz struct wd_softc *sc = (struct wd_softc *)self; 409*fd114b83Sthib struct buf *bp; 410f97ca3e4Scsapuntz int s, bmaj, cmaj, mn; 411f97ca3e4Scsapuntz 412f97ca3e4Scsapuntz /* Remove unprocessed buffers from queue */ 413f97ca3e4Scsapuntz s = splbio(); 414*fd114b83Sthib while ((bp = BUFQ_DEQUEUE(sc->sc_bufq)) != NULL) { 415f97ca3e4Scsapuntz bp->b_error = ENXIO; 416f97ca3e4Scsapuntz bp->b_flags |= B_ERROR; 417f97ca3e4Scsapuntz biodone(bp); 418f97ca3e4Scsapuntz } 419f97ca3e4Scsapuntz splx(s); 420f97ca3e4Scsapuntz 42176717e86Skrw /* Locate the lowest minor number to be detached. */ 422a83b1495Skrw mn = DISKMINOR(self->dv_unit, 0); 423f97ca3e4Scsapuntz 424f97ca3e4Scsapuntz for (bmaj = 0; bmaj < nblkdev; bmaj++) 425f97ca3e4Scsapuntz if (bdevsw[bmaj].d_open == wdopen) 426f97ca3e4Scsapuntz vdevgone(bmaj, mn, mn + MAXPARTITIONS - 1, VBLK); 427f97ca3e4Scsapuntz for (cmaj = 0; cmaj < nchrdev; cmaj++) 428f97ca3e4Scsapuntz if (cdevsw[cmaj].d_open == wdopen) 429f97ca3e4Scsapuntz vdevgone(cmaj, mn, mn + MAXPARTITIONS - 1, VCHR); 430f97ca3e4Scsapuntz 431f97ca3e4Scsapuntz /* Get rid of the shutdown hook. */ 432f97ca3e4Scsapuntz if (sc->sc_sdhook != NULL) 433f97ca3e4Scsapuntz shutdownhook_disestablish(sc->sc_sdhook); 434f97ca3e4Scsapuntz 435a23d39c4Smiod /* Detach disk. */ 436*fd114b83Sthib bufq_destroy(sc->sc_bufq); 437a23d39c4Smiod disk_detach(&sc->sc_dk); 438a23d39c4Smiod 439f97ca3e4Scsapuntz return (0); 440f97ca3e4Scsapuntz } 441f97ca3e4Scsapuntz 4427481efa2Scsapuntz /* 4437481efa2Scsapuntz * Read/write routine for a buffer. Validates the arguments and schedules the 4447481efa2Scsapuntz * transfer. Does not wait for the transfer to complete. 4457481efa2Scsapuntz */ 4467481efa2Scsapuntz void 44706ad70d2Sgrange wdstrategy(struct buf *bp) 4487481efa2Scsapuntz { 449f97ca3e4Scsapuntz struct wd_softc *wd; 4507481efa2Scsapuntz int s; 451f97ca3e4Scsapuntz 452a83b1495Skrw wd = wdlookup(DISKUNIT(bp->b_dev)); 453f97ca3e4Scsapuntz if (wd == NULL) { 454f97ca3e4Scsapuntz bp->b_error = ENXIO; 455f97ca3e4Scsapuntz goto bad; 456f97ca3e4Scsapuntz } 457f97ca3e4Scsapuntz 4587481efa2Scsapuntz WDCDEBUG_PRINT(("wdstrategy (%s)\n", wd->sc_dev.dv_xname), 4597481efa2Scsapuntz DEBUG_XFERS); 4607481efa2Scsapuntz 4617481efa2Scsapuntz /* Valid request? */ 4627481efa2Scsapuntz if (bp->b_blkno < 0 || 4637481efa2Scsapuntz (bp->b_bcount % wd->sc_dk.dk_label->d_secsize) != 0 || 4647481efa2Scsapuntz (bp->b_bcount / wd->sc_dk.dk_label->d_secsize) >= (1 << NBBY)) { 4657481efa2Scsapuntz bp->b_error = EINVAL; 4667481efa2Scsapuntz goto bad; 4677481efa2Scsapuntz } 4687481efa2Scsapuntz 4697481efa2Scsapuntz /* If device invalidated (e.g. media change, door open), error. */ 4707481efa2Scsapuntz if ((wd->sc_flags & WDF_LOADED) == 0) { 4717481efa2Scsapuntz bp->b_error = EIO; 4727481efa2Scsapuntz goto bad; 4737481efa2Scsapuntz } 4747481efa2Scsapuntz 4757481efa2Scsapuntz /* If it's a null transfer, return immediately. */ 4767481efa2Scsapuntz if (bp->b_bcount == 0) 4777481efa2Scsapuntz goto done; 4787481efa2Scsapuntz 4797481efa2Scsapuntz /* 4807481efa2Scsapuntz * Do bounds checking, adjust transfer. if error, process. 4817481efa2Scsapuntz * If end of partition, just return. 4827481efa2Scsapuntz */ 4832307e81aSkrw if (bounds_check_with_label(bp, wd->sc_dk.dk_label, 4847481efa2Scsapuntz (wd->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0) 4857481efa2Scsapuntz goto done; 4867481efa2Scsapuntz /* Queue transfer on drive, activate drive and controller if idle. */ 487*fd114b83Sthib BUFQ_QUEUE(wd->sc_bufq, bp); 4887481efa2Scsapuntz s = splbio(); 4897481efa2Scsapuntz wdstart(wd); 4907481efa2Scsapuntz splx(s); 491f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 4927481efa2Scsapuntz return; 4937481efa2Scsapuntz bad: 4947481efa2Scsapuntz bp->b_flags |= B_ERROR; 4957481efa2Scsapuntz done: 4967481efa2Scsapuntz /* Toss transfer; we're done early. */ 4977481efa2Scsapuntz bp->b_resid = bp->b_bcount; 498343aa71cSart s = splbio(); 4997481efa2Scsapuntz biodone(bp); 500343aa71cSart splx(s); 501f97ca3e4Scsapuntz if (wd != NULL) 502f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 5037481efa2Scsapuntz } 5047481efa2Scsapuntz 5057481efa2Scsapuntz /* 5067481efa2Scsapuntz * Queue a drive for I/O. 5077481efa2Scsapuntz */ 5087481efa2Scsapuntz void 50906ad70d2Sgrange wdstart(void *arg) 5107481efa2Scsapuntz { 5117481efa2Scsapuntz struct wd_softc *wd = arg; 512*fd114b83Sthib struct buf *bp = NULL; 5137481efa2Scsapuntz 5147481efa2Scsapuntz WDCDEBUG_PRINT(("wdstart %s\n", wd->sc_dev.dv_xname), 5157481efa2Scsapuntz DEBUG_XFERS); 5167481efa2Scsapuntz while (wd->openings > 0) { 5177481efa2Scsapuntz 5187481efa2Scsapuntz /* Is there a buf for us ? */ 519*fd114b83Sthib if ((bp = BUFQ_DEQUEUE(wd->sc_bufq)) == NULL) 5207481efa2Scsapuntz return; 5217481efa2Scsapuntz /* 5227481efa2Scsapuntz * Make the command. First lock the device 5237481efa2Scsapuntz */ 5247481efa2Scsapuntz wd->openings--; 5257481efa2Scsapuntz 5267481efa2Scsapuntz wd->retries = 0; 5277481efa2Scsapuntz __wdstart(wd, bp); 5287481efa2Scsapuntz } 5297481efa2Scsapuntz } 5307481efa2Scsapuntz 5317481efa2Scsapuntz void 53206ad70d2Sgrange __wdstart(struct wd_softc *wd, struct buf *bp) 5337481efa2Scsapuntz { 53415aab03cSderaadt daddr64_t nblks; 535cec793cfSgrange 536e16633b4Sderaadt wd->sc_wdc_bio.blkno = bp->b_blkno + 537e16633b4Sderaadt DL_GETPOFFSET(&wd->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)]); 5387481efa2Scsapuntz wd->sc_wdc_bio.blkno /= (wd->sc_dk.dk_label->d_secsize / DEV_BSIZE); 5397481efa2Scsapuntz wd->sc_wdc_bio.blkdone =0; 5407481efa2Scsapuntz wd->sc_bp = bp; 5417481efa2Scsapuntz /* 5427481efa2Scsapuntz * If we're retrying, retry in single-sector mode. This will give us 5437481efa2Scsapuntz * the sector number of the problem, and will eventually allow the 5447481efa2Scsapuntz * transfer to succeed. 5457481efa2Scsapuntz */ 546a9a2d616Ssthen if (wd->retries >= WDIORETRIES_SINGLE) 5477481efa2Scsapuntz wd->sc_wdc_bio.flags = ATA_SINGLE; 5487481efa2Scsapuntz else 5497481efa2Scsapuntz wd->sc_wdc_bio.flags = 0; 550cec793cfSgrange nblks = bp->b_bcount / wd->sc_dk.dk_label->d_secsize; 551cec793cfSgrange if ((wd->sc_flags & WDF_LBA48) && 552cec793cfSgrange /* use LBA48 only if really need */ 55381a81281Saaron ((wd->sc_wdc_bio.blkno + nblks - 1 > LBA48_THRESHOLD) || 55481a81281Saaron (nblks > 0xff))) 5554b4c6f4eSgluk wd->sc_wdc_bio.flags |= ATA_LBA48; 5567481efa2Scsapuntz if (wd->sc_flags & WDF_LBA) 5577481efa2Scsapuntz wd->sc_wdc_bio.flags |= ATA_LBA; 5587481efa2Scsapuntz if (bp->b_flags & B_READ) 5597481efa2Scsapuntz wd->sc_wdc_bio.flags |= ATA_READ; 5607481efa2Scsapuntz wd->sc_wdc_bio.bcount = bp->b_bcount; 5617481efa2Scsapuntz wd->sc_wdc_bio.databuf = bp->b_data; 562f97ca3e4Scsapuntz wd->sc_wdc_bio.wd = wd; 5637481efa2Scsapuntz /* Instrumentation. */ 5647481efa2Scsapuntz disk_busy(&wd->sc_dk); 5657481efa2Scsapuntz switch (wdc_ata_bio(wd->drvp, &wd->sc_wdc_bio)) { 5667481efa2Scsapuntz case WDC_TRY_AGAIN: 567e15e1c8cSblambert timeout_add_sec(&wd->sc_restart_timeout, 1); 5687481efa2Scsapuntz break; 5697481efa2Scsapuntz case WDC_QUEUED: 5707481efa2Scsapuntz break; 5717481efa2Scsapuntz case WDC_COMPLETE: 57213456e59Scsapuntz /* 57313456e59Scsapuntz * This code is never executed because we never set 57413456e59Scsapuntz * the ATA_POLL flag above 57513456e59Scsapuntz */ 57613456e59Scsapuntz #if 0 57713456e59Scsapuntz if (wd->sc_wdc_bio.flags & ATA_POLL) 5787481efa2Scsapuntz wddone(wd); 57913456e59Scsapuntz #endif 5807481efa2Scsapuntz break; 5817481efa2Scsapuntz default: 5827481efa2Scsapuntz panic("__wdstart: bad return code from wdc_ata_bio()"); 5837481efa2Scsapuntz } 5847481efa2Scsapuntz } 5857481efa2Scsapuntz 5867481efa2Scsapuntz void 58706ad70d2Sgrange wddone(void *v) 5887481efa2Scsapuntz { 5897481efa2Scsapuntz struct wd_softc *wd = v; 5907481efa2Scsapuntz struct buf *bp = wd->sc_bp; 5917481efa2Scsapuntz char buf[256], *errbuf = buf; 5927481efa2Scsapuntz WDCDEBUG_PRINT(("wddone %s\n", wd->sc_dev.dv_xname), 5937481efa2Scsapuntz DEBUG_XFERS); 5947481efa2Scsapuntz 5957481efa2Scsapuntz bp->b_resid = wd->sc_wdc_bio.bcount; 5967481efa2Scsapuntz errbuf[0] = '\0'; 5977481efa2Scsapuntz switch (wd->sc_wdc_bio.error) { 598f97ca3e4Scsapuntz case ERR_NODEV: 599f97ca3e4Scsapuntz bp->b_flags |= B_ERROR; 600f97ca3e4Scsapuntz bp->b_error = ENXIO; 601f97ca3e4Scsapuntz break; 6027481efa2Scsapuntz case ERR_DMA: 6037481efa2Scsapuntz errbuf = "DMA error"; 6047481efa2Scsapuntz goto retry; 6057481efa2Scsapuntz case ERR_DF: 6067481efa2Scsapuntz errbuf = "device fault"; 6077481efa2Scsapuntz goto retry; 6087481efa2Scsapuntz case TIMEOUT: 6097481efa2Scsapuntz errbuf = "device timeout"; 6107481efa2Scsapuntz goto retry; 6117481efa2Scsapuntz case ERROR: 6127481efa2Scsapuntz /* Don't care about media change bits */ 6137481efa2Scsapuntz if (wd->sc_wdc_bio.r_error != 0 && 6147481efa2Scsapuntz (wd->sc_wdc_bio.r_error & ~(WDCE_MC | WDCE_MCR)) == 0) 6157481efa2Scsapuntz goto noerror; 616b6136c85Sho ata_perror(wd->drvp, wd->sc_wdc_bio.r_error, errbuf, 617b6136c85Sho sizeof buf); 618f97ca3e4Scsapuntz retry: 619f97ca3e4Scsapuntz /* Just reset and retry. Can we do more ? */ 6207481efa2Scsapuntz wdc_reset_channel(wd->drvp); 6217481efa2Scsapuntz diskerr(bp, "wd", errbuf, LOG_PRINTF, 6227481efa2Scsapuntz wd->sc_wdc_bio.blkdone, wd->sc_dk.dk_label); 6237481efa2Scsapuntz if (wd->retries++ < WDIORETRIES) { 6247481efa2Scsapuntz printf(", retrying\n"); 6257c4d2af8Sart timeout_add(&wd->sc_restart_timeout, RECOVERYTIME); 6267481efa2Scsapuntz return; 6277481efa2Scsapuntz } 6287481efa2Scsapuntz printf("\n"); 6297481efa2Scsapuntz bp->b_flags |= B_ERROR; 6307481efa2Scsapuntz bp->b_error = EIO; 6317481efa2Scsapuntz break; 6327481efa2Scsapuntz case NOERROR: 6337481efa2Scsapuntz noerror: if ((wd->sc_wdc_bio.flags & ATA_CORR) || wd->retries > 0) 6347481efa2Scsapuntz printf("%s: soft error (corrected)\n", 6357481efa2Scsapuntz wd->sc_dev.dv_xname); 6367481efa2Scsapuntz } 63735fa4c39Stedu disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid), 63835fa4c39Stedu (bp->b_flags & B_READ)); 6397481efa2Scsapuntz biodone(bp); 6407481efa2Scsapuntz wd->openings++; 6417481efa2Scsapuntz wdstart(wd); 6427481efa2Scsapuntz } 6437481efa2Scsapuntz 6447481efa2Scsapuntz void 64506ad70d2Sgrange wdrestart(void *v) 6467481efa2Scsapuntz { 6477481efa2Scsapuntz struct wd_softc *wd = v; 6487481efa2Scsapuntz struct buf *bp = wd->sc_bp; 6497481efa2Scsapuntz int s; 6507481efa2Scsapuntz WDCDEBUG_PRINT(("wdrestart %s\n", wd->sc_dev.dv_xname), 6517481efa2Scsapuntz DEBUG_XFERS); 6527481efa2Scsapuntz 6537481efa2Scsapuntz s = splbio(); 65469052e72Sderaadt disk_unbusy(&wd->sc_dk, 0, (bp->b_flags & B_READ)); 6557481efa2Scsapuntz __wdstart(v, bp); 6567481efa2Scsapuntz splx(s); 6577481efa2Scsapuntz } 6587481efa2Scsapuntz 6597481efa2Scsapuntz int 66006ad70d2Sgrange wdread(dev_t dev, struct uio *uio, int flags) 6617481efa2Scsapuntz { 6627481efa2Scsapuntz 6637481efa2Scsapuntz WDCDEBUG_PRINT(("wdread\n"), DEBUG_XFERS); 6647481efa2Scsapuntz return (physio(wdstrategy, NULL, dev, B_READ, minphys, uio)); 6657481efa2Scsapuntz } 6667481efa2Scsapuntz 6677481efa2Scsapuntz int 66806ad70d2Sgrange wdwrite(dev_t dev, struct uio *uio, int flags) 6697481efa2Scsapuntz { 6707481efa2Scsapuntz 6717481efa2Scsapuntz WDCDEBUG_PRINT(("wdwrite\n"), DEBUG_XFERS); 6727481efa2Scsapuntz return (physio(wdstrategy, NULL, dev, B_WRITE, minphys, uio)); 6737481efa2Scsapuntz } 6747481efa2Scsapuntz 6757481efa2Scsapuntz int 67606ad70d2Sgrange wdopen(dev_t dev, int flag, int fmt, struct proc *p) 6777481efa2Scsapuntz { 6787481efa2Scsapuntz struct wd_softc *wd; 6797481efa2Scsapuntz int unit, part; 6807481efa2Scsapuntz int error; 6817481efa2Scsapuntz 6827481efa2Scsapuntz WDCDEBUG_PRINT(("wdopen\n"), DEBUG_FUNCS); 683f97ca3e4Scsapuntz 684a83b1495Skrw unit = DISKUNIT(dev); 685f97ca3e4Scsapuntz wd = wdlookup(unit); 6867481efa2Scsapuntz if (wd == NULL) 6877481efa2Scsapuntz return ENXIO; 6887481efa2Scsapuntz 6897481efa2Scsapuntz /* 6907481efa2Scsapuntz * If this is the first open of this device, add a reference 6917481efa2Scsapuntz * to the adapter. 6927481efa2Scsapuntz */ 6937481efa2Scsapuntz if ((error = wdlock(wd)) != 0) 6947481efa2Scsapuntz goto bad4; 6957481efa2Scsapuntz 6967481efa2Scsapuntz if (wd->sc_dk.dk_openmask != 0) { 6977481efa2Scsapuntz /* 6987481efa2Scsapuntz * If any partition is open, but the disk has been invalidated, 6997481efa2Scsapuntz * disallow further opens. 7007481efa2Scsapuntz */ 7017481efa2Scsapuntz if ((wd->sc_flags & WDF_LOADED) == 0) { 7027481efa2Scsapuntz error = EIO; 7037481efa2Scsapuntz goto bad3; 7047481efa2Scsapuntz } 7057481efa2Scsapuntz } else { 7067481efa2Scsapuntz if ((wd->sc_flags & WDF_LOADED) == 0) { 7077481efa2Scsapuntz wd->sc_flags |= WDF_LOADED; 7087481efa2Scsapuntz 7097481efa2Scsapuntz /* Load the physical device parameters. */ 710e1a5e72aScsapuntz wd_get_params(wd, AT_WAIT, &wd->sc_params); 7117481efa2Scsapuntz 7127481efa2Scsapuntz /* Load the partition info if not already loaded. */ 713df591ed6Sderaadt if (wdgetdisklabel(dev, wd, 714df591ed6Sderaadt wd->sc_dk.dk_label, 0) == EIO) { 715df591ed6Sderaadt error = EIO; 716df591ed6Sderaadt goto bad; 717df591ed6Sderaadt } 7187481efa2Scsapuntz } 7197481efa2Scsapuntz } 7207481efa2Scsapuntz 721a83b1495Skrw part = DISKPART(dev); 7227481efa2Scsapuntz 7237481efa2Scsapuntz /* Check that the partition exists. */ 7247481efa2Scsapuntz if (part != RAW_PART && 7257481efa2Scsapuntz (part >= wd->sc_dk.dk_label->d_npartitions || 7267481efa2Scsapuntz wd->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { 7277481efa2Scsapuntz error = ENXIO; 7287481efa2Scsapuntz goto bad; 7297481efa2Scsapuntz } 7307481efa2Scsapuntz 7317481efa2Scsapuntz /* Insure only one open at a time. */ 7327481efa2Scsapuntz switch (fmt) { 7337481efa2Scsapuntz case S_IFCHR: 7347481efa2Scsapuntz wd->sc_dk.dk_copenmask |= (1 << part); 7357481efa2Scsapuntz break; 7367481efa2Scsapuntz case S_IFBLK: 7377481efa2Scsapuntz wd->sc_dk.dk_bopenmask |= (1 << part); 7387481efa2Scsapuntz break; 7397481efa2Scsapuntz } 7407481efa2Scsapuntz wd->sc_dk.dk_openmask = 7417481efa2Scsapuntz wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; 7427481efa2Scsapuntz 7437481efa2Scsapuntz wdunlock(wd); 744f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 7457481efa2Scsapuntz return 0; 7467481efa2Scsapuntz 7477481efa2Scsapuntz bad: 7487481efa2Scsapuntz if (wd->sc_dk.dk_openmask == 0) { 7497481efa2Scsapuntz } 7507481efa2Scsapuntz 7517481efa2Scsapuntz bad3: 7527481efa2Scsapuntz wdunlock(wd); 7537481efa2Scsapuntz bad4: 754f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 7557481efa2Scsapuntz return error; 7567481efa2Scsapuntz } 7577481efa2Scsapuntz 7587481efa2Scsapuntz int 75906ad70d2Sgrange wdclose(dev_t dev, int flag, int fmt, struct proc *p) 7607481efa2Scsapuntz { 761f97ca3e4Scsapuntz struct wd_softc *wd; 762a83b1495Skrw int part = DISKPART(dev); 763f97ca3e4Scsapuntz int error = 0; 764f97ca3e4Scsapuntz 765a83b1495Skrw wd = wdlookup(DISKUNIT(dev)); 766f97ca3e4Scsapuntz if (wd == NULL) 767f97ca3e4Scsapuntz return ENXIO; 7687481efa2Scsapuntz 7697481efa2Scsapuntz WDCDEBUG_PRINT(("wdclose\n"), DEBUG_FUNCS); 7707481efa2Scsapuntz if ((error = wdlock(wd)) != 0) 771f97ca3e4Scsapuntz goto exit; 7727481efa2Scsapuntz 7737481efa2Scsapuntz switch (fmt) { 7747481efa2Scsapuntz case S_IFCHR: 7757481efa2Scsapuntz wd->sc_dk.dk_copenmask &= ~(1 << part); 7767481efa2Scsapuntz break; 7777481efa2Scsapuntz case S_IFBLK: 7787481efa2Scsapuntz wd->sc_dk.dk_bopenmask &= ~(1 << part); 7797481efa2Scsapuntz break; 7807481efa2Scsapuntz } 7817481efa2Scsapuntz wd->sc_dk.dk_openmask = 7827481efa2Scsapuntz wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; 7837481efa2Scsapuntz 7847481efa2Scsapuntz if (wd->sc_dk.dk_openmask == 0) { 7857481efa2Scsapuntz wd_flushcache(wd, 0); 7867481efa2Scsapuntz /* XXXX Must wait for I/O to complete! */ 7877481efa2Scsapuntz } 7887481efa2Scsapuntz 7897481efa2Scsapuntz wdunlock(wd); 790f97ca3e4Scsapuntz 791f97ca3e4Scsapuntz exit: 792f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 793f97ca3e4Scsapuntz return (error); 7947481efa2Scsapuntz } 7957481efa2Scsapuntz 7967481efa2Scsapuntz void 79706ad70d2Sgrange wdgetdefaultlabel(struct wd_softc *wd, struct disklabel *lp) 7987481efa2Scsapuntz { 7997481efa2Scsapuntz WDCDEBUG_PRINT(("wdgetdefaultlabel\n"), DEBUG_FUNCS); 800afaefa16Sniklas bzero(lp, sizeof(struct disklabel)); 8017481efa2Scsapuntz 8027481efa2Scsapuntz lp->d_secsize = DEV_BSIZE; 803e16633b4Sderaadt DL_SETDSIZE(lp, wd->sc_capacity); 8047481efa2Scsapuntz lp->d_ntracks = wd->sc_params.atap_heads; 8057481efa2Scsapuntz lp->d_nsectors = wd->sc_params.atap_sectors; 8067481efa2Scsapuntz lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 807e16633b4Sderaadt lp->d_ncylinders = DL_GETDSIZE(lp) / lp->d_secpercyl; 808a2c80ea8Smillert if (wd->drvp->ata_vers == -1) { 8097481efa2Scsapuntz lp->d_type = DTYPE_ST506; 810bf3a6870Skrw strncpy(lp->d_typename, "ST506/MFM/RLL", sizeof lp->d_typename); 8117481efa2Scsapuntz } else { 8127481efa2Scsapuntz lp->d_type = DTYPE_ESDI; 813bf3a6870Skrw strncpy(lp->d_typename, "ESDI/IDE disk", sizeof lp->d_typename); 8147481efa2Scsapuntz } 815a2c80ea8Smillert /* XXX - user viscopy() like sd.c */ 816bf3a6870Skrw strncpy(lp->d_packname, wd->sc_params.atap_model, sizeof lp->d_packname); 8177481efa2Scsapuntz lp->d_flags = 0; 81822230921Sderaadt lp->d_version = 1; 8197481efa2Scsapuntz 8207481efa2Scsapuntz lp->d_magic = DISKMAGIC; 8217481efa2Scsapuntz lp->d_magic2 = DISKMAGIC; 8227481efa2Scsapuntz lp->d_checksum = dkcksum(lp); 8237481efa2Scsapuntz } 8247481efa2Scsapuntz 8257481efa2Scsapuntz /* 8267481efa2Scsapuntz * Fabricate a default disk label, and try to read the correct one. 8277481efa2Scsapuntz */ 828df591ed6Sderaadt int 82906ad70d2Sgrange wdgetdisklabel(dev_t dev, struct wd_softc *wd, struct disklabel *lp, 830c326f117Sderaadt int spoofonly) 8317481efa2Scsapuntz { 832df591ed6Sderaadt int error; 8337481efa2Scsapuntz 8347481efa2Scsapuntz WDCDEBUG_PRINT(("wdgetdisklabel\n"), DEBUG_FUNCS); 8357481efa2Scsapuntz 8367481efa2Scsapuntz wdgetdefaultlabel(wd, lp); 8377481efa2Scsapuntz 8387481efa2Scsapuntz if (wd->drvp->state > RECAL) 8397481efa2Scsapuntz wd->drvp->drive_flags |= DRIVE_RESET; 840df591ed6Sderaadt error = readdisklabel(DISKLABELDEV(dev), wdstrategy, lp, 841657ac4baSkrw spoofonly); 8427481efa2Scsapuntz if (wd->drvp->state > RECAL) 8437481efa2Scsapuntz wd->drvp->drive_flags |= DRIVE_RESET; 844df591ed6Sderaadt return (error); 8457481efa2Scsapuntz } 8467481efa2Scsapuntz 8477481efa2Scsapuntz int 84806ad70d2Sgrange wdioctl(dev_t dev, u_long xfer, caddr_t addr, int flag, struct proc *p) 8497481efa2Scsapuntz { 850f97ca3e4Scsapuntz struct wd_softc *wd; 851ce578d69Sderaadt struct disklabel *lp; 852f97ca3e4Scsapuntz int error = 0; 8537481efa2Scsapuntz 8547481efa2Scsapuntz WDCDEBUG_PRINT(("wdioctl\n"), DEBUG_FUNCS); 8557481efa2Scsapuntz 856a83b1495Skrw wd = wdlookup(DISKUNIT(dev)); 857f97ca3e4Scsapuntz if (wd == NULL) 858f97ca3e4Scsapuntz return ENXIO; 859f97ca3e4Scsapuntz 860f97ca3e4Scsapuntz if ((wd->sc_flags & WDF_LOADED) == 0) { 861f97ca3e4Scsapuntz error = EIO; 862f97ca3e4Scsapuntz goto exit; 863f97ca3e4Scsapuntz } 8647481efa2Scsapuntz 8657481efa2Scsapuntz switch (xfer) { 8661dfe2d86Sderaadt case DIOCRLDINFO: 867ce578d69Sderaadt lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK); 868c326f117Sderaadt wdgetdisklabel(dev, wd, lp, 0); 869ce578d69Sderaadt bcopy(lp, wd->sc_dk.dk_label, sizeof(*lp)); 870ce578d69Sderaadt free(lp, M_TEMP); 871f97ca3e4Scsapuntz goto exit; 872ce578d69Sderaadt 873c326f117Sderaadt case DIOCGPDINFO: 874c326f117Sderaadt wdgetdisklabel(dev, wd, (struct disklabel *)addr, 1); 875f97ca3e4Scsapuntz goto exit; 876e1a5e72aScsapuntz 8777481efa2Scsapuntz case DIOCGDINFO: 8787481efa2Scsapuntz *(struct disklabel *)addr = *(wd->sc_dk.dk_label); 879f97ca3e4Scsapuntz goto exit; 8807481efa2Scsapuntz 8817481efa2Scsapuntz case DIOCGPART: 8827481efa2Scsapuntz ((struct partinfo *)addr)->disklab = wd->sc_dk.dk_label; 8837481efa2Scsapuntz ((struct partinfo *)addr)->part = 884a83b1495Skrw &wd->sc_dk.dk_label->d_partitions[DISKPART(dev)]; 885f97ca3e4Scsapuntz goto exit; 8867481efa2Scsapuntz 8877481efa2Scsapuntz case DIOCWDINFO: 8887481efa2Scsapuntz case DIOCSDINFO: 889f97ca3e4Scsapuntz if ((flag & FWRITE) == 0) { 890f97ca3e4Scsapuntz error = EBADF; 891f97ca3e4Scsapuntz goto exit; 892f97ca3e4Scsapuntz } 8937481efa2Scsapuntz 8947481efa2Scsapuntz if ((error = wdlock(wd)) != 0) 895f97ca3e4Scsapuntz goto exit; 8967481efa2Scsapuntz wd->sc_flags |= WDF_LABELLING; 8977481efa2Scsapuntz 8987481efa2Scsapuntz error = setdisklabel(wd->sc_dk.dk_label, 899c326f117Sderaadt (struct disklabel *)addr, /*wd->sc_dk.dk_openmask : */0); 9007481efa2Scsapuntz if (error == 0) { 9017481efa2Scsapuntz if (wd->drvp->state > RECAL) 9027481efa2Scsapuntz wd->drvp->drive_flags |= DRIVE_RESET; 9037481efa2Scsapuntz if (xfer == DIOCWDINFO) 904a83b1495Skrw error = writedisklabel(DISKLABELDEV(dev), 905c326f117Sderaadt wdstrategy, wd->sc_dk.dk_label); 9067481efa2Scsapuntz } 9077481efa2Scsapuntz 9087481efa2Scsapuntz wd->sc_flags &= ~WDF_LABELLING; 9097481efa2Scsapuntz wdunlock(wd); 910f97ca3e4Scsapuntz goto exit; 9117481efa2Scsapuntz 9127481efa2Scsapuntz case DIOCWLABEL: 913f97ca3e4Scsapuntz if ((flag & FWRITE) == 0) { 914f97ca3e4Scsapuntz error = EBADF; 915f97ca3e4Scsapuntz goto exit; 916f97ca3e4Scsapuntz } 917f97ca3e4Scsapuntz 9187481efa2Scsapuntz if (*(int *)addr) 9197481efa2Scsapuntz wd->sc_flags |= WDF_WLABEL; 9207481efa2Scsapuntz else 9217481efa2Scsapuntz wd->sc_flags &= ~WDF_WLABEL; 922f97ca3e4Scsapuntz goto exit; 9237481efa2Scsapuntz 9247481efa2Scsapuntz #ifdef notyet 9257481efa2Scsapuntz case DIOCWFORMAT: 9267481efa2Scsapuntz if ((flag & FWRITE) == 0) 9277481efa2Scsapuntz return EBADF; 9287481efa2Scsapuntz { 92971f8714bSkrw struct format_op *fop; 9307481efa2Scsapuntz struct iovec aiov; 9317481efa2Scsapuntz struct uio auio; 9327481efa2Scsapuntz 9337481efa2Scsapuntz fop = (struct format_op *)addr; 9347481efa2Scsapuntz aiov.iov_base = fop->df_buf; 9357481efa2Scsapuntz aiov.iov_len = fop->df_count; 9367481efa2Scsapuntz auio.uio_iov = &aiov; 9377481efa2Scsapuntz auio.uio_iovcnt = 1; 9387481efa2Scsapuntz auio.uio_resid = fop->df_count; 9397481efa2Scsapuntz auio.uio_segflg = 0; 9407481efa2Scsapuntz auio.uio_offset = 9417481efa2Scsapuntz fop->df_startblk * wd->sc_dk.dk_label->d_secsize; 9427481efa2Scsapuntz auio.uio_procp = p; 9437481efa2Scsapuntz error = physio(wdformat, NULL, dev, B_WRITE, minphys, 9447481efa2Scsapuntz &auio); 9457481efa2Scsapuntz fop->df_count -= auio.uio_resid; 9467481efa2Scsapuntz fop->df_reg[0] = wdc->sc_status; 9477481efa2Scsapuntz fop->df_reg[1] = wdc->sc_error; 948f97ca3e4Scsapuntz goto exit; 9497481efa2Scsapuntz } 9507481efa2Scsapuntz #endif 9517481efa2Scsapuntz 9527481efa2Scsapuntz default: 95318885d83Smickey error = wdc_ioctl(wd->drvp, xfer, addr, flag, p); 954f97ca3e4Scsapuntz goto exit; 9557481efa2Scsapuntz } 9567481efa2Scsapuntz 9577481efa2Scsapuntz #ifdef DIAGNOSTIC 9587481efa2Scsapuntz panic("wdioctl: impossible"); 9597481efa2Scsapuntz #endif 960f97ca3e4Scsapuntz 961f97ca3e4Scsapuntz exit: 962f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 963f97ca3e4Scsapuntz return (error); 9647481efa2Scsapuntz } 9657481efa2Scsapuntz 9667481efa2Scsapuntz #ifdef B_FORMAT 9677481efa2Scsapuntz int 9687481efa2Scsapuntz wdformat(struct buf *bp) 9697481efa2Scsapuntz { 9707481efa2Scsapuntz 9717481efa2Scsapuntz bp->b_flags |= B_FORMAT; 9727481efa2Scsapuntz return wdstrategy(bp); 9737481efa2Scsapuntz } 9747481efa2Scsapuntz #endif 9757481efa2Scsapuntz 97615aab03cSderaadt daddr64_t 97706ad70d2Sgrange wdsize(dev_t dev) 9787481efa2Scsapuntz { 9797481efa2Scsapuntz struct wd_softc *wd; 980f97ca3e4Scsapuntz int part, omask; 98115aab03cSderaadt int64_t size; 9827481efa2Scsapuntz 9837481efa2Scsapuntz WDCDEBUG_PRINT(("wdsize\n"), DEBUG_FUNCS); 9847481efa2Scsapuntz 985a83b1495Skrw wd = wdlookup(DISKUNIT(dev)); 9867481efa2Scsapuntz if (wd == NULL) 9877481efa2Scsapuntz return (-1); 9887481efa2Scsapuntz 989a83b1495Skrw part = DISKPART(dev); 9907481efa2Scsapuntz omask = wd->sc_dk.dk_openmask & (1 << part); 9917481efa2Scsapuntz 992f97ca3e4Scsapuntz if (omask == 0 && wdopen(dev, 0, S_IFBLK, NULL) != 0) { 993f97ca3e4Scsapuntz size = -1; 994f97ca3e4Scsapuntz goto exit; 995f97ca3e4Scsapuntz } 996f97ca3e4Scsapuntz 997e16633b4Sderaadt size = DL_GETPSIZE(&wd->sc_dk.dk_label->d_partitions[part]) * 9987481efa2Scsapuntz (wd->sc_dk.dk_label->d_secsize / DEV_BSIZE); 9997481efa2Scsapuntz if (omask == 0 && wdclose(dev, 0, S_IFBLK, NULL) != 0) 1000f97ca3e4Scsapuntz size = -1; 1001f97ca3e4Scsapuntz 1002f97ca3e4Scsapuntz exit: 1003f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 10047481efa2Scsapuntz return (size); 10057481efa2Scsapuntz } 10067481efa2Scsapuntz 10077481efa2Scsapuntz /* #define WD_DUMP_NOT_TRUSTED if you just want to watch */ 10087481efa2Scsapuntz static int wddoingadump = 0; 10097481efa2Scsapuntz static int wddumprecalibrated = 0; 10107481efa2Scsapuntz static int wddumpmulti = 1; 10117481efa2Scsapuntz 10127481efa2Scsapuntz /* 10137481efa2Scsapuntz * Dump core after a system crash. 10147481efa2Scsapuntz */ 10157481efa2Scsapuntz int 101615aab03cSderaadt wddump(dev_t dev, daddr64_t blkno, caddr_t va, size_t size) 10177481efa2Scsapuntz { 10187481efa2Scsapuntz struct wd_softc *wd; /* disk unit to do the I/O */ 10197481efa2Scsapuntz struct disklabel *lp; /* disk's disklabel */ 10207481efa2Scsapuntz int unit, part; 10217481efa2Scsapuntz int nblks; /* total number of sectors left to write */ 10227481efa2Scsapuntz int err; 10237481efa2Scsapuntz char errbuf[256]; 10247481efa2Scsapuntz 10257481efa2Scsapuntz /* Check if recursive dump; if so, punt. */ 10267481efa2Scsapuntz if (wddoingadump) 10277481efa2Scsapuntz return EFAULT; 10287481efa2Scsapuntz wddoingadump = 1; 10297481efa2Scsapuntz 1030a83b1495Skrw unit = DISKUNIT(dev); 1031f97ca3e4Scsapuntz wd = wdlookup(unit); 1032f97ca3e4Scsapuntz if (wd == NULL) 10337481efa2Scsapuntz return ENXIO; 10347481efa2Scsapuntz 1035a83b1495Skrw part = DISKPART(dev); 10367481efa2Scsapuntz 10377481efa2Scsapuntz /* Make sure it was initialized. */ 10387481efa2Scsapuntz if (wd->drvp->state < READY) 10397481efa2Scsapuntz return ENXIO; 10407481efa2Scsapuntz 10417481efa2Scsapuntz /* Convert to disk sectors. Request must be a multiple of size. */ 10427481efa2Scsapuntz lp = wd->sc_dk.dk_label; 10437481efa2Scsapuntz if ((size % lp->d_secsize) != 0) 10447481efa2Scsapuntz return EFAULT; 10457481efa2Scsapuntz nblks = size / lp->d_secsize; 10467481efa2Scsapuntz blkno = blkno / (lp->d_secsize / DEV_BSIZE); 10477481efa2Scsapuntz 10487481efa2Scsapuntz /* Check transfer bounds against partition size. */ 1049e16633b4Sderaadt if ((blkno < 0) || ((blkno + nblks) > DL_GETPSIZE(&lp->d_partitions[part]))) 10507481efa2Scsapuntz return EINVAL; 10517481efa2Scsapuntz 10527481efa2Scsapuntz /* Offset block number to start of partition. */ 1053e16633b4Sderaadt blkno += DL_GETPOFFSET(&lp->d_partitions[part]); 10547481efa2Scsapuntz 10557481efa2Scsapuntz /* Recalibrate, if first dump transfer. */ 10567481efa2Scsapuntz if (wddumprecalibrated == 0) { 10577481efa2Scsapuntz wddumpmulti = wd->sc_multi; 10587481efa2Scsapuntz wddumprecalibrated = 1; 10597481efa2Scsapuntz wd->drvp->state = RECAL; 10607481efa2Scsapuntz } 10617481efa2Scsapuntz 10627481efa2Scsapuntz while (nblks > 0) { 10637481efa2Scsapuntz wd->sc_wdc_bio.blkno = blkno; 10647481efa2Scsapuntz wd->sc_wdc_bio.flags = ATA_POLL; 10654b4c6f4eSgluk if (wd->sc_flags & WDF_LBA48) 10664b4c6f4eSgluk wd->sc_wdc_bio.flags |= ATA_LBA48; 10677481efa2Scsapuntz if (wd->sc_flags & WDF_LBA) 10687481efa2Scsapuntz wd->sc_wdc_bio.flags |= ATA_LBA; 10697481efa2Scsapuntz wd->sc_wdc_bio.bcount = 10707481efa2Scsapuntz min(nblks, wddumpmulti) * lp->d_secsize; 10717481efa2Scsapuntz wd->sc_wdc_bio.databuf = va; 1072f97ca3e4Scsapuntz wd->sc_wdc_bio.wd = wd; 10737481efa2Scsapuntz #ifndef WD_DUMP_NOT_TRUSTED 10747481efa2Scsapuntz switch (wdc_ata_bio(wd->drvp, &wd->sc_wdc_bio)) { 10757481efa2Scsapuntz case WDC_TRY_AGAIN: 10767481efa2Scsapuntz panic("wddump: try again"); 10777481efa2Scsapuntz break; 10787481efa2Scsapuntz case WDC_QUEUED: 10797481efa2Scsapuntz panic("wddump: polled command has been queued"); 10807481efa2Scsapuntz break; 10817481efa2Scsapuntz case WDC_COMPLETE: 10827481efa2Scsapuntz break; 10837481efa2Scsapuntz } 10847481efa2Scsapuntz switch(wd->sc_wdc_bio.error) { 10857481efa2Scsapuntz case TIMEOUT: 10867481efa2Scsapuntz printf("wddump: device timed out"); 10877481efa2Scsapuntz err = EIO; 10887481efa2Scsapuntz break; 10897481efa2Scsapuntz case ERR_DF: 10907481efa2Scsapuntz printf("wddump: drive fault"); 10917481efa2Scsapuntz err = EIO; 10927481efa2Scsapuntz break; 10937481efa2Scsapuntz case ERR_DMA: 10947481efa2Scsapuntz printf("wddump: DMA error"); 10957481efa2Scsapuntz err = EIO; 10967481efa2Scsapuntz break; 10977481efa2Scsapuntz case ERROR: 10987481efa2Scsapuntz errbuf[0] = '\0'; 1099b6136c85Sho ata_perror(wd->drvp, wd->sc_wdc_bio.r_error, errbuf, 1100b6136c85Sho sizeof errbuf); 11017481efa2Scsapuntz printf("wddump: %s", errbuf); 11027481efa2Scsapuntz err = EIO; 11037481efa2Scsapuntz break; 11047481efa2Scsapuntz case NOERROR: 11057481efa2Scsapuntz err = 0; 11067481efa2Scsapuntz break; 11077481efa2Scsapuntz default: 11087481efa2Scsapuntz panic("wddump: unknown error type"); 11097481efa2Scsapuntz } 11107481efa2Scsapuntz if (err != 0) { 11117481efa2Scsapuntz printf("\n"); 11127481efa2Scsapuntz return err; 11137481efa2Scsapuntz } 11147481efa2Scsapuntz #else /* WD_DUMP_NOT_TRUSTED */ 11157481efa2Scsapuntz /* Let's just talk about this first... */ 11167481efa2Scsapuntz printf("wd%d: dump addr 0x%x, cylin %d, head %d, sector %d\n", 11177481efa2Scsapuntz unit, va, cylin, head, sector); 11187481efa2Scsapuntz delay(500 * 1000); /* half a second */ 11197481efa2Scsapuntz #endif 11207481efa2Scsapuntz 11217481efa2Scsapuntz /* update block count */ 11227481efa2Scsapuntz nblks -= min(nblks, wddumpmulti); 11237481efa2Scsapuntz blkno += min(nblks, wddumpmulti); 11247481efa2Scsapuntz va += min(nblks, wddumpmulti) * lp->d_secsize; 11257481efa2Scsapuntz } 11267481efa2Scsapuntz 11277481efa2Scsapuntz wddoingadump = 0; 11287481efa2Scsapuntz return 0; 11297481efa2Scsapuntz } 11307481efa2Scsapuntz 11317481efa2Scsapuntz int 113206ad70d2Sgrange wd_get_params(struct wd_softc *wd, u_int8_t flags, struct ataparams *params) 11337481efa2Scsapuntz { 11347481efa2Scsapuntz switch (ata_get_params(wd->drvp, flags, params)) { 11357481efa2Scsapuntz case CMD_AGAIN: 11367481efa2Scsapuntz return 1; 11377481efa2Scsapuntz case CMD_ERR: 1138e9207eccSuwe /* If we already have drive parameters, reuse them. */ 1139e880da88Suwe if (wd->sc_params.atap_cylinders != 0) { 1140e9207eccSuwe if (params != &wd->sc_params) 1141e9207eccSuwe bcopy(&wd->sc_params, params, 1142e9207eccSuwe sizeof(struct ataparams)); 1143e880da88Suwe return 0; 1144e880da88Suwe } 11457481efa2Scsapuntz /* 11467481efa2Scsapuntz * We `know' there's a drive here; just assume it's old. 11477481efa2Scsapuntz * This geometry is only used to read the MBR and print a 11487481efa2Scsapuntz * (false) attach message. 11497481efa2Scsapuntz */ 1150e9207eccSuwe bzero(params, sizeof(struct ataparams)); 11517481efa2Scsapuntz strncpy(params->atap_model, "ST506", 11527481efa2Scsapuntz sizeof params->atap_model); 11537481efa2Scsapuntz params->atap_config = ATA_CFG_FIXED; 11547481efa2Scsapuntz params->atap_cylinders = 1024; 11557481efa2Scsapuntz params->atap_heads = 8; 11567481efa2Scsapuntz params->atap_sectors = 17; 11577481efa2Scsapuntz params->atap_multi = 1; 11587481efa2Scsapuntz params->atap_capabilities1 = params->atap_capabilities2 = 0; 11597481efa2Scsapuntz wd->drvp->ata_vers = -1; /* Mark it as pre-ATA */ 11607481efa2Scsapuntz return 0; 11617481efa2Scsapuntz case CMD_OK: 11627481efa2Scsapuntz return 0; 11637481efa2Scsapuntz default: 11647481efa2Scsapuntz panic("wd_get_params: bad return code from ata_get_params"); 11657481efa2Scsapuntz /* NOTREACHED */ 11667481efa2Scsapuntz } 11677481efa2Scsapuntz } 11687481efa2Scsapuntz 11697481efa2Scsapuntz void 117006ad70d2Sgrange wd_flushcache(struct wd_softc *wd, int flags) 11717481efa2Scsapuntz { 11727481efa2Scsapuntz struct wdc_command wdc_c; 11737481efa2Scsapuntz 11747481efa2Scsapuntz if (wd->drvp->ata_vers < 4) /* WDCC_FLUSHCACHE is here since ATA-4 */ 11757481efa2Scsapuntz return; 1176afaefa16Sniklas bzero(&wdc_c, sizeof(struct wdc_command)); 11775a1200c8Sgrange wdc_c.r_command = (wd->sc_flags & WDF_LBA48 ? WDCC_FLUSHCACHE_EXT : 11785a1200c8Sgrange WDCC_FLUSHCACHE); 11797481efa2Scsapuntz wdc_c.r_st_bmask = WDCS_DRDY; 11807481efa2Scsapuntz wdc_c.r_st_pmask = WDCS_DRDY; 11813e0c401eScsapuntz if (flags != 0) { 11823e0c401eScsapuntz wdc_c.flags = AT_POLL; 11833e0c401eScsapuntz } else { 11843e0c401eScsapuntz wdc_c.flags = AT_WAIT; 11853e0c401eScsapuntz } 11867481efa2Scsapuntz wdc_c.timeout = 30000; /* 30s timeout */ 11877481efa2Scsapuntz if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 11887481efa2Scsapuntz printf("%s: flush cache command didn't complete\n", 11897481efa2Scsapuntz wd->sc_dev.dv_xname); 11907481efa2Scsapuntz } 11917481efa2Scsapuntz if (wdc_c.flags & AT_TIMEOU) { 11927481efa2Scsapuntz printf("%s: flush cache command timeout\n", 11937481efa2Scsapuntz wd->sc_dev.dv_xname); 11947481efa2Scsapuntz } 11957481efa2Scsapuntz if (wdc_c.flags & AT_DF) { 11967481efa2Scsapuntz printf("%s: flush cache command: drive fault\n", 11977481efa2Scsapuntz wd->sc_dev.dv_xname); 11987481efa2Scsapuntz } 11997481efa2Scsapuntz /* 12007481efa2Scsapuntz * Ignore error register, it shouldn't report anything else 12017481efa2Scsapuntz * than COMMAND ABORTED, which means the device doesn't support 12027481efa2Scsapuntz * flush cache 12037481efa2Scsapuntz */ 12047481efa2Scsapuntz } 12057481efa2Scsapuntz 12067481efa2Scsapuntz void 1207f8fc1e98Skettenis wd_standby(struct wd_softc *wd, int flags) 1208f8fc1e98Skettenis { 1209f8fc1e98Skettenis struct wdc_command wdc_c; 1210f8fc1e98Skettenis 1211f8fc1e98Skettenis bzero(&wdc_c, sizeof(struct wdc_command)); 1212f8fc1e98Skettenis wdc_c.r_command = WDCC_STANDBY_IMMED; 1213f8fc1e98Skettenis wdc_c.r_st_bmask = WDCS_DRDY; 1214f8fc1e98Skettenis wdc_c.r_st_pmask = WDCS_DRDY; 1215f8fc1e98Skettenis if (flags != 0) { 1216f8fc1e98Skettenis wdc_c.flags = AT_POLL; 1217f8fc1e98Skettenis } else { 1218f8fc1e98Skettenis wdc_c.flags = AT_WAIT; 1219f8fc1e98Skettenis } 1220f8fc1e98Skettenis wdc_c.timeout = 1000; /* 1s timeout */ 1221f8fc1e98Skettenis if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 1222f8fc1e98Skettenis printf("%s: standby command didn't complete\n", 1223f8fc1e98Skettenis wd->sc_dev.dv_xname); 1224f8fc1e98Skettenis } 1225f8fc1e98Skettenis if (wdc_c.flags & AT_TIMEOU) { 1226f8fc1e98Skettenis printf("%s: standby command timeout\n", 1227f8fc1e98Skettenis wd->sc_dev.dv_xname); 1228f8fc1e98Skettenis } 1229f8fc1e98Skettenis if (wdc_c.flags & AT_DF) { 1230f8fc1e98Skettenis printf("%s: standby command: drive fault\n", 1231f8fc1e98Skettenis wd->sc_dev.dv_xname); 1232f8fc1e98Skettenis } 1233f8fc1e98Skettenis /* 1234f8fc1e98Skettenis * Ignore error register, it shouldn't report anything else 1235f8fc1e98Skettenis * than COMMAND ABORTED, which means the device doesn't support 1236f8fc1e98Skettenis * standby 1237f8fc1e98Skettenis */ 1238f8fc1e98Skettenis } 1239f8fc1e98Skettenis 1240f8fc1e98Skettenis void 124106ad70d2Sgrange wd_shutdown(void *arg) 12427481efa2Scsapuntz { 12437481efa2Scsapuntz struct wd_softc *wd = arg; 1244f8fc1e98Skettenis 12453e0c401eScsapuntz wd_flushcache(wd, AT_POLL); 1246f8fc1e98Skettenis wd_standby(wd, AT_POLL); 12477481efa2Scsapuntz } 1248