2015-01-02 22:42:05 +03:00
|
|
|
/* $NetBSD: wd.c,v 1.418 2015/01/02 19:42:06 christos Exp $ */
|
1998-10-12 20:09:10 +04:00
|
|
|
|
|
|
|
/*
|
2001-12-03 03:20:22 +03:00
|
|
|
* Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved.
|
1998-10-12 20:09:10 +04:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
2003-07-11 19:33:13 +04:00
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
1998-10-12 20:09:10 +04:00
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
1994-10-27 07:14:23 +03:00
|
|
|
|
1998-08-15 14:10:47 +04:00
|
|
|
/*-
|
2004-06-22 23:20:56 +04:00
|
|
|
* Copyright (c) 1998, 2003, 2004 The NetBSD Foundation, Inc.
|
1998-08-15 14:10:47 +04:00
|
|
|
* All rights reserved.
|
1994-11-22 12:33:59 +03:00
|
|
|
*
|
1998-08-15 14:10:47 +04:00
|
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
|
|
* by Charles M. Hannum and by Onno van der Linden.
|
1998-01-15 02:41:59 +03:00
|
|
|
*
|
1994-11-22 12:33:59 +03:00
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
1998-08-15 14:10:47 +04:00
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
1993-03-21 12:45:37 +03:00
|
|
|
*/
|
|
|
|
|
2001-11-13 15:51:12 +03:00
|
|
|
#include <sys/cdefs.h>
|
2015-01-02 22:42:05 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.418 2015/01/02 19:42:06 christos Exp $");
|
2001-11-13 15:51:12 +03:00
|
|
|
|
2007-04-08 10:58:47 +04:00
|
|
|
#include "opt_ata.h"
|
1998-10-12 20:09:10 +04:00
|
|
|
|
1993-12-20 12:05:17 +03:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
1994-02-25 21:30:37 +03:00
|
|
|
#include <sys/kernel.h>
|
1993-12-20 12:05:17 +03:00
|
|
|
#include <sys/conf.h>
|
|
|
|
#include <sys/file.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/buf.h>
|
2004-10-28 11:07:35 +04:00
|
|
|
#include <sys/bufq.h>
|
1993-12-20 12:05:17 +03:00
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <sys/malloc.h>
|
1994-03-10 08:18:33 +03:00
|
|
|
#include <sys/device.h>
|
1994-10-20 17:08:07 +03:00
|
|
|
#include <sys/disklabel.h>
|
|
|
|
#include <sys/disk.h>
|
1993-12-20 12:05:17 +03:00
|
|
|
#include <sys/syslog.h>
|
1996-04-29 23:50:47 +04:00
|
|
|
#include <sys/proc.h>
|
2007-12-18 18:30:40 +03:00
|
|
|
#include <sys/reboot.h>
|
1999-09-23 15:04:29 +04:00
|
|
|
#include <sys/vnode.h>
|
1997-10-10 05:09:03 +04:00
|
|
|
#include <sys/rnd.h>
|
1993-12-20 12:05:17 +03:00
|
|
|
|
2007-10-19 15:59:34 +04:00
|
|
|
#include <sys/intr.h>
|
|
|
|
#include <sys/bus.h>
|
1993-12-20 12:05:17 +03:00
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
#include <dev/ata/atareg.h>
|
|
|
|
#include <dev/ata/atavar.h>
|
|
|
|
#include <dev/ata/wdvar.h>
|
|
|
|
#include <dev/ic/wdcreg.h>
|
1998-11-20 02:44:20 +03:00
|
|
|
#include <sys/ataio.h>
|
1997-08-27 15:22:52 +04:00
|
|
|
#include "locators.h"
|
1993-03-21 12:45:37 +03:00
|
|
|
|
2006-09-22 08:48:38 +04:00
|
|
|
#include <prop/proplib.h>
|
|
|
|
|
1998-12-16 16:00:02 +03:00
|
|
|
#define WDIORETRIES_SINGLE 4 /* number of retries before single-sector */
|
1998-10-12 20:09:10 +04:00
|
|
|
#define WDIORETRIES 5 /* number of retries before giving up */
|
|
|
|
#define RECOVERYTIME hz/2 /* time to wait before retrying a cmd */
|
1993-07-07 09:50:35 +04:00
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
#define WDUNIT(dev) DISKUNIT(dev)
|
|
|
|
#define WDPART(dev) DISKPART(dev)
|
1999-09-23 15:04:29 +04:00
|
|
|
#define WDMINOR(unit, part) DISKMINOR(unit, part)
|
1994-10-14 21:26:22 +03:00
|
|
|
#define MAKEWDDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part)
|
|
|
|
|
|
|
|
#define WDLABELDEV(dev) (MAKEWDDEV(major(dev), WDUNIT(dev), RAW_PART))
|
1993-03-21 12:45:37 +03:00
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
#define DEBUG_INTR 0x01
|
|
|
|
#define DEBUG_XFERS 0x02
|
|
|
|
#define DEBUG_STATUS 0x04
|
|
|
|
#define DEBUG_FUNCS 0x08
|
|
|
|
#define DEBUG_PROBE 0x10
|
2004-08-13 08:10:49 +04:00
|
|
|
#ifdef ATADEBUG
|
2005-02-27 03:26:58 +03:00
|
|
|
int wdcdebug_wd_mask = 0x0;
|
2004-08-13 08:10:49 +04:00
|
|
|
#define ATADEBUG_PRINT(args, level) \
|
1998-10-12 20:09:10 +04:00
|
|
|
if (wdcdebug_wd_mask & (level)) \
|
|
|
|
printf args
|
1997-08-27 15:22:52 +04:00
|
|
|
#else
|
2004-08-13 08:10:49 +04:00
|
|
|
#define ATADEBUG_PRINT(args, level)
|
1997-08-27 15:22:52 +04:00
|
|
|
#endif
|
|
|
|
|
2009-05-12 18:16:35 +04:00
|
|
|
int wdprobe(device_t, cfdata_t, void *);
|
|
|
|
void wdattach(device_t, device_t, void *);
|
|
|
|
int wddetach(device_t, int);
|
2003-07-11 19:33:13 +04:00
|
|
|
int wdprint(void *, char *);
|
|
|
|
void wdperror(const struct wd_softc *);
|
1994-11-04 01:55:58 +03:00
|
|
|
|
2011-10-05 07:40:18 +04:00
|
|
|
static void wdminphys(struct buf *);
|
|
|
|
|
2009-05-20 03:43:44 +04:00
|
|
|
static int wdlastclose(device_t);
|
2010-02-25 01:37:54 +03:00
|
|
|
static bool wd_suspend(device_t, const pmf_qual_t *);
|
2007-12-11 05:02:31 +03:00
|
|
|
static int wd_standby(struct wd_softc *, int);
|
2007-12-09 23:27:42 +03:00
|
|
|
|
During shutdown, detach devices in an orderly fashion.
Call the detach routine for every device in the device tree, starting
with the leaves and moving toward the root, expecting that each
(pseudo-)device driver will use the opportunity to gracefully commit
outstandings transactions to the underlying (pseudo-)device and to
relinquish control of the hardware to the system BIOS.
Detaching devices is not suitable for every shutdown: in an emergency,
or if the system state is inconsistent, we should resort to a fast,
simple shutdown that uses only the pmf(9) shutdown hooks and the
(deprecated) shutdownhooks. For now, if the flag RB_NOSYNC is set in
boothowto, opt for the fast, simple shutdown.
Add a device flag, DVF_DETACH_SHUTDOWN, that indicates by its presence
that it is safe to detach a device during shutdown. Introduce macros
CFATTACH_DECL3() and CFATTACH_DECL3_NEW() for creating autoconf
attachments with default device flags. Add DVF_DETACH_SHUTDOWN
to configuration attachments for atabus(4), atw(4) at cardbus(4),
cardbus(4), cardslot(4), com(4) at isa(4), elanpar(4), elanpex(4),
elansc(4), gpio(4), npx(4) at isa(4), nsphyter(4), pci(4), pcib(4),
pcmcia(4), ppb(4), sip(4), wd(4), and wdc(4) at isa(4).
Add a device-detachment "reason" flag, DETACH_SHUTDOWN, that tells the
autoconf code and a device driver that the reason for detachment is
system shutdown.
Add a sysctl, kern.detachall, that tells the system to try to detach
every device at shutdown, regardless of any device's DVF_DETACH_SHUTDOWN
flag. The default for kern.detachall is 0. SET IT TO 1, PLEASE, TO
HELP TEST AND DEBUG DEVICE DETACHMENT AT SHUTDOWN.
This is a work in progress. In future work, I aim to treat
pseudo-devices more thoroughly, and to gracefully tear down a stack of
(pseudo-)disk drivers and filesystems, including cgd(4), vnd(4), and
raid(4) instances at shutdown.
Also commit some changes that are not easily untangled from the rest:
(1) begin to simplify device_t locking: rename struct pmf_private to
device_lock, and incorporate device_lock into struct device.
(2) #include <sys/device.h> in sys/pmf.h in order to get some
definitions that it needs. Stop unnecessarily #including <sys/device.h>
in sys/arch/x86/include/pic.h to keep the amd64, xen, and i386 releases
building.
2009-04-02 04:09:32 +04:00
|
|
|
CFATTACH_DECL3_NEW(wd, sizeof(struct wd_softc),
|
2009-05-19 23:56:10 +04:00
|
|
|
wdprobe, wdattach, wddetach, NULL, NULL, NULL, DVF_DETACH_SHUTDOWN);
|
1996-03-17 03:53:54 +03:00
|
|
|
|
1998-01-12 12:39:57 +03:00
|
|
|
extern struct cfdriver wd_cd;
|
1994-11-21 01:36:43 +03:00
|
|
|
|
2002-09-06 17:18:43 +04:00
|
|
|
dev_type_open(wdopen);
|
|
|
|
dev_type_close(wdclose);
|
|
|
|
dev_type_read(wdread);
|
|
|
|
dev_type_write(wdwrite);
|
|
|
|
dev_type_ioctl(wdioctl);
|
|
|
|
dev_type_strategy(wdstrategy);
|
|
|
|
dev_type_dump(wddump);
|
|
|
|
dev_type_size(wdsize);
|
2014-07-25 12:22:08 +04:00
|
|
|
static dev_type_discard(wddiscard);
|
2002-09-06 17:18:43 +04:00
|
|
|
|
|
|
|
const struct bdevsw wd_bdevsw = {
|
2014-03-16 09:20:22 +04:00
|
|
|
.d_open = wdopen,
|
|
|
|
.d_close = wdclose,
|
|
|
|
.d_strategy = wdstrategy,
|
|
|
|
.d_ioctl = wdioctl,
|
|
|
|
.d_dump = wddump,
|
|
|
|
.d_psize = wdsize,
|
2014-07-25 12:22:08 +04:00
|
|
|
.d_discard = wddiscard,
|
2014-03-16 09:20:22 +04:00
|
|
|
.d_flag = D_DISK
|
2002-09-06 17:18:43 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
const struct cdevsw wd_cdevsw = {
|
2014-03-16 09:20:22 +04:00
|
|
|
.d_open = wdopen,
|
|
|
|
.d_close = wdclose,
|
|
|
|
.d_read = wdread,
|
|
|
|
.d_write = wdwrite,
|
|
|
|
.d_ioctl = wdioctl,
|
|
|
|
.d_stop = nostop,
|
|
|
|
.d_tty = notty,
|
|
|
|
.d_poll = nopoll,
|
|
|
|
.d_mmap = nommap,
|
|
|
|
.d_kqfilter = nokqfilter,
|
2014-07-25 12:22:08 +04:00
|
|
|
.d_discard = wddiscard,
|
2014-03-16 09:20:22 +04:00
|
|
|
.d_flag = D_DISK
|
2002-09-06 17:18:43 +04:00
|
|
|
};
|
|
|
|
|
1998-11-19 22:46:12 +03:00
|
|
|
/*
|
|
|
|
* Glue necessary to hook WDCIOCCOMMAND into physio
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct wd_ioctl {
|
|
|
|
LIST_ENTRY(wd_ioctl) wi_list;
|
|
|
|
struct buf wi_bp;
|
|
|
|
struct uio wi_uio;
|
|
|
|
struct iovec wi_iov;
|
1998-11-20 02:44:20 +03:00
|
|
|
atareq_t wi_atareq;
|
1998-11-19 22:46:12 +03:00
|
|
|
struct wd_softc *wi_softc;
|
|
|
|
};
|
|
|
|
|
|
|
|
LIST_HEAD(, wd_ioctl) wi_head;
|
|
|
|
|
2003-07-11 19:33:13 +04:00
|
|
|
struct wd_ioctl *wi_find(struct buf *);
|
|
|
|
void wi_free(struct wd_ioctl *);
|
|
|
|
struct wd_ioctl *wi_get(void);
|
|
|
|
void wdioctlstrategy(struct buf *);
|
1998-11-19 22:46:12 +03:00
|
|
|
|
2003-07-11 19:33:13 +04:00
|
|
|
void wdgetdefaultlabel(struct wd_softc *, struct disklabel *);
|
|
|
|
void wdgetdisklabel(struct wd_softc *);
|
|
|
|
void wdstart(void *);
|
2010-01-20 01:28:30 +03:00
|
|
|
void wdstart1(struct wd_softc*, struct buf *);
|
2003-07-11 19:33:13 +04:00
|
|
|
void wdrestart(void *);
|
2003-12-14 08:38:20 +03:00
|
|
|
void wddone(void *);
|
2014-09-10 11:04:48 +04:00
|
|
|
int wd_get_params(struct wd_softc *, uint8_t, struct ataparams *);
|
2004-05-22 00:35:53 +04:00
|
|
|
int wd_flushcache(struct wd_softc *, int);
|
2014-07-25 12:22:08 +04:00
|
|
|
int wd_trim(struct wd_softc *, int, daddr_t, long);
|
2008-02-28 17:40:17 +03:00
|
|
|
bool wd_shutdown(device_t, int);
|
1994-11-21 01:36:43 +03:00
|
|
|
|
2003-07-11 19:33:13 +04:00
|
|
|
int wd_getcache(struct wd_softc *, int *);
|
|
|
|
int wd_setcache(struct wd_softc *, int);
|
2003-04-26 13:54:15 +04:00
|
|
|
|
2011-10-05 07:40:18 +04:00
|
|
|
struct dkdriver wddkdriver = { wdstrategy, wdminphys };
|
1994-03-10 22:57:20 +03:00
|
|
|
|
1998-03-25 12:58:24 +03:00
|
|
|
#ifdef HAS_BAD144_HANDLING
|
2003-07-11 19:33:13 +04:00
|
|
|
static void bad144intern(struct wd_softc *);
|
1998-03-25 12:58:24 +03:00
|
|
|
#endif
|
1994-03-10 23:05:30 +03:00
|
|
|
|
2003-03-22 01:40:56 +03:00
|
|
|
#define WD_QUIRK_SPLIT_MOD15_WRITE 0x0001 /* must split certain writes */
|
|
|
|
|
2006-08-01 11:19:07 +04:00
|
|
|
#define WD_QUIRK_FMT "\20\1SPLIT_MOD15_WRITE\2FORCE_LBA48"
|
|
|
|
|
2003-03-22 01:40:56 +03:00
|
|
|
/*
|
|
|
|
* Quirk table for IDE drives. Put more-specific matches first, since
|
2011-10-27 17:07:37 +04:00
|
|
|
* a simple globing routine is used for matching.
|
2003-03-22 01:40:56 +03:00
|
|
|
*/
|
|
|
|
static const struct wd_quirk {
|
|
|
|
const char *wdq_match; /* inquiry pattern to match */
|
|
|
|
int wdq_quirks; /* drive quirks */
|
|
|
|
} wd_quirk_table[] = {
|
|
|
|
/*
|
|
|
|
* Some Seagate S-ATA drives have a PHY which can get confused
|
|
|
|
* with the way data is packetized by some S-ATA controllers.
|
|
|
|
*
|
2003-04-18 04:35:14 +04:00
|
|
|
* The work-around is to split in two any write transfer whose
|
2003-03-22 01:40:56 +03:00
|
|
|
* sector count % 15 == 1 (assuming 512 byte sectors).
|
|
|
|
*
|
|
|
|
* XXX This is an incomplete list. There are at least a couple
|
|
|
|
* XXX more model numbers. If you have trouble with such transfers
|
|
|
|
* XXX (8K is the most common) on Seagate S-ATA drives, please
|
2003-12-04 16:57:30 +03:00
|
|
|
* XXX notify thorpej@NetBSD.org.
|
2011-10-27 17:07:37 +04:00
|
|
|
*
|
|
|
|
* The ST360015AS has not yet been confirmed to have this
|
|
|
|
* issue, however, it is the only other drive in the
|
|
|
|
* Seagate Barracuda Serial ATA V family.
|
|
|
|
*
|
2003-03-22 01:40:56 +03:00
|
|
|
*/
|
|
|
|
{ "ST3120023AS",
|
|
|
|
WD_QUIRK_SPLIT_MOD15_WRITE },
|
2003-04-18 08:32:19 +04:00
|
|
|
{ "ST380023AS",
|
|
|
|
WD_QUIRK_SPLIT_MOD15_WRITE },
|
2011-10-27 17:07:37 +04:00
|
|
|
{ "ST360015AS",
|
|
|
|
WD_QUIRK_SPLIT_MOD15_WRITE },
|
2003-03-22 01:40:56 +03:00
|
|
|
{ NULL,
|
|
|
|
0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct wd_quirk *
|
|
|
|
wd_lookup_quirks(const char *name)
|
|
|
|
{
|
|
|
|
const struct wd_quirk *wdq;
|
|
|
|
const char *estr;
|
|
|
|
|
|
|
|
for (wdq = wd_quirk_table; wdq->wdq_match != NULL; wdq++) {
|
|
|
|
/*
|
|
|
|
* We only want exact matches (which include matches
|
|
|
|
* against globbing characters).
|
|
|
|
*/
|
|
|
|
if (pmatch(name, wdq->wdq_match, &estr) == 2)
|
|
|
|
return (wdq);
|
|
|
|
}
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
1993-03-21 12:45:37 +03:00
|
|
|
int
|
2009-05-12 18:16:35 +04:00
|
|
|
wdprobe(device_t parent, cfdata_t match, void *aux)
|
1993-03-21 12:45:37 +03:00
|
|
|
{
|
2001-12-03 01:44:32 +03:00
|
|
|
struct ata_device *adev = aux;
|
1998-01-12 12:39:57 +03:00
|
|
|
|
2001-12-03 01:44:32 +03:00
|
|
|
if (adev == NULL)
|
1997-08-27 15:22:52 +04:00
|
|
|
return 0;
|
2001-12-03 01:44:32 +03:00
|
|
|
if (adev->adev_bustype->bustype_type != SCSIPI_BUSTYPE_ATA)
|
1997-08-27 15:22:52 +04:00
|
|
|
return 0;
|
|
|
|
|
2003-10-09 00:57:59 +04:00
|
|
|
if (match->cf_loc[ATA_HLCF_DRIVE] != ATA_HLCF_DRIVE_DEFAULT &&
|
|
|
|
match->cf_loc[ATA_HLCF_DRIVE] != adev->adev_drv_data->drive)
|
1998-10-12 20:09:10 +04:00
|
|
|
return 0;
|
1994-03-29 08:35:37 +04:00
|
|
|
return 1;
|
|
|
|
}
|
1994-03-10 08:18:33 +03:00
|
|
|
|
1994-03-29 08:35:37 +04:00
|
|
|
void
|
2009-05-12 18:16:35 +04:00
|
|
|
wdattach(device_t parent, device_t self, void *aux)
|
1994-03-29 08:35:37 +04:00
|
|
|
{
|
2008-03-18 23:46:35 +03:00
|
|
|
struct wd_softc *wd = device_private(self);
|
2001-12-03 01:44:32 +03:00
|
|
|
struct ata_device *adev= aux;
|
1994-03-29 08:35:37 +04:00
|
|
|
int i, blank;
|
2005-05-30 02:11:28 +04:00
|
|
|
char tbuf[41], pbuf[9], c, *p, *q;
|
2003-03-22 01:40:56 +03:00
|
|
|
const struct wd_quirk *wdq;
|
1994-03-04 07:15:24 +03:00
|
|
|
|
2008-03-18 23:46:35 +03:00
|
|
|
wd->sc_dev = self;
|
|
|
|
|
2007-11-07 11:59:03 +03:00
|
|
|
ATADEBUG_PRINT(("wdattach\n"), DEBUG_FUNCS | DEBUG_PROBE);
|
2007-07-10 00:51:58 +04:00
|
|
|
callout_init(&wd->sc_restart_ch, 0);
|
2005-10-15 21:29:10 +04:00
|
|
|
bufq_alloc(&wd->sc_q, BUFQ_DISK_DEFAULT_STRAT, BUFQ_SORT_RAWBLOCK);
|
2004-08-30 13:34:41 +04:00
|
|
|
#ifdef WD_SOFTBADSECT
|
2003-04-15 18:11:00 +04:00
|
|
|
SLIST_INIT(&wd->sc_bslist);
|
2004-08-30 13:34:41 +04:00
|
|
|
#endif
|
2001-12-03 03:11:15 +03:00
|
|
|
wd->atabus = adev->adev_bustype;
|
2001-12-03 01:44:32 +03:00
|
|
|
wd->openings = adev->adev_openings;
|
2003-01-20 08:29:53 +03:00
|
|
|
wd->drvp = adev->adev_drv_data;
|
2003-12-14 08:33:29 +03:00
|
|
|
|
|
|
|
wd->drvp->drv_done = wddone;
|
2012-07-31 19:50:31 +04:00
|
|
|
wd->drvp->drv_softc = wd->sc_dev; /* done in atabusconfig_thread()
|
|
|
|
but too late */
|
1994-11-05 02:30:15 +03:00
|
|
|
|
2003-05-18 01:52:03 +04:00
|
|
|
aprint_naive("\n");
|
2011-02-10 08:07:46 +03:00
|
|
|
aprint_normal("\n");
|
2003-05-18 01:52:03 +04:00
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
/* read our drive info */
|
2003-09-20 01:35:56 +04:00
|
|
|
if (wd_get_params(wd, AT_WAIT, &wd->sc_params) != 0) {
|
2011-02-10 08:07:46 +03:00
|
|
|
aprint_error_dev(self, "IDENTIFY failed\n");
|
2012-07-31 19:50:31 +04:00
|
|
|
goto out;
|
1998-10-12 20:09:10 +04:00
|
|
|
}
|
1997-08-27 15:22:52 +04:00
|
|
|
|
2005-05-30 02:11:28 +04:00
|
|
|
for (blank = 0, p = wd->sc_params.atap_model, q = tbuf, i = 0;
|
1998-10-12 20:09:10 +04:00
|
|
|
i < sizeof(wd->sc_params.atap_model); i++) {
|
1995-03-30 03:36:27 +04:00
|
|
|
c = *p++;
|
1994-03-04 07:15:24 +03:00
|
|
|
if (c == '\0')
|
|
|
|
break;
|
|
|
|
if (c != ' ') {
|
1995-03-30 03:36:27 +04:00
|
|
|
if (blank) {
|
|
|
|
*q++ = ' ';
|
|
|
|
blank = 0;
|
|
|
|
}
|
|
|
|
*q++ = c;
|
1994-03-04 07:15:24 +03:00
|
|
|
} else
|
|
|
|
blank = 1;
|
1999-09-22 13:51:03 +04:00
|
|
|
}
|
1995-03-30 03:36:27 +04:00
|
|
|
*q++ = '\0';
|
|
|
|
|
2011-02-10 08:07:46 +03:00
|
|
|
aprint_normal_dev(self, "<%s>\n", tbuf);
|
1998-04-26 09:28:23 +04:00
|
|
|
|
2005-05-30 02:11:28 +04:00
|
|
|
wdq = wd_lookup_quirks(tbuf);
|
2003-03-22 01:40:56 +03:00
|
|
|
if (wdq != NULL)
|
|
|
|
wd->sc_quirks = wdq->wdq_quirks;
|
|
|
|
|
2006-08-01 11:19:07 +04:00
|
|
|
if (wd->sc_quirks != 0) {
|
|
|
|
char sbuf[sizeof(WD_QUIRK_FMT) + 64];
|
2008-12-17 01:35:21 +03:00
|
|
|
snprintb(sbuf, sizeof(sbuf), WD_QUIRK_FMT, wd->sc_quirks);
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_normal_dev(self, "quirks %s\n", sbuf);
|
2006-08-01 11:19:07 +04:00
|
|
|
}
|
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
if ((wd->sc_params.atap_multi & 0xff) > 1) {
|
|
|
|
wd->sc_multi = wd->sc_params.atap_multi & 0xff;
|
1994-11-22 12:33:59 +03:00
|
|
|
} else {
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_multi = 1;
|
1994-11-22 12:33:59 +03:00
|
|
|
}
|
|
|
|
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_verbose_dev(self, "drive supports %d-sector PIO transfers,",
|
|
|
|
wd->sc_multi);
|
1998-04-26 09:28:23 +04:00
|
|
|
|
2002-01-13 20:24:28 +03:00
|
|
|
/* 48-bit LBA addressing */
|
2003-11-30 16:57:12 +03:00
|
|
|
if ((wd->sc_params.atap_cmd2_en & ATA_CMD2_LBA48) != 0)
|
2002-01-13 20:24:28 +03:00
|
|
|
wd->sc_flags |= WDF_LBA48;
|
|
|
|
|
1998-04-26 09:28:23 +04:00
|
|
|
/* Prior to ATA-4, LBA was optional. */
|
1998-10-12 20:09:10 +04:00
|
|
|
if ((wd->sc_params.atap_capabilities1 & WDC_CAP_LBA) != 0)
|
|
|
|
wd->sc_flags |= WDF_LBA;
|
1998-05-09 09:20:35 +04:00
|
|
|
#if 0
|
1998-04-26 09:28:23 +04:00
|
|
|
/* ATA-4 requires LBA. */
|
1998-10-12 20:09:10 +04:00
|
|
|
if (wd->sc_params.atap_ataversion != 0xffff &&
|
|
|
|
wd->sc_params.atap_ataversion >= WDC_VER_ATA4)
|
|
|
|
wd->sc_flags |= WDF_LBA;
|
1998-05-09 09:20:35 +04:00
|
|
|
#endif
|
1998-04-26 09:28:23 +04:00
|
|
|
|
2002-01-13 20:24:28 +03:00
|
|
|
if ((wd->sc_flags & WDF_LBA48) != 0) {
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(" LBA48 addressing\n");
|
2002-01-13 20:24:28 +03:00
|
|
|
wd->sc_capacity =
|
2014-09-10 11:04:48 +04:00
|
|
|
((uint64_t) wd->sc_params.atap_max_lba[3] << 48) |
|
|
|
|
((uint64_t) wd->sc_params.atap_max_lba[2] << 32) |
|
|
|
|
((uint64_t) wd->sc_params.atap_max_lba[1] << 16) |
|
|
|
|
((uint64_t) wd->sc_params.atap_max_lba[0] << 0);
|
2009-12-18 00:03:10 +03:00
|
|
|
wd->sc_capacity28 =
|
|
|
|
(wd->sc_params.atap_capacity[1] << 16) |
|
|
|
|
wd->sc_params.atap_capacity[0];
|
2002-01-13 20:24:28 +03:00
|
|
|
} else if ((wd->sc_flags & WDF_LBA) != 0) {
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(" LBA addressing\n");
|
2009-12-18 00:03:10 +03:00
|
|
|
wd->sc_capacity28 = wd->sc_capacity =
|
|
|
|
(wd->sc_params.atap_capacity[1] << 16) |
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_params.atap_capacity[0];
|
1998-04-26 09:28:23 +04:00
|
|
|
} else {
|
2007-02-10 00:55:00 +03:00
|
|
|
aprint_verbose(" chs addressing\n");
|
2009-12-18 00:03:10 +03:00
|
|
|
wd->sc_capacity28 = wd->sc_capacity =
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_params.atap_cylinders *
|
|
|
|
wd->sc_params.atap_heads *
|
|
|
|
wd->sc_params.atap_sectors;
|
1998-04-26 09:28:23 +04:00
|
|
|
}
|
2003-04-04 02:18:23 +04:00
|
|
|
format_bytes(pbuf, sizeof(pbuf), wd->sc_capacity * DEV_BSIZE);
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_normal_dev(self, "%s, %d cyl, %d head, %d sec, "
|
2003-04-04 02:18:23 +04:00
|
|
|
"%d bytes/sect x %llu sectors\n",
|
2008-03-18 23:46:35 +03:00
|
|
|
pbuf,
|
2003-08-03 21:53:04 +04:00
|
|
|
(wd->sc_flags & WDF_LBA) ? (int)(wd->sc_capacity /
|
|
|
|
(wd->sc_params.atap_heads * wd->sc_params.atap_sectors)) :
|
|
|
|
wd->sc_params.atap_cylinders,
|
2000-01-24 17:51:07 +03:00
|
|
|
wd->sc_params.atap_heads, wd->sc_params.atap_sectors,
|
2003-04-04 02:18:23 +04:00
|
|
|
DEV_BSIZE, (unsigned long long)wd->sc_capacity);
|
2000-01-24 17:51:07 +03:00
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("%s: atap_dmatiming_mimi=%d, atap_dmatiming_recom=%d\n",
|
2008-03-18 23:46:35 +03:00
|
|
|
device_xname(self), wd->sc_params.atap_dmatiming_mimi,
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_params.atap_dmatiming_recom), DEBUG_PROBE);
|
2012-07-31 19:50:31 +04:00
|
|
|
out:
|
1998-10-12 20:09:10 +04:00
|
|
|
/*
|
|
|
|
* Initialize and attach the disk structure.
|
|
|
|
*/
|
2006-10-25 08:04:45 +04:00
|
|
|
/* we fill in dk_info later */
|
2008-03-18 23:46:35 +03:00
|
|
|
disk_init(&wd->sc_dk, device_xname(wd->sc_dev), &wddkdriver);
|
1998-10-12 20:09:10 +04:00
|
|
|
disk_attach(&wd->sc_dk);
|
|
|
|
wd->sc_wdc_bio.lp = wd->sc_dk.dk_label;
|
2008-03-18 23:46:35 +03:00
|
|
|
rnd_attach_source(&wd->rnd_source, device_xname(wd->sc_dev),
|
2014-08-10 20:44:32 +04:00
|
|
|
RND_TYPE_DISK, RND_FLAG_DEFAULT);
|
2004-09-25 07:34:02 +04:00
|
|
|
|
|
|
|
/* Discover wedges on this disk. */
|
|
|
|
dkwedge_discover(&wd->sc_dk);
|
2007-12-09 23:27:42 +03:00
|
|
|
|
2008-02-28 17:40:17 +03:00
|
|
|
if (!pmf_device_register1(self, wd_suspend, NULL, wd_shutdown))
|
2007-12-09 23:27:42 +03:00
|
|
|
aprint_error_dev(self, "couldn't establish power handler\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2010-02-25 01:37:54 +03:00
|
|
|
wd_suspend(device_t dv, const pmf_qual_t *qual)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
|
|
|
struct wd_softc *sc = device_private(dv);
|
|
|
|
|
2013-10-30 19:41:14 +04:00
|
|
|
/* the adapter needs to be enabled */
|
|
|
|
if (sc->atabus->ata_addref(sc->drvp))
|
|
|
|
return true; /* no need to complain */
|
|
|
|
|
2008-02-22 00:52:06 +03:00
|
|
|
wd_flushcache(sc, AT_WAIT);
|
|
|
|
wd_standby(sc, AT_WAIT);
|
2013-10-30 19:41:14 +04:00
|
|
|
|
|
|
|
sc->atabus->ata_delref(sc->drvp);
|
2007-12-09 23:27:42 +03:00
|
|
|
return true;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
|
1999-09-23 15:04:29 +04:00
|
|
|
int
|
2009-05-12 18:16:35 +04:00
|
|
|
wddetach(device_t self, int flags)
|
1999-09-23 15:04:29 +04:00
|
|
|
{
|
2008-03-18 23:46:35 +03:00
|
|
|
struct wd_softc *sc = device_private(self);
|
2009-05-16 03:49:28 +04:00
|
|
|
int bmaj, cmaj, i, mn, rc, s;
|
|
|
|
|
2009-05-20 07:26:21 +04:00
|
|
|
if ((rc = disk_begindetach(&sc->sc_dk, wdlastclose, self, flags)) != 0)
|
2009-05-16 03:49:28 +04:00
|
|
|
return rc;
|
1999-09-23 15:04:29 +04:00
|
|
|
|
|
|
|
/* locate the major number */
|
2002-09-06 17:18:43 +04:00
|
|
|
bmaj = bdevsw_lookup_major(&wd_bdevsw);
|
|
|
|
cmaj = cdevsw_lookup_major(&wd_cdevsw);
|
1999-09-23 15:04:29 +04:00
|
|
|
|
2004-08-10 06:33:58 +04:00
|
|
|
/* Nuke the vnodes for any open instances. */
|
|
|
|
for (i = 0; i < MAXPARTITIONS; i++) {
|
2006-03-28 21:38:24 +04:00
|
|
|
mn = WDMINOR(device_unit(self), i);
|
2004-08-10 06:33:58 +04:00
|
|
|
vdevgone(bmaj, mn, mn, VBLK);
|
|
|
|
vdevgone(cmaj, mn, mn, VCHR);
|
|
|
|
}
|
|
|
|
|
2004-09-25 07:34:02 +04:00
|
|
|
/* Delete all of our wedges. */
|
|
|
|
dkwedge_delall(&sc->sc_dk);
|
|
|
|
|
1999-09-23 15:04:29 +04:00
|
|
|
s = splbio();
|
|
|
|
|
2003-07-11 19:33:13 +04:00
|
|
|
/* Kill off any queued buffers. */
|
2005-10-15 21:29:10 +04:00
|
|
|
bufq_drain(sc->sc_q);
|
1999-09-23 15:04:29 +04:00
|
|
|
|
2005-10-15 21:29:10 +04:00
|
|
|
bufq_free(sc->sc_q);
|
2004-08-05 02:44:04 +04:00
|
|
|
sc->atabus->ata_killpending(sc->drvp);
|
2002-07-21 19:32:17 +04:00
|
|
|
|
1999-09-23 15:04:29 +04:00
|
|
|
splx(s);
|
|
|
|
|
|
|
|
/* Detach disk. */
|
|
|
|
disk_detach(&sc->sc_dk);
|
2008-05-03 01:11:00 +04:00
|
|
|
disk_destroy(&sc->sc_dk);
|
1999-09-23 15:04:29 +04:00
|
|
|
|
2004-08-30 13:34:41 +04:00
|
|
|
#ifdef WD_SOFTBADSECT
|
2004-08-04 01:38:45 +04:00
|
|
|
/* Clean out the bad sector list */
|
|
|
|
while (!SLIST_EMPTY(&sc->sc_bslist)) {
|
|
|
|
void *head = SLIST_FIRST(&sc->sc_bslist);
|
|
|
|
SLIST_REMOVE_HEAD(&sc->sc_bslist, dbs_next);
|
|
|
|
free(head, M_TEMP);
|
|
|
|
}
|
|
|
|
sc->sc_bscount = 0;
|
2004-08-30 13:34:41 +04:00
|
|
|
#endif
|
2004-08-04 01:38:45 +04:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
pmf_device_deregister(self);
|
1999-09-23 15:04:29 +04:00
|
|
|
|
|
|
|
/* Unhook the entropy source. */
|
|
|
|
rnd_detach_source(&sc->rnd_source);
|
|
|
|
|
2008-12-05 21:20:19 +03:00
|
|
|
callout_destroy(&sc->sc_restart_ch);
|
|
|
|
|
2012-07-31 19:50:31 +04:00
|
|
|
sc->drvp->drive_type = ATA_DRIVET_NONE; /* no drive any more here */
|
|
|
|
sc->drvp->drive_flags = 0;
|
2004-08-04 02:03:46 +04:00
|
|
|
|
1999-09-23 15:04:29 +04:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1994-03-10 08:18:33 +03:00
|
|
|
/*
|
1995-03-30 03:36:27 +04:00
|
|
|
* Read/write routine for a buffer. Validates the arguments and schedules the
|
|
|
|
* transfer. Does not wait for the transfer to complete.
|
1993-03-21 12:45:37 +03:00
|
|
|
*/
|
1994-03-10 08:18:33 +03:00
|
|
|
void
|
2003-07-11 19:33:13 +04:00
|
|
|
wdstrategy(struct buf *bp)
|
1993-03-21 12:45:37 +03:00
|
|
|
{
|
2008-03-18 23:46:35 +03:00
|
|
|
struct wd_softc *wd =
|
2008-06-08 22:34:06 +04:00
|
|
|
device_lookup_private(&wd_cd, WDUNIT(bp->b_dev));
|
2000-02-07 23:16:47 +03:00
|
|
|
struct disklabel *lp = wd->sc_dk.dk_label;
|
|
|
|
daddr_t blkno;
|
1994-02-25 19:40:47 +03:00
|
|
|
int s;
|
2000-02-07 23:16:47 +03:00
|
|
|
|
2008-03-18 23:46:35 +03:00
|
|
|
ATADEBUG_PRINT(("wdstrategy (%s)\n", device_xname(wd->sc_dev)),
|
1998-10-13 13:33:59 +04:00
|
|
|
DEBUG_XFERS);
|
2003-07-11 19:33:13 +04:00
|
|
|
|
1995-03-30 03:36:27 +04:00
|
|
|
/* Valid request? */
|
|
|
|
if (bp->b_blkno < 0 ||
|
2000-02-07 23:16:47 +03:00
|
|
|
(bp->b_bcount % lp->d_secsize) != 0 ||
|
|
|
|
(bp->b_bcount / lp->d_secsize) >= (1 << NBBY)) {
|
1993-03-21 12:45:37 +03:00
|
|
|
bp->b_error = EINVAL;
|
2007-07-29 16:50:17 +04:00
|
|
|
goto done;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
2003-07-11 19:33:13 +04:00
|
|
|
|
2009-05-19 23:56:10 +04:00
|
|
|
/* If device invalidated (e.g. media change, door open,
|
2010-11-05 18:49:37 +03:00
|
|
|
* device detachment), then error.
|
2009-05-19 23:56:10 +04:00
|
|
|
*/
|
2010-11-05 18:49:37 +03:00
|
|
|
if ((wd->sc_flags & WDF_LOADED) == 0 ||
|
|
|
|
!device_is_enabled(wd->sc_dev)) {
|
1995-03-30 03:36:27 +04:00
|
|
|
bp->b_error = EIO;
|
2007-07-29 16:50:17 +04:00
|
|
|
goto done;
|
1995-03-30 03:36:27 +04:00
|
|
|
}
|
|
|
|
|
1994-10-20 16:44:46 +03:00
|
|
|
/* If it's a null transfer, return immediately. */
|
|
|
|
if (bp->b_bcount == 0)
|
|
|
|
goto done;
|
|
|
|
|
1995-01-13 13:46:32 +03:00
|
|
|
/*
|
|
|
|
* Do bounds checking, adjust transfer. if error, process.
|
|
|
|
* If end of partition, just return.
|
|
|
|
*/
|
2003-04-04 02:18:23 +04:00
|
|
|
if (WDPART(bp->b_dev) == RAW_PART) {
|
|
|
|
if (bounds_check_with_mediasize(bp, DEV_BSIZE,
|
|
|
|
wd->sc_capacity) <= 0)
|
|
|
|
goto done;
|
|
|
|
} else {
|
2003-05-11 03:12:28 +04:00
|
|
|
if (bounds_check_with_label(&wd->sc_dk, bp,
|
2003-04-04 02:18:23 +04:00
|
|
|
(wd->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0)
|
|
|
|
goto done;
|
|
|
|
}
|
2000-02-07 23:16:47 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now convert the block number to absolute and put it in
|
|
|
|
* terms of the device's logical block size.
|
|
|
|
*/
|
|
|
|
if (lp->d_secsize >= DEV_BSIZE)
|
|
|
|
blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE);
|
|
|
|
else
|
|
|
|
blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize);
|
|
|
|
|
|
|
|
if (WDPART(bp->b_dev) != RAW_PART)
|
|
|
|
blkno += lp->d_partitions[WDPART(bp->b_dev)].p_offset;
|
|
|
|
|
|
|
|
bp->b_rawblkno = blkno;
|
|
|
|
|
2004-08-30 13:34:41 +04:00
|
|
|
#ifdef WD_SOFTBADSECT
|
2003-04-15 18:11:00 +04:00
|
|
|
/*
|
|
|
|
* If the transfer about to be attempted contains only a block that
|
|
|
|
* is known to be bad then return an error for the transfer without
|
|
|
|
* even attempting to start a transfer up under the premis that we
|
|
|
|
* will just end up doing more retries for a transfer that will end
|
|
|
|
* up failing again.
|
|
|
|
* XXX:SMP - mutex required to protect with DIOCBSFLUSH
|
|
|
|
*/
|
|
|
|
if (__predict_false(!SLIST_EMPTY(&wd->sc_bslist))) {
|
|
|
|
struct disk_badsectors *dbs;
|
2004-06-02 00:53:03 +04:00
|
|
|
daddr_t maxblk = blkno + (bp->b_bcount >> DEV_BSHIFT) - 1;
|
2003-04-15 18:11:00 +04:00
|
|
|
|
|
|
|
SLIST_FOREACH(dbs, &wd->sc_bslist, dbs_next)
|
2003-04-15 22:27:26 +04:00
|
|
|
if ((dbs->dbs_min <= blkno && blkno <= dbs->dbs_max) ||
|
2003-04-16 04:07:17 +04:00
|
|
|
(dbs->dbs_min <= maxblk && maxblk <= dbs->dbs_max)){
|
|
|
|
bp->b_error = EIO;
|
2007-07-30 10:59:13 +04:00
|
|
|
goto done;
|
2003-04-16 04:07:17 +04:00
|
|
|
}
|
2003-04-15 18:11:00 +04:00
|
|
|
}
|
2004-08-30 13:34:41 +04:00
|
|
|
#endif
|
2003-04-15 18:11:00 +04:00
|
|
|
|
1994-03-10 08:18:33 +03:00
|
|
|
/* Queue transfer on drive, activate drive and controller if idle. */
|
1993-03-21 12:45:37 +03:00
|
|
|
s = splbio();
|
2009-01-13 16:33:58 +03:00
|
|
|
bufq_put(wd->sc_q, bp);
|
1997-08-27 15:22:52 +04:00
|
|
|
wdstart(wd);
|
1993-03-21 12:45:37 +03:00
|
|
|
splx(s);
|
1994-03-10 08:18:33 +03:00
|
|
|
return;
|
1993-03-21 12:45:37 +03:00
|
|
|
done:
|
1994-03-10 08:18:33 +03:00
|
|
|
/* Toss transfer; we're done early. */
|
1995-03-30 03:36:27 +04:00
|
|
|
bp->b_resid = bp->b_bcount;
|
1993-03-21 12:45:37 +03:00
|
|
|
biodone(bp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1995-03-30 03:36:27 +04:00
|
|
|
* Queue a drive for I/O.
|
1993-03-21 12:45:37 +03:00
|
|
|
*/
|
1994-11-21 01:36:43 +03:00
|
|
|
void
|
2003-07-11 19:33:13 +04:00
|
|
|
wdstart(void *arg)
|
1994-03-10 08:18:33 +03:00
|
|
|
{
|
1997-08-27 15:22:52 +04:00
|
|
|
struct wd_softc *wd = arg;
|
2000-01-22 02:39:55 +03:00
|
|
|
struct buf *bp = NULL;
|
1997-08-27 15:22:52 +04:00
|
|
|
|
2008-03-18 23:46:35 +03:00
|
|
|
ATADEBUG_PRINT(("wdstart %s\n", device_xname(wd->sc_dev)),
|
1998-10-13 13:33:59 +04:00
|
|
|
DEBUG_XFERS);
|
2010-11-05 18:49:37 +03:00
|
|
|
|
|
|
|
if (!device_is_active(wd->sc_dev))
|
|
|
|
return;
|
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
while (wd->openings > 0) {
|
1997-08-27 15:22:52 +04:00
|
|
|
|
|
|
|
/* Is there a buf for us ? */
|
2009-01-13 16:33:58 +03:00
|
|
|
if ((bp = bufq_get(wd->sc_q)) == NULL)
|
2000-01-22 02:39:55 +03:00
|
|
|
return;
|
2003-07-11 19:33:13 +04:00
|
|
|
|
|
|
|
/*
|
1997-08-27 15:22:52 +04:00
|
|
|
* Make the command. First lock the device
|
|
|
|
*/
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->openings--;
|
|
|
|
|
|
|
|
wd->retries = 0;
|
2010-01-20 01:28:30 +03:00
|
|
|
wdstart1(wd, bp);
|
1997-08-27 15:22:52 +04:00
|
|
|
}
|
1994-03-10 08:18:33 +03:00
|
|
|
}
|
|
|
|
|
2003-03-22 01:40:56 +03:00
|
|
|
static void
|
|
|
|
wd_split_mod15_write(struct buf *bp)
|
|
|
|
{
|
|
|
|
struct buf *obp = bp->b_private;
|
2008-03-18 23:46:35 +03:00
|
|
|
struct wd_softc *sc =
|
2008-06-08 22:34:06 +04:00
|
|
|
device_lookup_private(&wd_cd, DISKUNIT(obp->b_dev));
|
2010-01-23 21:54:53 +03:00
|
|
|
int s;
|
2003-03-22 01:40:56 +03:00
|
|
|
|
2007-07-29 16:50:17 +04:00
|
|
|
if (__predict_false(bp->b_error != 0)) {
|
2003-03-22 01:40:56 +03:00
|
|
|
/*
|
|
|
|
* Propagate the error. If this was the first half of
|
|
|
|
* the original transfer, make sure to account for that
|
|
|
|
* in the residual.
|
|
|
|
*/
|
|
|
|
if (bp->b_data == obp->b_data)
|
|
|
|
bp->b_resid += bp->b_bcount;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this was the second half of the transfer, we're all done!
|
|
|
|
*/
|
|
|
|
if (bp->b_data != obp->b_data)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Advance the pointer to the second half and issue that command
|
|
|
|
* using the same opening.
|
|
|
|
*/
|
2008-01-02 14:48:20 +03:00
|
|
|
bp->b_flags = obp->b_flags;
|
|
|
|
bp->b_oflags = obp->b_oflags;
|
|
|
|
bp->b_cflags = obp->b_cflags;
|
2007-03-04 08:59:00 +03:00
|
|
|
bp->b_data = (char *)bp->b_data + bp->b_bcount;
|
2003-03-22 01:40:56 +03:00
|
|
|
bp->b_blkno += (bp->b_bcount / 512);
|
|
|
|
bp->b_rawblkno += (bp->b_bcount / 512);
|
2010-01-23 21:54:53 +03:00
|
|
|
s = splbio();
|
2010-01-20 01:28:30 +03:00
|
|
|
wdstart1(sc, bp);
|
2010-01-23 21:54:53 +03:00
|
|
|
splx(s);
|
2003-03-22 01:40:56 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
done:
|
|
|
|
obp->b_error = bp->b_error;
|
|
|
|
obp->b_resid = bp->b_resid;
|
2010-01-23 21:54:53 +03:00
|
|
|
s = splbio();
|
2006-01-04 13:13:05 +03:00
|
|
|
putiobuf(bp);
|
2003-03-22 01:40:56 +03:00
|
|
|
biodone(obp);
|
|
|
|
sc->openings++;
|
2010-01-23 21:54:53 +03:00
|
|
|
splx(s);
|
2003-03-22 01:40:56 +03:00
|
|
|
/* wddone() will call wdstart() */
|
|
|
|
}
|
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
void
|
2010-01-20 01:28:30 +03:00
|
|
|
wdstart1(struct wd_softc *wd, struct buf *bp)
|
1998-10-12 20:09:10 +04:00
|
|
|
{
|
2000-02-07 23:16:47 +03:00
|
|
|
|
2003-03-22 01:40:56 +03:00
|
|
|
/*
|
|
|
|
* Deal with the "split mod15 write" quirk. We just divide the
|
|
|
|
* transfer in two, doing the first half and then then second half
|
|
|
|
* with the same command opening.
|
|
|
|
*
|
|
|
|
* Note we MUST do this here, because we can't let insertion
|
|
|
|
* into the bufq cause the transfers to be re-merged.
|
|
|
|
*/
|
|
|
|
if (__predict_false((wd->sc_quirks & WD_QUIRK_SPLIT_MOD15_WRITE) != 0 &&
|
|
|
|
(bp->b_flags & B_READ) == 0 &&
|
|
|
|
bp->b_bcount > 512 &&
|
|
|
|
((bp->b_bcount / 512) % 15) == 1)) {
|
|
|
|
struct buf *nbp;
|
|
|
|
|
|
|
|
/* already at splbio */
|
2008-01-02 14:48:20 +03:00
|
|
|
nbp = getiobuf(NULL, false);
|
2003-03-22 01:40:56 +03:00
|
|
|
if (__predict_false(nbp == NULL)) {
|
|
|
|
/* No memory -- fail the iop. */
|
|
|
|
bp->b_error = ENOMEM;
|
|
|
|
bp->b_resid = bp->b_bcount;
|
|
|
|
biodone(bp);
|
|
|
|
wd->openings++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nbp->b_error = 0;
|
|
|
|
nbp->b_proc = bp->b_proc;
|
|
|
|
nbp->b_dev = bp->b_dev;
|
|
|
|
|
|
|
|
nbp->b_bcount = bp->b_bcount / 2;
|
|
|
|
nbp->b_bufsize = bp->b_bcount / 2;
|
|
|
|
nbp->b_data = bp->b_data;
|
|
|
|
|
|
|
|
nbp->b_blkno = bp->b_blkno;
|
|
|
|
nbp->b_rawblkno = bp->b_rawblkno;
|
|
|
|
|
2008-01-02 14:48:20 +03:00
|
|
|
nbp->b_flags = bp->b_flags;
|
|
|
|
nbp->b_oflags = bp->b_oflags;
|
|
|
|
nbp->b_cflags = bp->b_cflags;
|
2003-03-22 01:40:56 +03:00
|
|
|
nbp->b_iodone = wd_split_mod15_write;
|
|
|
|
|
|
|
|
/* Put ptr to orig buf in b_private and use new buf */
|
|
|
|
nbp->b_private = bp;
|
2004-01-10 17:39:50 +03:00
|
|
|
|
|
|
|
BIO_COPYPRIO(nbp, bp);
|
|
|
|
|
2003-03-22 01:40:56 +03:00
|
|
|
bp = nbp;
|
|
|
|
}
|
|
|
|
|
2000-02-07 23:16:47 +03:00
|
|
|
wd->sc_wdc_bio.blkno = bp->b_rawblkno;
|
2009-12-18 00:03:10 +03:00
|
|
|
wd->sc_wdc_bio.bcount = bp->b_bcount;
|
|
|
|
wd->sc_wdc_bio.databuf = bp->b_data;
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_wdc_bio.blkdone =0;
|
2011-07-30 08:42:03 +04:00
|
|
|
KASSERT(bp == wd->sc_bp || wd->sc_bp == NULL);
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_bp = bp;
|
|
|
|
/*
|
|
|
|
* If we're retrying, retry in single-sector mode. This will give us
|
|
|
|
* the sector number of the problem, and will eventually allow the
|
1998-11-11 22:38:27 +03:00
|
|
|
* transfer to succeed.
|
1998-10-12 20:09:10 +04:00
|
|
|
*/
|
2004-06-22 23:20:14 +04:00
|
|
|
if (wd->retries >= WDIORETRIES_SINGLE)
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_wdc_bio.flags = ATA_SINGLE;
|
|
|
|
else
|
|
|
|
wd->sc_wdc_bio.flags = 0;
|
2004-09-28 22:22:33 +04:00
|
|
|
if (wd->sc_flags & WDF_LBA48 &&
|
2009-12-18 00:03:10 +03:00
|
|
|
(wd->sc_wdc_bio.blkno +
|
|
|
|
wd->sc_wdc_bio.bcount / wd->sc_dk.dk_label->d_secsize) >
|
|
|
|
wd->sc_capacity28)
|
2002-01-13 20:24:28 +03:00
|
|
|
wd->sc_wdc_bio.flags |= ATA_LBA48;
|
1998-10-12 20:09:10 +04:00
|
|
|
if (wd->sc_flags & WDF_LBA)
|
|
|
|
wd->sc_wdc_bio.flags |= ATA_LBA;
|
|
|
|
if (bp->b_flags & B_READ)
|
|
|
|
wd->sc_wdc_bio.flags |= ATA_READ;
|
|
|
|
/* Instrumentation. */
|
|
|
|
disk_busy(&wd->sc_dk);
|
2001-12-03 03:11:15 +03:00
|
|
|
switch (wd->atabus->ata_bio(wd->drvp, &wd->sc_wdc_bio)) {
|
2004-08-12 09:02:50 +04:00
|
|
|
case ATACMD_TRY_AGAIN:
|
2000-03-23 10:01:25 +03:00
|
|
|
callout_reset(&wd->sc_restart_ch, hz, wdrestart, wd);
|
1998-10-12 20:09:10 +04:00
|
|
|
break;
|
2004-08-12 09:02:50 +04:00
|
|
|
case ATACMD_QUEUED:
|
|
|
|
case ATACMD_COMPLETE:
|
1998-10-12 20:09:10 +04:00
|
|
|
break;
|
|
|
|
default:
|
2010-01-20 01:28:30 +03:00
|
|
|
panic("wdstart1: bad return code from ata_bio()");
|
1998-10-12 20:09:10 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2003-07-11 19:33:13 +04:00
|
|
|
wddone(void *v)
|
1998-10-12 20:09:10 +04:00
|
|
|
{
|
2008-03-18 23:46:35 +03:00
|
|
|
struct wd_softc *wd = device_private(v);
|
1998-10-12 20:09:10 +04:00
|
|
|
struct buf *bp = wd->sc_bp;
|
2002-06-28 20:40:50 +04:00
|
|
|
const char *errmsg;
|
|
|
|
int do_perror = 0;
|
1998-10-12 20:09:10 +04:00
|
|
|
|
2008-03-18 23:46:35 +03:00
|
|
|
ATADEBUG_PRINT(("wddone %s\n", device_xname(wd->sc_dev)),
|
2007-11-07 11:56:41 +03:00
|
|
|
DEBUG_XFERS);
|
2001-06-13 22:17:38 +04:00
|
|
|
if (bp == NULL)
|
|
|
|
return;
|
1998-10-12 20:09:10 +04:00
|
|
|
bp->b_resid = wd->sc_wdc_bio.bcount;
|
|
|
|
switch (wd->sc_wdc_bio.error) {
|
|
|
|
case ERR_DMA:
|
2002-06-28 20:40:50 +04:00
|
|
|
errmsg = "DMA error";
|
1998-10-12 20:09:10 +04:00
|
|
|
goto retry;
|
|
|
|
case ERR_DF:
|
2002-06-28 20:40:50 +04:00
|
|
|
errmsg = "device fault";
|
1998-10-12 20:09:10 +04:00
|
|
|
goto retry;
|
|
|
|
case TIMEOUT:
|
2002-06-28 20:40:50 +04:00
|
|
|
errmsg = "device timeout";
|
1998-10-12 20:09:10 +04:00
|
|
|
goto retry;
|
2004-08-01 01:26:42 +04:00
|
|
|
case ERR_RESET:
|
|
|
|
errmsg = "channel reset";
|
|
|
|
goto retry2;
|
1998-10-12 20:09:10 +04:00
|
|
|
case ERROR:
|
|
|
|
/* Don't care about media change bits */
|
|
|
|
if (wd->sc_wdc_bio.r_error != 0 &&
|
|
|
|
(wd->sc_wdc_bio.r_error & ~(WDCE_MC | WDCE_MCR)) == 0)
|
|
|
|
goto noerror;
|
2002-06-28 20:40:50 +04:00
|
|
|
errmsg = "error";
|
|
|
|
do_perror = 1;
|
1998-10-12 20:09:10 +04:00
|
|
|
retry: /* Just reset and retry. Can we do more ? */
|
2012-07-31 19:50:31 +04:00
|
|
|
(*wd->atabus->ata_reset_drive)(wd->drvp, AT_RST_NOCMD, NULL);
|
2004-08-01 01:26:42 +04:00
|
|
|
retry2:
|
2002-06-28 20:40:50 +04:00
|
|
|
diskerr(bp, "wd", errmsg, LOG_PRINTF,
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_wdc_bio.blkdone, wd->sc_dk.dk_label);
|
2002-06-28 20:40:50 +04:00
|
|
|
if (wd->retries < WDIORETRIES)
|
2007-09-16 22:41:47 +04:00
|
|
|
printf(", retrying");
|
|
|
|
printf("\n");
|
2002-06-28 20:40:50 +04:00
|
|
|
if (do_perror)
|
|
|
|
wdperror(wd);
|
|
|
|
if (wd->retries < WDIORETRIES) {
|
|
|
|
wd->retries++;
|
2000-03-23 10:01:25 +03:00
|
|
|
callout_reset(&wd->sc_restart_ch, RECOVERYTIME,
|
|
|
|
wdrestart, wd);
|
1998-10-12 20:09:10 +04:00
|
|
|
return;
|
|
|
|
}
|
2003-04-15 18:11:00 +04:00
|
|
|
|
2004-08-30 13:34:41 +04:00
|
|
|
#ifdef WD_SOFTBADSECT
|
2003-04-15 18:11:00 +04:00
|
|
|
/*
|
|
|
|
* Not all errors indicate a failed block but those that do,
|
|
|
|
* put the block on the bad-block list for the device. Only
|
|
|
|
* do this for reads because the drive should do it for writes,
|
|
|
|
* itself, according to Manuel.
|
|
|
|
*/
|
2003-04-15 21:42:44 +04:00
|
|
|
if ((bp->b_flags & B_READ) &&
|
2003-04-15 18:11:00 +04:00
|
|
|
((wd->drvp->ata_vers >= 4 && wd->sc_wdc_bio.r_error & 64) ||
|
2003-04-15 21:42:44 +04:00
|
|
|
(wd->drvp->ata_vers < 4 && wd->sc_wdc_bio.r_error & 192))) {
|
2003-04-15 18:11:00 +04:00
|
|
|
struct disk_badsectors *dbs;
|
|
|
|
|
|
|
|
dbs = malloc(sizeof *dbs, M_TEMP, M_WAITOK);
|
|
|
|
dbs->dbs_min = bp->b_rawblkno;
|
2004-06-02 00:53:03 +04:00
|
|
|
dbs->dbs_max = dbs->dbs_min + (bp->b_bcount >> DEV_BSHIFT) - 1;
|
2003-04-15 18:11:00 +04:00
|
|
|
microtime(&dbs->dbs_failedat);
|
|
|
|
SLIST_INSERT_HEAD(&wd->sc_bslist, dbs, dbs_next);
|
2003-04-15 22:27:26 +04:00
|
|
|
wd->sc_bscount++;
|
2003-04-15 18:11:00 +04:00
|
|
|
}
|
2004-08-30 13:34:41 +04:00
|
|
|
#endif
|
1998-10-12 20:09:10 +04:00
|
|
|
bp->b_error = EIO;
|
|
|
|
break;
|
|
|
|
case NOERROR:
|
|
|
|
noerror: if ((wd->sc_wdc_bio.flags & ATA_CORR) || wd->retries > 0)
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_error_dev(wd->sc_dev,
|
|
|
|
"soft error (corrected)\n");
|
1999-10-20 19:22:24 +04:00
|
|
|
break;
|
|
|
|
case ERR_NODEV:
|
|
|
|
bp->b_error = EIO;
|
|
|
|
break;
|
1998-10-12 20:09:10 +04:00
|
|
|
}
|
2012-06-26 13:49:24 +04:00
|
|
|
if (__predict_false(bp->b_error != 0) && bp->b_resid == 0) {
|
|
|
|
/*
|
|
|
|
* the disk or controller sometimes report a complete
|
|
|
|
* xfer, when there has been an error. This is wrong,
|
|
|
|
* assume nothing got transfered in this case
|
|
|
|
*/
|
|
|
|
bp->b_resid = bp->b_bcount;
|
|
|
|
}
|
2002-11-01 14:31:50 +03:00
|
|
|
disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid),
|
|
|
|
(bp->b_flags & B_READ));
|
1998-10-12 20:09:10 +04:00
|
|
|
rnd_add_uint32(&wd->rnd_source, bp->b_blkno);
|
2003-03-22 01:40:56 +03:00
|
|
|
/* XXX Yuck, but we don't want to increment openings in this case */
|
2008-01-02 14:48:20 +03:00
|
|
|
if (__predict_false(bp->b_iodone == wd_split_mod15_write))
|
2003-03-22 01:40:56 +03:00
|
|
|
biodone(bp);
|
|
|
|
else {
|
|
|
|
biodone(bp);
|
|
|
|
wd->openings++;
|
|
|
|
}
|
2011-07-30 08:42:03 +04:00
|
|
|
KASSERT(wd->sc_bp != NULL);
|
|
|
|
wd->sc_bp = NULL;
|
1998-10-12 20:09:10 +04:00
|
|
|
wdstart(wd);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2003-07-11 19:33:13 +04:00
|
|
|
wdrestart(void *v)
|
1998-10-12 20:09:10 +04:00
|
|
|
{
|
|
|
|
struct wd_softc *wd = v;
|
|
|
|
struct buf *bp = wd->sc_bp;
|
|
|
|
int s;
|
2007-11-07 11:59:03 +03:00
|
|
|
|
2008-03-18 23:46:35 +03:00
|
|
|
ATADEBUG_PRINT(("wdrestart %s\n", device_xname(wd->sc_dev)),
|
1998-10-13 13:33:59 +04:00
|
|
|
DEBUG_XFERS);
|
1998-10-12 20:09:10 +04:00
|
|
|
s = splbio();
|
2010-01-20 01:28:30 +03:00
|
|
|
wdstart1(v, bp);
|
1998-10-12 20:09:10 +04:00
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
2011-10-05 07:40:18 +04:00
|
|
|
static void
|
|
|
|
wdminphys(struct buf *bp)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (bp->b_bcount > (512 * 128)) {
|
|
|
|
bp->b_bcount = (512 * 128);
|
|
|
|
}
|
|
|
|
minphys(bp);
|
|
|
|
}
|
|
|
|
|
1995-07-04 11:15:28 +04:00
|
|
|
int
|
2006-11-16 04:32:37 +03:00
|
|
|
wdread(dev_t dev, struct uio *uio, int flags)
|
1995-07-04 11:15:28 +04:00
|
|
|
{
|
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("wdread\n"), DEBUG_XFERS);
|
2011-10-05 07:40:18 +04:00
|
|
|
return (physio(wdstrategy, NULL, dev, B_READ, wdminphys, uio));
|
1995-07-04 11:15:28 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2006-11-16 04:32:37 +03:00
|
|
|
wdwrite(dev_t dev, struct uio *uio, int flags)
|
1995-07-04 11:15:28 +04:00
|
|
|
{
|
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("wdwrite\n"), DEBUG_XFERS);
|
2011-10-05 07:40:18 +04:00
|
|
|
return (physio(wdstrategy, NULL, dev, B_WRITE, wdminphys, uio));
|
1995-07-04 11:15:28 +04:00
|
|
|
}
|
|
|
|
|
1993-03-21 12:45:37 +03:00
|
|
|
int
|
2006-11-16 04:32:37 +03:00
|
|
|
wdopen(dev_t dev, int flag, int fmt, struct lwp *l)
|
1993-03-21 12:45:37 +03:00
|
|
|
{
|
1994-03-10 08:18:33 +03:00
|
|
|
struct wd_softc *wd;
|
2000-07-06 04:43:04 +04:00
|
|
|
int part, error;
|
1997-08-27 15:22:52 +04:00
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("wdopen\n"), DEBUG_FUNCS);
|
2008-06-08 22:34:06 +04:00
|
|
|
wd = device_lookup_private(&wd_cd, WDUNIT(dev));
|
1997-06-19 00:39:55 +04:00
|
|
|
if (wd == NULL)
|
2000-07-06 04:43:04 +04:00
|
|
|
return (ENXIO);
|
1998-11-20 04:23:52 +03:00
|
|
|
|
2008-03-18 23:46:35 +03:00
|
|
|
if (! device_is_active(wd->sc_dev))
|
2004-08-04 02:03:46 +04:00
|
|
|
return (ENODEV);
|
|
|
|
|
2012-07-31 19:50:31 +04:00
|
|
|
if (wd->sc_capacity == 0)
|
|
|
|
return (ENODEV);
|
|
|
|
|
2004-09-25 07:34:02 +04:00
|
|
|
part = WDPART(dev);
|
|
|
|
|
2007-07-21 23:51:47 +04:00
|
|
|
mutex_enter(&wd->sc_dk.dk_openlock);
|
2004-09-25 07:34:02 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If there are wedges, and this is not RAW_PART, then we
|
|
|
|
* need to fail.
|
|
|
|
*/
|
|
|
|
if (wd->sc_dk.dk_nwedges != 0 && part != RAW_PART) {
|
|
|
|
error = EBUSY;
|
|
|
|
goto bad1;
|
|
|
|
}
|
|
|
|
|
1998-11-20 04:23:52 +03:00
|
|
|
/*
|
|
|
|
* If this is the first open of this device, add a reference
|
|
|
|
* to the adapter.
|
|
|
|
*/
|
|
|
|
if (wd->sc_dk.dk_openmask == 0 &&
|
2001-12-03 03:11:15 +03:00
|
|
|
(error = wd->atabus->ata_addref(wd->drvp)) != 0)
|
2004-09-25 07:34:02 +04:00
|
|
|
goto bad1;
|
1994-11-21 01:36:43 +03:00
|
|
|
|
1994-11-22 06:23:49 +03:00
|
|
|
if (wd->sc_dk.dk_openmask != 0) {
|
|
|
|
/*
|
|
|
|
* If any partition is open, but the disk has been invalidated,
|
|
|
|
* disallow further opens.
|
|
|
|
*/
|
1998-10-12 20:09:10 +04:00
|
|
|
if ((wd->sc_flags & WDF_LOADED) == 0) {
|
1995-04-01 14:29:41 +04:00
|
|
|
error = EIO;
|
2004-09-25 07:34:02 +04:00
|
|
|
goto bad2;
|
1995-04-01 14:29:41 +04:00
|
|
|
}
|
1994-11-22 06:23:49 +03:00
|
|
|
} else {
|
1998-10-12 20:09:10 +04:00
|
|
|
if ((wd->sc_flags & WDF_LOADED) == 0) {
|
1994-11-21 01:36:43 +03:00
|
|
|
|
|
|
|
/* Load the physical device parameters. */
|
2012-07-31 19:50:31 +04:00
|
|
|
if (wd_get_params(wd, AT_WAIT, &wd->sc_params) != 0) {
|
|
|
|
aprint_error_dev(wd->sc_dev,
|
|
|
|
"IDENTIFY failed\n");
|
|
|
|
error = EIO;
|
|
|
|
goto bad2;
|
|
|
|
}
|
|
|
|
wd->sc_flags |= WDF_LOADED;
|
1994-11-21 01:36:43 +03:00
|
|
|
/* Load the partition info if not already loaded. */
|
|
|
|
wdgetdisklabel(wd);
|
|
|
|
}
|
1993-04-06 14:06:28 +04:00
|
|
|
}
|
1994-11-21 01:36:43 +03:00
|
|
|
|
1994-11-22 06:23:49 +03:00
|
|
|
/* Check that the partition exists. */
|
1994-10-20 16:44:46 +03:00
|
|
|
if (part != RAW_PART &&
|
1996-01-08 01:01:38 +03:00
|
|
|
(part >= wd->sc_dk.dk_label->d_npartitions ||
|
|
|
|
wd->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
|
1994-10-20 16:44:46 +03:00
|
|
|
error = ENXIO;
|
2004-09-25 07:34:02 +04:00
|
|
|
goto bad2;
|
1994-10-20 16:44:46 +03:00
|
|
|
}
|
2003-07-11 19:33:13 +04:00
|
|
|
|
1994-03-10 08:18:33 +03:00
|
|
|
/* Insure only one open at a time. */
|
1993-04-06 14:06:28 +04:00
|
|
|
switch (fmt) {
|
|
|
|
case S_IFCHR:
|
1994-10-20 17:08:07 +03:00
|
|
|
wd->sc_dk.dk_copenmask |= (1 << part);
|
1993-04-06 14:06:28 +04:00
|
|
|
break;
|
|
|
|
case S_IFBLK:
|
1994-10-20 17:08:07 +03:00
|
|
|
wd->sc_dk.dk_bopenmask |= (1 << part);
|
1993-04-06 14:06:28 +04:00
|
|
|
break;
|
|
|
|
}
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_dk.dk_openmask =
|
|
|
|
wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask;
|
1994-03-05 11:17:06 +03:00
|
|
|
|
2007-07-21 23:51:47 +04:00
|
|
|
mutex_exit(&wd->sc_dk.dk_openlock);
|
1994-02-25 19:40:47 +03:00
|
|
|
return 0;
|
1994-10-20 16:44:46 +03:00
|
|
|
|
2004-09-25 07:34:02 +04:00
|
|
|
bad2:
|
1998-11-20 04:23:52 +03:00
|
|
|
if (wd->sc_dk.dk_openmask == 0)
|
2001-12-03 03:11:15 +03:00
|
|
|
wd->atabus->ata_delref(wd->drvp);
|
2004-09-25 07:34:02 +04:00
|
|
|
bad1:
|
2007-07-21 23:51:47 +04:00
|
|
|
mutex_exit(&wd->sc_dk.dk_openlock);
|
1994-10-20 16:44:46 +03:00
|
|
|
return error;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
|
2009-05-16 03:49:28 +04:00
|
|
|
/*
|
|
|
|
* Caller must hold wd->sc_dk.dk_openlock.
|
|
|
|
*/
|
2009-05-20 03:43:44 +04:00
|
|
|
static int
|
|
|
|
wdlastclose(device_t self)
|
2009-05-16 03:49:28 +04:00
|
|
|
{
|
2009-05-20 03:43:44 +04:00
|
|
|
struct wd_softc *wd = device_private(self);
|
|
|
|
|
2009-05-16 03:49:28 +04:00
|
|
|
wd_flushcache(wd, AT_WAIT);
|
|
|
|
|
|
|
|
if (! (wd->sc_flags & WDF_KLABEL))
|
|
|
|
wd->sc_flags &= ~WDF_LOADED;
|
|
|
|
|
|
|
|
wd->atabus->ata_delref(wd->drvp);
|
2009-05-20 03:43:44 +04:00
|
|
|
|
|
|
|
return 0;
|
2009-05-16 03:49:28 +04:00
|
|
|
}
|
|
|
|
|
1995-03-30 03:36:27 +04:00
|
|
|
int
|
2006-11-16 04:32:37 +03:00
|
|
|
wdclose(dev_t dev, int flag, int fmt, struct lwp *l)
|
1995-03-30 03:36:27 +04:00
|
|
|
{
|
2008-03-18 23:46:35 +03:00
|
|
|
struct wd_softc *wd =
|
2008-06-08 22:34:06 +04:00
|
|
|
device_lookup_private(&wd_cd, WDUNIT(dev));
|
1995-03-30 03:36:27 +04:00
|
|
|
int part = WDPART(dev);
|
2003-07-11 19:33:13 +04:00
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("wdclose\n"), DEBUG_FUNCS);
|
2004-09-25 07:34:02 +04:00
|
|
|
|
2007-07-21 23:51:47 +04:00
|
|
|
mutex_enter(&wd->sc_dk.dk_openlock);
|
1995-03-30 03:36:27 +04:00
|
|
|
|
|
|
|
switch (fmt) {
|
|
|
|
case S_IFCHR:
|
|
|
|
wd->sc_dk.dk_copenmask &= ~(1 << part);
|
|
|
|
break;
|
|
|
|
case S_IFBLK:
|
|
|
|
wd->sc_dk.dk_bopenmask &= ~(1 << part);
|
|
|
|
break;
|
|
|
|
}
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_dk.dk_openmask =
|
|
|
|
wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask;
|
1995-03-30 03:36:27 +04:00
|
|
|
|
2009-05-16 03:49:28 +04:00
|
|
|
if (wd->sc_dk.dk_openmask == 0)
|
2009-05-20 03:43:44 +04:00
|
|
|
wdlastclose(wd->sc_dev);
|
1995-03-30 03:36:27 +04:00
|
|
|
|
2007-07-21 23:51:47 +04:00
|
|
|
mutex_exit(&wd->sc_dk.dk_openlock);
|
1995-03-30 03:36:27 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1994-11-21 01:36:43 +03:00
|
|
|
void
|
2003-07-11 19:33:13 +04:00
|
|
|
wdgetdefaultlabel(struct wd_softc *wd, struct disklabel *lp)
|
1994-11-21 01:36:43 +03:00
|
|
|
{
|
1997-08-27 15:22:52 +04:00
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("wdgetdefaultlabel\n"), DEBUG_FUNCS);
|
1998-10-12 20:09:10 +04:00
|
|
|
memset(lp, 0, sizeof(struct disklabel));
|
1994-11-21 01:36:43 +03:00
|
|
|
|
1995-08-06 03:50:23 +04:00
|
|
|
lp->d_secsize = DEV_BSIZE;
|
1998-10-12 20:09:10 +04:00
|
|
|
lp->d_ntracks = wd->sc_params.atap_heads;
|
|
|
|
lp->d_nsectors = wd->sc_params.atap_sectors;
|
2003-08-03 21:53:04 +04:00
|
|
|
lp->d_ncylinders = (wd->sc_flags & WDF_LBA) ? wd->sc_capacity /
|
|
|
|
(wd->sc_params.atap_heads * wd->sc_params.atap_sectors) :
|
|
|
|
wd->sc_params.atap_cylinders;
|
1995-08-06 03:50:23 +04:00
|
|
|
lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
|
1994-11-21 01:36:43 +03:00
|
|
|
|
1999-11-10 17:11:34 +03:00
|
|
|
if (strcmp(wd->sc_params.atap_model, "ST506") == 0)
|
2015-01-02 22:42:05 +03:00
|
|
|
lp->d_type = DKTYPE_ST506;
|
1999-11-10 17:11:34 +03:00
|
|
|
else
|
2015-01-02 22:42:05 +03:00
|
|
|
lp->d_type = DKTYPE_ESDI;
|
1999-11-10 17:11:34 +03:00
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
strncpy(lp->d_typename, wd->sc_params.atap_model, 16);
|
1998-09-08 01:28:22 +04:00
|
|
|
strncpy(lp->d_packname, "fictitious", 16);
|
2003-04-04 02:18:23 +04:00
|
|
|
if (wd->sc_capacity > UINT32_MAX)
|
|
|
|
lp->d_secperunit = UINT32_MAX;
|
|
|
|
else
|
|
|
|
lp->d_secperunit = wd->sc_capacity;
|
1995-08-06 03:50:23 +04:00
|
|
|
lp->d_rpm = 3600;
|
|
|
|
lp->d_interleave = 1;
|
|
|
|
lp->d_flags = 0;
|
|
|
|
|
|
|
|
lp->d_partitions[RAW_PART].p_offset = 0;
|
2014-10-11 18:05:11 +04:00
|
|
|
lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
|
1995-08-06 03:50:23 +04:00
|
|
|
lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
|
|
|
|
lp->d_npartitions = RAW_PART + 1;
|
|
|
|
|
|
|
|
lp->d_magic = DISKMAGIC;
|
|
|
|
lp->d_magic2 = DISKMAGIC;
|
|
|
|
lp->d_checksum = dkcksum(lp);
|
1997-10-09 03:05:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fabricate a default disk label, and try to read the correct one.
|
|
|
|
*/
|
|
|
|
void
|
2003-07-11 19:33:13 +04:00
|
|
|
wdgetdisklabel(struct wd_softc *wd)
|
1997-10-09 03:05:22 +04:00
|
|
|
{
|
|
|
|
struct disklabel *lp = wd->sc_dk.dk_label;
|
2003-05-02 12:45:10 +04:00
|
|
|
const char *errstring;
|
2004-08-21 04:28:34 +04:00
|
|
|
int s;
|
1997-10-09 03:05:22 +04:00
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("wdgetdisklabel\n"), DEBUG_FUNCS);
|
1997-10-09 03:05:22 +04:00
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
memset(wd->sc_dk.dk_cpulabel, 0, sizeof(struct cpu_disklabel));
|
1997-10-09 03:05:22 +04:00
|
|
|
|
|
|
|
wdgetdefaultlabel(wd, lp);
|
1994-11-21 01:36:43 +03:00
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_badsect[0] = -1;
|
1994-11-22 12:33:59 +03:00
|
|
|
|
2004-08-21 04:28:34 +04:00
|
|
|
if (wd->drvp->state > RESET) {
|
|
|
|
s = splbio();
|
2012-07-31 19:50:31 +04:00
|
|
|
wd->drvp->drive_flags |= ATA_DRIVE_RESET;
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
|
|
|
}
|
2008-03-18 23:46:35 +03:00
|
|
|
errstring = readdisklabel(MAKEWDDEV(0, device_unit(wd->sc_dev),
|
2006-03-28 21:38:24 +04:00
|
|
|
RAW_PART), wdstrategy, lp,
|
|
|
|
wd->sc_dk.dk_cpulabel);
|
1994-11-21 01:36:43 +03:00
|
|
|
if (errstring) {
|
|
|
|
/*
|
|
|
|
* This probably happened because the drive's default
|
|
|
|
* geometry doesn't match the DOS geometry. We
|
|
|
|
* assume the DOS geometry is now in the label and try
|
|
|
|
* again. XXX This is a kluge.
|
|
|
|
*/
|
2004-08-21 04:28:34 +04:00
|
|
|
if (wd->drvp->state > RESET) {
|
|
|
|
s = splbio();
|
2012-07-31 19:50:31 +04:00
|
|
|
wd->drvp->drive_flags |= ATA_DRIVE_RESET;
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
|
|
|
}
|
2008-03-18 23:46:35 +03:00
|
|
|
errstring = readdisklabel(MAKEWDDEV(0, device_unit(wd->sc_dev),
|
1998-10-12 20:09:10 +04:00
|
|
|
RAW_PART), wdstrategy, lp, wd->sc_dk.dk_cpulabel);
|
1994-11-21 01:36:43 +03:00
|
|
|
}
|
|
|
|
if (errstring) {
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_error_dev(wd->sc_dev, "%s\n", errstring);
|
1994-11-21 01:36:43 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-08-21 04:28:34 +04:00
|
|
|
if (wd->drvp->state > RESET) {
|
|
|
|
s = splbio();
|
2012-07-31 19:50:31 +04:00
|
|
|
wd->drvp->drive_flags |= ATA_DRIVE_RESET;
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
|
|
|
}
|
1998-03-25 12:58:24 +03:00
|
|
|
#ifdef HAS_BAD144_HANDLING
|
1995-08-06 03:50:23 +04:00
|
|
|
if ((lp->d_flags & D_BADSECT) != 0)
|
1994-11-21 01:36:43 +03:00
|
|
|
bad144intern(wd);
|
1998-03-25 12:58:24 +03:00
|
|
|
#endif
|
1994-11-21 01:36:43 +03:00
|
|
|
}
|
|
|
|
|
2001-12-03 03:11:15 +03:00
|
|
|
void
|
2003-07-11 19:33:13 +04:00
|
|
|
wdperror(const struct wd_softc *wd)
|
2001-12-03 03:11:15 +03:00
|
|
|
{
|
2002-06-28 20:40:50 +04:00
|
|
|
static const char *const errstr0_3[] = {"address mark not found",
|
2001-12-03 03:11:15 +03:00
|
|
|
"track 0 not found", "aborted command", "media change requested",
|
|
|
|
"id not found", "media changed", "uncorrectable data error",
|
|
|
|
"bad block detected"};
|
2002-06-28 20:40:50 +04:00
|
|
|
static const char *const errstr4_5[] = {
|
|
|
|
"obsolete (address mark not found)",
|
2001-12-03 03:11:15 +03:00
|
|
|
"no media/write protected", "aborted command",
|
|
|
|
"media change requested", "id not found", "media changed",
|
|
|
|
"uncorrectable data error", "interface CRC error"};
|
2003-07-11 19:33:13 +04:00
|
|
|
const char *const *errstr;
|
2001-12-03 03:11:15 +03:00
|
|
|
int i;
|
2005-05-30 02:11:28 +04:00
|
|
|
const char *sep = "";
|
2001-12-03 03:11:15 +03:00
|
|
|
|
2008-03-18 23:46:35 +03:00
|
|
|
const char *devname = device_xname(wd->sc_dev);
|
2002-06-28 20:40:50 +04:00
|
|
|
struct ata_drive_datas *drvp = wd->drvp;
|
|
|
|
int errno = wd->sc_wdc_bio.r_error;
|
|
|
|
|
2001-12-03 03:11:15 +03:00
|
|
|
if (drvp->ata_vers >= 4)
|
|
|
|
errstr = errstr4_5;
|
|
|
|
else
|
|
|
|
errstr = errstr0_3;
|
|
|
|
|
2002-06-28 20:40:50 +04:00
|
|
|
printf("%s: (", devname);
|
|
|
|
|
2001-12-03 03:11:15 +03:00
|
|
|
if (errno == 0)
|
2002-06-28 20:40:50 +04:00
|
|
|
printf("error not notified");
|
2001-12-03 03:11:15 +03:00
|
|
|
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
|
|
if (errno & (1 << i)) {
|
2002-06-28 20:40:50 +04:00
|
|
|
printf("%s%s", sep, errstr[i]);
|
2001-12-03 03:11:15 +03:00
|
|
|
sep = ", ";
|
|
|
|
}
|
|
|
|
}
|
2002-06-28 20:40:50 +04:00
|
|
|
printf(")\n");
|
2001-12-03 03:11:15 +03:00
|
|
|
}
|
|
|
|
|
1993-03-21 12:45:37 +03:00
|
|
|
int
|
2007-03-04 08:59:00 +03:00
|
|
|
wdioctl(dev_t dev, u_long xfer, void *addr, int flag, struct lwp *l)
|
1993-03-21 12:45:37 +03:00
|
|
|
{
|
2008-03-18 23:46:35 +03:00
|
|
|
struct wd_softc *wd =
|
2008-06-08 22:34:06 +04:00
|
|
|
device_lookup_private(&wd_cd, WDUNIT(dev));
|
2013-08-19 18:58:57 +04:00
|
|
|
int error, s;
|
2001-01-07 21:09:01 +03:00
|
|
|
#ifdef __HAVE_OLD_DISKLABEL
|
2002-12-15 04:56:02 +03:00
|
|
|
struct disklabel *newlabel = NULL;
|
2001-01-07 21:09:01 +03:00
|
|
|
#endif
|
1997-08-27 15:22:52 +04:00
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("wdioctl\n"), DEBUG_FUNCS);
|
1997-08-27 15:22:52 +04:00
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
if ((wd->sc_flags & WDF_LOADED) == 0)
|
1994-11-21 01:36:43 +03:00
|
|
|
return EIO;
|
|
|
|
|
2014-12-31 22:52:04 +03:00
|
|
|
error = disk_ioctl(&wd->sc_dk, dev, xfer, addr, flag, l);
|
2006-10-25 08:04:45 +04:00
|
|
|
if (error != EPASSTHROUGH)
|
2014-12-31 22:52:04 +03:00
|
|
|
return error;
|
2006-10-25 08:04:45 +04:00
|
|
|
|
2013-08-19 18:58:57 +04:00
|
|
|
error = 0;
|
1997-08-27 15:22:52 +04:00
|
|
|
switch (xfer) {
|
1998-03-25 12:58:24 +03:00
|
|
|
#ifdef HAS_BAD144_HANDLING
|
1993-03-21 12:45:37 +03:00
|
|
|
case DIOCSBAD:
|
1993-04-06 14:06:28 +04:00
|
|
|
if ((flag & FWRITE) == 0)
|
1994-03-03 04:58:21 +03:00
|
|
|
return EBADF;
|
1996-01-08 01:01:38 +03:00
|
|
|
wd->sc_dk.dk_cpulabel->bad = *(struct dkbad *)addr;
|
|
|
|
wd->sc_dk.dk_label->d_flags |= D_BADSECT;
|
1994-03-10 08:18:33 +03:00
|
|
|
bad144intern(wd);
|
1994-03-03 04:58:21 +03:00
|
|
|
return 0;
|
1998-03-25 12:58:24 +03:00
|
|
|
#endif
|
2004-08-30 13:34:41 +04:00
|
|
|
#ifdef WD_SOFTBADSECT
|
2003-04-15 18:11:00 +04:00
|
|
|
case DIOCBSLIST :
|
|
|
|
{
|
2014-09-10 11:04:48 +04:00
|
|
|
uint32_t count, missing, skip;
|
2003-04-15 22:27:26 +04:00
|
|
|
struct disk_badsecinfo dbsi;
|
2003-04-15 18:11:00 +04:00
|
|
|
struct disk_badsectors *dbs;
|
|
|
|
size_t available;
|
2007-09-05 09:36:19 +04:00
|
|
|
uint8_t *laddr;
|
2003-04-15 18:11:00 +04:00
|
|
|
|
2003-04-15 22:27:26 +04:00
|
|
|
dbsi = *(struct disk_badsecinfo *)addr;
|
|
|
|
missing = wd->sc_bscount;
|
2003-04-15 18:11:00 +04:00
|
|
|
count = 0;
|
2003-04-15 22:27:26 +04:00
|
|
|
available = dbsi.dbsi_bufsize;
|
|
|
|
skip = dbsi.dbsi_skip;
|
2007-09-05 09:36:19 +04:00
|
|
|
laddr = (uint8_t *)dbsi.dbsi_buffer;
|
2003-04-15 22:27:26 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We start this loop with the expectation that all of the
|
|
|
|
* entries will be missed and decrement this counter each
|
|
|
|
* time we either skip over one (already copied out) or
|
|
|
|
* we actually copy it back to user space. The structs
|
|
|
|
* holding the bad sector information are copied directly
|
|
|
|
* back to user space whilst the summary is returned via
|
|
|
|
* the struct passed in via the ioctl.
|
|
|
|
*/
|
2003-04-15 18:11:00 +04:00
|
|
|
SLIST_FOREACH(dbs, &wd->sc_bslist, dbs_next) {
|
2003-04-15 22:27:26 +04:00
|
|
|
if (skip > 0) {
|
|
|
|
missing--;
|
|
|
|
skip--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (available < sizeof(*dbs))
|
2003-04-15 18:11:00 +04:00
|
|
|
break;
|
2003-04-15 22:27:26 +04:00
|
|
|
available -= sizeof(*dbs);
|
2003-04-15 18:11:00 +04:00
|
|
|
copyout(dbs, laddr, sizeof(*dbs));
|
|
|
|
laddr += sizeof(*dbs);
|
2003-04-15 22:27:26 +04:00
|
|
|
missing--;
|
2003-04-15 18:11:00 +04:00
|
|
|
count++;
|
|
|
|
}
|
2003-04-15 22:27:26 +04:00
|
|
|
dbsi.dbsi_left = missing;
|
|
|
|
dbsi.dbsi_copied = count;
|
|
|
|
*(struct disk_badsecinfo *)addr = dbsi;
|
2003-04-15 18:11:00 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DIOCBSFLUSH :
|
|
|
|
/* Clean out the bad sector list */
|
|
|
|
while (!SLIST_EMPTY(&wd->sc_bslist)) {
|
|
|
|
void *head = SLIST_FIRST(&wd->sc_bslist);
|
|
|
|
SLIST_REMOVE_HEAD(&wd->sc_bslist, dbs_next);
|
|
|
|
free(head, M_TEMP);
|
|
|
|
}
|
2003-04-15 22:27:26 +04:00
|
|
|
wd->sc_bscount = 0;
|
2003-04-15 18:11:00 +04:00
|
|
|
return 0;
|
2004-08-30 13:34:41 +04:00
|
|
|
#endif
|
2003-07-11 19:33:13 +04:00
|
|
|
|
1995-03-23 14:33:18 +03:00
|
|
|
case DIOCWDINFO:
|
1993-04-06 14:06:28 +04:00
|
|
|
case DIOCSDINFO:
|
2001-01-07 21:09:01 +03:00
|
|
|
#ifdef __HAVE_OLD_DISKLABEL
|
|
|
|
case ODIOCWDINFO:
|
|
|
|
case ODIOCSDINFO:
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
struct disklabel *lp;
|
|
|
|
|
2002-12-15 04:56:02 +03:00
|
|
|
if ((flag & FWRITE) == 0)
|
|
|
|
return EBADF;
|
|
|
|
|
2001-01-07 21:09:01 +03:00
|
|
|
#ifdef __HAVE_OLD_DISKLABEL
|
|
|
|
if (xfer == ODIOCSDINFO || xfer == ODIOCWDINFO) {
|
2011-11-25 17:55:40 +04:00
|
|
|
newlabel = malloc(sizeof *newlabel, M_TEMP,
|
|
|
|
M_WAITOK | M_ZERO);
|
2002-12-15 04:56:02 +03:00
|
|
|
if (newlabel == NULL)
|
|
|
|
return EIO;
|
|
|
|
memcpy(newlabel, addr, sizeof (struct olddisklabel));
|
|
|
|
lp = newlabel;
|
2001-01-07 21:09:01 +03:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
lp = (struct disklabel *)addr;
|
|
|
|
|
2007-07-21 23:51:47 +04:00
|
|
|
mutex_enter(&wd->sc_dk.dk_openlock);
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_flags |= WDF_LABELLING;
|
1995-03-23 14:33:18 +03:00
|
|
|
|
1996-01-08 01:01:38 +03:00
|
|
|
error = setdisklabel(wd->sc_dk.dk_label,
|
2001-01-07 21:09:01 +03:00
|
|
|
lp, /*wd->sc_dk.dk_openmask : */0,
|
1996-01-08 01:01:38 +03:00
|
|
|
wd->sc_dk.dk_cpulabel);
|
1993-04-06 14:06:28 +04:00
|
|
|
if (error == 0) {
|
2004-08-21 04:28:34 +04:00
|
|
|
if (wd->drvp->state > RESET) {
|
|
|
|
s = splbio();
|
2012-07-31 19:50:31 +04:00
|
|
|
wd->drvp->drive_flags |= ATA_DRIVE_RESET;
|
2004-08-21 04:28:34 +04:00
|
|
|
splx(s);
|
|
|
|
}
|
2001-01-07 21:09:01 +03:00
|
|
|
if (xfer == DIOCWDINFO
|
|
|
|
#ifdef __HAVE_OLD_DISKLABEL
|
|
|
|
|| xfer == ODIOCWDINFO
|
|
|
|
#endif
|
|
|
|
)
|
1995-03-23 14:33:18 +03:00
|
|
|
error = writedisklabel(WDLABELDEV(dev),
|
1996-01-08 01:01:38 +03:00
|
|
|
wdstrategy, wd->sc_dk.dk_label,
|
|
|
|
wd->sc_dk.dk_cpulabel);
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
1995-03-23 14:33:18 +03:00
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_flags &= ~WDF_LABELLING;
|
2007-07-21 23:51:47 +04:00
|
|
|
mutex_exit(&wd->sc_dk.dk_openlock);
|
2002-12-15 04:56:02 +03:00
|
|
|
#ifdef __HAVE_OLD_DISKLABEL
|
|
|
|
if (newlabel != NULL)
|
|
|
|
free(newlabel, M_TEMP);
|
|
|
|
#endif
|
1994-03-03 04:58:21 +03:00
|
|
|
return error;
|
2001-01-07 21:09:01 +03:00
|
|
|
}
|
1999-12-24 00:23:19 +03:00
|
|
|
|
|
|
|
case DIOCKLABEL:
|
|
|
|
if (*(int *)addr)
|
|
|
|
wd->sc_flags |= WDF_KLABEL;
|
|
|
|
else
|
|
|
|
wd->sc_flags &= ~WDF_KLABEL;
|
|
|
|
return 0;
|
2003-07-11 19:33:13 +04:00
|
|
|
|
1994-02-26 03:00:17 +03:00
|
|
|
case DIOCWLABEL:
|
1993-04-06 14:06:28 +04:00
|
|
|
if ((flag & FWRITE) == 0)
|
1994-03-03 04:58:21 +03:00
|
|
|
return EBADF;
|
1994-10-20 16:44:46 +03:00
|
|
|
if (*(int *)addr)
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_flags |= WDF_WLABEL;
|
1994-10-20 16:44:46 +03:00
|
|
|
else
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_flags &= ~WDF_WLABEL;
|
1994-03-03 04:58:21 +03:00
|
|
|
return 0;
|
1997-10-09 03:05:22 +04:00
|
|
|
|
|
|
|
case DIOCGDEFLABEL:
|
|
|
|
wdgetdefaultlabel(wd, (struct disklabel *)addr);
|
|
|
|
return 0;
|
2001-01-07 21:09:01 +03:00
|
|
|
#ifdef __HAVE_OLD_DISKLABEL
|
|
|
|
case ODIOCGDEFLABEL:
|
2002-12-15 04:56:02 +03:00
|
|
|
newlabel = malloc(sizeof *newlabel, M_TEMP, M_WAITOK);
|
|
|
|
if (newlabel == NULL)
|
|
|
|
return EIO;
|
|
|
|
wdgetdefaultlabel(wd, newlabel);
|
|
|
|
if (newlabel->d_npartitions <= OLDMAXPARTITIONS)
|
|
|
|
memcpy(addr, &newlabel, sizeof (struct olddisklabel));
|
|
|
|
else
|
|
|
|
error = ENOTTY;
|
|
|
|
free(newlabel, M_TEMP);
|
|
|
|
return error;
|
2001-01-07 21:09:01 +03:00
|
|
|
#endif
|
1997-10-09 03:05:22 +04:00
|
|
|
|
1993-03-21 12:45:37 +03:00
|
|
|
#ifdef notyet
|
|
|
|
case DIOCWFORMAT:
|
|
|
|
if ((flag & FWRITE) == 0)
|
1994-03-03 04:58:21 +03:00
|
|
|
return EBADF;
|
1998-10-12 20:09:10 +04:00
|
|
|
{
|
1994-03-03 04:58:21 +03:00
|
|
|
register struct format_op *fop;
|
|
|
|
struct iovec aiov;
|
|
|
|
struct uio auio;
|
2003-07-11 19:33:13 +04:00
|
|
|
|
1994-03-03 04:58:21 +03:00
|
|
|
fop = (struct format_op *)addr;
|
|
|
|
aiov.iov_base = fop->df_buf;
|
|
|
|
aiov.iov_len = fop->df_count;
|
|
|
|
auio.uio_iov = &aiov;
|
|
|
|
auio.uio_iovcnt = 1;
|
|
|
|
auio.uio_resid = fop->df_count;
|
|
|
|
auio.uio_offset =
|
1998-10-12 20:09:10 +04:00
|
|
|
fop->df_startblk * wd->sc_dk.dk_label->d_secsize;
|
2006-03-01 15:38:10 +03:00
|
|
|
auio.uio_vmspace = l->l_proc->p_vmspace;
|
2011-10-05 07:40:18 +04:00
|
|
|
error = physio(wdformat, NULL, dev, B_WRITE, wdminphys,
|
1994-03-10 08:18:33 +03:00
|
|
|
&auio);
|
1994-03-03 04:58:21 +03:00
|
|
|
fop->df_count -= auio.uio_resid;
|
1994-03-10 08:18:33 +03:00
|
|
|
fop->df_reg[0] = wdc->sc_status;
|
|
|
|
fop->df_reg[1] = wdc->sc_error;
|
1994-03-03 04:58:21 +03:00
|
|
|
return error;
|
1998-10-12 20:09:10 +04:00
|
|
|
}
|
1993-03-21 12:45:37 +03:00
|
|
|
#endif
|
2003-04-26 13:54:15 +04:00
|
|
|
case DIOCGCACHE:
|
|
|
|
return wd_getcache(wd, (int *)addr);
|
|
|
|
|
|
|
|
case DIOCSCACHE:
|
|
|
|
return wd_setcache(wd, *(int *)addr);
|
1998-11-19 22:46:12 +03:00
|
|
|
|
2004-05-22 00:35:53 +04:00
|
|
|
case DIOCCACHESYNC:
|
|
|
|
return wd_flushcache(wd, AT_WAIT);
|
|
|
|
|
1998-11-20 02:44:20 +03:00
|
|
|
case ATAIOCCOMMAND:
|
1998-11-19 22:46:12 +03:00
|
|
|
/*
|
|
|
|
* Make sure this command is (relatively) safe first
|
|
|
|
*/
|
1998-11-20 02:44:20 +03:00
|
|
|
if ((((atareq_t *) addr)->flags & ATACMD_READ) == 0 &&
|
1998-11-19 22:46:12 +03:00
|
|
|
(flag & FWRITE) == 0)
|
|
|
|
return (EBADF);
|
|
|
|
{
|
|
|
|
struct wd_ioctl *wi;
|
1998-11-20 02:44:20 +03:00
|
|
|
atareq_t *atareq = (atareq_t *) addr;
|
2005-05-30 02:11:28 +04:00
|
|
|
int error1;
|
1998-11-19 22:46:12 +03:00
|
|
|
|
|
|
|
wi = wi_get();
|
|
|
|
wi->wi_softc = wd;
|
1998-11-20 02:44:20 +03:00
|
|
|
wi->wi_atareq = *atareq;
|
1998-11-19 22:46:12 +03:00
|
|
|
|
1998-11-20 02:44:20 +03:00
|
|
|
if (atareq->datalen && atareq->flags &
|
|
|
|
(ATACMD_READ | ATACMD_WRITE)) {
|
2007-07-01 13:48:37 +04:00
|
|
|
void *tbuf;
|
|
|
|
if (atareq->datalen < DEV_BSIZE
|
|
|
|
&& atareq->command == WDCC_IDENTIFY) {
|
|
|
|
tbuf = malloc(DEV_BSIZE, M_TEMP, M_WAITOK);
|
|
|
|
wi->wi_iov.iov_base = tbuf;
|
|
|
|
wi->wi_iov.iov_len = DEV_BSIZE;
|
|
|
|
UIO_SETUP_SYSSPACE(&wi->wi_uio);
|
|
|
|
} else {
|
|
|
|
tbuf = NULL;
|
|
|
|
wi->wi_iov.iov_base = atareq->databuf;
|
|
|
|
wi->wi_iov.iov_len = atareq->datalen;
|
|
|
|
wi->wi_uio.uio_vmspace = l->l_proc->p_vmspace;
|
|
|
|
}
|
1998-11-19 22:46:12 +03:00
|
|
|
wi->wi_uio.uio_iov = &wi->wi_iov;
|
|
|
|
wi->wi_uio.uio_iovcnt = 1;
|
1998-11-20 02:44:20 +03:00
|
|
|
wi->wi_uio.uio_resid = atareq->datalen;
|
1998-11-19 22:46:12 +03:00
|
|
|
wi->wi_uio.uio_offset = 0;
|
|
|
|
wi->wi_uio.uio_rw =
|
1998-11-20 02:44:20 +03:00
|
|
|
(atareq->flags & ATACMD_READ) ? B_READ : B_WRITE;
|
2005-05-30 02:11:28 +04:00
|
|
|
error1 = physio(wdioctlstrategy, &wi->wi_bp, dev,
|
1998-11-20 02:44:20 +03:00
|
|
|
(atareq->flags & ATACMD_READ) ? B_READ : B_WRITE,
|
2011-10-05 07:40:18 +04:00
|
|
|
wdminphys, &wi->wi_uio);
|
2007-07-01 13:48:37 +04:00
|
|
|
if (tbuf != NULL && error1 == 0) {
|
|
|
|
error1 = copyout(tbuf, atareq->databuf,
|
|
|
|
atareq->datalen);
|
|
|
|
free(tbuf, M_TEMP);
|
|
|
|
}
|
1998-11-19 22:46:12 +03:00
|
|
|
} else {
|
|
|
|
/* No need to call physio if we don't have any
|
|
|
|
user data */
|
|
|
|
wi->wi_bp.b_flags = 0;
|
|
|
|
wi->wi_bp.b_data = 0;
|
|
|
|
wi->wi_bp.b_bcount = 0;
|
|
|
|
wi->wi_bp.b_dev = 0;
|
2005-12-11 15:16:03 +03:00
|
|
|
wi->wi_bp.b_proc = l->l_proc;
|
1998-11-19 22:46:12 +03:00
|
|
|
wdioctlstrategy(&wi->wi_bp);
|
2005-05-30 02:11:28 +04:00
|
|
|
error1 = wi->wi_bp.b_error;
|
1998-11-19 22:46:12 +03:00
|
|
|
}
|
1998-11-20 02:44:20 +03:00
|
|
|
*atareq = wi->wi_atareq;
|
1998-11-19 22:46:12 +03:00
|
|
|
wi_free(wi);
|
2005-05-30 02:11:28 +04:00
|
|
|
return(error1);
|
1998-11-19 22:46:12 +03:00
|
|
|
}
|
|
|
|
|
2005-12-26 13:36:47 +03:00
|
|
|
case DIOCGSTRATEGY:
|
|
|
|
{
|
|
|
|
struct disk_strategy *dks = (void *)addr;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
strlcpy(dks->dks_name, bufq_getstrategyname(wd->sc_q),
|
|
|
|
sizeof(dks->dks_name));
|
|
|
|
splx(s);
|
|
|
|
dks->dks_paramlen = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DIOCSSTRATEGY:
|
|
|
|
{
|
|
|
|
struct disk_strategy *dks = (void *)addr;
|
|
|
|
struct bufq_state *new;
|
|
|
|
struct bufq_state *old;
|
|
|
|
|
|
|
|
if ((flag & FWRITE) == 0) {
|
|
|
|
return EBADF;
|
|
|
|
}
|
|
|
|
if (dks->dks_param != NULL) {
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
dks->dks_name[sizeof(dks->dks_name) - 1] = 0; /* ensure term */
|
|
|
|
error = bufq_alloc(&new, dks->dks_name,
|
|
|
|
BUFQ_EXACT|BUFQ_SORT_RAWBLOCK);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
s = splbio();
|
|
|
|
old = wd->sc_q;
|
|
|
|
bufq_move(new, old);
|
|
|
|
wd->sc_q = new;
|
|
|
|
splx(s);
|
|
|
|
bufq_free(old);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1993-03-21 12:45:37 +03:00
|
|
|
default:
|
1994-03-03 04:58:21 +03:00
|
|
|
return ENOTTY;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
1994-03-03 04:58:21 +03:00
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
panic("wdioctl: impossible");
|
|
|
|
#endif
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
|
2014-07-25 12:22:08 +04:00
|
|
|
static int
|
|
|
|
wddiscard(dev_t dev, off_t pos, off_t len)
|
|
|
|
{
|
|
|
|
struct wd_softc *wd = device_lookup_private(&wd_cd, WDUNIT(dev));
|
|
|
|
daddr_t bno;
|
|
|
|
long size, done;
|
|
|
|
long maxatonce, amount;
|
|
|
|
int result;
|
|
|
|
|
|
|
|
if (!(wd->sc_params.atap_ata_major & WDC_VER_ATA7)
|
|
|
|
|| !(wd->sc_params.support_dsm & ATA_SUPPORT_DSM_TRIM)) {
|
|
|
|
/* not supported; ignore request */
|
|
|
|
ATADEBUG_PRINT(("wddiscard (unsupported)\n"), DEBUG_FUNCS);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
maxatonce = 0xffff; /*wd->sc_params.max_dsm_blocks*/
|
|
|
|
|
|
|
|
ATADEBUG_PRINT(("wddiscard\n"), DEBUG_FUNCS);
|
|
|
|
|
|
|
|
if ((wd->sc_flags & WDF_LOADED) == 0)
|
|
|
|
return EIO;
|
|
|
|
|
|
|
|
/* round the start up and the end down */
|
|
|
|
bno = (pos + DEV_BSIZE - 1) >> DEV_BSHIFT;
|
|
|
|
size = ((pos + len) >> DEV_BSHIFT) - bno;
|
|
|
|
|
|
|
|
done = 0;
|
|
|
|
while (done < size) {
|
|
|
|
amount = size - done;
|
|
|
|
if (amount > maxatonce) {
|
|
|
|
amount = maxatonce;
|
|
|
|
}
|
|
|
|
result = wd_trim(wd, WDPART(dev), bno + done, amount);
|
|
|
|
if (result) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
done += amount;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1994-02-26 03:00:17 +03:00
|
|
|
#ifdef B_FORMAT
|
1993-03-21 12:45:37 +03:00
|
|
|
int
|
|
|
|
wdformat(struct buf *bp)
|
|
|
|
{
|
1994-02-26 03:00:17 +03:00
|
|
|
|
1993-03-21 12:45:37 +03:00
|
|
|
bp->b_flags |= B_FORMAT;
|
1994-02-25 19:40:47 +03:00
|
|
|
return wdstrategy(bp);
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int
|
2003-07-11 19:33:13 +04:00
|
|
|
wdsize(dev_t dev)
|
1993-03-21 12:45:37 +03:00
|
|
|
{
|
1994-03-10 08:18:33 +03:00
|
|
|
struct wd_softc *wd;
|
2000-07-06 04:43:04 +04:00
|
|
|
int part, omask;
|
1994-11-21 01:36:43 +03:00
|
|
|
int size;
|
1997-08-27 15:22:52 +04:00
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("wdsize\n"), DEBUG_FUNCS);
|
1997-08-27 15:22:52 +04:00
|
|
|
|
2008-06-08 22:34:06 +04:00
|
|
|
wd = device_lookup_private(&wd_cd, WDUNIT(dev));
|
1997-06-19 00:39:55 +04:00
|
|
|
if (wd == NULL)
|
|
|
|
return (-1);
|
|
|
|
|
1994-11-21 01:36:43 +03:00
|
|
|
part = WDPART(dev);
|
1997-06-19 00:39:55 +04:00
|
|
|
omask = wd->sc_dk.dk_openmask & (1 << part);
|
|
|
|
|
|
|
|
if (omask == 0 && wdopen(dev, 0, S_IFBLK, NULL) != 0)
|
|
|
|
return (-1);
|
1996-01-08 01:01:38 +03:00
|
|
|
if (wd->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)
|
1994-11-21 01:36:43 +03:00
|
|
|
size = -1;
|
|
|
|
else
|
1997-06-24 04:20:44 +04:00
|
|
|
size = wd->sc_dk.dk_label->d_partitions[part].p_size *
|
1998-10-12 20:09:10 +04:00
|
|
|
(wd->sc_dk.dk_label->d_secsize / DEV_BSIZE);
|
1997-06-19 00:39:55 +04:00
|
|
|
if (omask == 0 && wdclose(dev, 0, S_IFBLK, NULL) != 0)
|
|
|
|
return (-1);
|
|
|
|
return (size);
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
|
1995-06-26 09:16:55 +04:00
|
|
|
/* #define WD_DUMP_NOT_TRUSTED if you just want to watch */
|
1998-10-12 20:09:10 +04:00
|
|
|
static int wddoingadump = 0;
|
|
|
|
static int wddumprecalibrated = 0;
|
1995-06-26 09:16:55 +04:00
|
|
|
|
1994-03-10 08:18:33 +03:00
|
|
|
/*
|
|
|
|
* Dump core after a system crash.
|
|
|
|
*/
|
1993-03-21 12:45:37 +03:00
|
|
|
int
|
2007-03-04 08:59:00 +03:00
|
|
|
wddump(dev_t dev, daddr_t blkno, void *va, size_t size)
|
1993-03-21 12:45:37 +03:00
|
|
|
{
|
1995-06-26 09:16:55 +04:00
|
|
|
struct wd_softc *wd; /* disk unit to do the I/O */
|
1998-10-12 20:09:10 +04:00
|
|
|
struct disklabel *lp; /* disk's disklabel */
|
2000-07-06 04:43:04 +04:00
|
|
|
int part, err;
|
1998-10-12 20:09:10 +04:00
|
|
|
int nblks; /* total number of sectors left to write */
|
1995-06-26 09:16:55 +04:00
|
|
|
|
|
|
|
/* Check if recursive dump; if so, punt. */
|
1994-11-23 04:35:43 +03:00
|
|
|
if (wddoingadump)
|
|
|
|
return EFAULT;
|
|
|
|
wddoingadump = 1;
|
1994-03-05 11:17:06 +03:00
|
|
|
|
2008-06-08 22:34:06 +04:00
|
|
|
wd = device_lookup_private(&wd_cd, WDUNIT(dev));
|
2000-07-06 04:43:04 +04:00
|
|
|
if (wd == NULL)
|
|
|
|
return (ENXIO);
|
1995-06-26 09:16:55 +04:00
|
|
|
|
1995-08-06 03:50:23 +04:00
|
|
|
part = WDPART(dev);
|
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
/* Convert to disk sectors. Request must be a multiple of size. */
|
1996-01-08 01:01:38 +03:00
|
|
|
lp = wd->sc_dk.dk_label;
|
1995-08-06 03:50:23 +04:00
|
|
|
if ((size % lp->d_secsize) != 0)
|
1995-06-26 09:16:55 +04:00
|
|
|
return EFAULT;
|
1995-08-06 03:50:23 +04:00
|
|
|
nblks = size / lp->d_secsize;
|
|
|
|
blkno = blkno / (lp->d_secsize / DEV_BSIZE);
|
1994-11-23 04:35:43 +03:00
|
|
|
|
1994-03-10 08:18:33 +03:00
|
|
|
/* Check transfer bounds against partition size. */
|
1995-08-06 03:50:23 +04:00
|
|
|
if ((blkno < 0) || ((blkno + nblks) > lp->d_partitions[part].p_size))
|
2003-07-11 19:33:13 +04:00
|
|
|
return EINVAL;
|
1995-06-26 09:16:55 +04:00
|
|
|
|
|
|
|
/* Offset block number to start of partition. */
|
1995-08-06 03:50:23 +04:00
|
|
|
blkno += lp->d_partitions[part].p_offset;
|
1995-06-26 09:16:55 +04:00
|
|
|
|
|
|
|
/* Recalibrate, if first dump transfer. */
|
|
|
|
if (wddumprecalibrated == 0) {
|
|
|
|
wddumprecalibrated = 1;
|
2004-08-20 21:19:44 +04:00
|
|
|
(*wd->atabus->ata_reset_drive)(wd->drvp,
|
2012-07-31 19:50:31 +04:00
|
|
|
AT_POLL | AT_RST_EMERG, NULL);
|
2000-05-27 20:11:16 +04:00
|
|
|
wd->drvp->state = RESET;
|
1994-03-07 08:54:44 +03:00
|
|
|
}
|
2003-07-11 19:33:13 +04:00
|
|
|
|
2005-08-29 23:05:54 +04:00
|
|
|
wd->sc_bp = NULL;
|
|
|
|
wd->sc_wdc_bio.blkno = blkno;
|
|
|
|
wd->sc_wdc_bio.flags = ATA_POLL;
|
|
|
|
if (wd->sc_flags & WDF_LBA48 &&
|
2009-12-18 00:03:10 +03:00
|
|
|
(wd->sc_wdc_bio.blkno + nblks) > wd->sc_capacity28)
|
2005-08-29 23:05:54 +04:00
|
|
|
wd->sc_wdc_bio.flags |= ATA_LBA48;
|
|
|
|
if (wd->sc_flags & WDF_LBA)
|
|
|
|
wd->sc_wdc_bio.flags |= ATA_LBA;
|
|
|
|
wd->sc_wdc_bio.bcount = nblks * lp->d_secsize;
|
|
|
|
wd->sc_wdc_bio.databuf = va;
|
1995-06-26 09:16:55 +04:00
|
|
|
#ifndef WD_DUMP_NOT_TRUSTED
|
2008-12-13 22:38:20 +03:00
|
|
|
switch (err = wd->atabus->ata_bio(wd->drvp, &wd->sc_wdc_bio)) {
|
2005-08-29 23:05:54 +04:00
|
|
|
case ATACMD_TRY_AGAIN:
|
|
|
|
panic("wddump: try again");
|
|
|
|
break;
|
|
|
|
case ATACMD_QUEUED:
|
|
|
|
panic("wddump: polled command has been queued");
|
|
|
|
break;
|
|
|
|
case ATACMD_COMPLETE:
|
|
|
|
break;
|
2008-12-13 22:38:20 +03:00
|
|
|
default:
|
|
|
|
panic("wddump: unknown atacmd code %d", err);
|
2005-08-29 23:05:54 +04:00
|
|
|
}
|
2008-12-13 22:38:20 +03:00
|
|
|
switch(err = wd->sc_wdc_bio.error) {
|
2005-08-29 23:05:54 +04:00
|
|
|
case TIMEOUT:
|
|
|
|
printf("wddump: device timed out");
|
|
|
|
err = EIO;
|
|
|
|
break;
|
|
|
|
case ERR_DF:
|
|
|
|
printf("wddump: drive fault");
|
|
|
|
err = EIO;
|
|
|
|
break;
|
|
|
|
case ERR_DMA:
|
|
|
|
printf("wddump: DMA error");
|
|
|
|
err = EIO;
|
|
|
|
break;
|
|
|
|
case ERROR:
|
|
|
|
printf("wddump: ");
|
|
|
|
wdperror(wd);
|
|
|
|
err = EIO;
|
|
|
|
break;
|
|
|
|
case NOERROR:
|
|
|
|
err = 0;
|
|
|
|
break;
|
|
|
|
default:
|
2008-12-13 22:38:20 +03:00
|
|
|
panic("wddump: unknown error type %d", err);
|
2005-08-29 23:05:54 +04:00
|
|
|
}
|
|
|
|
if (err != 0) {
|
|
|
|
printf("\n");
|
|
|
|
return err;
|
|
|
|
}
|
1995-06-26 09:16:55 +04:00
|
|
|
#else /* WD_DUMP_NOT_TRUSTED */
|
2005-08-29 23:05:54 +04:00
|
|
|
/* Let's just talk about this first... */
|
|
|
|
printf("wd%d: dump addr 0x%x, cylin %d, head %d, sector %d\n",
|
|
|
|
unit, va, cylin, head, sector);
|
|
|
|
delay(500 * 1000); /* half a second */
|
1995-06-26 09:16:55 +04:00
|
|
|
#endif
|
1993-03-21 12:45:37 +03:00
|
|
|
|
1995-06-26 09:16:55 +04:00
|
|
|
wddoingadump = 0;
|
1994-02-25 19:40:47 +03:00
|
|
|
return 0;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
1993-04-06 14:06:28 +04:00
|
|
|
|
1998-03-25 12:58:24 +03:00
|
|
|
#ifdef HAS_BAD144_HANDLING
|
1993-04-06 14:06:28 +04:00
|
|
|
/*
|
|
|
|
* Internalize the bad sector table.
|
|
|
|
*/
|
|
|
|
void
|
2003-07-11 19:33:13 +04:00
|
|
|
bad144intern(struct wd_softc *wd)
|
1993-04-06 14:06:28 +04:00
|
|
|
{
|
1996-01-08 01:01:38 +03:00
|
|
|
struct dkbad *bt = &wd->sc_dk.dk_cpulabel->bad;
|
|
|
|
struct disklabel *lp = wd->sc_dk.dk_label;
|
1994-10-20 21:37:45 +03:00
|
|
|
int i = 0;
|
1994-03-04 06:43:48 +03:00
|
|
|
|
2004-08-13 08:10:49 +04:00
|
|
|
ATADEBUG_PRINT(("bad144intern\n"), DEBUG_XFERS);
|
1997-08-27 15:22:52 +04:00
|
|
|
|
1998-03-25 12:58:24 +03:00
|
|
|
for (; i < NBT_BAD; i++) {
|
1994-10-20 21:37:45 +03:00
|
|
|
if (bt->bt_bad[i].bt_cyl == 0xffff)
|
1994-03-04 06:43:48 +03:00
|
|
|
break;
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_badsect[i] =
|
1994-10-20 21:37:45 +03:00
|
|
|
bt->bt_bad[i].bt_cyl * lp->d_secpercyl +
|
|
|
|
(bt->bt_bad[i].bt_trksec >> 8) * lp->d_nsectors +
|
|
|
|
(bt->bt_bad[i].bt_trksec & 0xff);
|
1993-04-06 14:06:28 +04:00
|
|
|
}
|
1998-03-25 12:58:24 +03:00
|
|
|
for (; i < NBT_BAD+1; i++)
|
1998-10-12 20:09:10 +04:00
|
|
|
wd->sc_badsect[i] = -1;
|
1993-07-06 04:42:43 +04:00
|
|
|
}
|
1998-03-25 12:58:24 +03:00
|
|
|
#endif
|
1993-12-13 13:16:56 +03:00
|
|
|
|
2006-09-22 08:48:38 +04:00
|
|
|
static void
|
2006-11-16 04:32:37 +03:00
|
|
|
wd_params_to_properties(struct wd_softc *wd, struct ataparams *params)
|
2006-09-22 08:48:38 +04:00
|
|
|
{
|
2013-05-29 04:47:48 +04:00
|
|
|
struct disk_geom *dg = &wd->sc_dk.dk_geom;
|
2006-09-22 08:48:38 +04:00
|
|
|
|
2013-05-29 04:47:48 +04:00
|
|
|
memset(dg, 0, sizeof(*dg));
|
2006-09-22 08:48:38 +04:00
|
|
|
|
2013-05-29 04:47:48 +04:00
|
|
|
dg->dg_secperunit = wd->sc_capacity;
|
|
|
|
dg->dg_secsize = DEV_BSIZE /* XXX 512? */;
|
|
|
|
dg->dg_nsectors = wd->sc_params.atap_sectors;
|
|
|
|
dg->dg_ntracks = wd->sc_params.atap_heads;
|
|
|
|
if ((wd->sc_flags & WDF_LBA) == 0)
|
|
|
|
dg->dg_ncylinders = wd->sc_params.atap_cylinders;
|
2006-10-26 09:04:18 +04:00
|
|
|
|
2013-05-29 04:47:48 +04:00
|
|
|
/* XXX Should have a case for ATA here, too. */
|
|
|
|
const char *cp = strcmp(wd->sc_params.atap_model, "ST506") ?
|
|
|
|
"ST506" : "ESDI";
|
2006-10-25 08:04:45 +04:00
|
|
|
|
2013-05-29 04:47:48 +04:00
|
|
|
disk_set_info(wd->sc_dev, &wd->sc_dk, cp);
|
2006-09-22 08:48:38 +04:00
|
|
|
}
|
|
|
|
|
1998-10-12 20:09:10 +04:00
|
|
|
int
|
2014-09-10 11:04:48 +04:00
|
|
|
wd_get_params(struct wd_softc *wd, uint8_t flags, struct ataparams *params)
|
1994-03-07 08:54:44 +03:00
|
|
|
{
|
2007-11-07 11:59:03 +03:00
|
|
|
|
2001-12-03 03:11:15 +03:00
|
|
|
switch (wd->atabus->ata_get_params(wd->drvp, flags, params)) {
|
1998-10-12 20:09:10 +04:00
|
|
|
case CMD_AGAIN:
|
|
|
|
return 1;
|
|
|
|
case CMD_ERR:
|
2012-07-31 19:50:31 +04:00
|
|
|
if (wd->drvp->drive_type != ATA_DRIVET_OLD)
|
|
|
|
return 1;
|
1998-10-12 20:09:10 +04:00
|
|
|
/*
|
|
|
|
* We `know' there's a drive here; just assume it's old.
|
|
|
|
* This geometry is only used to read the MBR and print a
|
|
|
|
* (false) attach message.
|
|
|
|
*/
|
|
|
|
strncpy(params->atap_model, "ST506",
|
|
|
|
sizeof params->atap_model);
|
|
|
|
params->atap_config = ATA_CFG_FIXED;
|
|
|
|
params->atap_cylinders = 1024;
|
|
|
|
params->atap_heads = 8;
|
|
|
|
params->atap_sectors = 17;
|
|
|
|
params->atap_multi = 1;
|
|
|
|
params->atap_capabilities1 = params->atap_capabilities2 = 0;
|
1999-01-18 23:06:24 +03:00
|
|
|
wd->drvp->ata_vers = -1; /* Mark it as pre-ATA */
|
2006-09-22 08:48:38 +04:00
|
|
|
/* FALLTHROUGH */
|
1998-10-12 20:09:10 +04:00
|
|
|
case CMD_OK:
|
2006-09-22 08:48:38 +04:00
|
|
|
wd_params_to_properties(wd, params);
|
1998-10-12 20:09:10 +04:00
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
panic("wd_get_params: bad return code from ata_get_params");
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
1994-03-07 08:54:44 +03:00
|
|
|
}
|
1998-11-19 22:46:12 +03:00
|
|
|
|
2003-04-26 13:54:15 +04:00
|
|
|
int
|
2003-07-11 19:33:13 +04:00
|
|
|
wd_getcache(struct wd_softc *wd, int *bitsp)
|
2003-04-26 13:54:15 +04:00
|
|
|
{
|
|
|
|
struct ataparams params;
|
|
|
|
|
|
|
|
if (wd_get_params(wd, AT_WAIT, ¶ms) != 0)
|
|
|
|
return EIO;
|
|
|
|
if (params.atap_cmd_set1 == 0x0000 ||
|
|
|
|
params.atap_cmd_set1 == 0xffff ||
|
|
|
|
(params.atap_cmd_set1 & WDC_CMD1_CACHE) == 0) {
|
|
|
|
*bitsp = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
*bitsp = DKCACHE_WCHANGE | DKCACHE_READ;
|
|
|
|
if (params.atap_cmd1_en & WDC_CMD1_CACHE)
|
|
|
|
*bitsp |= DKCACHE_WRITE;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-08-10 06:33:58 +04:00
|
|
|
const char at_errbits[] = "\20\10ERROR\11TIMEOU\12DF";
|
|
|
|
|
2003-04-26 13:54:15 +04:00
|
|
|
int
|
2003-07-11 19:33:13 +04:00
|
|
|
wd_setcache(struct wd_softc *wd, int bits)
|
2003-04-26 13:54:15 +04:00
|
|
|
{
|
|
|
|
struct ataparams params;
|
2004-08-12 08:57:19 +04:00
|
|
|
struct ata_command ata_c;
|
2003-04-26 13:54:15 +04:00
|
|
|
|
|
|
|
if (wd_get_params(wd, AT_WAIT, ¶ms) != 0)
|
|
|
|
return EIO;
|
|
|
|
|
|
|
|
if (params.atap_cmd_set1 == 0x0000 ||
|
2003-04-27 18:33:20 +04:00
|
|
|
params.atap_cmd_set1 == 0xffff ||
|
2003-04-26 13:54:15 +04:00
|
|
|
(params.atap_cmd_set1 & WDC_CMD1_CACHE) == 0)
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
2003-04-27 18:27:36 +04:00
|
|
|
if ((bits & DKCACHE_READ) == 0 ||
|
|
|
|
(bits & DKCACHE_SAVE) != 0)
|
2003-04-26 13:54:15 +04:00
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
2004-08-12 08:57:19 +04:00
|
|
|
memset(&ata_c, 0, sizeof(struct ata_command));
|
|
|
|
ata_c.r_command = SET_FEATURES;
|
|
|
|
ata_c.r_st_bmask = 0;
|
|
|
|
ata_c.r_st_pmask = 0;
|
|
|
|
ata_c.timeout = 30000; /* 30s timeout */
|
|
|
|
ata_c.flags = AT_WAIT;
|
2003-04-26 13:54:15 +04:00
|
|
|
if (bits & DKCACHE_WRITE)
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.r_features = WDSF_WRITE_CACHE_EN;
|
2003-04-26 13:54:15 +04:00
|
|
|
else
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.r_features = WDSF_WRITE_CACHE_DS;
|
2004-08-12 09:02:50 +04:00
|
|
|
if (wd->atabus->ata_exec_command(wd->drvp, &ata_c) != ATACMD_COMPLETE) {
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_error_dev(wd->sc_dev,
|
|
|
|
"wd_setcache command not complete\n");
|
2003-04-26 13:54:15 +04:00
|
|
|
return EIO;
|
|
|
|
}
|
2004-08-12 08:57:19 +04:00
|
|
|
if (ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
|
2004-08-10 06:33:58 +04:00
|
|
|
char sbuf[sizeof(at_errbits) + 64];
|
2008-12-17 01:35:21 +03:00
|
|
|
snprintb(sbuf, sizeof(sbuf), at_errbits, ata_c.flags);
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_error_dev(wd->sc_dev, "wd_setcache: status=%s\n", sbuf);
|
2004-08-10 06:33:58 +04:00
|
|
|
return EIO;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
static int
|
2004-08-10 06:33:58 +04:00
|
|
|
wd_standby(struct wd_softc *wd, int flags)
|
|
|
|
{
|
2004-08-12 08:57:19 +04:00
|
|
|
struct ata_command ata_c;
|
|
|
|
|
|
|
|
memset(&ata_c, 0, sizeof(struct ata_command));
|
|
|
|
ata_c.r_command = WDCC_STANDBY_IMMED;
|
|
|
|
ata_c.r_st_bmask = WDCS_DRDY;
|
|
|
|
ata_c.r_st_pmask = WDCS_DRDY;
|
|
|
|
ata_c.flags = flags;
|
|
|
|
ata_c.timeout = 30000; /* 30s timeout */
|
2004-08-12 09:02:50 +04:00
|
|
|
if (wd->atabus->ata_exec_command(wd->drvp, &ata_c) != ATACMD_COMPLETE) {
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_error_dev(wd->sc_dev,
|
2007-12-09 23:27:42 +03:00
|
|
|
"standby immediate command didn't complete\n");
|
2004-08-10 06:33:58 +04:00
|
|
|
return EIO;
|
|
|
|
}
|
2004-08-12 08:57:19 +04:00
|
|
|
if (ata_c.flags & AT_ERROR) {
|
|
|
|
if (ata_c.r_error == WDCE_ABRT) /* command not supported */
|
2004-08-10 06:33:58 +04:00
|
|
|
return ENODEV;
|
|
|
|
}
|
2004-08-12 08:57:19 +04:00
|
|
|
if (ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
|
2004-08-10 06:33:58 +04:00
|
|
|
char sbuf[sizeof(at_errbits) + 64];
|
2008-12-17 01:35:21 +03:00
|
|
|
snprintb(sbuf, sizeof(sbuf), at_errbits, ata_c.flags);
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_error_dev(wd->sc_dev, "wd_standby: status=%s\n", sbuf);
|
2003-04-26 13:54:15 +04:00
|
|
|
return EIO;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-05-22 00:35:53 +04:00
|
|
|
int
|
2003-07-11 19:33:13 +04:00
|
|
|
wd_flushcache(struct wd_softc *wd, int flags)
|
1999-01-18 23:06:24 +03:00
|
|
|
{
|
2004-08-12 08:57:19 +04:00
|
|
|
struct ata_command ata_c;
|
1999-01-18 23:06:24 +03:00
|
|
|
|
2004-11-19 01:14:53 +03:00
|
|
|
/*
|
|
|
|
* WDCC_FLUSHCACHE is here since ATA-4, but some drives report
|
|
|
|
* only ATA-2 and still support it.
|
|
|
|
*/
|
|
|
|
if (wd->drvp->ata_vers < 4 &&
|
|
|
|
((wd->sc_params.atap_cmd_set2 & WDC_CMD2_FC) == 0 ||
|
|
|
|
wd->sc_params.atap_cmd_set2 == 0xffff))
|
2004-05-22 00:35:53 +04:00
|
|
|
return ENODEV;
|
2004-08-12 08:57:19 +04:00
|
|
|
memset(&ata_c, 0, sizeof(struct ata_command));
|
2004-05-04 00:11:40 +04:00
|
|
|
if ((wd->sc_params.atap_cmd2_en & ATA_CMD2_LBA48) != 0 &&
|
2012-07-22 22:42:11 +04:00
|
|
|
(wd->sc_params.atap_cmd2_en & ATA_CMD2_FCE) != 0) {
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.r_command = WDCC_FLUSHCACHE_EXT;
|
2012-07-22 22:42:11 +04:00
|
|
|
flags |= AT_LBA48;
|
|
|
|
} else
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.r_command = WDCC_FLUSHCACHE;
|
|
|
|
ata_c.r_st_bmask = WDCS_DRDY;
|
|
|
|
ata_c.r_st_pmask = WDCS_DRDY;
|
2012-07-22 22:42:11 +04:00
|
|
|
ata_c.flags = flags | AT_READREG;
|
2012-07-22 22:37:31 +04:00
|
|
|
ata_c.timeout = 300000; /* 5m timeout */
|
2004-08-12 09:02:50 +04:00
|
|
|
if (wd->atabus->ata_exec_command(wd->drvp, &ata_c) != ATACMD_COMPLETE) {
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_error_dev(wd->sc_dev,
|
|
|
|
"flush cache command didn't complete\n");
|
2004-05-22 00:35:53 +04:00
|
|
|
return EIO;
|
1999-01-18 23:06:24 +03:00
|
|
|
}
|
2004-08-12 08:57:19 +04:00
|
|
|
if (ata_c.flags & AT_ERROR) {
|
|
|
|
if (ata_c.r_error == WDCE_ABRT) /* command not supported */
|
2004-05-22 00:35:53 +04:00
|
|
|
return ENODEV;
|
1999-01-18 23:06:24 +03:00
|
|
|
}
|
2004-08-12 08:57:19 +04:00
|
|
|
if (ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
|
2004-08-10 06:33:58 +04:00
|
|
|
char sbuf[sizeof(at_errbits) + 64];
|
2008-12-17 01:35:21 +03:00
|
|
|
snprintb(sbuf, sizeof(sbuf), at_errbits, ata_c.flags);
|
2008-03-18 23:46:35 +03:00
|
|
|
aprint_error_dev(wd->sc_dev, "wd_flushcache: status=%s\n",
|
2004-08-10 06:33:58 +04:00
|
|
|
sbuf);
|
2004-05-22 00:35:53 +04:00
|
|
|
return EIO;
|
1999-01-18 23:06:24 +03:00
|
|
|
}
|
2004-05-22 00:35:53 +04:00
|
|
|
return 0;
|
1999-01-18 23:06:24 +03:00
|
|
|
}
|
|
|
|
|
2012-10-19 21:09:06 +04:00
|
|
|
int
|
2014-07-25 12:22:08 +04:00
|
|
|
wd_trim(struct wd_softc *wd, int part, daddr_t bno, long size)
|
2012-10-19 21:09:06 +04:00
|
|
|
{
|
|
|
|
struct ata_command ata_c;
|
|
|
|
unsigned char *req;
|
|
|
|
|
|
|
|
if (part != RAW_PART)
|
|
|
|
bno += wd->sc_dk.dk_label->d_partitions[part].p_offset;;
|
|
|
|
|
|
|
|
req = kmem_zalloc(512, KM_SLEEP);
|
|
|
|
req[0] = bno & 0xff;
|
|
|
|
req[1] = (bno >> 8) & 0xff;
|
|
|
|
req[2] = (bno >> 16) & 0xff;
|
|
|
|
req[3] = (bno >> 24) & 0xff;
|
|
|
|
req[4] = (bno >> 32) & 0xff;
|
|
|
|
req[5] = (bno >> 40) & 0xff;
|
2014-07-25 12:22:08 +04:00
|
|
|
req[6] = size & 0xff;
|
|
|
|
req[7] = (size >> 8) & 0xff;
|
2012-10-19 21:09:06 +04:00
|
|
|
|
|
|
|
memset(&ata_c, 0, sizeof(struct ata_command));
|
|
|
|
ata_c.r_command = ATA_DATA_SET_MANAGEMENT;
|
|
|
|
ata_c.r_count = 1;
|
|
|
|
ata_c.r_features = ATA_SUPPORT_DSM_TRIM;
|
|
|
|
ata_c.r_st_bmask = WDCS_DRDY;
|
|
|
|
ata_c.r_st_pmask = WDCS_DRDY;
|
|
|
|
ata_c.timeout = 30000; /* 30s timeout */
|
|
|
|
ata_c.data = req;
|
|
|
|
ata_c.bcount = 512;
|
|
|
|
ata_c.flags |= AT_WRITE | AT_WAIT;
|
|
|
|
if (wd->atabus->ata_exec_command(wd->drvp, &ata_c) != ATACMD_COMPLETE) {
|
|
|
|
aprint_error_dev(wd->sc_dev,
|
|
|
|
"trim command didn't complete\n");
|
|
|
|
kmem_free(req, 512);
|
|
|
|
return EIO;
|
|
|
|
}
|
|
|
|
kmem_free(req, 512);
|
|
|
|
if (ata_c.flags & AT_ERROR) {
|
|
|
|
if (ata_c.r_error == WDCE_ABRT) /* command not supported */
|
|
|
|
return ENODEV;
|
|
|
|
}
|
|
|
|
if (ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
|
|
|
|
char sbuf[sizeof(at_errbits) + 64];
|
|
|
|
snprintb(sbuf, sizeof(sbuf), at_errbits, ata_c.flags);
|
|
|
|
aprint_error_dev(wd->sc_dev, "wd_trim: status=%s\n",
|
|
|
|
sbuf);
|
|
|
|
return EIO;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-02-28 17:40:17 +03:00
|
|
|
bool
|
|
|
|
wd_shutdown(device_t dev, int how)
|
2008-02-22 00:52:06 +03:00
|
|
|
{
|
2008-02-28 17:40:17 +03:00
|
|
|
struct wd_softc *wd = device_private(dev);
|
2009-02-06 16:43:11 +03:00
|
|
|
|
|
|
|
/* the adapter needs to be enabled */
|
|
|
|
if (wd->atabus->ata_addref(wd->drvp))
|
|
|
|
return true; /* no need to complain */
|
|
|
|
|
2008-02-22 00:52:06 +03:00
|
|
|
wd_flushcache(wd, AT_POLL);
|
2008-02-28 17:40:17 +03:00
|
|
|
if ((how & RB_POWERDOWN) == RB_POWERDOWN)
|
2008-02-22 00:52:06 +03:00
|
|
|
wd_standby(wd, AT_POLL);
|
2008-02-28 17:40:17 +03:00
|
|
|
return true;
|
2008-02-22 00:52:06 +03:00
|
|
|
}
|
|
|
|
|
1998-11-19 22:46:12 +03:00
|
|
|
/*
|
|
|
|
* Allocate space for a ioctl queue structure. Mostly taken from
|
|
|
|
* scsipi_ioctl.c
|
|
|
|
*/
|
|
|
|
struct wd_ioctl *
|
2003-07-11 19:33:13 +04:00
|
|
|
wi_get(void)
|
1998-11-19 22:46:12 +03:00
|
|
|
{
|
|
|
|
struct wd_ioctl *wi;
|
|
|
|
int s;
|
|
|
|
|
2002-01-12 19:29:30 +03:00
|
|
|
wi = malloc(sizeof(struct wd_ioctl), M_TEMP, M_WAITOK|M_ZERO);
|
2008-01-02 14:48:20 +03:00
|
|
|
buf_init(&wi->wi_bp);
|
1998-11-19 22:46:12 +03:00
|
|
|
s = splbio();
|
|
|
|
LIST_INSERT_HEAD(&wi_head, wi, wi_list);
|
|
|
|
splx(s);
|
|
|
|
return (wi);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free an ioctl structure and remove it from our list
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2003-07-11 19:33:13 +04:00
|
|
|
wi_free(struct wd_ioctl *wi)
|
1998-11-19 22:46:12 +03:00
|
|
|
{
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
LIST_REMOVE(wi, wi_list);
|
|
|
|
splx(s);
|
2008-01-02 14:48:20 +03:00
|
|
|
buf_destroy(&wi->wi_bp);
|
1998-11-19 22:46:12 +03:00
|
|
|
free(wi, M_TEMP);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find a wd_ioctl structure based on the struct buf.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct wd_ioctl *
|
2003-07-11 19:33:13 +04:00
|
|
|
wi_find(struct buf *bp)
|
1998-11-19 22:46:12 +03:00
|
|
|
{
|
|
|
|
struct wd_ioctl *wi;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
for (wi = wi_head.lh_first; wi != 0; wi = wi->wi_list.le_next)
|
|
|
|
if (bp == &wi->wi_bp)
|
|
|
|
break;
|
|
|
|
splx(s);
|
|
|
|
return (wi);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ioctl pseudo strategy routine
|
|
|
|
*
|
|
|
|
* This is mostly stolen from scsipi_ioctl.c:scsistrategy(). What
|
|
|
|
* happens here is:
|
|
|
|
*
|
|
|
|
* - wdioctl() queues a wd_ioctl structure.
|
|
|
|
*
|
|
|
|
* - wdioctl() calls physio/wdioctlstrategy based on whether or not
|
|
|
|
* user space I/O is required. If physio() is called, physio() eventually
|
|
|
|
* calls wdioctlstrategy().
|
|
|
|
*
|
2001-12-03 03:11:15 +03:00
|
|
|
* - In either case, wdioctlstrategy() calls wd->atabus->ata_exec_command()
|
1998-11-19 22:46:12 +03:00
|
|
|
* to perform the actual command
|
|
|
|
*
|
|
|
|
* The reason for the use of the pseudo strategy routine is because
|
|
|
|
* when doing I/O to/from user space, physio _really_ wants to be in
|
|
|
|
* the loop. We could put the entire buffer into the ioctl request
|
|
|
|
* structure, but that won't scale if we want to do things like download
|
|
|
|
* microcode.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2003-07-11 19:33:13 +04:00
|
|
|
wdioctlstrategy(struct buf *bp)
|
1998-11-19 22:46:12 +03:00
|
|
|
{
|
|
|
|
struct wd_ioctl *wi;
|
2004-08-12 08:57:19 +04:00
|
|
|
struct ata_command ata_c;
|
1998-11-19 22:46:12 +03:00
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
wi = wi_find(bp);
|
|
|
|
if (wi == NULL) {
|
2005-11-01 23:44:04 +03:00
|
|
|
printf("wdioctlstrategy: "
|
|
|
|
"No matching ioctl request found in queue\n");
|
1998-11-19 22:46:12 +03:00
|
|
|
error = EINVAL;
|
2008-01-02 14:48:20 +03:00
|
|
|
goto bad;
|
1998-11-19 22:46:12 +03:00
|
|
|
}
|
|
|
|
|
2004-08-12 08:57:19 +04:00
|
|
|
memset(&ata_c, 0, sizeof(ata_c));
|
1998-11-19 22:46:12 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Abort if physio broke up the transfer
|
|
|
|
*/
|
|
|
|
|
1998-11-20 02:44:20 +03:00
|
|
|
if (bp->b_bcount != wi->wi_atareq.datalen) {
|
1998-11-19 22:46:12 +03:00
|
|
|
printf("physio split wd ioctl request... cannot proceed\n");
|
|
|
|
error = EIO;
|
2008-01-02 14:48:20 +03:00
|
|
|
goto bad;
|
1998-11-19 22:46:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Abort if we didn't get a buffer size that was a multiple of
|
|
|
|
* our sector size (or was larger than NBBY)
|
|
|
|
*/
|
|
|
|
|
|
|
|
if ((bp->b_bcount % wi->wi_softc->sc_dk.dk_label->d_secsize) != 0 ||
|
|
|
|
(bp->b_bcount / wi->wi_softc->sc_dk.dk_label->d_secsize) >=
|
|
|
|
(1 << NBBY)) {
|
|
|
|
error = EINVAL;
|
2008-01-02 14:48:20 +03:00
|
|
|
goto bad;
|
1998-11-19 22:46:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure a timeout was supplied in the ioctl request
|
|
|
|
*/
|
|
|
|
|
1998-11-20 02:44:20 +03:00
|
|
|
if (wi->wi_atareq.timeout == 0) {
|
1998-11-19 22:46:12 +03:00
|
|
|
error = EINVAL;
|
2008-01-02 14:48:20 +03:00
|
|
|
goto bad;
|
1998-11-19 22:46:12 +03:00
|
|
|
}
|
|
|
|
|
1998-11-20 02:44:20 +03:00
|
|
|
if (wi->wi_atareq.flags & ATACMD_READ)
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.flags |= AT_READ;
|
1998-11-20 02:44:20 +03:00
|
|
|
else if (wi->wi_atareq.flags & ATACMD_WRITE)
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.flags |= AT_WRITE;
|
1998-11-19 22:46:12 +03:00
|
|
|
|
1998-11-24 02:00:26 +03:00
|
|
|
if (wi->wi_atareq.flags & ATACMD_READREG)
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.flags |= AT_READREG;
|
|
|
|
|
2012-01-25 00:04:07 +04:00
|
|
|
if ((wi->wi_atareq.flags & ATACMD_LBA) != 0)
|
|
|
|
ata_c.flags |= AT_LBA;
|
|
|
|
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.flags |= AT_WAIT;
|
|
|
|
|
|
|
|
ata_c.timeout = wi->wi_atareq.timeout;
|
|
|
|
ata_c.r_command = wi->wi_atareq.command;
|
2012-01-25 00:04:07 +04:00
|
|
|
ata_c.r_lba = ((wi->wi_atareq.head & 0x0f) << 24) |
|
|
|
|
(wi->wi_atareq.cylinder << 8) |
|
|
|
|
wi->wi_atareq.sec_num;
|
2004-08-12 08:57:19 +04:00
|
|
|
ata_c.r_count = wi->wi_atareq.sec_count;
|
|
|
|
ata_c.r_features = wi->wi_atareq.features;
|
|
|
|
ata_c.r_st_bmask = WDCS_DRDY;
|
|
|
|
ata_c.r_st_pmask = WDCS_DRDY;
|
|
|
|
ata_c.data = wi->wi_bp.b_data;
|
|
|
|
ata_c.bcount = wi->wi_bp.b_bcount;
|
|
|
|
|
|
|
|
if (wi->wi_softc->atabus->ata_exec_command(wi->wi_softc->drvp, &ata_c)
|
2004-08-12 09:02:50 +04:00
|
|
|
!= ATACMD_COMPLETE) {
|
1998-11-20 02:44:20 +03:00
|
|
|
wi->wi_atareq.retsts = ATACMD_ERROR;
|
2008-01-02 14:48:20 +03:00
|
|
|
goto bad;
|
1998-11-19 22:46:12 +03:00
|
|
|
}
|
|
|
|
|
2004-08-12 08:57:19 +04:00
|
|
|
if (ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
|
|
|
|
if (ata_c.flags & AT_ERROR) {
|
1998-11-20 02:44:20 +03:00
|
|
|
wi->wi_atareq.retsts = ATACMD_ERROR;
|
2004-08-12 08:57:19 +04:00
|
|
|
wi->wi_atareq.error = ata_c.r_error;
|
|
|
|
} else if (ata_c.flags & AT_DF)
|
1998-11-20 02:44:20 +03:00
|
|
|
wi->wi_atareq.retsts = ATACMD_DF;
|
1998-11-19 22:46:12 +03:00
|
|
|
else
|
1998-11-20 02:44:20 +03:00
|
|
|
wi->wi_atareq.retsts = ATACMD_TIMEOUT;
|
1998-11-24 02:00:26 +03:00
|
|
|
} else {
|
1998-11-20 02:44:20 +03:00
|
|
|
wi->wi_atareq.retsts = ATACMD_OK;
|
1998-11-24 02:00:26 +03:00
|
|
|
if (wi->wi_atareq.flags & ATACMD_READREG) {
|
2012-01-25 00:04:07 +04:00
|
|
|
wi->wi_atareq.command = ata_c.r_status;
|
|
|
|
wi->wi_atareq.features = ata_c.r_error;
|
2004-08-12 08:57:19 +04:00
|
|
|
wi->wi_atareq.sec_count = ata_c.r_count;
|
2012-01-25 00:04:07 +04:00
|
|
|
wi->wi_atareq.sec_num = ata_c.r_lba & 0xff;
|
|
|
|
wi->wi_atareq.head = (ata_c.r_device & 0xf0) |
|
|
|
|
((ata_c.r_lba >> 24) & 0x0f);
|
|
|
|
wi->wi_atareq.cylinder = (ata_c.r_lba >> 8) & 0xffff;
|
2004-08-12 08:57:19 +04:00
|
|
|
wi->wi_atareq.error = ata_c.r_error;
|
1998-11-24 02:00:26 +03:00
|
|
|
}
|
|
|
|
}
|
1998-11-19 22:46:12 +03:00
|
|
|
|
2008-01-02 14:48:20 +03:00
|
|
|
bp->b_error = 0;
|
|
|
|
biodone(bp);
|
|
|
|
return;
|
|
|
|
bad:
|
1998-11-19 22:46:12 +03:00
|
|
|
bp->b_error = error;
|
2013-01-10 02:03:49 +04:00
|
|
|
bp->b_resid = bp->b_bcount;
|
1998-11-19 22:46:12 +03:00
|
|
|
biodone(bp);
|
|
|
|
}
|