1*4b1a56afSjsg /* $OpenBSD: wd.c,v 1.128 2022/01/09 05:42:37 jsg 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 * 167481efa2Scsapuntz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 177481efa2Scsapuntz * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 187481efa2Scsapuntz * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 197481efa2Scsapuntz * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 207481efa2Scsapuntz * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 217481efa2Scsapuntz * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 227481efa2Scsapuntz * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 237481efa2Scsapuntz * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 247481efa2Scsapuntz * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 257481efa2Scsapuntz * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 267481efa2Scsapuntz */ 277481efa2Scsapuntz 287481efa2Scsapuntz /*- 29805cc48eSsf * Copyright (c) 1998, 2003, 2004 The NetBSD Foundation, Inc. 307481efa2Scsapuntz * All rights reserved. 317481efa2Scsapuntz * 327481efa2Scsapuntz * This code is derived from software contributed to The NetBSD Foundation 337481efa2Scsapuntz * by Charles M. Hannum and by Onno van der Linden. 347481efa2Scsapuntz * 357481efa2Scsapuntz * Redistribution and use in source and binary forms, with or without 367481efa2Scsapuntz * modification, are permitted provided that the following conditions 377481efa2Scsapuntz * are met: 387481efa2Scsapuntz * 1. Redistributions of source code must retain the above copyright 397481efa2Scsapuntz * notice, this list of conditions and the following disclaimer. 407481efa2Scsapuntz * 2. Redistributions in binary form must reproduce the above copyright 417481efa2Scsapuntz * notice, this list of conditions and the following disclaimer in the 427481efa2Scsapuntz * documentation and/or other materials provided with the distribution. 437481efa2Scsapuntz * 447481efa2Scsapuntz * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 457481efa2Scsapuntz * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 467481efa2Scsapuntz * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 477481efa2Scsapuntz * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 487481efa2Scsapuntz * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 497481efa2Scsapuntz * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 507481efa2Scsapuntz * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 517481efa2Scsapuntz * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 527481efa2Scsapuntz * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 537481efa2Scsapuntz * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 547481efa2Scsapuntz * POSSIBILITY OF SUCH DAMAGE. 557481efa2Scsapuntz */ 567481efa2Scsapuntz 577481efa2Scsapuntz #if 0 587481efa2Scsapuntz #include "rnd.h" 597481efa2Scsapuntz #endif 607481efa2Scsapuntz 617481efa2Scsapuntz #include <sys/param.h> 627481efa2Scsapuntz #include <sys/systm.h> 637481efa2Scsapuntz #include <sys/kernel.h> 647481efa2Scsapuntz #include <sys/conf.h> 65c0cd3489Sguenther #include <sys/fcntl.h> 667481efa2Scsapuntz #include <sys/stat.h> 677481efa2Scsapuntz #include <sys/ioctl.h> 68fd114b83Sthib #include <sys/mutex.h> 697481efa2Scsapuntz #include <sys/buf.h> 707481efa2Scsapuntz #include <sys/uio.h> 717481efa2Scsapuntz #include <sys/malloc.h> 727481efa2Scsapuntz #include <sys/device.h> 737481efa2Scsapuntz #include <sys/disklabel.h> 747481efa2Scsapuntz #include <sys/disk.h> 757481efa2Scsapuntz #include <sys/syslog.h> 767f58a11fSjsg #include <sys/timeout.h> 77f97ca3e4Scsapuntz #include <sys/vnode.h> 7891f4f7d8Sdlg #include <sys/dkio.h> 7914cbac95Skettenis #include <sys/reboot.h> 807481efa2Scsapuntz 817481efa2Scsapuntz #include <machine/intr.h> 827481efa2Scsapuntz #include <machine/bus.h> 837481efa2Scsapuntz 847481efa2Scsapuntz #include <dev/ata/atareg.h> 857481efa2Scsapuntz #include <dev/ata/atavar.h> 867481efa2Scsapuntz #include <dev/ata/wdvar.h> 877481efa2Scsapuntz #include <dev/ic/wdcreg.h> 88f5423f8cScsapuntz #include <dev/ic/wdcvar.h> 897481efa2Scsapuntz #if 0 907481efa2Scsapuntz #include "locators.h" 917481efa2Scsapuntz #endif 927481efa2Scsapuntz 9381a81281Saaron #define LBA48_THRESHOLD (0xfffffff) /* 128GB / DEV_BSIZE */ 9481a81281Saaron 957481efa2Scsapuntz #define WDIORETRIES_SINGLE 4 /* number of retries before single-sector */ 967481efa2Scsapuntz #define WDIORETRIES 5 /* number of retries before giving up */ 97c6a53756Skn #define RECOVERYTIME_MSEC 500 /* time to wait before retrying a cmd */ 987481efa2Scsapuntz 997481efa2Scsapuntz #define DEBUG_INTR 0x01 1007481efa2Scsapuntz #define DEBUG_XFERS 0x02 1017481efa2Scsapuntz #define DEBUG_STATUS 0x04 1027481efa2Scsapuntz #define DEBUG_FUNCS 0x08 1037481efa2Scsapuntz #define DEBUG_PROBE 0x10 1047481efa2Scsapuntz #ifdef WDCDEBUG 1057481efa2Scsapuntz extern int wdcdebug_wd_mask; /* init'ed in ata_wdc.c */ 106d6986e4fSgrange #define WDCDEBUG_PRINT(args, level) do { \ 107d6986e4fSgrange if ((wdcdebug_wd_mask & (level)) != 0) \ 108d6986e4fSgrange printf args; \ 109d6986e4fSgrange } while (0) 1107481efa2Scsapuntz #else 1117481efa2Scsapuntz #define WDCDEBUG_PRINT(args, level) 1127481efa2Scsapuntz #endif 1137481efa2Scsapuntz 1147481efa2Scsapuntz 1157481efa2Scsapuntz #define sc_drive sc_wdc_bio.drive 1167481efa2Scsapuntz #define sc_mode sc_wdc_bio.mode 1177481efa2Scsapuntz #define sc_multi sc_wdc_bio.multi 1187481efa2Scsapuntz 119c4071fd1Smillert int wdprobe(struct device *, void *, void *); 120c4071fd1Smillert void wdattach(struct device *, struct device *, void *); 121c4071fd1Smillert int wddetach(struct device *, int); 122e78728c7Spirofti int wdactivate(struct device *, int); 123c4071fd1Smillert int wdprint(void *, char *); 1247481efa2Scsapuntz 1257481efa2Scsapuntz struct cfattach wd_ca = { 126f97ca3e4Scsapuntz sizeof(struct wd_softc), wdprobe, wdattach, 127a23d39c4Smiod wddetach, wdactivate 1287481efa2Scsapuntz }; 1297481efa2Scsapuntz 1307481efa2Scsapuntz struct cfdriver wd_cd = { 1317481efa2Scsapuntz NULL, "wd", DV_DISK 1327481efa2Scsapuntz }; 1337481efa2Scsapuntz 134c4071fd1Smillert void wdgetdefaultlabel(struct wd_softc *, struct disklabel *); 135df591ed6Sderaadt int wdgetdisklabel(dev_t dev, struct wd_softc *, struct disklabel *, int); 136c4071fd1Smillert void wdstrategy(struct buf *); 137c4071fd1Smillert void wdstart(void *); 138c4071fd1Smillert void __wdstart(struct wd_softc*, struct buf *); 139c4071fd1Smillert void wdrestart(void *); 140c4071fd1Smillert int wd_get_params(struct wd_softc *, u_int8_t, struct ataparams *); 141805cc48eSsf int wd_flushcache(struct wd_softc *, int); 142f8fc1e98Skettenis void wd_standby(struct wd_softc *, int); 1437481efa2Scsapuntz 1447481efa2Scsapuntz /* XXX: these should go elsewhere */ 1457481efa2Scsapuntz cdev_decl(wd); 1467481efa2Scsapuntz bdev_decl(wd); 1477481efa2Scsapuntz 14831efee77Sjsing #define wdlookup(unit) (struct wd_softc *)disk_lookup(&wd_cd, (unit)) 149f97ca3e4Scsapuntz 1507481efa2Scsapuntz 1517481efa2Scsapuntz int 15206ad70d2Sgrange wdprobe(struct device *parent, void *match_, void *aux) 1537481efa2Scsapuntz { 1547481efa2Scsapuntz struct ata_atapi_attach *aa_link = aux; 1557481efa2Scsapuntz struct cfdata *match = match_; 1567481efa2Scsapuntz 1577481efa2Scsapuntz if (aa_link == NULL) 1587481efa2Scsapuntz return 0; 1597481efa2Scsapuntz if (aa_link->aa_type != T_ATA) 1607481efa2Scsapuntz return 0; 1617481efa2Scsapuntz 1627481efa2Scsapuntz if (match->cf_loc[0] != -1 && 1637481efa2Scsapuntz match->cf_loc[0] != aa_link->aa_channel) 1647481efa2Scsapuntz return 0; 1657481efa2Scsapuntz 1667481efa2Scsapuntz if (match->cf_loc[1] != -1 && 1677481efa2Scsapuntz match->cf_loc[1] != aa_link->aa_drv_data->drive) 1687481efa2Scsapuntz return 0; 1697481efa2Scsapuntz 1707481efa2Scsapuntz return 1; 1717481efa2Scsapuntz } 1727481efa2Scsapuntz 1737481efa2Scsapuntz void 17406ad70d2Sgrange wdattach(struct device *parent, struct device *self, void *aux) 1757481efa2Scsapuntz { 1767481efa2Scsapuntz struct wd_softc *wd = (void *)self; 1777481efa2Scsapuntz struct ata_atapi_attach *aa_link= aux; 17842a17170Sjsg struct wdc_command wdc_c; 1797481efa2Scsapuntz int i, blank; 1807481efa2Scsapuntz char buf[41], c, *p, *q; 1817481efa2Scsapuntz WDCDEBUG_PRINT(("wdattach\n"), DEBUG_FUNCS | DEBUG_PROBE); 1827481efa2Scsapuntz 1837481efa2Scsapuntz wd->openings = aa_link->aa_openings; 184f97ca3e4Scsapuntz wd->drvp = aa_link->aa_drv_data; 185f97ca3e4Scsapuntz 18602acd7cbSray strlcpy(wd->drvp->drive_name, wd->sc_dev.dv_xname, 18702acd7cbSray sizeof(wd->drvp->drive_name)); 188f97ca3e4Scsapuntz wd->drvp->cf_flags = wd->sc_dev.dv_cfdata->cf_flags; 1897481efa2Scsapuntz 190ec8802a4Scsapuntz if ((NERRS_MAX - 2) > 0) 191ec8802a4Scsapuntz wd->drvp->n_dmaerrs = NERRS_MAX - 2; 192ec8802a4Scsapuntz else 193ec8802a4Scsapuntz wd->drvp->n_dmaerrs = 0; 194ec8802a4Scsapuntz 1957481efa2Scsapuntz /* read our drive info */ 196f97ca3e4Scsapuntz if (wd_get_params(wd, at_poll, &wd->sc_params) != 0) { 1977481efa2Scsapuntz printf("%s: IDENTIFY failed\n", wd->sc_dev.dv_xname); 1987481efa2Scsapuntz return; 1997481efa2Scsapuntz } 2007481efa2Scsapuntz 2017481efa2Scsapuntz for (blank = 0, p = wd->sc_params.atap_model, q = buf, i = 0; 2027481efa2Scsapuntz i < sizeof(wd->sc_params.atap_model); i++) { 2037481efa2Scsapuntz c = *p++; 2047481efa2Scsapuntz if (c == '\0') 2057481efa2Scsapuntz break; 2067481efa2Scsapuntz if (c != ' ') { 2077481efa2Scsapuntz if (blank) { 2087481efa2Scsapuntz *q++ = ' '; 2097481efa2Scsapuntz blank = 0; 2107481efa2Scsapuntz } 2117481efa2Scsapuntz *q++ = c; 2127481efa2Scsapuntz } else 2137481efa2Scsapuntz blank = 1; 2147481efa2Scsapuntz } 2157481efa2Scsapuntz *q++ = '\0'; 2167481efa2Scsapuntz 2177481efa2Scsapuntz printf(": <%s>\n", buf); 2187481efa2Scsapuntz 21912dc5567Scsapuntz wdc_probe_caps(wd->drvp, &wd->sc_params); 22012dc5567Scsapuntz wdc_print_caps(wd->drvp); 22112dc5567Scsapuntz 2227481efa2Scsapuntz if ((wd->sc_params.atap_multi & 0xff) > 1) { 2237481efa2Scsapuntz wd->sc_multi = wd->sc_params.atap_multi & 0xff; 2247481efa2Scsapuntz } else { 2257481efa2Scsapuntz wd->sc_multi = 1; 2267481efa2Scsapuntz } 2277481efa2Scsapuntz 228c3a9df52Sderaadt printf("%s: %d-sector PIO,", wd->sc_dev.dv_xname, wd->sc_multi); 2297481efa2Scsapuntz 2304b4c6f4eSgluk /* use 48-bit LBA if enabled */ 2314b4c6f4eSgluk if ((wd->sc_params.atap_cmd2_en & ATAPI_CMD2_48AD) != 0) 2324b4c6f4eSgluk wd->sc_flags |= WDF_LBA48; 2334b4c6f4eSgluk 2347481efa2Scsapuntz /* Prior to ATA-4, LBA was optional. */ 2357481efa2Scsapuntz if ((wd->sc_params.atap_capabilities1 & WDC_CAP_LBA) != 0) 2367481efa2Scsapuntz wd->sc_flags |= WDF_LBA; 2377481efa2Scsapuntz #if 0 2387481efa2Scsapuntz /* ATA-4 requires LBA. */ 2397481efa2Scsapuntz if (wd->sc_params.atap_ataversion != 0xffff && 2407481efa2Scsapuntz wd->sc_params.atap_ataversion >= WDC_VER_ATA4) 2417481efa2Scsapuntz wd->sc_flags |= WDF_LBA; 2427481efa2Scsapuntz #endif 2437481efa2Scsapuntz 2444b4c6f4eSgluk if ((wd->sc_flags & WDF_LBA48) != 0) { 2454b4c6f4eSgluk wd->sc_capacity = 2464b4c6f4eSgluk (((u_int64_t)wd->sc_params.atap_max_lba[3] << 48) | 2474b4c6f4eSgluk ((u_int64_t)wd->sc_params.atap_max_lba[2] << 32) | 2484b4c6f4eSgluk ((u_int64_t)wd->sc_params.atap_max_lba[1] << 16) | 2494b4c6f4eSgluk (u_int64_t)wd->sc_params.atap_max_lba[0]); 2502a13a35bSgrange printf(" LBA48, %lluMB, %llu sectors\n", 2514b4c6f4eSgluk wd->sc_capacity / (1048576 / DEV_BSIZE), 2524b4c6f4eSgluk wd->sc_capacity); 2534b4c6f4eSgluk } else if ((wd->sc_flags & WDF_LBA) != 0) { 2547481efa2Scsapuntz wd->sc_capacity = 2557481efa2Scsapuntz (wd->sc_params.atap_capacity[1] << 16) | 2567481efa2Scsapuntz wd->sc_params.atap_capacity[0]; 2572a13a35bSgrange printf(" LBA, %lluMB, %llu sectors\n", 2587481efa2Scsapuntz wd->sc_capacity / (1048576 / DEV_BSIZE), 2597481efa2Scsapuntz wd->sc_capacity); 2607481efa2Scsapuntz } else { 2617481efa2Scsapuntz wd->sc_capacity = 2627481efa2Scsapuntz wd->sc_params.atap_cylinders * 2637481efa2Scsapuntz wd->sc_params.atap_heads * 2647481efa2Scsapuntz wd->sc_params.atap_sectors; 2654b4c6f4eSgluk printf(" CHS, %lluMB, %d cyl, %d head, %d sec, %llu sectors\n", 2667481efa2Scsapuntz wd->sc_capacity / (1048576 / DEV_BSIZE), 2677481efa2Scsapuntz wd->sc_params.atap_cylinders, 2687481efa2Scsapuntz wd->sc_params.atap_heads, 2697481efa2Scsapuntz wd->sc_params.atap_sectors, 2707481efa2Scsapuntz wd->sc_capacity); 2717481efa2Scsapuntz } 2727481efa2Scsapuntz WDCDEBUG_PRINT(("%s: atap_dmatiming_mimi=%d, atap_dmatiming_recom=%d\n", 2737481efa2Scsapuntz self->dv_xname, wd->sc_params.atap_dmatiming_mimi, 2747481efa2Scsapuntz wd->sc_params.atap_dmatiming_recom), DEBUG_PROBE); 27542a17170Sjsg 27644323affSjsg /* use read look ahead if supported */ 277119641fdSjsg if (wd->sc_params.atap_cmd_set1 & WDC_CMD1_AHEAD) { 278119641fdSjsg bzero(&wdc_c, sizeof(struct wdc_command)); 279119641fdSjsg wdc_c.r_command = SET_FEATURES; 280ee15be18Skrw wdc_c.r_features = WDSF_READAHEAD_EN; 281119641fdSjsg wdc_c.timeout = 1000; 282119641fdSjsg wdc_c.flags = at_poll; 283119641fdSjsg 284119641fdSjsg if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 285119641fdSjsg printf("%s: enable look ahead command didn't " 286119641fdSjsg "complete\n", wd->sc_dev.dv_xname); 287119641fdSjsg } 288119641fdSjsg } 289119641fdSjsg 29044323affSjsg /* use write cache if supported */ 291119641fdSjsg if (wd->sc_params.atap_cmd_set1 & WDC_CMD1_CACHE) { 292119641fdSjsg bzero(&wdc_c, sizeof(struct wdc_command)); 293119641fdSjsg wdc_c.r_command = SET_FEATURES; 294ee15be18Skrw wdc_c.r_features = WDSF_EN_WR_CACHE; 295119641fdSjsg wdc_c.timeout = 1000; 296119641fdSjsg wdc_c.flags = at_poll; 297119641fdSjsg 298119641fdSjsg if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 299119641fdSjsg printf("%s: enable write cache command didn't " 300119641fdSjsg "complete\n", wd->sc_dev.dv_xname); 301119641fdSjsg } 302119641fdSjsg } 30344323affSjsg 30442a17170Sjsg /* 305*4b1a56afSjsg * FREEZE LOCK the drive so malicious users can't lock it on us. 30642a17170Sjsg * As there is no harm in issuing this to drives that don't 30742a17170Sjsg * support the security feature set we just send it, and don't 30842a17170Sjsg * bother checking if the drive sends a command abort to tell us it 30942a17170Sjsg * doesn't support it. 31042a17170Sjsg */ 31142a17170Sjsg bzero(&wdc_c, sizeof(struct wdc_command)); 31242a17170Sjsg 31342a17170Sjsg wdc_c.r_command = WDCC_SEC_FREEZE_LOCK; 31442a17170Sjsg wdc_c.timeout = 1000; 31542a17170Sjsg wdc_c.flags = at_poll; 31642a17170Sjsg if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 31742a17170Sjsg printf("%s: freeze lock command didn't complete\n", 31842a17170Sjsg wd->sc_dev.dv_xname); 31942a17170Sjsg } 32042a17170Sjsg 3217481efa2Scsapuntz /* 32273b308c6Sjsing * Initialize disk structures. 3237481efa2Scsapuntz */ 3247481efa2Scsapuntz wd->sc_dk.dk_name = wd->sc_dev.dv_xname; 325c2235760Sdlg bufq_init(&wd->sc_bufq, BUFQ_DEFAULT); 3267c4d2af8Sart timeout_set(&wd->sc_restart_timeout, wdrestart, wd); 32773b308c6Sjsing 32873b308c6Sjsing /* Attach disk. */ 3292690bc4bSjsing disk_attach(&wd->sc_dev, &wd->sc_dk); 33073b308c6Sjsing wd->sc_wdc_bio.lp = wd->sc_dk.dk_label; 3317481efa2Scsapuntz } 3327481efa2Scsapuntz 333f97ca3e4Scsapuntz int 334e78728c7Spirofti wdactivate(struct device *self, int act) 335f97ca3e4Scsapuntz { 3366753d76aSkettenis struct wd_softc *wd = (void *)self; 337f97ca3e4Scsapuntz int rv = 0; 338f97ca3e4Scsapuntz 339f97ca3e4Scsapuntz switch (act) { 3406753d76aSkettenis case DVACT_SUSPEND: 3411e4b376cSderaadt break; 3421e4b376cSderaadt case DVACT_POWERDOWN: 34345326f51Skettenis wd_flushcache(wd, AT_POLL); 3441e4b376cSderaadt if (boothowto & RB_POWERDOWN) 345f8fc1e98Skettenis wd_standby(wd, AT_POLL); 3466753d76aSkettenis break; 347dff40dcaSmlarkin case DVACT_RESUME: 348dff40dcaSmlarkin /* 349dff40dcaSmlarkin * Do two resets separated by a small delay. The 350dff40dcaSmlarkin * first wakes the controller, the second resets 351ba8cbb4aSmiod * the channel. 352dff40dcaSmlarkin */ 353dff40dcaSmlarkin wdc_disable_intr(wd->drvp->chnl_softc); 354ba8cbb4aSmiod wdc_reset_channel(wd->drvp, 1); 355dff40dcaSmlarkin delay(10000); 356ba8cbb4aSmiod wdc_reset_channel(wd->drvp, 0); 357dff40dcaSmlarkin wdc_enable_intr(wd->drvp->chnl_softc); 358ba8cbb4aSmiod wd_get_params(wd, at_poll, &wd->sc_params); 359dff40dcaSmlarkin break; 360f97ca3e4Scsapuntz } 361f97ca3e4Scsapuntz return (rv); 362f97ca3e4Scsapuntz } 363f97ca3e4Scsapuntz 364f97ca3e4Scsapuntz int 36506ad70d2Sgrange wddetach(struct device *self, int flags) 366f97ca3e4Scsapuntz { 367f97ca3e4Scsapuntz struct wd_softc *sc = (struct wd_softc *)self; 368f97ca3e4Scsapuntz 3694072b0baSderaadt timeout_del(&sc->sc_restart_timeout); 3704072b0baSderaadt 37199b6117dSmatthew bufq_drain(&sc->sc_bufq); 372f97ca3e4Scsapuntz 373e1d64023Smatthew disk_gone(wdopen, self->dv_unit); 374f97ca3e4Scsapuntz 375a23d39c4Smiod /* Detach disk. */ 376c2235760Sdlg bufq_destroy(&sc->sc_bufq); 377a23d39c4Smiod disk_detach(&sc->sc_dk); 378a23d39c4Smiod 379f97ca3e4Scsapuntz return (0); 380f97ca3e4Scsapuntz } 381f97ca3e4Scsapuntz 3827481efa2Scsapuntz /* 3837481efa2Scsapuntz * Read/write routine for a buffer. Validates the arguments and schedules the 3847481efa2Scsapuntz * transfer. Does not wait for the transfer to complete. 3857481efa2Scsapuntz */ 3867481efa2Scsapuntz void 38706ad70d2Sgrange wdstrategy(struct buf *bp) 3887481efa2Scsapuntz { 389f97ca3e4Scsapuntz struct wd_softc *wd; 3907481efa2Scsapuntz int s; 391f97ca3e4Scsapuntz 392a83b1495Skrw wd = wdlookup(DISKUNIT(bp->b_dev)); 393f97ca3e4Scsapuntz if (wd == NULL) { 394f97ca3e4Scsapuntz bp->b_error = ENXIO; 395f97ca3e4Scsapuntz goto bad; 396f97ca3e4Scsapuntz } 397f97ca3e4Scsapuntz 3987481efa2Scsapuntz WDCDEBUG_PRINT(("wdstrategy (%s)\n", wd->sc_dev.dv_xname), 3997481efa2Scsapuntz DEBUG_XFERS); 4007481efa2Scsapuntz 4017481efa2Scsapuntz /* If device invalidated (e.g. media change, door open), error. */ 4027481efa2Scsapuntz if ((wd->sc_flags & WDF_LOADED) == 0) { 4037481efa2Scsapuntz bp->b_error = EIO; 4047481efa2Scsapuntz goto bad; 4057481efa2Scsapuntz } 4067481efa2Scsapuntz 407d1e5b14cSmatthew /* Validate the request. */ 408d1e5b14cSmatthew if (bounds_check_with_label(bp, wd->sc_dk.dk_label) == -1) 4097481efa2Scsapuntz goto done; 4107481efa2Scsapuntz 411d1e5b14cSmatthew /* Check that the number of sectors can fit in a byte. */ 412d1e5b14cSmatthew if ((bp->b_bcount / wd->sc_dk.dk_label->d_secsize) >= (1 << NBBY)) { 413d1e5b14cSmatthew bp->b_error = EINVAL; 414d1e5b14cSmatthew goto bad; 415d1e5b14cSmatthew } 416d1e5b14cSmatthew 4177481efa2Scsapuntz /* Queue transfer on drive, activate drive and controller if idle. */ 418c2235760Sdlg bufq_queue(&wd->sc_bufq, bp); 4197481efa2Scsapuntz s = splbio(); 4207481efa2Scsapuntz wdstart(wd); 4217481efa2Scsapuntz splx(s); 422f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 4237481efa2Scsapuntz return; 424d1e5b14cSmatthew 4257481efa2Scsapuntz bad: 4267481efa2Scsapuntz bp->b_flags |= B_ERROR; 4277481efa2Scsapuntz bp->b_resid = bp->b_bcount; 428d1e5b14cSmatthew done: 429343aa71cSart s = splbio(); 4307481efa2Scsapuntz biodone(bp); 431343aa71cSart splx(s); 432f97ca3e4Scsapuntz if (wd != NULL) 433f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 4347481efa2Scsapuntz } 4357481efa2Scsapuntz 4367481efa2Scsapuntz /* 4377481efa2Scsapuntz * Queue a drive for I/O. 4387481efa2Scsapuntz */ 4397481efa2Scsapuntz void 44006ad70d2Sgrange wdstart(void *arg) 4417481efa2Scsapuntz { 4427481efa2Scsapuntz struct wd_softc *wd = arg; 443fd114b83Sthib struct buf *bp = NULL; 4447481efa2Scsapuntz 4457481efa2Scsapuntz WDCDEBUG_PRINT(("wdstart %s\n", wd->sc_dev.dv_xname), 4467481efa2Scsapuntz DEBUG_XFERS); 4477481efa2Scsapuntz while (wd->openings > 0) { 4487481efa2Scsapuntz 4497481efa2Scsapuntz /* Is there a buf for us ? */ 450c2235760Sdlg if ((bp = bufq_dequeue(&wd->sc_bufq)) == NULL) 4517481efa2Scsapuntz return; 4527481efa2Scsapuntz /* 4537481efa2Scsapuntz * Make the command. First lock the device 4547481efa2Scsapuntz */ 4557481efa2Scsapuntz wd->openings--; 4567481efa2Scsapuntz 4577481efa2Scsapuntz wd->retries = 0; 4587481efa2Scsapuntz __wdstart(wd, bp); 4597481efa2Scsapuntz } 4607481efa2Scsapuntz } 4617481efa2Scsapuntz 4627481efa2Scsapuntz void 46306ad70d2Sgrange __wdstart(struct wd_softc *wd, struct buf *bp) 4647481efa2Scsapuntz { 4652e7ffe50Skrw struct disklabel *lp; 4662e7ffe50Skrw u_int64_t nsecs; 467cec793cfSgrange 4682e7ffe50Skrw lp = wd->sc_dk.dk_label; 4692e7ffe50Skrw wd->sc_wdc_bio.blkno = DL_BLKTOSEC(lp, bp->b_blkno + DL_SECTOBLK(lp, 4702e7ffe50Skrw DL_GETPOFFSET(&lp->d_partitions[DISKPART(bp->b_dev)]))); 4717481efa2Scsapuntz wd->sc_wdc_bio.blkdone =0; 4727481efa2Scsapuntz wd->sc_bp = bp; 4737481efa2Scsapuntz /* 4747481efa2Scsapuntz * If we're retrying, retry in single-sector mode. This will give us 4757481efa2Scsapuntz * the sector number of the problem, and will eventually allow the 4767481efa2Scsapuntz * transfer to succeed. 4777481efa2Scsapuntz */ 478a9a2d616Ssthen if (wd->retries >= WDIORETRIES_SINGLE) 4797481efa2Scsapuntz wd->sc_wdc_bio.flags = ATA_SINGLE; 4807481efa2Scsapuntz else 4817481efa2Scsapuntz wd->sc_wdc_bio.flags = 0; 4822e7ffe50Skrw nsecs = howmany(bp->b_bcount, lp->d_secsize); 483cec793cfSgrange if ((wd->sc_flags & WDF_LBA48) && 484cec793cfSgrange /* use LBA48 only if really need */ 4852e7ffe50Skrw ((wd->sc_wdc_bio.blkno + nsecs - 1 >= LBA48_THRESHOLD) || 4862e7ffe50Skrw (nsecs > 0xff))) 4874b4c6f4eSgluk wd->sc_wdc_bio.flags |= ATA_LBA48; 4887481efa2Scsapuntz if (wd->sc_flags & WDF_LBA) 4897481efa2Scsapuntz wd->sc_wdc_bio.flags |= ATA_LBA; 4907481efa2Scsapuntz if (bp->b_flags & B_READ) 4917481efa2Scsapuntz wd->sc_wdc_bio.flags |= ATA_READ; 4927481efa2Scsapuntz wd->sc_wdc_bio.bcount = bp->b_bcount; 4937481efa2Scsapuntz wd->sc_wdc_bio.databuf = bp->b_data; 494f97ca3e4Scsapuntz wd->sc_wdc_bio.wd = wd; 4957481efa2Scsapuntz /* Instrumentation. */ 4967481efa2Scsapuntz disk_busy(&wd->sc_dk); 4977481efa2Scsapuntz switch (wdc_ata_bio(wd->drvp, &wd->sc_wdc_bio)) { 4987481efa2Scsapuntz case WDC_TRY_AGAIN: 499e15e1c8cSblambert timeout_add_sec(&wd->sc_restart_timeout, 1); 5007481efa2Scsapuntz break; 5017481efa2Scsapuntz case WDC_QUEUED: 5027481efa2Scsapuntz break; 5037481efa2Scsapuntz case WDC_COMPLETE: 50413456e59Scsapuntz /* 50513456e59Scsapuntz * This code is never executed because we never set 50613456e59Scsapuntz * the ATA_POLL flag above 50713456e59Scsapuntz */ 50813456e59Scsapuntz #if 0 50913456e59Scsapuntz if (wd->sc_wdc_bio.flags & ATA_POLL) 5107481efa2Scsapuntz wddone(wd); 51113456e59Scsapuntz #endif 5127481efa2Scsapuntz break; 5137481efa2Scsapuntz default: 5147481efa2Scsapuntz panic("__wdstart: bad return code from wdc_ata_bio()"); 5157481efa2Scsapuntz } 5167481efa2Scsapuntz } 5177481efa2Scsapuntz 5187481efa2Scsapuntz void 51906ad70d2Sgrange wddone(void *v) 5207481efa2Scsapuntz { 5217481efa2Scsapuntz struct wd_softc *wd = v; 5227481efa2Scsapuntz struct buf *bp = wd->sc_bp; 5237481efa2Scsapuntz char buf[256], *errbuf = buf; 5247481efa2Scsapuntz WDCDEBUG_PRINT(("wddone %s\n", wd->sc_dev.dv_xname), 5257481efa2Scsapuntz DEBUG_XFERS); 5267481efa2Scsapuntz 5277481efa2Scsapuntz bp->b_resid = wd->sc_wdc_bio.bcount; 5287481efa2Scsapuntz errbuf[0] = '\0'; 5297481efa2Scsapuntz switch (wd->sc_wdc_bio.error) { 530f97ca3e4Scsapuntz case ERR_NODEV: 531f97ca3e4Scsapuntz bp->b_flags |= B_ERROR; 532f97ca3e4Scsapuntz bp->b_error = ENXIO; 533f97ca3e4Scsapuntz break; 5347481efa2Scsapuntz case ERR_DMA: 5357481efa2Scsapuntz errbuf = "DMA error"; 5367481efa2Scsapuntz goto retry; 5377481efa2Scsapuntz case ERR_DF: 5387481efa2Scsapuntz errbuf = "device fault"; 5397481efa2Scsapuntz goto retry; 5407481efa2Scsapuntz case TIMEOUT: 5417481efa2Scsapuntz errbuf = "device timeout"; 5427481efa2Scsapuntz goto retry; 5437481efa2Scsapuntz case ERROR: 5447481efa2Scsapuntz /* Don't care about media change bits */ 5457481efa2Scsapuntz if (wd->sc_wdc_bio.r_error != 0 && 5467481efa2Scsapuntz (wd->sc_wdc_bio.r_error & ~(WDCE_MC | WDCE_MCR)) == 0) 5477481efa2Scsapuntz goto noerror; 548b6136c85Sho ata_perror(wd->drvp, wd->sc_wdc_bio.r_error, errbuf, 549b6136c85Sho sizeof buf); 550f97ca3e4Scsapuntz retry: 551f97ca3e4Scsapuntz /* Just reset and retry. Can we do more ? */ 552ba8cbb4aSmiod wdc_reset_channel(wd->drvp, 0); 5537481efa2Scsapuntz diskerr(bp, "wd", errbuf, LOG_PRINTF, 5547481efa2Scsapuntz wd->sc_wdc_bio.blkdone, wd->sc_dk.dk_label); 5557481efa2Scsapuntz if (wd->retries++ < WDIORETRIES) { 5567481efa2Scsapuntz printf(", retrying\n"); 557c6a53756Skn timeout_add_msec(&wd->sc_restart_timeout, 558c6a53756Skn RECOVERYTIME_MSEC); 5597481efa2Scsapuntz return; 5607481efa2Scsapuntz } 5617481efa2Scsapuntz printf("\n"); 5627481efa2Scsapuntz bp->b_flags |= B_ERROR; 5637481efa2Scsapuntz bp->b_error = EIO; 5647481efa2Scsapuntz break; 5657481efa2Scsapuntz case NOERROR: 5667481efa2Scsapuntz noerror: if ((wd->sc_wdc_bio.flags & ATA_CORR) || wd->retries > 0) 5677481efa2Scsapuntz printf("%s: soft error (corrected)\n", 5687481efa2Scsapuntz wd->sc_dev.dv_xname); 5697481efa2Scsapuntz } 57035fa4c39Stedu disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid), 571d40269afSderaadt bp->b_blkno, (bp->b_flags & B_READ)); 5727481efa2Scsapuntz biodone(bp); 5737481efa2Scsapuntz wd->openings++; 5747481efa2Scsapuntz wdstart(wd); 5757481efa2Scsapuntz } 5767481efa2Scsapuntz 5777481efa2Scsapuntz void 57806ad70d2Sgrange wdrestart(void *v) 5797481efa2Scsapuntz { 5807481efa2Scsapuntz struct wd_softc *wd = v; 5817481efa2Scsapuntz struct buf *bp = wd->sc_bp; 5824072b0baSderaadt struct channel_softc *chnl; 5837481efa2Scsapuntz int s; 5847481efa2Scsapuntz WDCDEBUG_PRINT(("wdrestart %s\n", wd->sc_dev.dv_xname), 5857481efa2Scsapuntz DEBUG_XFERS); 5867481efa2Scsapuntz 5874072b0baSderaadt chnl = (struct channel_softc *)(wd->drvp->chnl_softc); 5884072b0baSderaadt if (chnl->dying) 5894072b0baSderaadt return; 5904072b0baSderaadt 5917481efa2Scsapuntz s = splbio(); 592d40269afSderaadt disk_unbusy(&wd->sc_dk, 0, 0, (bp->b_flags & B_READ)); 5937481efa2Scsapuntz __wdstart(v, bp); 5947481efa2Scsapuntz splx(s); 5957481efa2Scsapuntz } 5967481efa2Scsapuntz 5977481efa2Scsapuntz int 59806ad70d2Sgrange wdread(dev_t dev, struct uio *uio, int flags) 5997481efa2Scsapuntz { 6007481efa2Scsapuntz 6017481efa2Scsapuntz WDCDEBUG_PRINT(("wdread\n"), DEBUG_XFERS); 60290f6ca67Smatthew return (physio(wdstrategy, dev, B_READ, minphys, uio)); 6037481efa2Scsapuntz } 6047481efa2Scsapuntz 6057481efa2Scsapuntz int 60606ad70d2Sgrange wdwrite(dev_t dev, struct uio *uio, int flags) 6077481efa2Scsapuntz { 6087481efa2Scsapuntz 6097481efa2Scsapuntz WDCDEBUG_PRINT(("wdwrite\n"), DEBUG_XFERS); 61090f6ca67Smatthew return (physio(wdstrategy, dev, B_WRITE, minphys, uio)); 6117481efa2Scsapuntz } 6127481efa2Scsapuntz 6137481efa2Scsapuntz int 61406ad70d2Sgrange wdopen(dev_t dev, int flag, int fmt, struct proc *p) 6157481efa2Scsapuntz { 6167481efa2Scsapuntz struct wd_softc *wd; 6174072b0baSderaadt struct channel_softc *chnl; 6187481efa2Scsapuntz int unit, part; 6197481efa2Scsapuntz int error; 6207481efa2Scsapuntz 6217481efa2Scsapuntz WDCDEBUG_PRINT(("wdopen\n"), DEBUG_FUNCS); 622f97ca3e4Scsapuntz 623a83b1495Skrw unit = DISKUNIT(dev); 624f97ca3e4Scsapuntz wd = wdlookup(unit); 6257481efa2Scsapuntz if (wd == NULL) 6267481efa2Scsapuntz return ENXIO; 6274072b0baSderaadt chnl = (struct channel_softc *)(wd->drvp->chnl_softc); 6284072b0baSderaadt if (chnl->dying) 6294072b0baSderaadt return (ENXIO); 6307481efa2Scsapuntz 6317481efa2Scsapuntz /* 6327481efa2Scsapuntz * If this is the first open of this device, add a reference 6337481efa2Scsapuntz * to the adapter. 6347481efa2Scsapuntz */ 63585bbddc9Sderaadt if ((error = disk_lock(&wd->sc_dk)) != 0) 6367481efa2Scsapuntz goto bad4; 6377481efa2Scsapuntz 6387481efa2Scsapuntz if (wd->sc_dk.dk_openmask != 0) { 6397481efa2Scsapuntz /* 6407481efa2Scsapuntz * If any partition is open, but the disk has been invalidated, 6417481efa2Scsapuntz * disallow further opens. 6427481efa2Scsapuntz */ 6437481efa2Scsapuntz if ((wd->sc_flags & WDF_LOADED) == 0) { 6447481efa2Scsapuntz error = EIO; 6457481efa2Scsapuntz goto bad3; 6467481efa2Scsapuntz } 6477481efa2Scsapuntz } else { 6487481efa2Scsapuntz if ((wd->sc_flags & WDF_LOADED) == 0) { 6497481efa2Scsapuntz wd->sc_flags |= WDF_LOADED; 6507481efa2Scsapuntz 6517481efa2Scsapuntz /* Load the physical device parameters. */ 652e1a5e72aScsapuntz wd_get_params(wd, AT_WAIT, &wd->sc_params); 6537481efa2Scsapuntz 6547481efa2Scsapuntz /* Load the partition info if not already loaded. */ 655df591ed6Sderaadt if (wdgetdisklabel(dev, wd, 656df591ed6Sderaadt wd->sc_dk.dk_label, 0) == EIO) { 657df591ed6Sderaadt error = EIO; 658df591ed6Sderaadt goto bad; 659df591ed6Sderaadt } 6607481efa2Scsapuntz } 6617481efa2Scsapuntz } 6627481efa2Scsapuntz 663a83b1495Skrw part = DISKPART(dev); 6647481efa2Scsapuntz 665e1d64023Smatthew if ((error = disk_openpart(&wd->sc_dk, part, fmt, 1)) != 0) 6667481efa2Scsapuntz goto bad; 6677481efa2Scsapuntz 66885bbddc9Sderaadt disk_unlock(&wd->sc_dk); 669f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 6707481efa2Scsapuntz return 0; 6717481efa2Scsapuntz 6727481efa2Scsapuntz bad: 6737481efa2Scsapuntz if (wd->sc_dk.dk_openmask == 0) { 6747481efa2Scsapuntz } 6757481efa2Scsapuntz 6767481efa2Scsapuntz bad3: 67785bbddc9Sderaadt disk_unlock(&wd->sc_dk); 6787481efa2Scsapuntz bad4: 679f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 6807481efa2Scsapuntz return error; 6817481efa2Scsapuntz } 6827481efa2Scsapuntz 6837481efa2Scsapuntz int 68406ad70d2Sgrange wdclose(dev_t dev, int flag, int fmt, struct proc *p) 6857481efa2Scsapuntz { 686f97ca3e4Scsapuntz struct wd_softc *wd; 687a83b1495Skrw int part = DISKPART(dev); 688f97ca3e4Scsapuntz 689a83b1495Skrw wd = wdlookup(DISKUNIT(dev)); 690f97ca3e4Scsapuntz if (wd == NULL) 691f97ca3e4Scsapuntz return ENXIO; 6927481efa2Scsapuntz 6937481efa2Scsapuntz WDCDEBUG_PRINT(("wdclose\n"), DEBUG_FUNCS); 6945f30bcfcSderaadt 6955f30bcfcSderaadt disk_lock_nointr(&wd->sc_dk); 6967481efa2Scsapuntz 697e1d64023Smatthew disk_closepart(&wd->sc_dk, part, fmt); 6987481efa2Scsapuntz 6997481efa2Scsapuntz if (wd->sc_dk.dk_openmask == 0) { 700edc82994Sderaadt wd_flushcache(wd, AT_WAIT); 7017481efa2Scsapuntz /* XXXX Must wait for I/O to complete! */ 7027481efa2Scsapuntz } 7037481efa2Scsapuntz 70485bbddc9Sderaadt disk_unlock(&wd->sc_dk); 705f97ca3e4Scsapuntz 706f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 7075f30bcfcSderaadt return (0); 7087481efa2Scsapuntz } 7097481efa2Scsapuntz 7107481efa2Scsapuntz void 71106ad70d2Sgrange wdgetdefaultlabel(struct wd_softc *wd, struct disklabel *lp) 7127481efa2Scsapuntz { 7137481efa2Scsapuntz WDCDEBUG_PRINT(("wdgetdefaultlabel\n"), DEBUG_FUNCS); 714afaefa16Sniklas bzero(lp, sizeof(struct disklabel)); 7157481efa2Scsapuntz 7167481efa2Scsapuntz lp->d_secsize = DEV_BSIZE; 717e16633b4Sderaadt DL_SETDSIZE(lp, wd->sc_capacity); 7187481efa2Scsapuntz lp->d_ntracks = wd->sc_params.atap_heads; 7197481efa2Scsapuntz lp->d_nsectors = wd->sc_params.atap_sectors; 7207481efa2Scsapuntz lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 721e16633b4Sderaadt lp->d_ncylinders = DL_GETDSIZE(lp) / lp->d_secpercyl; 722a2c80ea8Smillert if (wd->drvp->ata_vers == -1) { 7237481efa2Scsapuntz lp->d_type = DTYPE_ST506; 724bf3a6870Skrw strncpy(lp->d_typename, "ST506/MFM/RLL", sizeof lp->d_typename); 7257481efa2Scsapuntz } else { 7267481efa2Scsapuntz lp->d_type = DTYPE_ESDI; 727bf3a6870Skrw strncpy(lp->d_typename, "ESDI/IDE disk", sizeof lp->d_typename); 7287481efa2Scsapuntz } 729a2c80ea8Smillert /* XXX - user viscopy() like sd.c */ 730bf3a6870Skrw strncpy(lp->d_packname, wd->sc_params.atap_model, sizeof lp->d_packname); 7317481efa2Scsapuntz lp->d_flags = 0; 73222230921Sderaadt lp->d_version = 1; 7337481efa2Scsapuntz 7347481efa2Scsapuntz lp->d_magic = DISKMAGIC; 7357481efa2Scsapuntz lp->d_magic2 = DISKMAGIC; 7367481efa2Scsapuntz lp->d_checksum = dkcksum(lp); 7377481efa2Scsapuntz } 7387481efa2Scsapuntz 7397481efa2Scsapuntz /* 7407481efa2Scsapuntz * Fabricate a default disk label, and try to read the correct one. 7417481efa2Scsapuntz */ 742df591ed6Sderaadt int 74306ad70d2Sgrange wdgetdisklabel(dev_t dev, struct wd_softc *wd, struct disklabel *lp, 744c326f117Sderaadt int spoofonly) 7457481efa2Scsapuntz { 746df591ed6Sderaadt int error; 7477481efa2Scsapuntz 7487481efa2Scsapuntz WDCDEBUG_PRINT(("wdgetdisklabel\n"), DEBUG_FUNCS); 7497481efa2Scsapuntz 7507481efa2Scsapuntz wdgetdefaultlabel(wd, lp); 7517481efa2Scsapuntz 7527481efa2Scsapuntz if (wd->drvp->state > RECAL) 7537481efa2Scsapuntz wd->drvp->drive_flags |= DRIVE_RESET; 754df591ed6Sderaadt error = readdisklabel(DISKLABELDEV(dev), wdstrategy, lp, 755657ac4baSkrw spoofonly); 7567481efa2Scsapuntz if (wd->drvp->state > RECAL) 7577481efa2Scsapuntz wd->drvp->drive_flags |= DRIVE_RESET; 758df591ed6Sderaadt return (error); 7597481efa2Scsapuntz } 7607481efa2Scsapuntz 7617481efa2Scsapuntz int 76206ad70d2Sgrange wdioctl(dev_t dev, u_long xfer, caddr_t addr, int flag, struct proc *p) 7637481efa2Scsapuntz { 764f97ca3e4Scsapuntz struct wd_softc *wd; 765ce578d69Sderaadt struct disklabel *lp; 766f97ca3e4Scsapuntz int error = 0; 7677481efa2Scsapuntz 7687481efa2Scsapuntz WDCDEBUG_PRINT(("wdioctl\n"), DEBUG_FUNCS); 7697481efa2Scsapuntz 770a83b1495Skrw wd = wdlookup(DISKUNIT(dev)); 771f97ca3e4Scsapuntz if (wd == NULL) 772f97ca3e4Scsapuntz return ENXIO; 773f97ca3e4Scsapuntz 774f97ca3e4Scsapuntz if ((wd->sc_flags & WDF_LOADED) == 0) { 775f97ca3e4Scsapuntz error = EIO; 776f97ca3e4Scsapuntz goto exit; 777f97ca3e4Scsapuntz } 7787481efa2Scsapuntz 7797481efa2Scsapuntz switch (xfer) { 7801dfe2d86Sderaadt case DIOCRLDINFO: 781ce578d69Sderaadt lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK); 782c326f117Sderaadt wdgetdisklabel(dev, wd, lp, 0); 783ce578d69Sderaadt bcopy(lp, wd->sc_dk.dk_label, sizeof(*lp)); 784688f9b10Sderaadt free(lp, M_TEMP, sizeof(*lp)); 785f97ca3e4Scsapuntz goto exit; 786ce578d69Sderaadt 787c326f117Sderaadt case DIOCGPDINFO: 788c326f117Sderaadt wdgetdisklabel(dev, wd, (struct disklabel *)addr, 1); 789f97ca3e4Scsapuntz goto exit; 790e1a5e72aScsapuntz 7917481efa2Scsapuntz case DIOCGDINFO: 7927481efa2Scsapuntz *(struct disklabel *)addr = *(wd->sc_dk.dk_label); 793f97ca3e4Scsapuntz goto exit; 7947481efa2Scsapuntz 7957481efa2Scsapuntz case DIOCGPART: 7967481efa2Scsapuntz ((struct partinfo *)addr)->disklab = wd->sc_dk.dk_label; 7977481efa2Scsapuntz ((struct partinfo *)addr)->part = 798a83b1495Skrw &wd->sc_dk.dk_label->d_partitions[DISKPART(dev)]; 799f97ca3e4Scsapuntz goto exit; 8007481efa2Scsapuntz 8017481efa2Scsapuntz case DIOCWDINFO: 8027481efa2Scsapuntz case DIOCSDINFO: 803f97ca3e4Scsapuntz if ((flag & FWRITE) == 0) { 804f97ca3e4Scsapuntz error = EBADF; 805f97ca3e4Scsapuntz goto exit; 806f97ca3e4Scsapuntz } 8077481efa2Scsapuntz 80885bbddc9Sderaadt if ((error = disk_lock(&wd->sc_dk)) != 0) 809f97ca3e4Scsapuntz goto exit; 8107481efa2Scsapuntz 8117481efa2Scsapuntz error = setdisklabel(wd->sc_dk.dk_label, 812a2f17f2eSmatthew (struct disklabel *)addr, wd->sc_dk.dk_openmask); 8137481efa2Scsapuntz if (error == 0) { 8147481efa2Scsapuntz if (wd->drvp->state > RECAL) 8157481efa2Scsapuntz wd->drvp->drive_flags |= DRIVE_RESET; 8167481efa2Scsapuntz if (xfer == DIOCWDINFO) 817a83b1495Skrw error = writedisklabel(DISKLABELDEV(dev), 818c326f117Sderaadt wdstrategy, wd->sc_dk.dk_label); 8197481efa2Scsapuntz } 8207481efa2Scsapuntz 82185bbddc9Sderaadt disk_unlock(&wd->sc_dk); 822f97ca3e4Scsapuntz goto exit; 8237481efa2Scsapuntz 824805cc48eSsf case DIOCCACHESYNC: 825805cc48eSsf if ((flag & FWRITE) == 0) { 826805cc48eSsf error = EBADF; 827805cc48eSsf goto exit; 828805cc48eSsf } 829805cc48eSsf error = wd_flushcache(wd, AT_WAIT); 830805cc48eSsf goto exit; 831805cc48eSsf 8327481efa2Scsapuntz default: 83318885d83Smickey error = wdc_ioctl(wd->drvp, xfer, addr, flag, p); 834f97ca3e4Scsapuntz goto exit; 8357481efa2Scsapuntz } 8367481efa2Scsapuntz 8377481efa2Scsapuntz #ifdef DIAGNOSTIC 8387481efa2Scsapuntz panic("wdioctl: impossible"); 8397481efa2Scsapuntz #endif 840f97ca3e4Scsapuntz 841f97ca3e4Scsapuntz exit: 842f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 843f97ca3e4Scsapuntz return (error); 8447481efa2Scsapuntz } 8457481efa2Scsapuntz 8467481efa2Scsapuntz #ifdef B_FORMAT 8477481efa2Scsapuntz int 8487481efa2Scsapuntz wdformat(struct buf *bp) 8497481efa2Scsapuntz { 8507481efa2Scsapuntz 8517481efa2Scsapuntz bp->b_flags |= B_FORMAT; 8527481efa2Scsapuntz return wdstrategy(bp); 8537481efa2Scsapuntz } 8547481efa2Scsapuntz #endif 8557481efa2Scsapuntz 8561abdbfdeSderaadt daddr_t 85706ad70d2Sgrange wdsize(dev_t dev) 8587481efa2Scsapuntz { 8597481efa2Scsapuntz struct wd_softc *wd; 8602d8e523bSkrw struct disklabel *lp; 861f97ca3e4Scsapuntz int part, omask; 862c1e37bc9Skrw daddr_t size; 8637481efa2Scsapuntz 8647481efa2Scsapuntz WDCDEBUG_PRINT(("wdsize\n"), DEBUG_FUNCS); 8657481efa2Scsapuntz 866a83b1495Skrw wd = wdlookup(DISKUNIT(dev)); 8677481efa2Scsapuntz if (wd == NULL) 8687481efa2Scsapuntz return (-1); 8697481efa2Scsapuntz 870a83b1495Skrw part = DISKPART(dev); 8717481efa2Scsapuntz omask = wd->sc_dk.dk_openmask & (1 << part); 8727481efa2Scsapuntz 873f97ca3e4Scsapuntz if (omask == 0 && wdopen(dev, 0, S_IFBLK, NULL) != 0) { 874f97ca3e4Scsapuntz size = -1; 875f97ca3e4Scsapuntz goto exit; 876f97ca3e4Scsapuntz } 877f97ca3e4Scsapuntz 8782d8e523bSkrw lp = wd->sc_dk.dk_label; 8792d8e523bSkrw size = DL_SECTOBLK(lp, DL_GETPSIZE(&lp->d_partitions[part])); 8807481efa2Scsapuntz if (omask == 0 && wdclose(dev, 0, S_IFBLK, NULL) != 0) 881f97ca3e4Scsapuntz size = -1; 882f97ca3e4Scsapuntz 883f97ca3e4Scsapuntz exit: 884f97ca3e4Scsapuntz device_unref(&wd->sc_dev); 8857481efa2Scsapuntz return (size); 8867481efa2Scsapuntz } 8877481efa2Scsapuntz 8887481efa2Scsapuntz /* #define WD_DUMP_NOT_TRUSTED if you just want to watch */ 8897481efa2Scsapuntz static int wddoingadump = 0; 8907481efa2Scsapuntz static int wddumprecalibrated = 0; 8917481efa2Scsapuntz static int wddumpmulti = 1; 8927481efa2Scsapuntz 8937481efa2Scsapuntz /* 8947481efa2Scsapuntz * Dump core after a system crash. 8957481efa2Scsapuntz */ 8967481efa2Scsapuntz int 8971abdbfdeSderaadt wddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size) 8987481efa2Scsapuntz { 8997481efa2Scsapuntz struct wd_softc *wd; /* disk unit to do the I/O */ 9007481efa2Scsapuntz struct disklabel *lp; /* disk's disklabel */ 9017481efa2Scsapuntz int unit, part; 9027481efa2Scsapuntz int nblks; /* total number of sectors left to write */ 903b2c3bd7dSkrw int nwrt; /* sectors to write with current i/o. */ 9047481efa2Scsapuntz int err; 9057481efa2Scsapuntz char errbuf[256]; 9067481efa2Scsapuntz 9077481efa2Scsapuntz /* Check if recursive dump; if so, punt. */ 9087481efa2Scsapuntz if (wddoingadump) 9097481efa2Scsapuntz return EFAULT; 9107481efa2Scsapuntz wddoingadump = 1; 9117481efa2Scsapuntz 912a83b1495Skrw unit = DISKUNIT(dev); 913f97ca3e4Scsapuntz wd = wdlookup(unit); 914f97ca3e4Scsapuntz if (wd == NULL) 9157481efa2Scsapuntz return ENXIO; 9167481efa2Scsapuntz 917a83b1495Skrw part = DISKPART(dev); 9187481efa2Scsapuntz 9197481efa2Scsapuntz /* Make sure it was initialized. */ 9207481efa2Scsapuntz if (wd->drvp->state < READY) 9217481efa2Scsapuntz return ENXIO; 9227481efa2Scsapuntz 9237481efa2Scsapuntz /* Convert to disk sectors. Request must be a multiple of size. */ 9247481efa2Scsapuntz lp = wd->sc_dk.dk_label; 9257481efa2Scsapuntz if ((size % lp->d_secsize) != 0) 9267481efa2Scsapuntz return EFAULT; 9277481efa2Scsapuntz nblks = size / lp->d_secsize; 9287481efa2Scsapuntz blkno = blkno / (lp->d_secsize / DEV_BSIZE); 9297481efa2Scsapuntz 9307481efa2Scsapuntz /* Check transfer bounds against partition size. */ 931e16633b4Sderaadt if ((blkno < 0) || ((blkno + nblks) > DL_GETPSIZE(&lp->d_partitions[part]))) 9327481efa2Scsapuntz return EINVAL; 9337481efa2Scsapuntz 9347481efa2Scsapuntz /* Offset block number to start of partition. */ 935e16633b4Sderaadt blkno += DL_GETPOFFSET(&lp->d_partitions[part]); 9367481efa2Scsapuntz 9377481efa2Scsapuntz /* Recalibrate, if first dump transfer. */ 9387481efa2Scsapuntz if (wddumprecalibrated == 0) { 9397481efa2Scsapuntz wddumpmulti = wd->sc_multi; 9407481efa2Scsapuntz wddumprecalibrated = 1; 9417481efa2Scsapuntz wd->drvp->state = RECAL; 9427481efa2Scsapuntz } 9437481efa2Scsapuntz 9447481efa2Scsapuntz while (nblks > 0) { 945b2c3bd7dSkrw nwrt = min(nblks, wddumpmulti); 9467481efa2Scsapuntz wd->sc_wdc_bio.blkno = blkno; 9477481efa2Scsapuntz wd->sc_wdc_bio.flags = ATA_POLL; 9484b4c6f4eSgluk if (wd->sc_flags & WDF_LBA48) 9494b4c6f4eSgluk wd->sc_wdc_bio.flags |= ATA_LBA48; 9507481efa2Scsapuntz if (wd->sc_flags & WDF_LBA) 9517481efa2Scsapuntz wd->sc_wdc_bio.flags |= ATA_LBA; 952b2c3bd7dSkrw wd->sc_wdc_bio.bcount = nwrt * lp->d_secsize; 9537481efa2Scsapuntz wd->sc_wdc_bio.databuf = va; 954f97ca3e4Scsapuntz wd->sc_wdc_bio.wd = wd; 9557481efa2Scsapuntz #ifndef WD_DUMP_NOT_TRUSTED 9567481efa2Scsapuntz switch (wdc_ata_bio(wd->drvp, &wd->sc_wdc_bio)) { 9577481efa2Scsapuntz case WDC_TRY_AGAIN: 9587481efa2Scsapuntz panic("wddump: try again"); 9597481efa2Scsapuntz break; 9607481efa2Scsapuntz case WDC_QUEUED: 9617481efa2Scsapuntz panic("wddump: polled command has been queued"); 9627481efa2Scsapuntz break; 9637481efa2Scsapuntz case WDC_COMPLETE: 9647481efa2Scsapuntz break; 9657481efa2Scsapuntz } 9667481efa2Scsapuntz switch(wd->sc_wdc_bio.error) { 9677481efa2Scsapuntz case TIMEOUT: 9687481efa2Scsapuntz printf("wddump: device timed out"); 9697481efa2Scsapuntz err = EIO; 9707481efa2Scsapuntz break; 9717481efa2Scsapuntz case ERR_DF: 9727481efa2Scsapuntz printf("wddump: drive fault"); 9737481efa2Scsapuntz err = EIO; 9747481efa2Scsapuntz break; 9757481efa2Scsapuntz case ERR_DMA: 9767481efa2Scsapuntz printf("wddump: DMA error"); 9777481efa2Scsapuntz err = EIO; 9787481efa2Scsapuntz break; 9797481efa2Scsapuntz case ERROR: 9807481efa2Scsapuntz errbuf[0] = '\0'; 981b6136c85Sho ata_perror(wd->drvp, wd->sc_wdc_bio.r_error, errbuf, 982b6136c85Sho sizeof errbuf); 9837481efa2Scsapuntz printf("wddump: %s", errbuf); 9847481efa2Scsapuntz err = EIO; 9857481efa2Scsapuntz break; 9867481efa2Scsapuntz case NOERROR: 9877481efa2Scsapuntz err = 0; 9887481efa2Scsapuntz break; 9897481efa2Scsapuntz default: 9907481efa2Scsapuntz panic("wddump: unknown error type"); 9917481efa2Scsapuntz } 9927481efa2Scsapuntz if (err != 0) { 9937481efa2Scsapuntz printf("\n"); 9947481efa2Scsapuntz return err; 9957481efa2Scsapuntz } 9967481efa2Scsapuntz #else /* WD_DUMP_NOT_TRUSTED */ 9977481efa2Scsapuntz /* Let's just talk about this first... */ 9987481efa2Scsapuntz printf("wd%d: dump addr 0x%x, cylin %d, head %d, sector %d\n", 9997481efa2Scsapuntz unit, va, cylin, head, sector); 10007481efa2Scsapuntz delay(500 * 1000); /* half a second */ 10017481efa2Scsapuntz #endif 10027481efa2Scsapuntz 10037481efa2Scsapuntz /* update block count */ 1004b2c3bd7dSkrw nblks -= nwrt; 1005b2c3bd7dSkrw blkno += nwrt; 1006b2c3bd7dSkrw va += nwrt * lp->d_secsize; 10077481efa2Scsapuntz } 10087481efa2Scsapuntz 10097481efa2Scsapuntz wddoingadump = 0; 10107481efa2Scsapuntz return 0; 10117481efa2Scsapuntz } 10127481efa2Scsapuntz 10137481efa2Scsapuntz int 101406ad70d2Sgrange wd_get_params(struct wd_softc *wd, u_int8_t flags, struct ataparams *params) 10157481efa2Scsapuntz { 10167481efa2Scsapuntz switch (ata_get_params(wd->drvp, flags, params)) { 10177481efa2Scsapuntz case CMD_AGAIN: 10187481efa2Scsapuntz return 1; 10197481efa2Scsapuntz case CMD_ERR: 1020e9207eccSuwe /* If we already have drive parameters, reuse them. */ 1021e880da88Suwe if (wd->sc_params.atap_cylinders != 0) { 1022e9207eccSuwe if (params != &wd->sc_params) 1023e9207eccSuwe bcopy(&wd->sc_params, params, 1024e9207eccSuwe sizeof(struct ataparams)); 1025e880da88Suwe return 0; 1026e880da88Suwe } 10277481efa2Scsapuntz /* 10287481efa2Scsapuntz * We `know' there's a drive here; just assume it's old. 10297481efa2Scsapuntz * This geometry is only used to read the MBR and print a 10307481efa2Scsapuntz * (false) attach message. 10317481efa2Scsapuntz */ 1032e9207eccSuwe bzero(params, sizeof(struct ataparams)); 10337481efa2Scsapuntz strncpy(params->atap_model, "ST506", 10347481efa2Scsapuntz sizeof params->atap_model); 10357481efa2Scsapuntz params->atap_config = ATA_CFG_FIXED; 10367481efa2Scsapuntz params->atap_cylinders = 1024; 10377481efa2Scsapuntz params->atap_heads = 8; 10387481efa2Scsapuntz params->atap_sectors = 17; 10397481efa2Scsapuntz params->atap_multi = 1; 10407481efa2Scsapuntz params->atap_capabilities1 = params->atap_capabilities2 = 0; 10417481efa2Scsapuntz wd->drvp->ata_vers = -1; /* Mark it as pre-ATA */ 10427481efa2Scsapuntz return 0; 10437481efa2Scsapuntz case CMD_OK: 10447481efa2Scsapuntz return 0; 10457481efa2Scsapuntz default: 10467481efa2Scsapuntz panic("wd_get_params: bad return code from ata_get_params"); 10477481efa2Scsapuntz /* NOTREACHED */ 10487481efa2Scsapuntz } 10497481efa2Scsapuntz } 10507481efa2Scsapuntz 1051805cc48eSsf int 105206ad70d2Sgrange wd_flushcache(struct wd_softc *wd, int flags) 10537481efa2Scsapuntz { 10547481efa2Scsapuntz struct wdc_command wdc_c; 10557481efa2Scsapuntz 10567481efa2Scsapuntz if (wd->drvp->ata_vers < 4) /* WDCC_FLUSHCACHE is here since ATA-4 */ 1057805cc48eSsf return EIO; 1058afaefa16Sniklas bzero(&wdc_c, sizeof(struct wdc_command)); 10595a1200c8Sgrange wdc_c.r_command = (wd->sc_flags & WDF_LBA48 ? WDCC_FLUSHCACHE_EXT : 10605a1200c8Sgrange WDCC_FLUSHCACHE); 10617481efa2Scsapuntz wdc_c.r_st_bmask = WDCS_DRDY; 10627481efa2Scsapuntz wdc_c.r_st_pmask = WDCS_DRDY; 1063edc82994Sderaadt wdc_c.flags = flags; 10647481efa2Scsapuntz wdc_c.timeout = 30000; /* 30s timeout */ 10657481efa2Scsapuntz if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 10667481efa2Scsapuntz printf("%s: flush cache command didn't complete\n", 10677481efa2Scsapuntz wd->sc_dev.dv_xname); 1068805cc48eSsf return EIO; 10697481efa2Scsapuntz } 1070805cc48eSsf if (wdc_c.flags & ERR_NODEV) 1071805cc48eSsf return ENODEV; 10727481efa2Scsapuntz if (wdc_c.flags & AT_TIMEOU) { 10737481efa2Scsapuntz printf("%s: flush cache command timeout\n", 10747481efa2Scsapuntz wd->sc_dev.dv_xname); 1075805cc48eSsf return EIO; 1076805cc48eSsf } 1077805cc48eSsf if (wdc_c.flags & AT_ERROR) { 1078805cc48eSsf if (wdc_c.r_error == WDCE_ABRT) /* command not supported */ 1079805cc48eSsf return ENODEV; 1080805cc48eSsf printf("%s: flush cache command: error 0x%x\n", 1081805cc48eSsf wd->sc_dev.dv_xname, wdc_c.r_error); 1082805cc48eSsf return EIO; 10837481efa2Scsapuntz } 10847481efa2Scsapuntz if (wdc_c.flags & AT_DF) { 10857481efa2Scsapuntz printf("%s: flush cache command: drive fault\n", 10867481efa2Scsapuntz wd->sc_dev.dv_xname); 1087805cc48eSsf return EIO; 10887481efa2Scsapuntz } 1089805cc48eSsf return 0; 10907481efa2Scsapuntz } 10917481efa2Scsapuntz 10927481efa2Scsapuntz void 1093f8fc1e98Skettenis wd_standby(struct wd_softc *wd, int flags) 1094f8fc1e98Skettenis { 1095f8fc1e98Skettenis struct wdc_command wdc_c; 1096f8fc1e98Skettenis 1097f8fc1e98Skettenis bzero(&wdc_c, sizeof(struct wdc_command)); 1098f8fc1e98Skettenis wdc_c.r_command = WDCC_STANDBY_IMMED; 1099f8fc1e98Skettenis wdc_c.r_st_bmask = WDCS_DRDY; 1100f8fc1e98Skettenis wdc_c.r_st_pmask = WDCS_DRDY; 1101edc82994Sderaadt wdc_c.flags = flags; 11026693db94Skettenis wdc_c.timeout = 30000; /* 30s timeout */ 1103f8fc1e98Skettenis if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) { 1104f8fc1e98Skettenis printf("%s: standby command didn't complete\n", 1105f8fc1e98Skettenis wd->sc_dev.dv_xname); 1106f8fc1e98Skettenis } 1107f8fc1e98Skettenis if (wdc_c.flags & AT_TIMEOU) { 1108f8fc1e98Skettenis printf("%s: standby command timeout\n", 1109f8fc1e98Skettenis wd->sc_dev.dv_xname); 1110f8fc1e98Skettenis } 1111f8fc1e98Skettenis if (wdc_c.flags & AT_DF) { 1112f8fc1e98Skettenis printf("%s: standby command: drive fault\n", 1113f8fc1e98Skettenis wd->sc_dev.dv_xname); 1114f8fc1e98Skettenis } 1115f8fc1e98Skettenis /* 1116f8fc1e98Skettenis * Ignore error register, it shouldn't report anything else 1117f8fc1e98Skettenis * than COMMAND ABORTED, which means the device doesn't support 1118f8fc1e98Skettenis * standby 1119f8fc1e98Skettenis */ 1120f8fc1e98Skettenis } 1121