summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorJason Downs <downsj@cvs.openbsd.org>1996-06-08 09:12:48 +0000
committerJason Downs <downsj@cvs.openbsd.org>1996-06-08 09:12:48 +0000
commitfdc68389b9dfe6e5d35bc6cb6b180e9daf9e61e2 (patch)
treea4460d2ec87ff4da7cc88b4cae0ebcf64034dc98 /sys/dev
parent9a887c22a316f9b94faed82ec7af6c138ff71262 (diff)
Initial support ATAPI CD-ROMs.
Based on Manuel Bouyer's drivers.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/atapi/acd.c1337
-rw-r--r--sys/dev/atapi/atapi.h250
-rw-r--r--sys/dev/atapi/atapiconf.c676
-rw-r--r--sys/dev/atapi/atapilink.h352
-rw-r--r--sys/dev/atapi/files.atapi14
-rw-r--r--sys/dev/isa/files.isa16
-rw-r--r--sys/dev/isa/wd.c1278
-rw-r--r--sys/dev/isa/wdc.c1686
-rw-r--r--sys/dev/isa/wdlink.h151
-rw-r--r--sys/dev/isa/wdreg.h47
10 files changed, 4742 insertions, 1065 deletions
diff --git a/sys/dev/atapi/acd.c b/sys/dev/atapi/acd.c
new file mode 100644
index 00000000000..3b95e279722
--- /dev/null
+++ b/sys/dev/atapi/acd.c
@@ -0,0 +1,1337 @@
+/* $NetBSD: $ */
+
+/*
+ * Copyright (c) 1996 Manuel Bouyer. All rights reserved.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/disk.h>
+#include <sys/cdio.h>
+#include <sys/proc.h>
+
+#include <dev/atapi/atapilink.h>
+#include <dev/atapi/atapi.h>
+
+#define CDUNIT(z) DISKUNIT(z)
+#define CDPART(z) DISKPART(z)
+#define MAKECDDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part)
+
+#ifdef ACD_DEBUG
+#define ACD_DEBUG_PRINT(args) printf args
+#else
+#define ACD_DEBUG_PRINT(args)
+#endif
+
+struct acd_softc {
+ struct device sc_dev;
+ struct disk sc_dk;
+
+ int flags;
+#define CDF_LOCKED 0x01
+#define CDF_WANTED 0x02
+#define CDF_WLABEL 0x04 /* label is writable */
+#define CDF_LABELLING 0x08 /* writing label */
+ struct at_dev_link *ad_link; /* contains our drive number, etc ... */
+ struct cd_parms {
+ int blksize;
+ u_long disksize; /* total number sectors */
+ } params;
+ struct buf buf_queue;
+};
+
+int acdmatch __P((struct device *, void *, void *));
+void acdattach __P((struct device *, struct device *, void *));
+
+struct cfattach acd_ca = {
+ sizeof(struct acd_softc), acdmatch, acdattach
+};
+
+struct cfdriver acd_cd = {
+ NULL, "acd", DV_DISK
+};
+
+void acdgetdisklabel __P((struct acd_softc *));
+int acd_get_parms __P((struct acd_softc *, int));
+void acdstrategy __P((struct buf *));
+void acdstart __P((struct acd_softc *));
+int acd_pause __P((struct acd_softc *, int));
+void acdminphys __P((struct buf*));
+u_long acd_size __P((struct acd_softc*, int));
+int acddone __P((struct atapi_command_packet *));
+
+struct dkdriver acddkdriver = { acdstrategy };
+
+/*
+ * Called by the low level atapi code to find the right driver
+ * for a drive on the bus.
+ */
+int
+acdmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct cfdata *cf = match;
+ struct at_dev_link *sa = aux;
+
+#ifdef ATAPI_DEBUG_PROBE
+ printf("acdmatch: device %d\n",
+ sa->id.config.device_type & ATAPI_DEVICE_TYPE_MASK);
+#endif
+
+ if ((sa->id.config.device_type & ATAPI_DEVICE_TYPE_MASK) ==
+ ATAPI_DEVICE_TYPE_CD)
+ return 1;
+ return 0;
+}
+
+/*
+ * The routine called by the low level atapi routine when it discovers
+ * A device suitable for this driver
+ */
+void
+acdattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct acd_softc *acd = (void *)self;
+ struct at_dev_link *sa = aux;
+ struct mode_sense cmd;
+ struct cappage cap;
+ int print_type = 0;
+ char *mtype;
+
+ printf("\n");
+
+ sa->device_softc = acd;
+ sa->start = acdstart;
+ sa->done = acddone;
+ sa->flags |= ADEV_REMOVABLE;
+ sa->openings = 1;
+ acd->ad_link = sa;
+
+ /*
+ * Initialize and attach the disk structure.
+ */
+ acd->sc_dk.dk_driver = &acddkdriver;
+ acd->sc_dk.dk_name = acd->sc_dev.dv_xname;
+ disk_attach(&acd->sc_dk);
+
+ dk_establish(&acd->sc_dk, &acd->sc_dev);
+
+ (void)atapi_test_unit_ready(sa, A_POLLED | A_SILENT);
+ delay(1000);
+ (void)atapi_test_unit_ready(sa, A_POLLED | A_SILENT);
+
+ /* To clear media change, etc ...*/
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_MODE_SENSE;
+ cmd.page_code_control = CAP_PAGE;
+ _lto2b(sizeof(cap), cmd.length);
+ if (atapi_exec_cmd(sa, &cmd , sizeof(cmd), &cap, sizeof(cap),
+ B_READ, A_POLLED) != 0) {
+ printf("%s: can't MODE SENSE: atapi_exec_cmd failed\n",
+ self->dv_xname);
+ return;
+ }
+
+ /* Determine media type. */
+ switch (cap.medium_type) {
+ case MDT_UNKNOWN:
+ mtype = "medium type unknown";
+ break;
+
+ case MDT_DATA_120:
+ mtype = "120mm data disc";
+ break;
+
+ case MDT_AUDIO_120:
+ mtype = "120mm audio disc";
+ break;
+
+ case MDT_COMB_120:
+ mtype = "120mm data/audio disc";
+ break;
+
+ case MDT_PHOTO_120:
+ mtype = "120mm photo disc";
+ break;
+
+ case MDT_DATA_80:
+ mtype = "80mm data disc";
+ break;
+
+ case MDT_AUDIO_80:
+ mtype = "80mm audio disc";
+ break;
+
+ case MDT_COMB_80:
+ mtype = "80mm data/audio disc";
+ break;
+
+ case MDT_PHOTO_80:
+ mtype = "80mm photo disc";
+ break;
+
+ case MDT_NO_DISC:
+ mtype = "drive empty";
+ break;
+
+ case MDT_DOOR_OPEN:
+ mtype = "door open";
+ break;
+
+ case MDT_FMT_ERROR:
+ mtype = "medium format error";
+ break;
+
+ default:
+ mtype = "unknown type";
+ print_type = 1;
+ break;
+ }
+
+ /*
+ * Display information about the drive.
+ */
+ printf("%s: %s", self->dv_xname, mtype);
+ if (print_type)
+ printf(" (type 0x%x)", cap.medium_type);
+ printf("\n");
+}
+
+/*
+ * Wait interruptibly for an exclusive lock.
+ *
+ * XXX
+ * Several drivers do this; it should be abstracted and made MP-safe.
+ */
+int
+acdlock(acd)
+ struct acd_softc *acd;
+{
+ int error;
+
+ while ((acd->flags & CDF_LOCKED) != 0) {
+ acd->flags |= CDF_WANTED;
+ if ((error = tsleep(acd, PRIBIO | PCATCH, "acdlck", 0)) != 0)
+ return error;
+ }
+ acd->flags |= CDF_LOCKED;
+ return 0;
+}
+
+/*
+ * Unlock and wake up any waiters.
+ */
+void
+acdunlock(acd)
+ struct acd_softc *acd;
+{
+
+ acd->flags &= ~CDF_LOCKED;
+ if ((acd->flags & CDF_WANTED) != 0) {
+ acd->flags &= ~CDF_WANTED;
+ wakeup(acd);
+ }
+}
+
+/*
+ * open the device. Make sure the partition info is a up-to-date as can be.
+ */
+int
+acdopen(dev, flag, fmt)
+ dev_t dev;
+ int flag, fmt;
+{
+ struct acd_softc *acd;
+ struct at_dev_link *ad_link;
+ int unit, part;
+ int error;
+
+ ACD_DEBUG_PRINT(("acd: open\n"));
+
+ unit = CDUNIT(dev);
+ if (unit >= acd_cd.cd_ndevs)
+ return ENXIO;
+ acd = acd_cd.cd_devs[unit];
+ if (acd == NULL)
+ return ENXIO;
+
+ ad_link = acd->ad_link;
+
+ if ((error = atapi_test_unit_ready(ad_link,0)) != 0) {
+ if (error != UNIT_ATTENTION)
+ return EIO;
+ if ((ad_link->flags & ADEV_OPEN) != 0)
+ return EIO;
+ }
+
+ if (error = acdlock(acd))
+ return error;
+
+ if (acd->sc_dk.dk_openmask != 0) {
+ /*
+ * If any partition is open, but the disk has been invalidated,
+ * disallow further opens.
+ */
+ if ((ad_link->flags & ADEV_MEDIA_LOADED) == 0) {
+ error = EIO;
+ goto bad3;
+ }
+ } else {
+ ad_link->flags |= ADEV_OPEN;
+
+ /* Lock the pack in. */
+ if (error = atapi_prevent(ad_link, PR_PREVENT))
+ goto bad;
+
+ if ((ad_link->flags & ADEV_MEDIA_LOADED) == 0) {
+ ad_link->flags |= ADEV_MEDIA_LOADED;
+
+ /* Load the physical device parameters. */
+ if (acd_get_parms(acd, 0) != 0) {
+ error = ENXIO;
+ goto bad2;
+ }
+
+ /* Fabricate a disk label. */
+ acdgetdisklabel(acd);
+ }
+ }
+
+ part = CDPART(dev);
+
+ /* Check that the partition exists. */
+ if (part != RAW_PART &&
+ (part >= acd->sc_dk.dk_label->d_npartitions ||
+ acd->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
+ error = ENXIO;
+ goto bad;
+ }
+
+ /* Insure only one open at a time. */
+ switch (fmt) {
+ case S_IFCHR:
+ acd->sc_dk.dk_copenmask |= (1 << part);
+ break;
+ case S_IFBLK:
+ acd->sc_dk.dk_bopenmask |= (1 << part);
+ break;
+ }
+ acd->sc_dk.dk_openmask =
+ acd->sc_dk.dk_copenmask | acd->sc_dk.dk_bopenmask;
+
+ ACD_DEBUG_PRINT(("acd: open complete\n"));
+
+ acdunlock(acd);
+ return 0;
+
+bad2:
+ ad_link->flags &= ~ADEV_MEDIA_LOADED;
+
+bad:
+ if (acd->sc_dk.dk_openmask == 0) {
+ atapi_prevent(ad_link, PR_ALLOW);
+ ad_link->flags &= ~ADEV_OPEN;
+ }
+
+bad3:
+ acdunlock(acd);
+ return error;
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+int
+acdclose(dev, flag, fmt)
+ dev_t dev;
+ int flag, fmt;
+{
+ struct acd_softc *acd = acd_cd.cd_devs[CDUNIT(dev)];
+ int part = CDPART(dev);
+ int error;
+
+ if ((error = acdlock(acd)) != 0)
+ return error;
+
+ switch (fmt) {
+ case S_IFCHR:
+ acd->sc_dk.dk_copenmask &= ~(1 << part);
+ break;
+ case S_IFBLK:
+ acd->sc_dk.dk_bopenmask &= ~(1 << part);
+ break;
+ }
+ acd->sc_dk.dk_openmask =
+ acd->sc_dk.dk_copenmask | acd->sc_dk.dk_bopenmask;
+
+ if (acd->sc_dk.dk_openmask == 0) {
+ /* XXXX Must wait for I/O to complete! */
+
+ atapi_prevent(acd->ad_link, PR_ALLOW);
+ acd->ad_link->flags &= ~ADEV_OPEN;
+ }
+
+ acdunlock(acd);
+ return 0;
+}
+
+/*
+ * Actually translate the requested transfer into one the physical driver can
+ * understand. The transfer is described by a buf and will include only one
+ * physical transfer.
+ */
+void
+acdstrategy(bp)
+ struct buf *bp;
+{
+ struct acd_softc *acd = acd_cd.cd_devs[CDUNIT(bp->b_dev)];
+ int opri;
+
+ ACD_DEBUG_PRINT(("acdstrategy\n"));
+
+ /*
+ * The transfer must be a whole number of blocks.
+ */
+ if ((bp->b_bcount % acd->sc_dk.dk_label->d_secsize) != 0) {
+ bp->b_error = EINVAL;
+ goto bad;
+ }
+ if ((bp->b_flags & (B_READ|B_WRITE)) == B_WRITE) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+ /*
+ * If the device has been made invalid, error out
+ * maybe the media changed
+ */
+ if ((acd->ad_link->flags & ADEV_MEDIA_LOADED) == 0) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /*
+ * If it's a null transfer, return immediately
+ */
+ if (bp->b_bcount == 0)
+ goto done;
+
+ /*
+ * Do bounds checking, adjust transfer. if error, process.
+ * If end of partition, just return.
+ */
+ if (CDPART(bp->b_dev) != RAW_PART &&
+ bounds_check_with_label(bp, acd->sc_dk.dk_label,
+ (acd->flags & (CDF_WLABEL|CDF_LABELLING)) != 0) <= 0)
+ goto done;
+
+ opri = splbio();
+
+ /*
+ * Place it in the queue of disk activities for this disk
+ */
+ disksort(&acd->buf_queue, bp);
+
+ /*
+ * Tell the device to get going on the transfer if it's
+ * not doing anything, otherwise just wait for completion
+ */
+ acdstart(acd);
+
+ splx(opri);
+ return;
+
+bad:
+ bp->b_flags |= B_ERROR;
+done:
+ /*
+ * Correctly set the buf to indicate a completed xfer
+ */
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+}
+
+/*
+ * acdstart looks to see if there is a buf waiting for the device
+ * and that the device is not already busy. If both are true,
+ * It deques the buf and creates a atapi command to perform the
+ * transfer in the buf. The transfer request will call atapi_done
+ * on completion, which will in turn call this routine again
+ * so that the next queued transfer is performed.
+ * The bufs are queued by the strategy routine (cdstrategy)
+ *
+ * This routine is also called after other non-queued requests
+ * have been made of the atapi driver, to ensure that the queue
+ * continues to be drained.
+ *
+ * must be called at the correct (highish) spl level
+ * cdstart() is called at splbio from cdstrategy and atapi_done
+ */
+void
+acdstart(acd)
+ struct acd_softc *acd;
+{
+ struct at_dev_link *ad_link;
+ struct buf *bp = 0;
+ struct buf *dp;
+ struct read cmd;
+ u_int32_t blkno, nblks;
+ struct partition *p;
+
+ ACD_DEBUG_PRINT(("acd: acdstart\n"));
+
+#ifdef DIAGNOSTIC
+ if (acd == NULL) {
+ printf("acdstart: null acd\n");
+ return;
+ }
+#endif
+
+ ad_link = acd->ad_link;
+
+#ifdef DIAGNOSTIC
+ if (ad_link == NULL) {
+ printf("acdstart: null ad_link\n");
+ return;
+ }
+#endif
+ /*
+ * Check if the device has room for another command
+ */
+ while (ad_link->openings > 0) {
+ /*
+ * there is excess capacity, but a special waits
+ * It'll need the adapter as soon as we clear out of the
+ * way and let it run (user level wait).
+ */
+ if (ad_link->flags & ADEV_WAITING) {
+#ifdef ATAPI_DEBUG
+ printf("acdstart: waking up\n");
+#endif
+ ad_link->flags &= ~ADEV_WAITING;
+ wakeup((caddr_t)ad_link);
+ return;
+ }
+
+ /*
+ * See if there is a buf with work for us to do..
+ */
+ dp = &acd->buf_queue;
+#ifdef ACD_DEBUG
+ if (dp == NULL) {
+ printf("acdstart: null dp\n");
+ return;
+ }
+#endif
+ if ((bp = dp->b_actf) == NULL) /* yes, an assign */
+ return;
+
+ ACD_DEBUG_PRINT(("acdstart: a buf\n"));
+
+ dp->b_actf = bp->b_actf;
+
+ /*
+ * If the device has become invalid, abort all the
+ * reads and writes until all files have been closed and
+ * re-opened
+ */
+ if ((ad_link->flags & ADEV_MEDIA_LOADED) == 0) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ continue;
+ }
+
+ /*
+ *
+ * First, translate the block to absolute and put it in terms
+ * of the logical blocksize of the device.
+ */
+ blkno =
+ bp->b_blkno / (acd->sc_dk.dk_label->d_secsize / DEV_BSIZE);
+ if (CDPART(bp->b_dev) != RAW_PART) {
+ p =
+ &acd->sc_dk.dk_label->d_partitions[CDPART(bp->b_dev)];
+ blkno += p->p_offset;
+ }
+ nblks = howmany(bp->b_bcount, acd->sc_dk.dk_label->d_secsize);
+
+ ACD_DEBUG_PRINT(("acdstart: blkno %d nblk %d\n",
+ blkno, nblks));
+
+ /*
+ * Fill out the atapi command
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_READ;
+ _lto4b(blkno, cmd.lba);
+ _lto2b(nblks, cmd.length);
+
+ /* Instrumentation. */
+ disk_busy(&acd->sc_dk);
+
+ /*
+ * Call the routine that chats with the adapter.
+ * Note: we cannot sleep as we may be an interrupt
+ */
+ if (atapi_exec_io(ad_link, &cmd, sizeof(cmd), bp, A_NOSLEEP))
+ printf("%s: not queued", acd->sc_dev.dv_xname);
+ }
+}
+
+int
+acdread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+
+ return (physio(acdstrategy, NULL, dev, B_READ, acdminphys, uio));
+}
+
+int
+acdwrite(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+
+ return (physio(acdstrategy, NULL, dev, B_WRITE, acdminphys, uio));
+}
+
+/*
+ * Perform special action on behalf of the user.
+ * Knows about the internals of this device
+ */
+int
+acdioctl(dev, cmd, addr, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ struct acd_softc *acd = acd_cd.cd_devs[CDUNIT(dev)];
+ int error;
+
+ /*
+ * If the device is not valid.. abandon ship
+ */
+ if ((acd->ad_link->flags & ADEV_MEDIA_LOADED) == 0)
+ return EIO;
+
+ switch (cmd) {
+ case DIOCGDINFO:
+ *(struct disklabel *)addr = *acd->sc_dk.dk_label;
+ return 0;
+
+ case DIOCGPART:
+ ((struct partinfo *)addr)->disklab = acd->sc_dk.dk_label;
+ ((struct partinfo *)addr)->part =
+ &acd->sc_dk.dk_label->d_partitions[CDPART(dev)];
+ return 0;
+
+ case DIOCWDINFO:
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ return EBADF;
+
+ if (error = acdlock(acd))
+ return error;
+ acd->flags |= CDF_LABELLING;
+
+ error = setdisklabel(acd->sc_dk.dk_label,
+ (struct disklabel *)addr, /*acd->sc_dk.dk_openmask : */0,
+ acd->sc_dk.dk_cpulabel);
+ if (error == 0) {
+ /* XXX ?? */
+ }
+
+ acd->flags &= ~CDF_LABELLING;
+ acdunlock(acd);
+ return error;
+
+ case DIOCWLABEL:
+ return EROFS;
+
+#ifdef notyet
+ case CDIOCPLAYTRACKS:
+ {
+ struct ioc_play_track *args = (struct ioc_play_track *)addr;
+ struct acd_mode_data data;
+ if (error = acd_get_mode(acd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = acd_set_mode(acd, &data))
+ return error;
+ return acd_play_tracks(acd, args->start_track,
+ args->start_index, args->end_track, args->end_index);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCPLAYMSF:
+ {
+ struct ioc_play_msf *args
+ = (struct ioc_play_msf *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = cd_set_mode(cd, &data))
+ return error;
+ return cd_play_msf(cd, args->start_m, args->start_s,
+ args->start_f, args->end_m, args->end_s, args->end_f);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCPLAYBLOCKS:
+ {
+ struct ioc_play_blocks *args
+ = (struct ioc_play_blocks *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = cd_set_mode(cd, &data))
+ return error;
+ return cd_play(cd, args->blk, args->len);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCREADSUBCHANNEL:
+ {
+ struct ioc_read_subchannel *args
+ = (struct ioc_read_subchannel *)addr;
+ struct cd_sub_channel_info data;
+ int len = args->data_len;
+ if (len > sizeof(data) ||
+ len < sizeof(struct cd_sub_channel_header))
+ return EINVAL;
+ if (error = cd_read_subchannel(cd, args->address_format,
+ args->data_format, args->track, &data, len))
+ return error;
+ len = min(len, ((data.header.data_len[0] << 8) +
+ data.header.data_len[1] +
+ sizeof(struct cd_sub_channel_header)));
+ return copyout(&data, args->data, len);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOREADTOCHEADER:
+ {
+ struct ioc_toc_header th;
+ if (error = cd_read_toc(cd, 0, 0, &th, sizeof(th)))
+ return error;
+ th.len = ntohs(th.len);
+ bcopy(&th, addr, sizeof(th));
+ return 0;
+ }
+#endif
+
+#ifdef notyet
+ case CDIOREADTOCENTRYS:
+ {
+ struct cd_toc {
+ struct ioc_toc_header header;
+ struct cd_toc_entry entries[65];
+ } data;
+ struct ioc_read_toc_entry *te =
+ (struct ioc_read_toc_entry *)addr;
+ struct ioc_toc_header *th;
+ int len = te->data_len;
+ th = &data.header;
+
+ if (len > sizeof(data.entries) ||
+ len < sizeof(struct cd_toc_entry))
+ return EINVAL;
+ if (error = cd_read_toc(cd, te->address_format,
+ te->starting_track, (struct cd_toc_entry *)&data,
+ len + sizeof(struct ioc_toc_header)))
+ return error;
+ len = min(len, ntohs(th->len) - (sizeof(th->starting_track) +
+ sizeof(th->ending_track)));
+ return copyout(data.entries, te->data, len);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETPATCH:
+ {
+ struct ioc_patch *arg = (struct ioc_patch *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = arg->patch[0];
+ data.page.audio.port[RIGHT_PORT].channels = arg->patch[1];
+ data.page.audio.port[2].channels = arg->patch[2];
+ data.page.audio.port[3].channels = arg->patch[3];
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCGETVOL:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ arg->vol[LEFT_PORT] = data.page.audio.port[LEFT_PORT].volume;
+ arg->vol[RIGHT_PORT] = data.page.audio.port[RIGHT_PORT].volume;
+ arg->vol[2] = data.page.audio.port[2].volume;
+ arg->vol[3] = data.page.audio.port[3].volume;
+ return 0;
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETVOL:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = CHANNEL_0;
+ data.page.audio.port[LEFT_PORT].volume = arg->vol[LEFT_PORT];
+ data.page.audio.port[RIGHT_PORT].channels = CHANNEL_1;
+ data.page.audio.port[RIGHT_PORT].volume = arg->vol[RIGHT_PORT];
+ data.page.audio.port[2].volume = arg->vol[2];
+ data.page.audio.port[3].volume = arg->vol[3];
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETMONO:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels =
+ LEFT_CHANNEL | RIGHT_CHANNEL | 4 | 8;
+ data.page.audio.port[RIGHT_PORT].channels =
+ LEFT_CHANNEL | RIGHT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETSTEREO:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL;
+ data.page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETMUTE:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = 0;
+ data.page.audio.port[RIGHT_PORT].channels = 0;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETLEFT:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL;
+ data.page.audio.port[RIGHT_PORT].channels = LEFT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETRIGHT:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = RIGHT_CHANNEL;
+ data.page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+ case CDIOCRESUME:
+ return acd_pause(acd, PA_RESUME);
+
+ case CDIOCPAUSE:
+ return acd_pause(acd, PA_PAUSE);
+
+ case CDIOCSTART:
+ return atapi_start_stop(acd->ad_link, SSS_START, 0);
+
+ case CDIOCSTOP:
+ return atapi_start_stop(acd->ad_link, SSS_STOP, 0);
+
+ case CDIOCEJECT:
+ case DIOCEJECT:
+ return atapi_start_stop(acd->ad_link, SSS_STOP|SSS_LOEJ, 0);
+
+ case CDIOCALLOW:
+ return atapi_prevent(acd->ad_link, PR_ALLOW);
+
+ case CDIOCPREVENT:
+ return atapi_prevent(acd->ad_link, PR_PREVENT);
+
+ case DIOCLOCK:
+ return atapi_prevent(acd->ad_link,
+ (*(int *)addr) ? PR_PREVENT : PR_ALLOW);
+ case CDIOCRESET:
+ return acd_reset(acd);
+
+ default:
+ return ENOTTY;
+ }
+
+#ifdef DIAGNOSTIC
+ panic("cdioctl: impossible");
+#endif
+}
+
+/*
+ * Load the label information on the named device
+ * Actually fabricate a disklabel
+ *
+ * EVENTUALLY take information about different
+ * data tracks from the TOC and put it in the disklabel
+ */
+void
+acdgetdisklabel(acd)
+ struct acd_softc *acd;
+{
+ struct disklabel *lp = acd->sc_dk.dk_label;
+
+ bzero(lp, sizeof(struct disklabel));
+ bzero(acd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel));
+
+#if 0 /* XXX */
+ lp->d_secsize = acd->params.blksize;
+#endif
+ lp->d_secsize = 2048;
+ lp->d_ntracks = 1;
+ lp->d_nsectors = 100;
+ lp->d_ncylinders = (acd->params.disksize / 100) + 1;
+ lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
+
+ strncpy(lp->d_typename, "ATAPI CD-ROM", 16);
+ lp->d_type = DTYPE_SCSI; /* XXX */
+ strncpy(lp->d_packname, "fictitious", 16);
+ lp->d_secperunit = acd->params.disksize;
+ lp->d_rpm = 300;
+ lp->d_interleave = 1;
+ lp->d_flags = D_REMOVABLE;
+
+ lp->d_partitions[0].p_offset = 0;
+ lp->d_partitions[0].p_size =
+ lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
+ lp->d_partitions[0].p_fstype = FS_ISO9660;
+ lp->d_partitions[RAW_PART].p_offset = 0;
+ lp->d_partitions[RAW_PART].p_size =
+ lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
+ lp->d_partitions[RAW_PART].p_fstype = FS_ISO9660;
+ lp->d_npartitions = RAW_PART + 1;
+
+ lp->d_magic = DISKMAGIC;
+ lp->d_magic2 = DISKMAGIC;
+ lp->d_checksum = dkcksum(lp);
+}
+
+/*
+ * Find out from the device what it's capacity is
+ */
+u_long
+acd_size(cd, flags)
+ struct acd_softc *cd;
+ int flags;
+{
+ struct read_cd_capacity_data rdcap;
+ struct read_cd_capacity cmd;
+ u_long blksize;
+ u_long size;
+
+ /*
+ * make up a atapi command and ask the atapi driver to do
+ * it for you.
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_READ_CD_CAPACITY;
+ cmd.len = sizeof(rdcap);
+
+ /*
+ * If the command works, interpret the result as a 4 byte
+ * number of blocks and a blocksize
+ */
+ if (atapi_exec_cmd(cd->ad_link, &cmd , sizeof(cmd),
+ &rdcap, sizeof(rdcap), B_READ, 0) != 0) {
+#ifdef ATAPI_DEBUG
+ printf("ATAPI_READ_CD_CAPACITY failed\n");
+#endif
+ return 0;
+ }
+
+ blksize = ntohl(rdcap.blksize);
+ if (blksize < 512)
+ blksize = 2048; /* some drives lie ! */
+ cd->params.blksize = blksize;
+
+ size = ntohl(size);
+ if (size < 100)
+ size = 400000; /* ditto */
+ cd->params.disksize = size;
+#ifdef ATAPI_DEBUG
+ printf("acd_size: %ld %ld\n",blksize,size);
+#endif
+
+ return size;
+}
+
+#ifdef notyet
+/*
+ * Get the requested page into the buffer given
+ */
+int
+cd_get_mode(cd, data, page)
+ struct acd_softc *cd;
+ struct cd_mode_data *data;
+ int page;
+{
+ struct scsi_mode_sense scsi_cmd;
+ int error;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ bzero(data, sizeof(*data));
+ scsi_cmd.opcode = MODE_SENSE;
+ scsi_cmd.page = page;
+ scsi_cmd.length = sizeof(*data) & 0xff;
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), (u_char *)data, sizeof(*data), CDRETRIES, 20000,
+ NULL, SCSI_DATA_IN);
+}
+
+/*
+ * Get the requested page into the buffer given
+ */
+int
+cd_set_mode(cd, data)
+ struct acd_softc *cd;
+ struct cd_mode_data *data;
+{
+ struct scsi_mode_select scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = MODE_SELECT;
+ scsi_cmd.byte2 |= SMS_PF;
+ scsi_cmd.length = sizeof(*data) & 0xff;
+ data->header.data_length = 0;
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), (u_char *)data, sizeof(*data), CDRETRIES, 20000,
+ NULL, SCSI_DATA_OUT);
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+int
+cd_play(cd, blkno, nblks)
+ struct acd_softc *cd;
+ int blkno, nblks;
+{
+ struct scsi_play scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PLAY;
+ _lto4b(blkno, scsi_cmd.blk_addr);
+ _lto2b(nblks, scsi_cmd.xfer_len);
+
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, CDRETRIES, 200000, NULL, 0);
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+int
+cd_play_big(cd, blkno, nblks)
+ struct acd_softc *cd;
+ int blkno, nblks;
+{
+ struct scsi_play_big scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PLAY_BIG;
+ _lto4b(blkno, scsi_cmd.blk_addr);
+ _lto4b(nblks, scsi_cmd.xfer_len);
+
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, CDRETRIES, 20000, NULL, 0);
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+int
+cd_play_tracks(cd, strack, sindex, etrack, eindex)
+ struct acd_softc *cd;
+ int strack, sindex, etrack, eindex;
+{
+ struct scsi_play_track scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PLAY_TRACK;
+ scsi_cmd.start_track = strack;
+ scsi_cmd.start_index = sindex;
+ scsi_cmd.end_track = etrack;
+ scsi_cmd.end_index = eindex;
+
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, CDRETRIES, 20000, NULL, 0);
+}
+
+/*
+ * Get scsi driver to send a "play msf" command
+ */
+int
+cd_play_msf(cd, startm, starts, startf, endm, ends, endf)
+ struct acd_softc *cd;
+ int startm, starts, startf, endm, ends, endf;
+{
+ struct scsi_play_msf scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PLAY_MSF;
+ scsi_cmd.start_m = startm;
+ scsi_cmd.start_s = starts;
+ scsi_cmd.start_f = startf;
+ scsi_cmd.end_m = endm;
+ scsi_cmd.end_s = ends;
+ scsi_cmd.end_f = endf;
+
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, CDRETRIES, 2000, NULL, 0);
+}
+
+#endif /* notyet */
+
+/*
+ * Get atapi driver to send a "start up" command
+ */
+int
+acd_pause(acd, go)
+ struct acd_softc *acd;
+ int go;
+{
+ struct pause_resume cmd;
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_PAUSE_RESUME;
+ cmd.resume = go & 0xff;
+
+ return atapi_exec_cmd(acd->ad_link, &cmd , sizeof(cmd), 0, 0, 0, 0);
+}
+
+/*
+ * Get atapi driver to send a "RESET" command
+ */
+int
+acd_reset(acd)
+ struct acd_softc *acd;
+{
+#ifdef notyet
+ return atapi_soft_reset(acd->ad_link);
+#else
+ return 0;
+#endif
+}
+
+#ifdef notyet
+/*
+ * Read subchannel
+ */
+int
+cd_read_subchannel(cd, mode, format, track, data, len)
+ struct acd_softc *cd;
+ int mode, format, len;
+ struct cd_sub_channel_info *data;
+{
+ struct scsi_read_subchannel scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = READ_SUBCHANNEL;
+ if (mode == CD_MSF_FORMAT)
+ scsi_cmd.byte2 |= CD_MSF;
+ scsi_cmd.byte3 = SRS_SUBQ;
+ scsi_cmd.subchan_format = format;
+ scsi_cmd.track = track;
+ _lto2b(len, scsi_cmd.data_len);
+
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(struct scsi_read_subchannel), (u_char *)data, len,
+ CDRETRIES, 5000, NULL, SCSI_DATA_IN);
+}
+
+/*
+ * Read table of contents
+ */
+int
+cd_read_toc(cd, mode, start, data, len)
+ struct acd_softc *cd;
+ int mode, start, len;
+ struct cd_toc_entry *data;
+{
+ struct scsi_read_toc scsi_cmd;
+ int ntoc;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+#if 0
+ if (len != sizeof(struct ioc_toc_header))
+ ntoc = ((len) - sizeof(struct ioc_toc_header)) /
+ sizeof(struct cd_toc_entry);
+ else
+#endif
+ ntoc = len;
+
+ scsi_cmd.opcode = READ_TOC;
+ if (mode == CD_MSF_FORMAT)
+ scsi_cmd.byte2 |= CD_MSF;
+ scsi_cmd.from_track = start;
+ _lto2b(ntoc, scsi_cmd.data_len);
+
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(struct scsi_read_toc), (u_char *)data, len, CDRETRIES,
+ 5000, NULL, SCSI_DATA_IN);
+}
+
+#endif /* notyet */
+
+/*
+ * Get the atapi driver to send a full inquiry to the device and use the
+ * results to fill out the disk parameter structure.
+ */
+int
+acd_get_parms(acd, flags)
+ struct acd_softc *acd;
+ int flags;
+{
+
+ /*
+ * give a number of sectors so that sec * trks * cyls
+ * is <= disk_size
+ */
+ if (acd_size(acd, flags) == 0)
+ return ENXIO;
+
+ return 0;
+}
+
+int
+acdsize(dev)
+ dev_t dev;
+{
+
+ /* CD-ROMs are read-only. */
+ return -1;
+}
+
+void acdminphys(bp)
+ struct buf *bp;
+{
+
+ minphys(bp);
+}
+
+int
+acddump(dev, blkno, va, size)
+ dev_t dev;
+ daddr_t blkno;
+ caddr_t va;
+ size_t size;
+{
+
+ /* Not implemented. */
+ return ENXIO;
+}
+
+int
+acddone(acp)
+ struct atapi_command_packet *acp;
+{
+ struct at_dev_link *ad_link = acp->ad_link;
+ struct acd_softc *acd = ad_link->device_softc;
+
+ if (acp->bp != NULL)
+ disk_unbusy(&acd->sc_dk,
+ (acp->bp->b_bcount - acp->bp->b_resid));
+
+ return (0);
+}
diff --git a/sys/dev/atapi/atapi.h b/sys/dev/atapi/atapi.h
new file mode 100644
index 00000000000..20ec1948981
--- /dev/null
+++ b/sys/dev/atapi/atapi.h
@@ -0,0 +1,250 @@
+/* $NetBSD: $ */
+
+/*
+ * Copyright (c) 1996 Manuel Bouyer. All rights reserved.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+/*
+ * Definition of atapi commands and associated data structures
+ */
+
+/*
+ * TEST UNIT READY (mandatory)
+ */
+#define ATAPI_TEST_UNIT_READY 0x00
+
+struct test_unit_ready {
+ u_int8_t operation_code;
+ u_int8_t reserved1[15];
+};
+
+/*
+ * START/STOP UNIT (mandatory)
+ */
+#define ATAPI_START_STOP_UNIT 0x1b
+
+struct start_stop_unit {
+ u_int8_t operation_code;
+ u_int8_t flags;
+#define START_STOP_IMMED 0x01
+ u_int8_t reserved2[2];
+ u_int8_t how;
+#define SSS_STOP 0x00
+#define SSS_START 0x01
+#define SSS_LOEJ 0x02
+ u_int8_t reserved4[11];
+};
+
+/*
+ * PREVENT/ALLOW MEDIUM REMOVAL (mandatory)
+ */
+#define ATAPI_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+
+struct prevent_allow_medium_removal {
+ u_int8_t operation_code;
+ u_int8_t reserved1[3];
+ u_int8_t how;
+#define PR_PREVENT 0x01
+#define PR_ALLOW 0x00
+ u_int8_t reserved3[11];
+};
+
+
+/*
+ * READ CD CAPACITY (mandatory)
+ */
+#define ATAPI_READ_CD_CAPACITY 0x25
+
+struct read_cd_capacity {
+ u_int8_t operation_code;
+ u_int8_t reserved1[7];
+ u_int8_t len;
+ u_int8_t reserved2[7];
+};
+
+/*
+ * Volume size info.
+ */
+struct read_cd_capacity_data {
+ u_long size; /* Volume size in blocks */
+ u_long blksize; /* Block size in bytes */
+};
+
+/*
+ * READ (10) (mandatory)
+ */
+#define ATAPI_READ 0x28
+
+struct read {
+ u_int8_t operation_code;
+ u_int8_t reserved1;
+ u_int8_t lba[4];
+ u_int8_t reserved2;
+ u_int8_t length[2];
+ u_int8_t reserved3[7];
+};
+
+/*
+ * PAUSE/RESUME (optional)
+ */
+#define ATAPI_PAUSE_RESUME 0x4b
+
+struct pause_resume {
+ u_int8_t operation_code;
+ u_int8_t reserved1[7];
+ u_int8_t resume;
+#define PA_PAUSE 0x00
+#define PA_RESUME 0x01
+ u_int8_t reserved3[7];
+};
+
+/*
+ * MODE SENSE (mandatory)
+ */
+#define ATAPI_MODE_SENSE 0x5a
+
+struct mode_sense {
+ u_int8_t operation_code;
+ u_int8_t reserved1;
+ u_int8_t page_code_control;
+#define PAGE_CODE_MASK 0x3f
+#define PAGE_CONTROL_MASK 0xc0
+ u_int8_t reserved2[4];
+ u_int8_t length[2];
+ u_int8_t reserved3[7];
+};
+
+struct cappage {
+ /* Mode data header */
+ u_int8_t data_length[2];
+ u_int8_t medium_type;
+#define MDT_UNKNOWN 0x00
+#define MDT_DATA_120 0x01
+#define MDT_AUDIO_120 0x02
+#define MDT_COMB_120 0x03
+#define MDT_PHOTO_120 0x04
+#define MDT_DATA_80 0x05
+#define MDT_AUDIO_80 0x06
+#define MDT_COMB_80 0x07
+#define MDT_PHOTO_80 0x08
+#define MDT_NO_DISC 0x70
+#define MDT_DOOR_OPEN 0x71
+#define MDT_FMT_ERROR 0x72
+ u_int8_t reserved1[5];
+
+ /* Capabilities page */
+ u_int8_t page_code;
+#define CAP_PAGE 0x2a
+ u_int8_t param_len;
+ u_int8_t reserved2[2];
+
+ u_int8_t format_cap;
+#define FORMAT_AUDIO_PLAY 0x01 /* audio play supported */
+#define FORMAT_COMPOSITE 0x02 /* composite audio/video supported */
+#define FORMAT_DPORT1 0x04 /* digital audio on port 1 */
+#define FORMAT_DPORT2 0x08 /* digital audio on port 2 */
+#define FORMAT_MODE2_FORM1 0x10 /* mode 2 form 1 (XA) read */
+#define FORMAT_MODE2_FORM2 0x20 /* mode 2 form 2 format */
+#define FORMAT_MULTISESSION 0x40 /* multi-session photo-CD */
+
+ u_int8_t ops_cap;
+#define OPS_CDDA 0x01 /* audio-CD read supported */
+#define OPS_CDDA_STREAM 0x02 /* CDDA streaming */
+#define OPS_RW 0x04 /* combined R-W subchannels */
+#define OPS_RW_CORR 0x08 /* R-W subchannel data corrected */
+#define OPS_C2 0x10 /* C2 error pointers supported */
+#define OPS_ISRC 0x20 /* can return the ISRC info */
+#define OPS_UPC 0x40 /* can return the catalog number UPC */
+
+ u_int8_t hw_cap;
+#define HW_LOCK 0x01 /* could be locked */
+#define HW_LOCKED 0x02 /* current lock state */
+#define HW_PREVENT 0x04 /* prevent jumper installed */
+#define HW_EJECT 0x08 /* can eject */
+#define HW_MECH_MASK 0xe0 /* loading mechanism type mask */
+
+#define MECH_CADDY 0x00
+#define MECH_TRAY 0x20
+#define MECH_POPUP 0x40
+#define MECH_CHANGER 0x80
+#define MECH_CARTRIDGE 0xa0
+
+ u_int8_t sep_cap;
+#define SEP_VOL 0x01 /* independent volume controls */
+#define SEP_MUTE 0x02 /* independent mute controls */
+
+ u_int8_t max_speed[2]; /* max raw data rate in bytes/1000 */
+ u_int8_t max_vol_levels[2]; /* number of discrete volume levels */
+ u_int8_t buf_size[2]; /* internal buffer size in bytes/1024 */
+ u_int8_t cur_speed[2]; /* current data rate in bytes/1000 */
+
+ /* Digital drive output format description (optional?) */
+ u_int8_t reserved3;
+ u_int8_t ddofd;
+#define DDOFD_BCKF 0x01 /* data valid on failing edge of BCK */
+#define DDOFD_RCH 0x02 /* hight LRCK indicated left channel */
+#define DDOFD_LSBF 0x04 /* set if LSB first */
+#define DDOFD_DLEN_MASK 0x18 /* mask of DLEN values */
+
+#define DLEN_32 0x00 /* 32 BCKs */
+#define DLEN_16 0x08 /* 16 BCKs */
+#define DLEN_24 0x10 /* 24 BCKs */
+#define DLEN_24_I2S 0x18 /* 24 BCKs (I2S) */
+
+ u_int8_t reserved4[2];
+};
+
+/* ATAPI error codes */
+#define ATAPI_SK_NO_SENSE 0x0
+#define ATAPI_SK_REC_ERROR 0x1 /* recovered error */
+#define ATAPI_SK_NOT_READY 0x2
+#define ATAPI_SK_MEDIUM_ERROR 0x3
+#define ATAPI_SK_HARDWARE_ERROR 0x4
+#define ATAPI_SK_ILLEGAL_REQUEST 0x5
+#define ATAPI_SK_UNIT_ATTENTION 0x6
+#define ATAPI_SK_DATA_PROTECT 0x7
+ /* 0x8 reserved */
+ /* 0x9-0xa reserved */
+#define ATAPI_SK_ABORTED_COMMAND 0xb
+ /* 0xc-0xd not referenced */
+#define ATAPI_SK_MISCOMPARE 0xe
+ /* 0xf reserved */
+
+#define ATAPI_MCR 0x08 /* media change requested */
+#define ATAPI_ABRT 0x04 /* aborted command */
+#define ATAPI_EOM 0x02 /* end of media */
+#define ATAPI_ILI 0x01 /* illegal length indication */
+
+
+int atapi_exec_cmd __P((struct at_dev_link *, void *, int,
+ void *, int, long, int));
+int atapi_exec_io __P((struct at_dev_link *, void *, int,
+ struct buf *, int));
+int atapi_test_unit_ready __P((struct at_dev_link *, int));
+int atapi_start_stop __P((struct at_dev_link *, int, int));
+int atapi_prevent __P((struct at_dev_link *, int));
diff --git a/sys/dev/atapi/atapiconf.c b/sys/dev/atapi/atapiconf.c
new file mode 100644
index 00000000000..85095a4c0a4
--- /dev/null
+++ b/sys/dev/atapi/atapiconf.c
@@ -0,0 +1,676 @@
+/* $NetBSD: $ */
+
+/*
+ * Copyright (c) 1996 Manuel Bouyer. All rights reserved.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+
+#include <dev/atapi/atapilink.h>
+#include <dev/atapi/atapi.h>
+
+#define SILENT_PRINTF(flags,string) if (!(flags & A_SILENT)) printf string
+
+#ifdef ATAPI_DEBUG_CMD
+#define ATAPI_DEBUG_CMD_PRINT(args) printf args
+#else
+#define ATAPI_DEBUG_CMD_PRINT(args)
+#endif
+
+#ifdef ATAPI_DEBUG_FCTN
+#define ATAPI_DEBUG_FCTN_PRINT(args) printf args
+#else
+#define ATAPI_DEBUG_FCTN_PRINT(args)
+#endif
+
+struct atapibus_softc {
+ struct device sc_dev;
+ struct bus_link *b_link;
+};
+
+LIST_HEAD(pkt_free_list, atapi_command_packet) pkt_free_list;
+
+static void bswap __P((char *, int));
+static void btrim __P((char *, int));
+
+int atapi_error __P((struct atapi_command_packet *));
+void atapi_sense __P((struct atapi_command_packet *, u_int8_t, u_int8_t));
+void at_print_addr __P((struct at_dev_link *, u_int8_t));
+
+int atapibusmatch __P((struct device *, void *, void *));
+void atapibusattach __P((struct device *, struct device *, void *));
+
+struct cfattach atapibus_ca = {
+ sizeof(struct atapibus_softc), atapibusmatch, atapibusattach
+};
+
+struct cfdriver atapibus_cd = {
+ NULL, "atapibus", DV_DULL
+};
+
+
+int
+atapibusmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct cfdata *cf = match;
+ struct bus_link *ab_link = aux;
+
+ if (ab_link == NULL)
+ return 0;
+ if (ab_link->type != BUS)
+ return 0;
+ return 1;
+}
+
+int
+atapiprint(aux, bus)
+ void *aux;
+ char *bus;
+{
+ struct at_dev_link *ad_link = aux;
+ struct atapi_identify *id = &ad_link->id;
+ char *dtype, *fixrem;
+
+ /*
+ * Figure out basic device type.
+ */
+ switch (id->config.device_type & ATAPI_DEVICE_TYPE_MASK) {
+ case ATAPI_DEVICE_TYPE_DAD:
+ dtype = "direct";
+ break;
+
+ case ATAPI_DEVICE_TYPE_CD:
+ dtype = "cdrom";
+ break;
+
+ case ATAPI_DEVICE_TYPE_OMD:
+ dtype = "optical";
+ break;
+
+ default:
+ dtype = "unknown";
+ break;
+ }
+
+ fixrem = (id->config.cmd_drq_rem & ATAPI_REMOVABLE) ?
+ "removable" : "fixed";
+
+ /*
+ * Shuffle string byte order.
+ * Mitsumi and NEC drives don't need this.
+ */
+ if (((id->model[0] == 'N' && id->model[1] == 'E') ||
+ (id->model[0] == 'F' && id->model[1] == 'X')) == 0)
+ bswap(id->model, sizeof(id->model));
+
+ /*
+ * XXX Poorly named... These appear to actually be in
+ * XXX network byte order, so bswap() is a no-op on
+ * XXX big-endian hosts. Clean me up, please.
+ */
+ bswap(id->serial_number, sizeof(id->serial_number));
+ bswap(id->firmware_revision, sizeof(id->firmware_revision));
+
+ /*
+ * Clean up the model name, serial and
+ * revision numbers.
+ */
+ btrim(id->model, sizeof(id->model));
+ btrim(id->serial_number, sizeof(id->serial_number));
+ btrim(id->firmware_revision, sizeof(id->firmware_revision));
+
+ if (bus != NULL)
+ printf("%s", bus);
+
+ printf(" drive %d: <%s, %s, %s> type %d/%s %s",
+ ad_link->drive, id->model, id->serial_number,
+ id->firmware_revision,
+ id->config.device_type & ATAPI_DEVICE_TYPE_MASK, dtype, fixrem);
+
+ return UNCONF;
+}
+
+
+void
+atapibusattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct atapibus_softc *ab = (struct atapibus_softc *)self;
+ struct bus_link *ab_link_proto = aux;
+ struct atapi_identify ids;
+ struct atapi_identify *id = &ids;
+ struct at_dev_link *ad_link;
+ int drive;
+
+ printf("\n");
+
+ ab_link_proto->atapibus_softc = (caddr_t)ab;
+ ab->b_link = ab_link_proto;
+
+ for (drive = 0; drive < 2 ; drive++) {
+ if (wdc_atapi_get_params(ab_link_proto, drive, id)) {
+#ifdef ATAPI_DEBUG_PROBE
+ printf("%s drive %d: cmdsz 0x%x drqtype 0x%x\n",
+ self->dv_xname, drive,
+ id->config.cmd_drq_rem & ATAPI_PACKET_SIZE_MASK,
+ id->config.cmd_drq_rem & ATAPI_DRQ_MASK);
+#endif
+
+ /*
+ * Allocate a device link and try and attach
+ * a driver to this device. If we fail, free
+ * the link.
+ */
+ ad_link = malloc(sizeof(*ad_link), M_DEVBUF, M_NOWAIT);
+ if (ad_link == NULL) {
+ printf("%s: can't allocate link for drive %d\n",
+ self->dv_xname, drive);
+ continue;
+ }
+
+ /* Fill in link. */
+ ad_link->drive = drive;
+ if (id->config.cmd_drq_rem & ATAPI_PACKET_SIZE_16)
+ ad_link->flags |= ACAP_LEN;
+ ad_link->flags |=
+ (id->config.cmd_drq_rem & ATAPI_DRQ_MASK) << 3;
+ ad_link->bus = ab_link_proto;
+ bcopy(id, &ad_link->id, sizeof(*id));
+
+ /* Try to find a match. */
+ if (config_found(self, ad_link, atapiprint) == NULL)
+ free(ad_link, M_DEVBUF);
+ }
+ }
+}
+
+static void
+bswap (buf, len)
+ char *buf;
+ int len;
+{
+ u_int16_t *p = (u_int16_t *)(buf + len);
+
+ while (--p >= (u_int16_t *)buf)
+ *p = ntohs(*p);
+}
+
+static void
+btrim (buf, len)
+ char *buf;
+ int len;
+{
+ char *p;
+
+ /* Remove the trailing spaces. */
+ for (p = buf; p < buf + len; ++p)
+ if (*p == '\0')
+ *p = ' ';
+
+ for (p = buf + len - 1; p >= buf && *p == ' '; --p)
+ *p = '\0';
+}
+
+int
+atapi_exec_cmd(ad_link, cmd, cmd_size, databuf, datalen, rw, flags)
+ struct at_dev_link *ad_link;
+ void *cmd;
+ int cmd_size;
+ void *databuf;
+ int datalen;
+ long rw;
+ int flags;
+{
+ struct atapi_command_packet *pkt;
+ struct bus_link *b_link = ad_link->bus;
+ int status, s;
+
+ /* Allocate packet. */
+ pkt = atapi_get_pkt(ad_link, flags);
+ if (pkt == NULL)
+ return -1;
+
+ /* Fill it out. */
+ bcopy(cmd, &pkt->cmd_store, cmd_size);
+ pkt->command = &pkt->cmd_store;
+ pkt->command_size = (ad_link->flags & ACAP_LEN) ? 16 : 12;
+ pkt->databuf = databuf;
+ pkt->data_size = datalen;
+ pkt->flags = rw | (flags & 0xff) | (ad_link->flags & 0x0300);
+ pkt->drive = ad_link->drive;
+
+ /* Send it to drive. */
+ wdc_atapi_send_command_packet(b_link, pkt);
+ if ((flags & (A_POLLED | A_NOSLEEP)) == 0) {
+ ATAPI_DEBUG_CMD_PRINT(("atapi_exec_cmd: sleeping\n"));
+
+ s = splbio();
+ while ((pkt->status & ITSDONE) == 0)
+ tsleep(pkt, PRIBIO + 1,"atapicmd", 0);
+ splx(s);
+
+ ATAPI_DEBUG_CMD_PRINT(("atapi_exec_cmd: done sleeping\n"));
+
+ status = pkt->status & STATUS_MASK;
+ atapi_free_pkt(pkt);
+ } else {
+ if ((flags & A_POLLED) != 0) {
+ if ((pkt->status & ERROR) && (pkt->error)) {
+ atapi_error(pkt);
+ SILENT_PRINTF(flags,("\n"));
+ }
+ }
+ status = pkt->status & STATUS_MASK;
+ if ((flags & A_POLLED) != 0)
+ atapi_free_pkt(pkt);
+ }
+
+ return status;
+}
+
+int
+atapi_exec_io(ad_link, cmd, cmd_size, bp, flags)
+ struct at_dev_link *ad_link;
+ void *cmd;
+ int cmd_size;
+ struct buf *bp;
+ int flags;
+{
+ struct atapi_command_packet *pkt;
+ struct bus_link *b_link = ad_link->bus;
+
+ /* Allocate a packet. */
+ pkt = atapi_get_pkt(ad_link, flags);
+ if (pkt == NULL) {
+ printf("atapi_exec_io: no pkt\n");
+ return ERROR;
+ }
+
+ /* Fill it in. */
+ bcopy(cmd, &pkt->cmd_store, cmd_size);
+ pkt->command = &pkt->cmd_store;
+ pkt->command_size = (ad_link->flags & ACAP_LEN) ? 16 : 12;
+ pkt->bp = bp;
+ pkt->databuf = bp->b_data;
+ pkt->data_size = bp->b_bcount;
+ pkt->flags = bp->b_flags & (B_READ|B_WRITE) | (flags & 0xff) |
+ (ad_link->flags & 0x0300);
+ pkt->drive = ad_link->drive;
+
+ wdc_atapi_send_command_packet(b_link, pkt);
+ return (pkt->status & STATUS_MASK);
+}
+
+void
+atapi_done(acp)
+ struct atapi_command_packet *acp;
+{
+ struct at_dev_link *ad_link = acp->ad_link;
+ struct buf *bp = acp->bp;
+ int error = 0;
+
+ ATAPI_DEBUG_CMD_PRINT(("atapi_done\n"));
+
+ if ((acp->status & ERROR) && (acp->error)) {
+ atapi_error(acp);
+ if (acp->status & RETRY) {
+ if (acp->retries <ATAPI_NRETRIES) {
+ acp->retries++;
+ acp->status = 0;
+ acp->error = 0;
+ SILENT_PRINTF(acp->flags & 0xff,
+ (", retry #%d\n", acp->retries));
+ wdc_atapi_send_command_packet(ad_link->bus,
+ acp);
+ return;
+ } else
+ acp->status = ERROR;
+ }
+ SILENT_PRINTF(acp->flags & 0xff,("\n"));
+ }
+ acp->status |= ITSDONE;
+
+ if (ad_link->done) {
+ ATAPI_DEBUG_CMD_PRINT(("calling private done\n"));
+ error = (*ad_link->done)(acp);
+ if (error == EJUSTRETURN)
+ return;
+ }
+ if (acp->bp == NULL) {
+ ATAPI_DEBUG_CMD_PRINT(("atapidone: wakeup acp\n"));
+ wakeup(acp);
+ return;
+ }
+
+ ATAPI_DEBUG_CMD_PRINT(("atapi_done: status %d\n", acp->status));
+
+ switch (acp->status & 0x0f) {
+ case MEDIA_CHANGE:
+ if (ad_link->flags & ADEV_REMOVABLE)
+ ad_link->flags &= ~ADEV_MEDIA_LOADED;
+
+ error = EIO;
+ break;
+
+ case NO_ERROR:
+ error = 0;
+ break;
+
+ case ERROR:
+ case END_OF_MEDIA:
+ default:
+ error = EIO;
+ break;
+ }
+
+ switch (acp->status & 0xf0) {
+ case NOT_READY:
+ case UNIT_ATTENTION:
+ if (ad_link->flags & ADEV_REMOVABLE)
+ ad_link->flags &= ~ADEV_MEDIA_LOADED;
+
+ error = EIO;
+ break;
+ }
+
+ if (error) {
+ bp->b_error = error;
+ bp->b_flags |= B_ERROR;
+ bp->b_resid = bp->b_bcount;
+ } else {
+ bp->b_error = 0;
+ bp->b_resid = acp->data_size;
+ }
+ biodone(bp);
+ atapi_free_pkt(acp);
+}
+
+struct atapi_command_packet *
+atapi_get_pkt(ad_link, flags)
+ struct at_dev_link *ad_link;
+ int flags;
+{
+ struct atapi_command_packet *pkt;
+ int s;
+
+ s = splbio();
+ while (ad_link->openings <= 0) {
+ if (flags & A_NOSLEEP) {
+ splx(s);
+ return 0;
+ }
+
+ ATAPI_DEBUG_CMD_PRINT(("atapi_get_pkt: sleeping\n"));
+
+ ad_link->flags |= ADEV_WAITING;
+ (void)tsleep(ad_link, PRIBIO, "getpkt", 0);
+ }
+
+ ad_link->openings--;
+
+ if ((pkt = pkt_free_list.lh_first) != 0) {
+ LIST_REMOVE(pkt, free_list);
+ splx(s);
+ } else {
+ splx(s);
+ pkt = malloc(sizeof(struct atapi_command_packet), M_DEVBUF,
+ ((flags & A_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK));
+ if (pkt == NULL) {
+ printf("atapi_get_pkt: cannot allocate pkt\n");
+ ad_link->openings++;
+ return 0;
+ }
+ }
+
+ bzero(pkt, sizeof(struct atapi_command_packet));
+ pkt->ad_link = ad_link;
+ return pkt;
+}
+
+void
+atapi_free_pkt(pkt)
+ struct atapi_command_packet *pkt;
+{
+ struct at_dev_link *ad_link = pkt->ad_link;
+ int s;
+
+ s = splbio();
+ LIST_INSERT_HEAD(&pkt_free_list, pkt, free_list);
+
+ ad_link->openings++;
+
+ if ((ad_link->flags & ADEV_WAITING) != 0) {
+ ad_link->flags &= ~ADEV_WAITING;
+ wakeup(ad_link);
+ } else {
+ if (ad_link->start) {
+ ATAPI_DEBUG_CMD_PRINT(("atapi_free_pkt: calling private start\n"));
+ (*ad_link->start)((void *)ad_link->device_softc);
+ }
+ }
+ splx(s);
+}
+
+int
+atapi_test_unit_ready(ad_link, flags)
+ struct at_dev_link *ad_link;
+ int flags;
+{
+ int ret;
+ struct test_unit_ready cmd;
+
+ ATAPI_DEBUG_FCTN_PRINT(("atapi_test_unit_ready: "));
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_TEST_UNIT_READY;
+
+ ret = atapi_exec_cmd(ad_link, &cmd, sizeof(cmd), 0, 0, 0, flags);
+
+ ATAPI_DEBUG_FCTN_PRINT(("atapi_test_unit_ready: ret %d\n", ret));
+
+ return ret;
+}
+
+int
+atapi_start_stop(ad_link, how, flags)
+ struct at_dev_link *ad_link;
+ int how;
+ int flags;
+{
+ struct start_stop_unit cmd;
+ int ret;
+
+ ATAPI_DEBUG_FCTN_PRINT(("atapi_start_stop: "));
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_START_STOP_UNIT;
+ cmd.how = how;
+
+ ret = atapi_exec_cmd(ad_link, &cmd, sizeof(cmd), 0,0,0,flags);
+
+ ATAPI_DEBUG_FCTN_PRINT(("ret %d\n", ret));
+
+ return ret;
+}
+
+int
+atapi_prevent(ad_link, how)
+ struct at_dev_link *ad_link;
+ int how;
+{
+ struct prevent_allow_medium_removal cmd;
+ int ret;
+
+ ATAPI_DEBUG_FCTN_PRINT(("atapi_prevent: "));
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_PREVENT_ALLOW_MEDIUM_REMOVAL;
+ cmd.how = how & 0xff;
+
+ ret = atapi_exec_cmd(ad_link, &cmd, sizeof(cmd), 0,0,0,0);
+
+ ATAPI_DEBUG_FCTN_PRINT(("ret %d\n", ret));
+
+ return ret;
+}
+
+int
+atapi_error(acp)
+ struct atapi_command_packet* acp;
+{
+ int flags, error, ret = -1;
+ struct at_dev_link *ad_link = acp->ad_link;
+
+ flags = acp->flags & 0xff;
+ error = acp->error;
+
+ at_print_addr(ad_link, acp->flags & 0xff);
+
+ if (error & ATAPI_MCR) {
+ SILENT_PRINTF(flags,("media change requested"));
+ acp->status = MEDIA_CHANGE;
+ }
+
+ if (error & ATAPI_ABRT) {
+ SILENT_PRINTF(flags,("command aborted"));
+ acp->status = ERROR;
+ }
+
+ if (error & ATAPI_EOM) {
+ SILENT_PRINTF(flags,("end of media"));
+ acp->status = END_OF_MEDIA;
+ }
+
+ if (error & ATAPI_ILI) {
+ SILENT_PRINTF(flags,("illegal length indication"));
+ acp->status = ERROR;
+ }
+
+ if ((error & 0x0f) == 0)
+ ret = 0;
+
+ atapi_sense(acp, error >> 4, flags);
+
+ if (((flags & A_SILENT) == 0) && (acp->status != NO_ERROR)) {
+ int i;
+ printf(", command:");
+ for (i = 0; i < acp->command_size; i++)
+ printf(" %2x", ((u_int8_t *)acp->command)[i]);
+ }
+
+ return ret;
+}
+
+void
+atapi_sense(acp, sense_key, flags)
+ struct atapi_command_packet *acp;
+ u_int8_t sense_key;
+ u_int8_t flags;
+{
+ struct at_dev_link *ad_link = acp->ad_link;
+
+ switch (sense_key) {
+ case ATAPI_SK_NO_SENSE:
+ break;
+
+ case ATAPI_SK_REC_ERROR:
+ SILENT_PRINTF(flags,("recovered error"));
+ acp->status = 0;
+ break;
+
+ case ATAPI_SK_NOT_READY:
+ SILENT_PRINTF(flags,("not ready"));
+ acp->status = NOT_READY;
+ break;
+
+ case ATAPI_SK_MEDIUM_ERROR:
+ SILENT_PRINTF(flags,("medium error"));
+ acp->status = ERROR;
+ break;
+
+ case ATAPI_SK_HARDWARE_ERROR:
+ SILENT_PRINTF(flags,("hardware error"));
+ acp->status = ERROR;
+ break;
+
+ case ATAPI_SK_ILLEGAL_REQUEST:
+ SILENT_PRINTF(flags,("illegal request"));
+ acp->status = ERROR;
+ break;
+
+ case ATAPI_SK_UNIT_ATTENTION:
+ SILENT_PRINTF(flags,("unit attention"));
+ acp->status = UNIT_ATTENTION;
+ if (ad_link->flags & ADEV_REMOVABLE)
+ ad_link->flags &= ~ADEV_MEDIA_LOADED;
+ break;
+
+ case ATAPI_SK_DATA_PROTECT:
+ SILENT_PRINTF(flags,("data protect"));
+ acp->status = ERROR;
+ break;
+
+ case ATAPI_SK_ABORTED_COMMAND:
+ SILENT_PRINTF(flags,("aborted command"));
+ acp->status = RETRY;
+ break;
+
+ case ATAPI_SK_MISCOMPARE:
+ SILENT_PRINTF(flags,("miscompare"));
+ acp->status = ERROR;
+ break;
+
+ default:
+ SILENT_PRINTF(flags,("unexpected sense key %02x", sense_key));
+ acp->status = ERROR;
+ }
+}
+
+void
+at_print_addr(ad_link, flags)
+ struct at_dev_link *ad_link;
+ u_int8_t flags;
+{
+
+ if (flags & A_SILENT)
+ return;
+
+ printf("%s(%s:%d): ", ad_link->device_softc ?
+ ((struct device *)ad_link->device_softc)->dv_xname : "probe",
+ ((struct device *)ad_link->bus->wdc_softc)->dv_xname,
+ ad_link->drive);
+}
diff --git a/sys/dev/atapi/atapilink.h b/sys/dev/atapi/atapilink.h
new file mode 100644
index 00000000000..7d01102a2a7
--- /dev/null
+++ b/sys/dev/atapi/atapilink.h
@@ -0,0 +1,352 @@
+/* $NetBSD: $ */
+
+/*
+ * Copyright (c) 1996 Manuel Bouyer. All rights reserved.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#undef ATAPI_DEBUG
+#undef ATAPI_DEBUG_PROBE
+
+struct bus_link {
+ u_int8_t type;
+#define DRIVE 0
+#define BUS 1
+ caddr_t wdc_softc;
+ caddr_t atapibus_softc;
+ struct wdc_link *ctlr_link;
+ u_int8_t ctrl;
+};
+
+struct atapi_identify {
+ struct config_s {
+ u_int8_t cmd_drq_rem;
+#define ATAPI_PACKET_SIZE_MASK 0x02
+#define ATAPI_PACKET_SIZE_12 0x00
+#define ATAPI_PACKET_SIZE_16 0x01
+
+#define ATAPI_DRQ_MASK 0x60
+#define ATAPI_MICROPROCESSOR_DRQ 0x00
+#define ATAPI_INTERRUPT_DRQ 0x20
+#define ATAPI_ACCELERATED_DRQ 0x40
+
+#define ATAPI_REMOVABLE 0x80
+
+ u_int8_t device_type;
+#define ATAPI_DEVICE_TYPE_MASK 0x1f
+#define ATAPI_DEVICE_TYPE_DAD 0x00 /* direct access device */
+ /* 0x1-0x4 reserved */
+#define ATAPI_DEVICE_TYPE_CD 0x05 /* CD-ROM */
+ /* 0x6 reserved */
+#define ATAPI_DEVICE_TYPE_OMD 0x07 /* optical memory device */
+ /* 0x8-0x1e reserved */
+#define ATAPI_DEVICE_TYPE_UNKNOWN 0x1f
+
+#define ATAPI_GC_PROTOCOL_MASK 0xc0 /* mask of protocol bits */
+ /* 0x00 and 0x01 are ATA */
+#define ATAPI_GC_PROTO_TYPE_ATAPI 0x80
+#define ATAPI_GC_PROTO_TYPE_RESERVED 0xc0
+ } config; /* general configuration */
+
+ u_int8_t cylinders[2];
+ u_int8_t reserved1[2];
+ u_int8_t heads[2];
+ u_int8_t unf_bytes_per_track[2];
+ u_int8_t unf_bytes_per_sector[2];
+ u_int8_t sectors_per_track[2];
+ u_int8_t reserved2[6];
+ char serial_number[20];
+ u_int8_t buffer_type[2];
+ u_int8_t buffer_size[2];
+ u_int8_t ECC_bytes_available[2];
+ char firmware_revision[8];
+ char model[40];
+ u_int8_t sector_count[2];
+ u_int8_t double_word[2]; /* == 0 for CD-ROMs */
+
+ struct capabilities_s {
+ u_int8_t vendor;
+ u_int8_t capflags;
+#define ATAPI_CAP_DMA 0x01 /* DMA supported */
+#define ATAPI_CAP_LBA 0x02 /* LBA supported */
+#define ATAPI_IORDY_DISABLE 0x04 /* IORDY can be disabled */
+#define ATAPI_IORDY 0x08 /* IORDY supported */
+ } capabilities;
+
+ u_int8_t reserved3[2];
+ u_int8_t PIO_cycle_timing[2];
+ u_int8_t DMA_cycle_timing[2];
+ u_int8_t validity[2]; /* of words 54-58, 64-70 in this table */
+
+#define ATAPI_VALID_FIRST 0x0 /* == 1 => words 54-58 are valid */
+#define ATAPI_VALID_SECOND 0x1 /* == 1 => words 64-70 are valid */
+
+ u_int8_t current_chs[6]; /* cylinder/head/sector */
+ u_int8_t current_capacity[4];
+ u_int8_t reserved4[2];
+ u_int8_t user_addressable_sectors[4];
+ u_int8_t singleword_DMA_mode[2];
+
+#define ATAPI_SW_DMA_MODE_AVAIL 0x00ff /* Mode 0 is supported */
+#define ATAPI_SW_DMA_MODE_ACTIVE 0xff00 /* which mode is active */
+
+ u_int8_t multiword_DMA_mode[2];
+
+#define ATAPI_MW_DMA_MODE_AVAIL 0x00ff /* Mode 0 is supported */
+#define ATAPI_MW_DMA_MODE_ACTIVE 0xff00 /* which mode is active */
+
+ u_int8_t enhanced_PIO_mode[2];
+
+#define ATAPI_ENHANCED_PIO_AVAIL 0x0001 /* PIO Mode 3 is supported */
+
+ u_int8_t blind_PIO_minimum_cycles[2];
+ u_int8_t mw_dma_tct[2]; /* multi-word DMA transfer cycle time */
+ u_int8_t min_PIO_tct_no_flow_control[2];
+ u_int8_t min_PIO_tct_with_flow_control[2];
+ u_int8_t reserved5[4];
+ u_int8_t reserved6[114];
+ u_int8_t vendor[64]; /* vendor unique */
+ u_int8_t reserved7[192];
+};
+
+struct at_dev_link {
+ void *device_softc;
+ u_int8_t drive;
+ u_int8_t openings;
+ struct atapi_identify id;
+ struct bus_link *bus;
+ u_int16_t flags;
+#define ADEV_REMOVABLE 0x001 /* media is removable */
+#define ADEV_MEDIA_LOADED 0x002 /* device figures are still valid */
+#define ADEV_WAITING 0x004 /* a process is waiting for this */
+#define ADEV_OPEN 0x008 /* at least 1 open session */
+#define ACAP_DRQ_MPROC 0x000 /* microprocessor DRQ */
+#define ACAP_DRQ_INTR 0x100 /* interrupt DRQ */
+#define ACAP_DRQ_ACCEL 0x200 /* accelerated DRQ */
+#define ACAP_LEN 0x400 /* 16 bit commands */
+ void (*start)(); /* device start routine */
+ int (*done)(); /* device done routine */
+};
+
+struct atapi_command_packet {
+ void *ad_link;
+ void *command;
+ char cmd_store[16];
+ int command_size;
+ struct buf *bp;
+ void *databuf;
+ int data_size;
+ int flags; /* handle B_READ/B_WRITE mask 0x00f00000 */
+ /* controller flags maks 0x0000000f */
+ /* ATAPI flags mask 0x000000f0 */
+ /* Capabilities flags 0x00000f00 */
+ u_int8_t drive;
+ u_int16_t status;
+#define STATUS_MASK 0xff
+#define NO_ERROR 0x00
+#define ERROR 0x01
+#define MEDIA_CHANGE 0x02
+#define END_OF_MEDIA 0x03
+#define NOT_READY 0x10
+#define UNIT_ATTENTION 0x20
+#define RETRY 0x40
+#define ITSDONE 0x100
+ u_int8_t error;
+ u_int8_t retries;
+#define ATAPI_NRETRIES 5
+ LIST_ENTRY(atapi_command_packet) free_list;
+};
+
+int wdc_atapi_get_params __P((struct bus_link *, u_int8_t,
+ struct atapi_identify *));
+void wdc_atapi_send_command_packet __P((struct bus_link *,
+ struct atapi_command_packet*));
+
+#define A_POLLED 0x10
+#define A_NOSLEEP 0x20
+#define A_SILENT 0x40
+
+void atapi_done __P((struct atapi_command_packet *));
+struct atapi_command_packet *atapi_get_pkt __P((struct at_dev_link *, int));
+void atapi_free_pkt __P((struct atapi_command_packet *));
+
+/*
+ * Functions used for reading and writing 2, 3, and 4 byte values
+ * in ATAPI commands.
+ */
+
+static __inline void _lto2b __P((u_int32_t val, u_int8_t *bytes));
+static __inline void _lto3b __P((u_int32_t val, u_int8_t *bytes));
+static __inline void _lto4b __P((u_int32_t val, u_int8_t *bytes));
+static __inline u_int32_t _2btol __P((u_int8_t *bytes));
+static __inline u_int32_t _3btol __P((u_int8_t *bytes));
+static __inline u_int32_t _4btol __P((u_int8_t *bytes));
+
+static __inline void _lto2l __P((u_int32_t val, u_int8_t *bytes));
+static __inline void _lto3l __P((u_int32_t val, u_int8_t *bytes));
+static __inline void _lto4l __P((u_int32_t val, u_int8_t *bytes));
+static __inline u_int32_t _2ltol __P((u_int8_t *bytes));
+static __inline u_int32_t _3ltol __P((u_int8_t *bytes));
+static __inline u_int32_t _4ltol __P((u_int8_t *bytes));
+
+static __inline void
+_lto2b(val, bytes)
+ u_int32_t val;
+ u_int8_t *bytes;
+{
+
+ bytes[0] = (val >> 8) & 0xff;
+ bytes[1] = val & 0xff;
+}
+
+static __inline void
+_lto3b(val, bytes)
+ u_int32_t val;
+ u_int8_t *bytes;
+{
+
+ bytes[0] = (val >> 16) & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+ bytes[2] = val & 0xff;
+}
+
+static __inline void
+_lto4b(val, bytes)
+ u_int32_t val;
+ u_int8_t *bytes;
+{
+
+ bytes[0] = (val >> 24) & 0xff;
+ bytes[1] = (val >> 16) & 0xff;
+ bytes[2] = (val >> 8) & 0xff;
+ bytes[3] = val & 0xff;
+}
+
+static __inline u_int32_t
+_2btol(bytes)
+ u_int8_t *bytes;
+{
+ register u_int32_t rv;
+
+ rv = (bytes[0] << 8) |
+ bytes[1];
+ return (rv);
+}
+
+static __inline u_int32_t
+_3btol(bytes)
+ u_int8_t *bytes;
+{
+ register u_int32_t rv;
+
+ rv = (bytes[0] << 16) |
+ (bytes[1] << 8) |
+ bytes[2];
+ return (rv);
+}
+
+static __inline u_int32_t
+_4btol(bytes)
+ u_int8_t *bytes;
+{
+ register u_int32_t rv;
+
+ rv = (bytes[0] << 24) |
+ (bytes[1] << 16) |
+ (bytes[2] << 8) |
+ bytes[3];
+ return (rv);
+}
+
+static __inline void
+_lto2l(val, bytes)
+ u_int32_t val;
+ u_int8_t *bytes;
+{
+
+ bytes[0] = val & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+}
+
+static __inline void
+_lto3l(val, bytes)
+ u_int32_t val;
+ u_int8_t *bytes;
+{
+
+ bytes[0] = val & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+ bytes[2] = (val >> 16) & 0xff;
+}
+
+static __inline void
+_lto4l(val, bytes)
+ u_int32_t val;
+ u_int8_t *bytes;
+{
+
+ bytes[0] = val & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+ bytes[2] = (val >> 16) & 0xff;
+ bytes[3] = (val >> 24) & 0xff;
+}
+
+static __inline u_int32_t
+_2ltol(bytes)
+ u_int8_t *bytes;
+{
+ register u_int32_t rv;
+
+ rv = bytes[0] |
+ (bytes[1] << 8);
+ return (rv);
+}
+
+static __inline u_int32_t
+_3ltol(bytes)
+ u_int8_t *bytes;
+{
+ register u_int32_t rv;
+
+ rv = bytes[0] |
+ (bytes[1] << 8) |
+ (bytes[2] << 16);
+ return (rv);
+}
+
+static __inline u_int32_t
+_4ltol(bytes)
+ u_int8_t *bytes;
+{
+ register u_int32_t rv;
+
+ rv = bytes[0] |
+ (bytes[1] << 8) |
+ (bytes[2] << 16) |
+ (bytes[3] << 24);
+ return (rv);
+}
diff --git a/sys/dev/atapi/files.atapi b/sys/dev/atapi/files.atapi
new file mode 100644
index 00000000000..508710c0960
--- /dev/null
+++ b/sys/dev/atapi/files.atapi
@@ -0,0 +1,14 @@
+#
+# Config.new file and device description for machine-independent ATAPI code.
+# Included by ports that need it. Ports that use it must provide
+# their own "major" declarations for the appropriate devices.
+
+define atapi {}
+file dev/atapi/atapiconf.c atapi
+
+device atapibus {drive = -1}
+attach atapibus at atapi
+
+device acd: disk
+attach acd at atapibus
+file dev/atapi/acd.c acd needs-flag
diff --git a/sys/dev/isa/files.isa b/sys/dev/isa/files.isa
index 48ede3fa3ff..753dc3cb2cb 100644
--- a/sys/dev/isa/files.isa
+++ b/sys/dev/isa/files.isa
@@ -1,4 +1,4 @@
-# $OpenBSD: files.isa,v 1.18 1996/05/27 01:11:49 deraadt Exp $
+# $OpenBSD: files.isa,v 1.19 1996/06/08 09:12:44 downsj Exp $
# $NetBSD: files.isa,v 1.21 1996/05/16 03:45:55 mycroft Exp $
#
# Config.new file and device description for machine-independent ISA code.
@@ -119,12 +119,14 @@ device pcd: disk, opti
attach pcd at isa
file dev/isa/pcd.c pcd needs-flag
-# ISA "wd" (ESDI/IDE/etc.) controllers
-device wdc {drive = -1}
-attach wdc at isa
-device wd: disk, isadma
-attach wd at wdc
-file dev/isa/wd.c wdc needs-flag
+# ISA "wd" (ESDI/IDE/etc.) controllers, ATAPI bus
+define ata {drive = -1}
+device wdc {drive = -1}: atapi, ata
+attach wdc at isa
+device wd: disk, isadma
+attach wd at wdc
+file dev/isa/wdc.c wdc needs-flag
+file dev/isa/wd.c wd needs-flag
# Wangtek- and Archive-compatible tape controller boards
device wt: tape, isadma
diff --git a/sys/dev/isa/wd.c b/sys/dev/isa/wd.c
index 82c2ac91ac5..d9061bd00eb 100644
--- a/sys/dev/isa/wd.c
+++ b/sys/dev/isa/wd.c
@@ -1,5 +1,4 @@
-/* $OpenBSD: wd.c,v 1.14 1996/06/01 09:35:36 deraadt Exp $ */
-/* $NetBSD: wd.c,v 1.150 1996/05/12 23:54:03 mycroft Exp $ */
+/* $NetBSD: wd.c,v 1.150 1996/05/12 23:54:03 mycroft Exp $ */
/*
* Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
@@ -56,21 +55,8 @@
#include <machine/pio.h>
#include <dev/isa/isavar.h>
-#include <dev/isa/isadmavar.h>
#include <dev/isa/wdreg.h>
-
-#define WAITTIME (8 * hz) /* time to wait for a completion
- (long enough for disk spin-ups) */
-#define RECOVERYTIME (hz / 2) /* time to recover from an error */
-
-#define WDCDELAY 100
-#define WDCNDELAY 100000 /* delay = 100us; so 10s for a controller state change */
-#if 0
-/* If you enable this, it will report any delays more than 100us * N long. */
-#define WDCNDELAY_DEBUG 10
-#endif
-
-#define WDIORETRIES 3 /* number of retries before giving up */
+#include <dev/isa/wdlink.h>
#define WDUNIT(dev) DISKUNIT(dev)
#define WDPART(dev) DISKPART(dev)
@@ -78,82 +64,22 @@
#define WDLABELDEV(dev) (MAKEWDDEV(major(dev), WDUNIT(dev), RAW_PART))
+#ifdef WDDEBUG
+#define WDDEBUG_PRINT(args) printf args
+#else
+#define WDDEBUG_PRINT(args)
+#endif
+
struct wd_softc {
struct device sc_dev;
struct disk sc_dk;
-
- /* Information about the current transfer: */
- daddr_t sc_blkno; /* starting block number */
- int sc_bcount; /* byte count left */
- int sc_skip; /* bytes already transferred */
- int sc_nblks; /* number of blocks currently transferring */
- int sc_nbytes; /* number of bytes currently transferring */
-
- /* Long-term state: */
- int sc_drive; /* physical unit number */
- int sc_state; /* control state */
-#define RECAL 0 /* recalibrate */
-#define RECAL_WAIT 1 /* done recalibrating */
-#define GEOMETRY 2 /* upload geometry */
-#define GEOMETRY_WAIT 3 /* done uploading geometry */
-#define MULTIMODE 4 /* set multiple mode */
-#define MULTIMODE_WAIT 5 /* done setting multiple mode */
-#define OPEN 6 /* done with open */
- int sc_mode; /* transfer mode */
-#define WDM_PIOSINGLE 0 /* single-sector PIO */
-#define WDM_PIOMULTI 1 /* multi-sector PIO */
-#define WDM_DMA 2 /* DMA */
- int sc_multiple; /* multiple for WDM_PIOMULTI */
- int sc_flags; /* drive characteistics found */
-#define WDF_LOCKED 0x01
-#define WDF_WANTED 0x02
-#define WDF_WLABEL 0x04 /* label is writable */
-#define WDF_LABELLING 0x08 /* writing label */
-/* XXX Nothing resets this yet, but disk change sensing will when ATAPI is
- implemented. */
-#define WDF_LOADED 0x10 /* parameters loaded */
-#define WDF_32BIT 0x20 /* can do 32-bit transfer */
-
- struct wdparams sc_params; /* ESDI/ATA drive parameters */
- daddr_t sc_badsect[127]; /* 126 plus trailing -1 marker */
-
- TAILQ_ENTRY(wd_softc) sc_drivechain;
+ struct wd_link *d_link;
struct buf sc_q;
};
-struct wdc_softc {
- struct device sc_dev;
- void *sc_ih;
-
- int sc_iobase; /* I/O port base */
- int sc_drq; /* DMA channel */
-
- TAILQ_HEAD(drivehead, wd_softc) sc_drives;
- int sc_flags;
-#define WDCF_ACTIVE 0x01 /* controller is active */
-#define WDCF_SINGLE 0x02 /* sector at a time mode */
-#define WDCF_ERROR 0x04 /* processing a disk error */
-#define WDCF_WANTED 0x08 /* XXX locking for wd_get_parms() */
- int sc_errors; /* errors during current transfer */
- u_char sc_status; /* copy of status register */
- u_char sc_error; /* copy of error register */
-};
-
-int wdcprobe __P((struct device *, void *, void *));
-void wdcattach __P((struct device *, struct device *, void *));
-int wdcintr __P((void *));
-
-struct cfattach wdc_ca = {
- sizeof(struct wdc_softc), wdcprobe, wdcattach
-};
-
-struct cfdriver wdc_cd = {
- NULL, "wdc", DV_DULL
-};
-
-int wdprobe __P((struct device *, void *, void *));
-void wdattach __P((struct device *, struct device *, void *));
-int wdprint __P((void *, char *));
+int wdprobe __P((struct device *, void *, void *));
+void wdattach __P((struct device *, struct device *, void *));
+int wdprint __P((void *, char *));
struct cfattach wd_ca = {
sizeof(struct wd_softc), wdprobe, wdattach
@@ -175,123 +101,29 @@ cdev_decl(wd);
bdev_decl(wd);
void wdfinish __P((struct wd_softc *, struct buf *));
-int dcintr __P((void *));
-void wdcstart __P((struct wdc_softc *));
-int wdcommand __P((struct wd_softc *, int, int, int, int, int));
-int wdcommandshort __P((struct wdc_softc *, int, int));
-int wdcontrol __P((struct wd_softc *));
-int wdsetctlr __P((struct wd_softc *));
+int wdsetctlr __P((struct wd_link *));
static void bad144intern __P((struct wd_softc *));
-int wdcreset __P((struct wdc_softc *));
-void wdcrestart __P((void *arg));
-void wdcunwedge __P((struct wdc_softc *));
-void wdctimeout __P((void *arg));
-void wderror __P((void *, struct buf *, char *));
-int wdcwait __P((struct wdc_softc *, int));
-int wdlock __P((struct wd_softc *));
-void wdunlock __P((struct wd_softc *));
-
-/* ST506 spec says that if READY or SEEKCMPLT go off, then the read or write
- command is aborted. */
-#define wait_for_drq(d) wdcwait(d, WDCS_DRDY | WDCS_DSC | WDCS_DRQ)
-#define wait_for_ready(d) wdcwait(d, WDCS_DRDY | WDCS_DSC)
-#define wait_for_unbusy(d) wdcwait(d, 0)
-
-int
-wdcprobe(parent, match, aux)
- struct device *parent;
- void *match, *aux;
-{
- struct wdc_softc *wdc = match;
- struct isa_attach_args *ia = aux;
- int iobase;
-
- wdc->sc_iobase = iobase = ia->ia_iobase;
-
- /* Check if we have registers that work. */
- outb(iobase+wd_error, 0x5a); /* Error register not writable, */
- outb(iobase+wd_cyl_lo, 0xa5); /* but all of cyllo are. */
- if (inb(iobase+wd_error) == 0x5a || inb(iobase+wd_cyl_lo) != 0xa5)
- return 0;
-
- if (wdcreset(wdc) != 0) {
- delay(500000);
- if (wdcreset(wdc) != 0)
- return 0;
- }
-
- /* Select drive 0. */
- outb(iobase+wd_sdh, WDSD_IBM | 0);
-
- /* Wait for controller to become ready. */
- if (wait_for_unbusy(wdc) < 0)
- return 0;
-
- /* Start drive diagnostics. */
- outb(iobase+wd_command, WDCC_DIAGNOSE);
-
- /* Wait for command to complete. */
- if (wait_for_unbusy(wdc) < 0)
- return 0;
-
- ia->ia_iosize = 8;
- ia->ia_msize = 0;
- return 1;
-}
-
-struct wdc_attach_args {
- int wa_drive;
-};
-
-int
-wdprint(aux, wdc)
- void *aux;
- char *wdc;
-{
- struct wdc_attach_args *wa = aux;
-
- if (!wdc)
- printf(" drive %d", wa->wa_drive);
- return QUIET;
-}
-
-void
-wdcattach(parent, self, aux)
- struct device *parent, *self;
- void *aux;
-{
- struct wdc_softc *wdc = (void *)self;
- struct isa_attach_args *ia = aux;
- struct wdc_attach_args wa;
-
- TAILQ_INIT(&wdc->sc_drives);
- wdc->sc_drq = ia->ia_drq;
-
- printf("\n");
-
- wdc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE,
- IPL_BIO, wdcintr, wdc, wdc->sc_dev.dv_xname);
-
- for (wa.wa_drive = 0; wa.wa_drive < 2; wa.wa_drive++)
- (void)config_found(self, (void *)&wa, wdprint);
-}
+int wdlock __P((struct wd_link *));
+void wdunlock __P((struct wd_link *));
int
wdprobe(parent, match, aux)
struct device *parent;
void *match, *aux;
{
- struct wdc_softc *wdc = (void *)parent;
+ caddr_t *wdc = (void *)parent;
struct cfdata *cf = match;
- struct wdc_attach_args *wa = aux;
- int drive = wa->wa_drive;
+ struct wd_link *d_link = aux;
+ int drive;
+
+ if (d_link == NULL)
+ return 0;
+ if (d_link->type != DRIVE)
+ return 0;
+ drive = d_link->sc_drive;
if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != drive)
return 0;
-
- if (wdcommandshort(wdc, drive, WDCC_RECAL) != 0 ||
- wait_for_ready(wdc) != 0)
- return 0;
return 1;
}
@@ -302,12 +134,14 @@ wdattach(parent, self, aux)
void *aux;
{
struct wd_softc *wd = (void *)self;
- struct wdc_softc *wdc = (void *)parent;
- struct wdc_attach_args *wa = aux;
+ struct caddr_t *wdc = (void *)parent;
+ struct wd_link *d_link= aux;
int i, blank;
char buf[41], c, *p, *q;
- wd->sc_drive = wa->wa_drive;
+ wd->d_link = d_link;
+ d_link->openings = 1;
+ d_link->wd_softc = (caddr_t)wd;
/*
* Initialize and attach the disk structure.
@@ -318,9 +152,11 @@ wdattach(parent, self, aux)
dk_establish(&wd->sc_dk, &wd->sc_dev);
- wd_get_parms(wd);
- for (blank = 0, p = wd->sc_params.wdp_model, q = buf, i = 0;
- i < sizeof(wd->sc_params.wdp_model); i++) {
+ d_link->sc_lp = wd->sc_dk.dk_label;
+
+ wdc_get_parms(d_link);
+ for (blank = 0, p = d_link->sc_params.wdp_model, q = buf, i = 0;
+ i < sizeof(d_link->sc_params.wdp_model); i++) {
c = *p++;
if (c == '\0')
break;
@@ -335,34 +171,36 @@ wdattach(parent, self, aux)
}
*q++ = '\0';
- printf(": %dMB, %d cyl, %d head, %d sec, %d bytes/sec <%s>\n",
- wd->sc_params.wdp_cylinders *
- (wd->sc_params.wdp_heads * wd->sc_params.wdp_sectors) /
+ printf(": <%s>\n", buf);
+ printf("%s: %dMB, %d cyl, %d head, %d sec, %d bytes/sec\n",
+ self->dv_xname,
+ d_link->sc_params.wdp_cylinders *
+ (d_link->sc_params.wdp_heads * d_link->sc_params.wdp_sectors) /
(1048576 / DEV_BSIZE),
- wd->sc_params.wdp_cylinders,
- wd->sc_params.wdp_heads,
- wd->sc_params.wdp_sectors,
- DEV_BSIZE,
- buf);
-
- if ((wd->sc_params.wdp_capabilities & WD_CAP_DMA) != 0 &&
- wdc->sc_drq != DRQUNK) {
- wd->sc_mode = WDM_DMA;
- } else if (wd->sc_params.wdp_maxmulti > 1) {
- wd->sc_mode = WDM_PIOMULTI;
- wd->sc_multiple = min(wd->sc_params.wdp_maxmulti, 16);
+ d_link->sc_params.wdp_cylinders,
+ d_link->sc_params.wdp_heads,
+ d_link->sc_params.wdp_sectors,
+ DEV_BSIZE);
+
+ if ((d_link->sc_params.wdp_capabilities & WD_CAP_DMA) != 0 &&
+ d_link->sc_mode == WDM_DMA) {
+ d_link->sc_mode = WDM_DMA;
+ } else if (d_link->sc_params.wdp_maxmulti > 1) {
+ d_link->sc_mode = WDM_PIOMULTI;
+ d_link->sc_multiple = min(d_link->sc_params.wdp_maxmulti, 16);
} else {
- wd->sc_mode = WDM_PIOSINGLE;
- wd->sc_multiple = 1;
+ d_link->sc_mode = WDM_PIOSINGLE;
+ d_link->sc_multiple = 1;
}
printf("%s: using", wd->sc_dev.dv_xname);
- if (wd->sc_mode == WDM_DMA)
+ if (d_link->sc_mode == WDM_DMA)
printf(" dma transfers,");
else
printf(" %d-sector %d-bit pio transfers,",
- wd->sc_multiple, (wd->sc_flags & WDF_32BIT) == 0 ? 16 : 32);
- if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0)
+ d_link->sc_multiple,
+ (d_link->sc_flags & WDF_32BIT) == 0 ? 16 : 32);
+ if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0)
printf(" lba addressing\n");
else
printf(" chs addressing\n");
@@ -377,6 +215,7 @@ wdstrategy(bp)
struct buf *bp;
{
struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(bp->b_dev)];
+ struct wd_link *d_link= wd->d_link;
int s;
/* Valid request? */
@@ -388,7 +227,7 @@ wdstrategy(bp)
}
/* If device invalidated (e.g. media change, door open), error. */
- if ((wd->sc_flags & WDF_LOADED) == 0) {
+ if ((d_link->sc_flags & WDF_LOADED) == 0) {
bp->b_error = EIO;
goto bad;
}
@@ -403,23 +242,13 @@ wdstrategy(bp)
*/
if (WDPART(bp->b_dev) != RAW_PART &&
bounds_check_with_label(bp, wd->sc_dk.dk_label,
- (wd->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0)
+ (d_link->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0)
goto done;
/* Queue transfer on drive, activate drive and controller if idle. */
s = splbio();
disksort(&wd->sc_q, bp);
- if (!wd->sc_q.b_active)
- wdstart(wd);
-#if 0
- else {
- struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
- if ((wdc->sc_flags & (WDCF_ACTIVE|WDCF_ERROR)) == 0) {
- printf("wdstrategy: controller inactive\n");
- wdcstart(wdc);
- }
- }
-#endif
+ wdstart(wd);
splx(s);
return;
@@ -438,57 +267,47 @@ void
wdstart(wd)
struct wd_softc *wd;
{
- struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
- int active = wdc->sc_drives.tqh_first != 0;
-
- /* Link onto controller queue. */
- wd->sc_q.b_active = 1;
- TAILQ_INSERT_TAIL(&wdc->sc_drives, wd, sc_drivechain);
-
- disk_busy(&wd->sc_dk);
-
- /* If controller not already active, start it. */
- if (!active)
- wdcstart(wdc);
-}
-
-/*
- * Finish an I/O operation. Clean up the drive and controller state, set the
- * residual count, and inform the upper layers that the operation is complete.
- */
-void
-wdfinish(wd, bp)
- struct wd_softc *wd;
- struct buf *bp;
-{
- struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
+ struct buf *dp, *bp=0;
+ struct wd_link *d_link = wd->d_link;
+ struct wdc_link *ctlr_link = d_link->ctlr_link;
+ struct wdc_xfer *xfer;
+ int blkno, nblks;
+ u_long p_offset;
+
+ while (d_link->openings > 0) {
+
+ /* Is there a buf for us ? */
+ dp = &wd->sc_q;
+ if ((bp = dp->b_actf) == NULL) /* yes, an assign */
+ return;
+ dp->b_actf = bp->b_actf;
+
+ /*
+ * Make the command. First lock the device
+ */
+ d_link->openings--;
+ if (WDPART(bp->b_dev) != RAW_PART)
+ p_offset =
+ wd->sc_dk.dk_label->d_partitions[WDPART(bp->b_dev)].p_offset;
+ else
+ p_offset = 0;
- wdc->sc_flags &= ~(WDCF_SINGLE | WDCF_ERROR);
- wdc->sc_errors = 0;
- /*
- * Move this drive to the end of the queue to give others a `fair'
- * chance.
- */
- if (wd->sc_drivechain.tqe_next) {
- TAILQ_REMOVE(&wdc->sc_drives, wd, sc_drivechain);
- if (bp->b_actf) {
- TAILQ_INSERT_TAIL(&wdc->sc_drives, wd, sc_drivechain);
- } else
- wd->sc_q.b_active = 0;
- }
- bp->b_resid = wd->sc_bcount;
- wd->sc_skip = 0;
- wd->sc_q.b_actf = bp->b_actf;
+ xfer = wdc_get_xfer(ctlr_link, 0);
+ if (xfer == NULL)
+ panic("wdc_xfer");
- disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid));
+ xfer->d_link = d_link;
+ xfer->c_bp = bp;
+ xfer->c_p_offset = p_offset;
+ xfer->databuf = bp->b_data;
+ xfer->c_bcount = bp->b_bcount;
+ xfer->c_flags |= bp->b_flags & (B_READ|B_WRITE);
+ xfer->c_blkno = bp->b_blkno;
- if (!wd->sc_q.b_actf) {
- TAILQ_REMOVE(&wdc->sc_drives, wd, sc_drivechain);
- wd->sc_q.b_active = 0;
- } else
+ /* Instrumentation. */
disk_busy(&wd->sc_dk);
-
- biodone(bp);
+ wdc_exec_xfer(wd->d_link,xfer);
+ }
}
int
@@ -498,6 +317,7 @@ wdread(dev, uio, flags)
int flags;
{
+ WDDEBUG_PRINT(("wdread\n"));
return (physio(wdstrategy, NULL, dev, B_READ, minphys, uio));
}
@@ -508,388 +328,37 @@ wdwrite(dev, uio, flags)
int flags;
{
+ WDDEBUG_PRINT(("wdwrite\n"));
return (physio(wdstrategy, NULL, dev, B_WRITE, minphys, uio));
}
/*
- * Start I/O on a controller. This does the calculation, and starts a read or
- * write operation. Called to from wdstart() to start a transfer, from
- * wdcintr() to continue a multi-sector transfer or start the next transfer, or
- * wdcrestart() after recovering from an error.
- */
-void
-wdcstart(wdc)
- struct wdc_softc *wdc;
-{
- struct wd_softc *wd;
- struct buf *bp;
- struct disklabel *lp;
- int nblks;
-
-#ifdef DIAGNOSTIC
- if ((wdc->sc_flags & WDCF_ACTIVE) != 0)
- panic("wdcstart: controller still active");
-#endif
-
- /*
- * XXX
- * This is a kluge. See comments in wd_get_parms().
- */
- if ((wdc->sc_flags & WDCF_WANTED) != 0) {
- wdc->sc_flags &= ~WDCF_WANTED;
- wakeup(wdc);
- return;
- }
-
-loop:
- /* Is there a drive for the controller to do a transfer with? */
- wd = wdc->sc_drives.tqh_first;
- if (wd == NULL)
- return;
-
- /* Is there a transfer to this drive? If not, deactivate drive. */
- bp = wd->sc_q.b_actf;
-
- if (wdc->sc_errors >= WDIORETRIES) {
- wderror(wd, bp, "hard error");
- bp->b_error = EIO;
- bp->b_flags |= B_ERROR;
- wdfinish(wd, bp);
- goto loop;
- }
-
- /* Do control operations specially. */
- if (wd->sc_state < OPEN) {
- /*
- * Actually, we want to be careful not to mess with the control
- * state if the device is currently busy, but we can assume
- * that we never get to this point if that's the case.
- */
- if (wdcontrol(wd) == 0) {
- /* The drive is busy. Wait. */
- return;
- }
- }
-
- /*
- * WDCF_ERROR is set by wdcunwedge() and wdcintr() when an error is
- * encountered. If we are in multi-sector mode, then we switch to
- * single-sector mode and retry the operation from the start.
- */
- if (wdc->sc_flags & WDCF_ERROR) {
- wdc->sc_flags &= ~WDCF_ERROR;
- if ((wdc->sc_flags & WDCF_SINGLE) == 0) {
- wdc->sc_flags |= WDCF_SINGLE;
- wd->sc_skip = 0;
- }
- }
-
- lp = wd->sc_dk.dk_label;
-
- /* When starting a transfer... */
- if (wd->sc_skip == 0) {
- int part = WDPART(bp->b_dev);
- daddr_t blkno;
-
-#ifdef WDDEBUG
- printf("\n%s: wdcstart %s %d@%d; map ", wd->sc_dev.dv_xname,
- (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount,
- bp->b_blkno);
-#endif
- wd->sc_bcount = bp->b_bcount;
- blkno = bp->b_blkno;
- if (part != RAW_PART)
- blkno += lp->d_partitions[part].p_offset;
- wd->sc_blkno = blkno / (lp->d_secsize / DEV_BSIZE);
- } else {
-#ifdef WDDEBUG
- printf(" %d)%x", wd->sc_skip, inb(wdc->sc_iobase+wd_altsts));
-#endif
- }
-
- /* When starting a multi-sector transfer, or doing single-sector
- transfers... */
- if (wd->sc_skip == 0 || (wdc->sc_flags & WDCF_SINGLE) != 0 ||
- wd->sc_mode == WDM_DMA) {
- daddr_t blkno = wd->sc_blkno;
- long cylin, head, sector;
- int command;
-
- if ((wdc->sc_flags & WDCF_SINGLE) != 0)
- nblks = 1;
- else if (wd->sc_mode != WDM_DMA)
- nblks = wd->sc_bcount / lp->d_secsize;
- else
- nblks = min(wd->sc_bcount / lp->d_secsize, 8);
-
- /* Check for bad sectors and adjust transfer, if necessary. */
- if ((lp->d_flags & D_BADSECT) != 0
-#ifdef B_FORMAT
- && (bp->b_flags & B_FORMAT) == 0
-#endif
- ) {
- long blkdiff;
- int i;
-
- for (i = 0; (blkdiff = wd->sc_badsect[i]) != -1; i++) {
- blkdiff -= blkno;
- if (blkdiff < 0)
- continue;
- if (blkdiff == 0) {
- /* Replace current block of transfer. */
- blkno =
- lp->d_secperunit - lp->d_nsectors - i - 1;
- }
- if (blkdiff < nblks) {
- /* Bad block inside transfer. */
- wdc->sc_flags |= WDCF_SINGLE;
- nblks = 1;
- }
- break;
- }
- /* Tranfer is okay now. */
- }
-
- if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) {
- sector = (blkno >> 0) & 0xff;
- cylin = (blkno >> 8) & 0xffff;
- head = (blkno >> 24) & 0xf;
- head |= WDSD_LBA;
- } else {
- sector = blkno % lp->d_nsectors;
- sector++; /* Sectors begin with 1, not 0. */
- blkno /= lp->d_nsectors;
- head = blkno % lp->d_ntracks;
- blkno /= lp->d_ntracks;
- cylin = blkno;
- head |= WDSD_CHS;
- }
-
- if (wd->sc_mode == WDM_PIOSINGLE ||
- (wdc->sc_flags & WDCF_SINGLE) != 0)
- wd->sc_nblks = 1;
- else if (wd->sc_mode == WDM_PIOMULTI)
- wd->sc_nblks = min(nblks, wd->sc_multiple);
- else
- wd->sc_nblks = nblks;
- wd->sc_nbytes = wd->sc_nblks * lp->d_secsize;
-
-#ifdef B_FORMAT
- if (bp->b_flags & B_FORMAT) {
- sector = lp->d_gap3;
- nblks = lp->d_nsectors;
- command = WDCC_FORMAT;
- } else
-#endif
- switch (wd->sc_mode) {
- case WDM_DMA:
- command = (bp->b_flags & B_READ) ?
- WDCC_READDMA : WDCC_WRITEDMA;
- /* Start the DMA channel and bounce the buffer if
- necessary. */
- isa_dmastart(
- bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE,
- bp->b_data + wd->sc_skip,
- wd->sc_nbytes, wdc->sc_drq);
- break;
- case WDM_PIOMULTI:
- command = (bp->b_flags & B_READ) ?
- WDCC_READMULTI : WDCC_WRITEMULTI;
- break;
- case WDM_PIOSINGLE:
- command = (bp->b_flags & B_READ) ?
- WDCC_READ : WDCC_WRITE;
- break;
- default:
-#ifdef DIAGNOSTIC
- panic("bad wd mode");
-#endif
- return;
- }
-
- /* Initiate command! */
- if (wdcommand(wd, command, cylin, head, sector, nblks) != 0) {
- wderror(wd, NULL,
- "wdcstart: timeout waiting for unbusy");
- wdcunwedge(wdc);
- return;
- }
-
-#ifdef WDDEBUG
- printf("sector %d cylin %d head %d addr %x sts %x\n", sector,
- cylin, head, bp->b_data, inb(wdc->sc_iobase+wd_altsts));
-#endif
- } else if (wd->sc_nblks > 1) {
- /* The number of blocks in the last stretch may be smaller. */
- nblks = wd->sc_bcount / lp->d_secsize;
- if (wd->sc_nblks > nblks) {
- wd->sc_nblks = nblks;
- wd->sc_nbytes = wd->sc_bcount;
- }
- }
-
- /* If this was a write and not using DMA, push the data. */
- if (wd->sc_mode != WDM_DMA &&
- (bp->b_flags & (B_READ|B_WRITE)) == B_WRITE) {
- if (wait_for_drq(wdc) < 0) {
- wderror(wd, NULL, "wdcstart: timeout waiting for drq");
- wdcunwedge(wdc);
- return;
- }
-
- /* Push out data. */
- if ((wd->sc_flags & WDF_32BIT) == 0)
- outsw(wdc->sc_iobase+wd_data, bp->b_data + wd->sc_skip,
- wd->sc_nbytes >> 1);
- else
- outsl(wdc->sc_iobase+wd_data, bp->b_data + wd->sc_skip,
- wd->sc_nbytes >> 2);
- }
-
- wdc->sc_flags |= WDCF_ACTIVE;
- timeout(wdctimeout, wdc, WAITTIME);
-}
-
-/*
- * Interrupt routine for the controller. Acknowledge the interrupt, check for
- * errors on the current operation, mark it done if necessary, and start the
- * next request. Also check for a partially done transfer, and continue with
- * the next chunk if so.
- */
-int
-wdcintr(arg)
- void *arg;
-{
- struct wdc_softc *wdc = arg;
- struct wd_softc *wd;
- struct buf *bp;
-
- if ((wdc->sc_flags & WDCF_ACTIVE) == 0)
- /* leave it alone if we didn't ask for this interrupt */
- return 0;
-
- wdc->sc_flags &= ~WDCF_ACTIVE;
- untimeout(wdctimeout, wdc);
-
- wd = wdc->sc_drives.tqh_first;
- bp = wd->sc_q.b_actf;
-
-#ifdef WDDEBUG
- printf("I%s ", wdc->sc_dev.dv_xname);
-#endif
-
- if (wait_for_unbusy(wdc) < 0) {
- wderror(wd, NULL, "wdcintr: timeout waiting for unbusy");
- wdc->sc_status |= WDCS_ERR; /* XXX */
- }
-
- /* Is it not a transfer, but a control operation? */
- if (wd->sc_state < OPEN) {
- if (wdcontrol(wd) == 0) {
- /* The drive is busy. Wait. */
- return 1;
- }
- wdcstart(wdc);
- return 1;
- }
-
- /* Turn off the DMA channel and unbounce the buffer. */
- if (wd->sc_mode == WDM_DMA)
- isa_dmadone(bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE,
- bp->b_data + wd->sc_skip, wd->sc_nbytes, wdc->sc_drq);
-
- /* Have we an error? */
- if (wdc->sc_status & WDCS_ERR) {
-#ifdef WDDEBUG
- wderror(wd, NULL, "wdcintr");
-#endif
- if ((wdc->sc_flags & WDCF_SINGLE) == 0) {
- wdc->sc_flags |= WDCF_ERROR;
- goto restart;
- }
-
-#ifdef B_FORMAT
- if (bp->b_flags & B_FORMAT)
- goto bad;
-#endif
-
- wdcunwedge(wdc);
- if (wdc->sc_errors < WDIORETRIES)
- return 1;
- wderror(wd, bp, "hard error");
-
-#ifdef B_FORMAT
- bad:
-#endif
- bp->b_error = EIO;
- bp->b_flags |= B_ERROR;
- goto done;
- }
-
- /* If this was a read and not using DMA, fetch the data. */
- if (wd->sc_mode != WDM_DMA &&
- (bp->b_flags & (B_READ|B_WRITE)) == B_READ) {
- if ((wdc->sc_status & (WDCS_DRDY | WDCS_DSC | WDCS_DRQ))
- != (WDCS_DRDY | WDCS_DSC | WDCS_DRQ)) {
- wderror(wd, NULL, "wdcintr: read intr before drq");
- wdcunwedge(wdc);
- return 1;
- }
-
- /* Pull in data. */
- if ((wd->sc_flags & WDF_32BIT) == 0)
- insw(wdc->sc_iobase+wd_data, bp->b_data + wd->sc_skip,
- wd->sc_nbytes >> 1);
- else
- insl(wdc->sc_iobase+wd_data, bp->b_data + wd->sc_skip,
- wd->sc_nbytes >> 2);
- }
-
- /* If we encountered any abnormalities, flag it as a soft error. */
- if (wdc->sc_errors > 0 ||
- (wdc->sc_status & WDCS_CORR) != 0) {
- wderror(wd, bp, "soft error (corrected)");
- wdc->sc_errors = 0;
- }
-
- /* Adjust pointers for the next block, if any. */
- wd->sc_blkno += wd->sc_nblks;
- wd->sc_skip += wd->sc_nbytes;
- wd->sc_bcount -= wd->sc_nbytes;
-
- /* See if this transfer is complete. */
- if (wd->sc_bcount > 0)
- goto restart;
-
-done:
- /* Done with this transfer, with or without error. */
- wdfinish(wd, bp);
-
-restart:
- /* Start the next operation, if any. */
- wdcstart(wdc);
-
- return 1;
-}
-
-/*
* Wait interruptibly for an exclusive lock.
*
* XXX
* Several drivers do this; it should be abstracted and made MP-safe.
*/
int
-wdlock(wd)
- struct wd_softc *wd;
+wdlock(d_link)
+ struct wd_link *d_link;
{
int error;
+ int s;
+
+ WDDEBUG_PRINT(("wdlock\n"));
+
+ s = splbio();
- while ((wd->sc_flags & WDF_LOCKED) != 0) {
- wd->sc_flags |= WDF_WANTED;
- if ((error = tsleep(wd, PRIBIO | PCATCH, "wdlck", 0)) != 0)
+ while ((d_link->sc_flags & WDF_LOCKED) != 0) {
+ d_link->sc_flags |= WDF_WANTED;
+ if ((error = tsleep(d_link, PRIBIO | PCATCH,
+ "wdlck", 0)) != 0) {
+ splx(s);
return error;
+ }
}
- wd->sc_flags |= WDF_LOCKED;
+ d_link->sc_flags |= WDF_LOCKED;
+ splx(s);
return 0;
}
@@ -897,14 +366,16 @@ wdlock(wd)
* Unlock and wake up any waiters.
*/
void
-wdunlock(wd)
- struct wd_softc *wd;
+wdunlock(d_link)
+ struct wd_link *d_link;
{
- wd->sc_flags &= ~WDF_LOCKED;
- if ((wd->sc_flags & WDF_WANTED) != 0) {
- wd->sc_flags &= ~WDF_WANTED;
- wakeup(wd);
+ WDDEBUG_PRINT(("wdunlock"));
+
+ d_link->sc_flags &= ~WDF_LOCKED;
+ if ((d_link->sc_flags & WDF_WANTED) != 0) {
+ d_link->sc_flags &= ~WDF_WANTED;
+ wakeup(d_link);
}
}
@@ -915,9 +386,12 @@ wdopen(dev, flag, fmt, p)
struct proc *p;
{
struct wd_softc *wd;
+ struct wd_link *d_link;
int unit, part;
int error;
-
+
+ WDDEBUG_PRINT(("wdopen\n"));
+
unit = WDUNIT(dev);
if (unit >= wd_cd.cd_ndevs)
return ENXIO;
@@ -925,7 +399,8 @@ wdopen(dev, flag, fmt, p)
if (wd == 0)
return ENXIO;
- if ((error = wdlock(wd)) != 0)
+ d_link = wd->d_link;
+ if ((error = wdlock(d_link)) != 0)
return error;
if (wd->sc_dk.dk_openmask != 0) {
@@ -933,16 +408,16 @@ wdopen(dev, flag, fmt, p)
* If any partition is open, but the disk has been invalidated,
* disallow further opens.
*/
- if ((wd->sc_flags & WDF_LOADED) == 0) {
+ if ((d_link->sc_flags & WDF_LOADED) == 0) {
error = EIO;
goto bad3;
}
} else {
- if ((wd->sc_flags & WDF_LOADED) == 0) {
- wd->sc_flags |= WDF_LOADED;
+ if ((d_link->sc_flags & WDF_LOADED) == 0) {
+ d_link->sc_flags |= WDF_LOADED;
/* Load the physical device parameters. */
- if (wd_get_parms(wd) != 0) {
+ if (wdc_get_parms(d_link) != 0) {
error = ENXIO;
goto bad2;
}
@@ -973,18 +448,18 @@ wdopen(dev, flag, fmt, p)
}
wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask;
- wdunlock(wd);
+ wdunlock(d_link);
return 0;
bad2:
- wd->sc_flags &= ~WDF_LOADED;
+ d_link->sc_flags &= ~WDF_LOADED;
bad:
if (wd->sc_dk.dk_openmask == 0) {
}
bad3:
- wdunlock(wd);
+ wdunlock(d_link);
return error;
}
@@ -998,7 +473,7 @@ wdclose(dev, flag, fmt, p)
int part = WDPART(dev);
int error;
- if ((error = wdlock(wd)) != 0)
+ if ((error = wdlock(wd->d_link)) != 0)
return error;
switch (fmt) {
@@ -1015,7 +490,7 @@ wdclose(dev, flag, fmt, p)
/* XXXX Must wait for I/O to complete! */
}
- wdunlock(wd);
+ wdunlock(wd->d_link);
return 0;
}
@@ -1027,22 +502,25 @@ wdgetdisklabel(wd)
struct wd_softc *wd;
{
struct disklabel *lp = wd->sc_dk.dk_label;
+ struct wd_link *d_link = wd->d_link;
char *errstring;
+ WDDEBUG_PRINT(("wdgetdisklabel\n"));
+
bzero(lp, sizeof(struct disklabel));
bzero(wd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel));
lp->d_secsize = DEV_BSIZE;
- lp->d_ntracks = wd->sc_params.wdp_heads;
- lp->d_nsectors = wd->sc_params.wdp_sectors;
- lp->d_ncylinders = wd->sc_params.wdp_cylinders;
+ lp->d_ntracks = d_link->sc_params.wdp_heads;
+ lp->d_nsectors = d_link->sc_params.wdp_sectors;
+ lp->d_ncylinders = d_link->sc_params.wdp_cylinders;
lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
#if 0
strncpy(lp->d_typename, "ST506 disk", 16);
lp->d_type = DTYPE_ST506;
#endif
- strncpy(lp->d_packname, wd->sc_params.wdp_model, 16);
+ strncpy(lp->d_packname, d_link->sc_params.wdp_model, 16);
lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
lp->d_rpm = 3600;
lp->d_interleave = 1;
@@ -1058,10 +536,10 @@ wdgetdisklabel(wd)
lp->d_magic2 = DISKMAGIC;
lp->d_checksum = dkcksum(lp);
- wd->sc_badsect[0] = -1;
+ d_link->sc_badsect[0] = -1;
- if (wd->sc_state > RECAL)
- wd->sc_state = RECAL;
+ if (d_link->sc_state > RECAL)
+ d_link->sc_state = RECAL;
errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART),
wdstrategy, lp, wd->sc_dk.dk_cpulabel);
if (errstring) {
@@ -1071,8 +549,8 @@ wdgetdisklabel(wd)
* assume the DOS geometry is now in the label and try
* again. XXX This is a kluge.
*/
- if (wd->sc_state > GEOMETRY)
- wd->sc_state = GEOMETRY;
+ if (d_link->sc_state > GEOMETRY)
+ d_link->sc_state = GEOMETRY;
errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART),
wdstrategy, lp, wd->sc_dk.dk_cpulabel);
}
@@ -1081,270 +559,55 @@ wdgetdisklabel(wd)
return;
}
- if (wd->sc_state > GEOMETRY)
- wd->sc_state = GEOMETRY;
+ if (d_link->sc_state > GEOMETRY)
+ d_link->sc_state = GEOMETRY;
if ((lp->d_flags & D_BADSECT) != 0)
bad144intern(wd);
}
-/*
- * Implement operations needed before read/write.
- * Returns 0 if operation still in progress, 1 if completed.
- */
-int
-wdcontrol(wd)
- struct wd_softc *wd;
-{
- struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
-
- switch (wd->sc_state) {
- case RECAL: /* Set SDH, step rate, do recal. */
- if (wdcommandshort(wdc, wd->sc_drive, WDCC_RECAL) != 0) {
- wderror(wd, NULL, "wdcontrol: recal failed (1)");
- goto bad;
- }
- wd->sc_state = RECAL_WAIT;
- break;
-
- case RECAL_WAIT:
- if (wdc->sc_status & WDCS_ERR) {
- wderror(wd, NULL, "wdcontrol: recal failed (2)");
- goto bad;
- }
- /* fall through */
- case GEOMETRY:
- if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0)
- goto multimode;
- if (wdsetctlr(wd) != 0) {
- /* Already printed a message. */
- goto bad;
- }
- wd->sc_state = GEOMETRY_WAIT;
- break;
-
- case GEOMETRY_WAIT:
- if (wdc->sc_status & WDCS_ERR) {
- wderror(wd, NULL, "wdcontrol: geometry failed");
- goto bad;
- }
- /* fall through */
- case MULTIMODE:
- multimode:
- if (wd->sc_mode != WDM_PIOMULTI)
- goto open;
- outb(wdc->sc_iobase+wd_seccnt, wd->sc_multiple);
- if (wdcommandshort(wdc, wd->sc_drive, WDCC_SETMULTI) != 0) {
- wderror(wd, NULL, "wdcontrol: setmulti failed (1)");
- goto bad;
- }
- wd->sc_state = MULTIMODE_WAIT;
- break;
-
- case MULTIMODE_WAIT:
- if (wdc->sc_status & WDCS_ERR) {
- wderror(wd, NULL, "wdcontrol: setmulti failed (2)");
- goto bad;
- }
- /* fall through */
- case OPEN:
- open:
- wdc->sc_errors = 0;
- wd->sc_state = OPEN;
- /*
- * The rest of the initialization can be done by normal means.
- */
- return 1;
-
- bad:
- wdcunwedge(wdc);
- return 0;
- }
-
- wdc->sc_flags |= WDCF_ACTIVE;
- timeout(wdctimeout, wdc, WAITTIME);
- return 0;
-}
-
-/*
- * Wait for the drive to become ready and send a command.
- * Return -1 if busy for too long or 0 otherwise.
- * Assumes interrupts are blocked.
- */
-int
-wdcommand(wd, command, cylin, head, sector, count)
- struct wd_softc *wd;
- int command;
- int cylin, head, sector, count;
-{
- struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
- int iobase = wdc->sc_iobase;
- int stat;
-
- /* Select drive, head, and addressing mode. */
- outb(iobase+wd_sdh, WDSD_IBM | (wd->sc_drive << 4) | head);
-
- /* Wait for it to become ready to accept a command. */
- if (command == WDCC_IDP)
- stat = wait_for_unbusy(wdc);
- else
- stat = wdcwait(wdc, WDCS_DRDY);
- if (stat < 0)
- return -1;
-
- /* Load parameters. */
- if (wd->sc_dk.dk_label->d_type == DTYPE_ST506)
- outb(iobase+wd_precomp, wd->sc_dk.dk_label->d_precompcyl / 4);
- else
- outb(iobase+wd_features, 0);
- outb(iobase+wd_cyl_lo, cylin);
- outb(iobase+wd_cyl_hi, cylin >> 8);
- outb(iobase+wd_sector, sector);
- outb(iobase+wd_seccnt, count);
-
- /* Send command. */
- outb(iobase+wd_command, command);
-
- return 0;
-}
-
-/*
- * Simplified version of wdcommand().
- */
-int
-wdcommandshort(wdc, drive, command)
- struct wdc_softc *wdc;
- int drive;
- int command;
-{
- int iobase = wdc->sc_iobase;
-
- /* Select drive. */
- outb(iobase+wd_sdh, WDSD_IBM | (drive << 4));
-
- if (wdcwait(wdc, WDCS_DRDY) < 0)
- return -1;
-
- outb(iobase+wd_command, command);
-
- return 0;
-}
/*
* Tell the drive what geometry to use.
*/
int
-wdsetctlr(wd)
- struct wd_softc *wd;
+wdsetctlr(d_link)
+ struct wd_link *d_link;
{
+ struct wd_softc *wd=(struct wd_softc *)d_link->wd_softc;
-#ifdef WDDEBUG
- printf("wd(%d,%d) C%dH%dS%d\n", wd->sc_dev.dv_unit, wd->sc_drive,
- wd->sc_dk.dk_label->d_ncylinders, wd->sc_dk.dk_label->d_ntracks,
- wd->sc_dk.dk_label->d_nsectors);
-#endif
-
- if (wdcommand(wd, WDCC_IDP, wd->sc_dk.dk_label->d_ncylinders,
+ WDDEBUG_PRINT(("wd(%d,%d) C%dH%dS%d\n", wd->sc_dev.dv_unit,
+ d_link->sc_drive, wd->sc_dk.dk_label->d_ncylinders,
+ wd->sc_dk.dk_label->d_ntracks, wd->sc_dk.dk_label->d_nsectors));
+
+ if (wdccommand(d_link, WDCC_IDP, d_link->sc_drive,
+ wd->sc_dk.dk_label->d_ncylinders,
wd->sc_dk.dk_label->d_ntracks - 1, 0,
wd->sc_dk.dk_label->d_nsectors) != 0) {
- wderror(wd, NULL, "wdsetctlr: geometry upload failed");
+ wderror(d_link, NULL, "wdsetctlr: geometry upload failed");
return -1;
}
return 0;
}
-/*
- * Get the drive parameters, if ESDI or ATA, or create fake ones for ST506.
- */
int
-wd_get_parms(wd)
- struct wd_softc *wd;
-{
- struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
- int i;
- char tb[DEV_BSIZE];
- int s, error;
-
- /*
- * XXX
- * The locking done here, and the length of time this may keep the rest
- * of the system suspended, is a kluge. This should be rewritten to
- * set up a transfer and queue it through wdstart(), but it's called
- * infrequently enough that this isn't a pressing matter.
- */
-
- s = splbio();
-
- while ((wdc->sc_flags & WDCF_ACTIVE) != 0) {
- wdc->sc_flags |= WDCF_WANTED;
- if ((error = tsleep(wdc, PRIBIO | PCATCH, "wdprm", 0)) != 0) {
- splx(s);
- return error;
- }
- }
-
- if (wdcommandshort(wdc, wd->sc_drive, WDCC_IDENTIFY) != 0 ||
- wait_for_drq(wdc) != 0) {
- /*
- * 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(wd->sc_dk.dk_label->d_typename, "ST506",
- sizeof wd->sc_dk.dk_label->d_typename);
- wd->sc_dk.dk_label->d_type = DTYPE_ST506;
-
- strncpy(wd->sc_params.wdp_model, "unknown",
- sizeof wd->sc_params.wdp_model);
- wd->sc_params.wdp_config = WD_CFG_FIXED;
- wd->sc_params.wdp_cylinders = 1024;
- wd->sc_params.wdp_heads = 8;
- wd->sc_params.wdp_sectors = 17;
- wd->sc_params.wdp_maxmulti = 0;
- wd->sc_params.wdp_usedmovsd = 0;
- wd->sc_params.wdp_capabilities = 0;
- } else {
- strncpy(wd->sc_dk.dk_label->d_typename, "ESDI/IDE",
- sizeof wd->sc_dk.dk_label->d_typename);
- wd->sc_dk.dk_label->d_type = DTYPE_ESDI;
-
- /* Read in parameter block. */
- insw(wdc->sc_iobase+wd_data, tb, sizeof(tb) / sizeof(short));
- bcopy(tb, &wd->sc_params, sizeof(struct wdparams));
-
- /* Shuffle string byte order. */
- for (i = 0; i < sizeof(wd->sc_params.wdp_model); i += 2) {
- u_short *p;
- p = (u_short *)(wd->sc_params.wdp_model + i);
- *p = ntohs(*p);
- }
- }
-
- /* Clear any leftover interrupt. */
- (void) inb(wdc->sc_iobase+wd_status);
-
- /* Restart the queue. */
- wdcstart(wdc);
-
- splx(s);
- return 0;
-}
-
-int
-wdioctl(dev, cmd, addr, flag, p)
+wdioctl(dev, xfer, addr, flag, p)
dev_t dev;
- u_long cmd;
+ u_long xfer;
caddr_t addr;
int flag;
struct proc *p;
{
struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)];
+ struct wd_link *d_link = wd->d_link;
int error;
-
- if ((wd->sc_flags & WDF_LOADED) == 0)
+
+ WDDEBUG_PRINT(("wdioctl\n"));
+
+ if ((d_link->sc_flags & WDF_LOADED) == 0)
return EIO;
- switch (cmd) {
+ switch (xfer) {
case DIOCSBAD:
if ((flag & FWRITE) == 0)
return EBADF;
@@ -1368,33 +631,33 @@ wdioctl(dev, cmd, addr, flag, p)
if ((flag & FWRITE) == 0)
return EBADF;
- if ((error = wdlock(wd)) != 0)
+ if ((error = wdlock(wd->d_link)) != 0)
return error;
- wd->sc_flags |= WDF_LABELLING;
+ d_link->sc_flags |= WDF_LABELLING;
error = setdisklabel(wd->sc_dk.dk_label,
(struct disklabel *)addr, /*wd->sc_dk.dk_openmask : */0,
wd->sc_dk.dk_cpulabel);
if (error == 0) {
- if (wd->sc_state > GEOMETRY)
- wd->sc_state = GEOMETRY;
- if (cmd == DIOCWDINFO)
+ if (d_link->sc_state > GEOMETRY)
+ d_link->sc_state = GEOMETRY;
+ if (xfer == DIOCWDINFO)
error = writedisklabel(WDLABELDEV(dev),
wdstrategy, wd->sc_dk.dk_label,
wd->sc_dk.dk_cpulabel);
}
- wd->sc_flags &= ~WDF_LABELLING;
- wdunlock(wd);
+ d_link->sc_flags &= ~WDF_LABELLING;
+ wdunlock(d_link);
return error;
case DIOCWLABEL:
if ((flag & FWRITE) == 0)
return EBADF;
if (*(int *)addr)
- wd->sc_flags |= WDF_WLABEL;
+ d_link->sc_flags |= WDF_WLABEL;
else
- wd->sc_flags &= ~WDF_WLABEL;
+ d_link->sc_flags &= ~WDF_WLABEL;
return 0;
#ifdef notyet
@@ -1451,7 +714,9 @@ wdsize(dev)
struct wd_softc *wd;
int part;
int size;
-
+
+ WDDEBUG_PRINT(("wdsize\n"));
+
if (wdopen(dev, 0, S_IFBLK, NULL) != 0)
return -1;
wd = wd_cd.cd_devs[WDUNIT(dev)];
@@ -1473,6 +738,9 @@ static int wddumprecalibrated;
/*
* Dump core after a system crash.
+ *
+ * XXX: This needs work! Currently, it's a major hack: the
+ * use of wdc_softc is very bad and should go away.
*/
int
wddump(dev, blkno, va, size)
@@ -1484,6 +752,7 @@ wddump(dev, blkno, va, size)
struct wd_softc *wd; /* disk unit to do the I/O */
struct wdc_softc *wdc; /* disk controller to do the I/O */
struct disklabel *lp; /* disk's disklabel */
+ struct wd_link *d_link;
int unit, part;
int nblks; /* total number of sectors left to write */
@@ -1496,13 +765,14 @@ wddump(dev, blkno, va, size)
if (unit >= wd_cd.cd_ndevs)
return ENXIO;
wd = wd_cd.cd_devs[unit];
- if (wd == 0)
+ if (wd == (struct wd_softc *)0)
return ENXIO;
+ d_link = wd->d_link;
part = WDPART(dev);
/* Make sure it was initialized. */
- if (wd->sc_state < OPEN)
+ if (d_link->sc_state < OPEN)
return ENXIO;
wdc = (void *)wd->sc_dev.dv_parent;
@@ -1524,10 +794,10 @@ wddump(dev, blkno, va, size)
/* Recalibrate, if first dump transfer. */
if (wddumprecalibrated == 0) {
wddumprecalibrated = 1;
- if (wdcommandshort(wdc, wd->sc_drive, WDCC_RECAL) != 0 ||
- wait_for_ready(wdc) != 0 || wdsetctlr(wd) != 0 ||
+ if (wdccommandshort(wdc, d_link->sc_drive, WDCC_RECAL) != 0 ||
+ wait_for_ready(wdc) != 0 || wdsetctlr(d_link) != 0 ||
wait_for_ready(wdc) != 0) {
- wderror(wd, NULL, "wddump: recal failed");
+ wderror(d_link, NULL, "wddump: recal failed");
return EIO;
}
}
@@ -1540,7 +810,7 @@ wddump(dev, blkno, va, size)
long blkdiff;
int i;
- for (i = 0; (blkdiff = wd->sc_badsect[i]) != -1; i++) {
+ for (i = 0; (blkdiff = d_link->sc_badsect[i]) != -1; i++) {
blkdiff -= xlt_blkno;
if (blkdiff < 0)
continue;
@@ -1554,7 +824,7 @@ wddump(dev, blkno, va, size)
/* Tranfer is okay now. */
}
- if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) {
+ if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) {
sector = (xlt_blkno >> 0) & 0xff;
cylin = (xlt_blkno >> 8) & 0xffff;
head = (xlt_blkno >> 24) & 0xf;
@@ -1570,17 +840,19 @@ wddump(dev, blkno, va, size)
}
#ifndef WD_DUMP_NOT_TRUSTED
- if (wdcommand(wd, WDCC_WRITE, cylin, head, sector, 1) != 0 ||
+ if (wdccommand(d_link, WDCC_WRITE, d_link->sc_drive, cylin, head, sector, 1) != 0 ||
wait_for_drq(wdc) != 0) {
- wderror(wd, NULL, "wddump: write failed");
+ wderror(d_link, NULL, "wddump: write failed");
return EIO;
}
- outsw(wdc->sc_iobase+wd_data, va, lp->d_secsize >> 1);
+ /* XXX XXX XXX */
+ outsw(wdc->sc_iobase + wd_data, va, lp->d_secsize >> 1);
/* Check data request (should be done). */
if (wait_for_ready(wdc) != 0) {
- wderror(wd, NULL, "wddump: timeout waiting for ready");
+ wderror(d_link, NULL,
+ "wddump: timeout waiting for ready");
return EIO;
}
#else /* WD_DUMP_NOT_TRUSTED */
@@ -1600,6 +872,8 @@ wddump(dev, blkno, va, size)
return 0;
}
#else /* __BDEVSW_DUMP_NEW_TYPE */
+
+
int
wddump(dev, blkno, va, size)
dev_t dev;
@@ -1622,157 +896,45 @@ bad144intern(wd)
{
struct dkbad *bt = &wd->sc_dk.dk_cpulabel->bad;
struct disklabel *lp = wd->sc_dk.dk_label;
+ struct wd_link *d_link = wd->d_link;
int i = 0;
+ WDDEBUG_PRINT(("bad144intern\n"));
+
for (; i < 126; i++) {
if (bt->bt_bad[i].bt_cyl == 0xffff)
break;
- wd->sc_badsect[i] =
+ d_link->sc_badsect[i] =
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);
}
for (; i < 127; i++)
- wd->sc_badsect[i] = -1;
-}
-
-int
-wdcreset(wdc)
- struct wdc_softc *wdc;
-{
- int iobase = wdc->sc_iobase;
-
- /* Reset the device. */
- outb(iobase+wd_ctlr, WDCTL_RST | WDCTL_IDS);
- delay(1000);
- outb(iobase+wd_ctlr, WDCTL_IDS);
- delay(1000);
- (void) inb(iobase+wd_error);
- outb(iobase+wd_ctlr, WDCTL_4BIT);
-
- if (wait_for_unbusy(wdc) < 0) {
- printf("%s: reset failed\n", wdc->sc_dev.dv_xname);
- return 1;
- }
-
- return 0;
-}
-
-void
-wdcrestart(arg)
- void *arg;
-{
- struct wdc_softc *wdc = arg;
- int s;
-
- s = splbio();
- wdcstart(wdc);
- splx(s);
-}
-
-/*
- * Unwedge the controller after an unexpected error. We do this by resetting
- * it, marking all drives for recalibration, and stalling the queue for a short
- * period to give the reset time to finish.
- * NOTE: We use a timeout here, so this routine must not be called during
- * autoconfig or dump.
- */
-void
-wdcunwedge(wdc)
- struct wdc_softc *wdc;
-{
- int unit;
-
- untimeout(wdctimeout, wdc);
- (void) wdcreset(wdc);
-
- /* Schedule recalibrate for all drives on this controller. */
- for (unit = 0; unit < wd_cd.cd_ndevs; unit++) {
- struct wd_softc *wd = wd_cd.cd_devs[unit];
- if (!wd || (void *)wd->sc_dev.dv_parent != wdc)
- continue;
- if (wd->sc_state > RECAL)
- wd->sc_state = RECAL;
- }
-
- wdc->sc_flags |= WDCF_ERROR;
- ++wdc->sc_errors;
-
- /* Wake up in a little bit and restart the operation. */
- timeout(wdcrestart, wdc, RECOVERYTIME);
-}
-
-int
-wdcwait(wdc, mask)
- struct wdc_softc *wdc;
- int mask;
-{
- int iobase = wdc->sc_iobase;
- int timeout = 0;
- u_char status;
-#ifdef WDCNDELAY_DEBUG
- extern int cold;
-#endif
-
- for (;;) {
- wdc->sc_status = status = inb(iobase+wd_status);
- if ((status & WDCS_BSY) == 0 && (status & mask) == mask)
- break;
- if (++timeout > WDCNDELAY)
- return -1;
- delay(WDCDELAY);
- }
- if (status & WDCS_ERR) {
- wdc->sc_error = inb(iobase+wd_error);
- return WDCS_ERR;
- }
-#ifdef WDCNDELAY_DEBUG
- /* After autoconfig, there should be no long delays. */
- if (!cold && timeout > WDCNDELAY_DEBUG)
- printf("%s: warning: busy-wait took %dus\n",
- wdc->sc_dev.dv_xname, WDCDELAY * timeout);
-#endif
- return 0;
+ d_link->sc_badsect[i] = -1;
}
void
-wdctimeout(arg)
- void *arg;
+wderror(d_link, bp, msg)
+ struct wd_link *d_link;
+ struct buf *bp;
+ char *msg;
{
- struct wdc_softc *wdc = (struct wdc_softc *)arg;
- int s;
+ struct wd_softc *wd = (struct wd_softc *)d_link->wd_softc;
- s = splbio();
- if ((wdc->sc_flags & WDCF_ACTIVE) != 0) {
- struct wd_softc *wd = wdc->sc_drives.tqh_first;
- struct buf *bp = wd->sc_q.b_actf;
-
- wdc->sc_flags &= ~WDCF_ACTIVE;
- wderror(wdc, NULL, "lost interrupt");
- printf("%s: lost interrupt: %sing %d@%s:%d\n",
- wdc->sc_dev.dv_xname,
- (bp->b_flags & B_READ) ? "read" : "writ",
- wd->sc_nblks, wd->sc_dev.dv_xname, wd->sc_blkno);
- wdcunwedge(wdc);
+ if (bp) {
+ diskerr(bp, "wd", msg, LOG_PRINTF, bp->b_bcount,
+ wd->sc_dk.dk_label);
+ printf("\n");
} else
- wderror(wdc, NULL, "missing untimeout");
- splx(s);
+ printf("%s: %s\n", wd->sc_dev.dv_xname, msg);
}
void
-wderror(dev, bp, msg)
- void *dev;
+wddone(d_link, bp)
+ struct wd_link *d_link;
struct buf *bp;
- char *msg;
{
- struct wd_softc *wd = dev;
- struct wdc_softc *wdc = dev;
+ struct wd_softc *wd = (void *)d_link->wd_softc;
- if (bp) {
- diskerr(bp, "wd", msg, LOG_PRINTF, wd->sc_skip / DEV_BSIZE,
- wd->sc_dk.dk_label);
- printf("\n");
- } else
- printf("%s: %s: status %b error %b\n", wdc->sc_dev.dv_xname,
- msg, wdc->sc_status, WDCS_BITS, wdc->sc_error, WDERR_BITS);
+ disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid));
}
diff --git a/sys/dev/isa/wdc.c b/sys/dev/isa/wdc.c
new file mode 100644
index 00000000000..c0e79d93e16
--- /dev/null
+++ b/sys/dev/isa/wdc.c
@@ -0,0 +1,1686 @@
+/* $NetBSD: wd.c,v 1.150 1996/05/12 23:54:03 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
+ *
+ * DMA and multi-sector PIO handling are derived from code contributed by
+ * Onno van der Linden.
+ *
+ * Atapi support added by Manuel Bouyer.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Charles M. Hannum.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#undef ATAPI_DEBUG_WDC
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/disk.h>
+#include <sys/syslog.h>
+#include <sys/proc.h>
+
+#include <vm/vm.h>
+
+#include <machine/cpu.h>
+#include <machine/intr.h>
+#include <machine/pio.h>
+
+#include <dev/isa/isavar.h>
+#include <dev/isa/isadmavar.h>
+#include <dev/isa/wdreg.h>
+#include <dev/isa/wdlink.h>
+
+#include <dev/atapi/atapilink.h>
+
+#define WAITTIME (10 * hz) /* time to wait for a completion */
+ /* this is a lot for hard drives, but not for cdroms */
+#define RECOVERYTIME hz/2
+#define WDCDELAY 100
+#define WDCNDELAY 100000 /* delay = 100us; so 10s for a controller state change */
+#if 0
+/* If you enable this, it will report any delays more than 100us * N long. */
+#define WDCNDELAY_DEBUG 50
+#endif
+
+#define WDIORETRIES 3 /* number of retries before giving up */
+
+#define WDPART(dev) DISKPART(dev)
+
+LIST_HEAD(xfer_free_list, wdc_xfer) xfer_free_list;
+
+int wdcprobe __P((struct device *, void *, void *));
+void wdcattach __P((struct device *, struct device *, void *));
+int wdcintr __P((void *));
+
+struct cfattach wdc_ca = {
+ sizeof(struct wdc_softc), wdcprobe, wdcattach
+};
+
+struct cfdriver wdc_cd = {
+ NULL, "wdc", DV_DULL
+};
+
+int wdc_ata_intr __P((struct wdc_softc *,struct wdc_xfer *));
+void wdcstart __P((struct wdc_softc *));
+void wdc_ata_start __P((struct wdc_softc *,struct wdc_xfer *));
+void wdc_atapi_start __P((struct wdc_softc *,struct wdc_xfer *));
+int wdcreset __P((struct wdc_softc *));
+void wdcrestart __P((void *arg));
+void wdcunwedge __P((struct wdc_softc *));
+void wdctimeout __P((void *arg));
+int wdccontrol __P((struct wd_link *));
+void wdc_ata_done __P((struct wdc_softc *, struct wdc_xfer *));
+void wdc_free_xfer __P((struct wdc_xfer *));
+void wdcerror __P((struct wdc_softc*, char *));
+void wdcbit_bucket __P(( struct wdc_softc *, int));
+int wdc_atapi_intr __P((struct wdc_softc *, struct wdc_xfer *));
+void wdc_atapi_done __P((struct wdc_softc *, struct wdc_xfer *));
+
+#ifdef ATAPI_DEBUG
+static int wdc_nxfer;
+#endif
+
+#ifdef WDDEBUG
+#define WDDEBUG_PRINT(args) printf args
+#else
+#define WDDEBUG_PRINT(args)
+#endif
+
+int
+wdcprobe(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct wdc_softc *wdc = match;
+ struct isa_attach_args *ia = aux;
+ int iobase;
+
+ wdc->sc_iobase = iobase = ia->ia_iobase;
+
+ /* Check if we have registers that work. */
+ outb(iobase+wd_error, 0x5a); /* Error register not writable, */
+ outb(iobase+wd_cyl_lo, 0xa5); /* but all of cyllo are. */
+ if (inb(iobase+wd_error) == 0x5a || inb(iobase+wd_cyl_lo) != 0xa5) {
+ /*
+ * Test for a controller with no IDE master, just one
+ * ATAPI device. Select drive 1, and try again.
+ */
+ outb(iobase+wd_sdh, WDSD_IBM | 0x10);
+ outb(iobase+wd_error, 0x5a);
+ outb(iobase+wd_cyl_lo, 0xa5);
+ if (inb(iobase+wd_error) == 0x5a || inb(iobase+wd_cyl_lo)
+ != 0xa5)
+ return 0;
+ wdc->sc_flags |= WDCF_ONESLAVE;
+ }
+
+ if (wdcreset(wdc) != 0) {
+ delay(500000);
+ if (wdcreset(wdc) != 0)
+ return 0;
+ }
+
+ /* Select drive 0 or ATAPI slave device */
+ if (wdc->sc_flags & WDCF_ONESLAVE)
+ outb(iobase+wd_sdh, WDSD_IBM | 0x10);
+ else
+ outb(iobase+wd_sdh, WDSD_IBM);
+
+ /* Wait for controller to become ready. */
+ if (wait_for_unbusy(wdc) < 0)
+ return 0;
+
+ /* Start drive diagnostics. */
+ outb(iobase+wd_command, WDCC_DIAGNOSE);
+
+ /* Wait for command to complete. */
+ if (wait_for_unbusy(wdc) < 0)
+ return 0;
+
+ ia->ia_iosize = 8;
+ ia->ia_msize = 0;
+ return 1;
+}
+
+int
+wdprint(aux, wdc)
+ void *aux;
+ char *wdc;
+{
+ struct wd_link *d_link = aux;
+
+ if (!wdc)
+ printf(" drive %d", d_link->sc_drive);
+ return QUIET;
+}
+
+void
+wdcattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct wdc_softc *wdc = (void *)self;
+ struct isa_attach_args *ia = aux;
+ int drive;
+
+ TAILQ_INIT(&wdc->sc_xfer);
+ wdc->sc_drq = ia->ia_drq;
+
+ printf("\n");
+
+ wdc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE,
+ IPL_BIO, wdcintr, wdc, wdc->sc_dev.dv_xname);
+
+ wdc->ctlr_link.flags = 0;
+#ifdef ATAPI_DEBUG
+ wdc_nxfer = 0;
+#endif
+
+ /*
+ * Attach standard IDE/ESDI/etc. disks to the controller.
+ */
+ for (drive = 0; drive < 2; drive++) {
+ /* controller active while autoconf */
+ wdc->sc_flags |= WDCF_ACTIVE;
+
+ if (wdccommandshort(wdc, drive, WDCC_RECAL) != 0 ||
+ wait_for_ready(wdc) != 0) {
+ wdc->d_link[drive] = NULL;
+ wdc->sc_flags &= ~WDCF_ACTIVE;
+ } else {
+ wdc->sc_flags &= ~WDCF_ACTIVE;
+ wdc->d_link[drive] = malloc(sizeof(struct wd_link),
+ M_DEVBUF, M_NOWAIT);
+ if (wdc->d_link[drive] == NULL) {
+ printf("%s: can't allocate link for drive %d\n",
+ self->dv_xname, drive);
+ continue;
+ }
+ bzero(wdc->d_link[drive],sizeof(struct wd_link));
+ wdc->d_link[drive]->type = DRIVE;
+ wdc->d_link[drive]->wdc_softc =(caddr_t) wdc;
+ wdc->d_link[drive]->ctlr_link = &(wdc->ctlr_link);
+ wdc->d_link[drive]->sc_drive = drive;
+ if (wdc->sc_drq != DRQUNK)
+ wdc->d_link[drive]->sc_mode = WDM_DMA;
+ else
+ wdc->d_link[drive]->sc_mode = 0;
+
+ (void)config_found(self, (void *)wdc->d_link[drive],
+ wdprint);
+ }
+ }
+
+ /*
+ * Attach an ATAPI bus, if configured.
+ */
+ wdc->ab_link = malloc(sizeof(struct bus_link), M_DEVBUF, M_NOWAIT);
+ if (wdc->ab_link == NULL) {
+ printf("%s: can't allocate ATAPI link\n", self->dv_xname);
+ return;
+ }
+ bzero(wdc->ab_link,sizeof(struct bus_link));
+ wdc->ab_link->type = BUS;
+ wdc->ab_link->wdc_softc = (caddr_t)wdc;
+ wdc->ab_link->ctlr_link = &(wdc->ctlr_link);
+ wdc->ab_link->ctrl = self->dv_unit;
+ (void)config_found(self, (void *)wdc->ab_link, NULL);
+}
+
+/*
+ * Start I/O on a controller. This does the calculation, and starts a read or
+ * write operation. Called to from wdstart() to start a transfer, from
+ * wdcintr() to continue a multi-sector transfer or start the next transfer, or
+ * wdcrestart() after recovering from an error.
+ */
+void
+wdcstart(wdc)
+ struct wdc_softc *wdc;
+{
+ struct wdc_xfer *xfer;
+
+ if ((wdc->sc_flags & WDCF_ACTIVE) != 0 ) {
+ WDDEBUG_PRINT(("wdcstart: already active\n"));
+ return; /* controller aleady active */
+ }
+#ifdef DIAGNOSTIC
+ if ((wdc->sc_flags & WDCF_IRQ_WAIT) != 0)
+ panic("wdcstart: controller waiting for irq\n");
+#endif
+ /*
+ * XXX
+ * This is a kluge. See comments in wd_get_parms().
+ */
+ if ((wdc->sc_flags & WDCF_WANTED) != 0) {
+#ifdef ATAPI_DEBUG_WDC
+ printf("WDCF_WANTED\n");
+#endif
+ wdc->sc_flags &= ~WDCF_WANTED;
+ wakeup(wdc);
+ return;
+ }
+ /* is there a xfer ? */
+ xfer = wdc->sc_xfer.tqh_first;
+ if (xfer == NULL) {
+#ifdef ATAPI_DEBUG2
+ printf("wdcstart: null xfer\n");
+#endif
+ return;
+ }
+ wdc->sc_flags |= WDCF_ACTIVE;
+ if (xfer->c_flags & C_ATAPI) {
+#ifdef ATAPI_DEBUG_WDC
+ printf("wdcstart: atapi\n");
+#endif
+ wdc_atapi_start(wdc,xfer);
+ } else {
+ wdc_ata_start(wdc,xfer);
+ }
+}
+
+
+void
+wdc_ata_start(wdc, xfer)
+ struct wdc_softc *wdc;
+ struct wdc_xfer *xfer;
+{
+ struct wd_link *d_link;
+ struct buf *bp = xfer->c_bp;
+ int nblks;
+
+ d_link=xfer->d_link;
+
+loop:
+
+ if (wdc->sc_errors >= WDIORETRIES) {
+ wderror(d_link, bp, "hard error");
+ xfer->c_flags |= C_ERROR;
+ wdc_ata_done(wdc, xfer);
+ return;
+ }
+
+ /* Do control operations specially. */
+ if (d_link->sc_state < OPEN) {
+ /*
+ * Actually, we want to be careful not to mess with the control
+ * state if the device is currently busy, but we can assume
+ * that we never get to this point if that's the case.
+ */
+ if (wdccontrol(d_link) == 0) {
+ /* The drive is busy. Wait. */
+ return;
+ }
+ }
+
+ /*
+ * WDCF_ERROR is set by wdcunwedge() and wdcintr() when an error is
+ * encountered. If we are in multi-sector mode, then we switch to
+ * single-sector mode and retry the operation from the start.
+ */
+ if (wdc->sc_flags & WDCF_ERROR) {
+ wdc->sc_flags &= ~WDCF_ERROR;
+ if ((wdc->sc_flags & WDCF_SINGLE) == 0) {
+ wdc->sc_flags |= WDCF_SINGLE;
+ xfer->c_skip = 0;
+ }
+ }
+
+
+ /* When starting a transfer... */
+ if (xfer->c_skip == 0) {
+ daddr_t blkno;
+
+ WDDEBUG_PRINT(("\n%s: wdc_ata_start %s %d@%d; map ",
+ wdc->sc_dev.dv_xname,
+ (xfer->c_flags & B_READ) ? "read" : "write",
+ xfer->c_bcount, xfer->c_blkno));
+
+ blkno = xfer->c_blkno+xfer->c_p_offset;
+ xfer->c_blkno = blkno / (d_link->sc_lp->d_secsize / DEV_BSIZE);
+ } else {
+ WDDEBUG_PRINT((" %d)%x", xfer->c_skip,
+ inb(wdc->sc_iobase + wd_altsts)));
+ }
+
+ /*
+ * When starting a multi-sector transfer, or doing single-sector
+ * transfers...
+ */
+ if (xfer->c_skip == 0 || (wdc->sc_flags & WDCF_SINGLE) != 0 ||
+ d_link->sc_mode == WDM_DMA) {
+ daddr_t blkno = xfer->c_blkno;
+ long cylin, head, sector;
+ int command;
+
+ if ((wdc->sc_flags & WDCF_SINGLE) != 0)
+ nblks = 1;
+ else if (d_link->sc_mode != WDM_DMA)
+ nblks = xfer->c_bcount / d_link->sc_lp->d_secsize;
+ else
+ nblks =
+ min(xfer->c_bcount / d_link->sc_lp->d_secsize, 8);
+
+ /* Check for bad sectors and adjust transfer, if necessary. */
+ if ((d_link->sc_lp->d_flags & D_BADSECT) != 0
+#ifdef B_FORMAT
+ && (bp->b_flags & B_FORMAT) == 0
+#endif
+ ) {
+ long blkdiff;
+ int i;
+
+ for (i = 0;
+ (blkdiff = d_link->sc_badsect[i]) != -1; i++) {
+ blkdiff -= blkno;
+ if (blkdiff < 0)
+ continue;
+ if (blkdiff == 0) {
+ /* Replace current block of transfer. */
+ blkno =
+ d_link->sc_lp->d_secperunit -
+ d_link->sc_lp->d_nsectors - i - 1;
+ }
+ if (blkdiff < nblks) {
+ /* Bad block inside transfer. */
+ wdc->sc_flags |= WDCF_SINGLE;
+ nblks = 1;
+ }
+ break;
+ }
+ /* Tranfer is okay now. */
+ }
+
+ if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) {
+ sector = (blkno >> 0) & 0xff;
+ cylin = (blkno >> 8) & 0xffff;
+ head = (blkno >> 24) & 0xf;
+ head |= WDSD_LBA;
+ } else {
+ sector = blkno % d_link->sc_lp->d_nsectors;
+ sector++; /* Sectors begin with 1, not 0. */
+ blkno /= d_link->sc_lp->d_nsectors;
+ head = blkno % d_link->sc_lp->d_ntracks;
+ blkno /= d_link->sc_lp->d_ntracks;
+ cylin = blkno;
+ head |= WDSD_CHS;
+ }
+
+ if (d_link->sc_mode == WDM_PIOSINGLE ||
+ (wdc->sc_flags & WDCF_SINGLE) != 0)
+ xfer->c_nblks = 1;
+ else if (d_link->sc_mode == WDM_PIOMULTI)
+ xfer->c_nblks = min(nblks, d_link->sc_multiple);
+ else
+ xfer->c_nblks = nblks;
+ xfer->c_nbytes = xfer->c_nblks * d_link->sc_lp->d_secsize;
+
+#ifdef B_FORMAT
+ if (bp->b_flags & B_FORMAT) {
+ sector = d_link->sc_lp->d_gap3;
+ nblks = d_link->sc_lp->d_nsectors;
+ command = WDCC_FORMAT;
+ } else
+#endif
+ switch (d_link->sc_mode) {
+ case WDM_DMA:
+ command = (xfer->c_flags & B_READ) ?
+ WDCC_READDMA : WDCC_WRITEDMA;
+ /*
+ * Start the DMA channel and bounce the buffer if
+ * necessary.
+ */
+ isa_dmastart(xfer->c_flags & B_READ ?
+ DMAMODE_READ : DMAMODE_WRITE,
+ xfer->databuf + xfer->c_skip,
+ xfer->c_nbytes, wdc->sc_drq);
+ break;
+
+ case WDM_PIOMULTI:
+ command = (xfer->c_flags & B_READ) ?
+ WDCC_READMULTI : WDCC_WRITEMULTI;
+ break;
+
+ case WDM_PIOSINGLE:
+ command = (xfer->c_flags & B_READ) ?
+ WDCC_READ : WDCC_WRITE;
+ break;
+
+ default:
+#ifdef DIAGNOSTIC
+ panic("bad wd mode");
+#endif
+ return;
+ }
+
+ /* Initiate command! */
+ if (wdccommand(d_link, command, d_link->sc_drive,
+ cylin, head, sector, nblks) != 0) {
+ wderror(d_link, NULL,
+ "wdc_ata_start: timeout waiting for unbusy");
+ wdcunwedge(wdc);
+ return;
+ }
+
+ WDDEBUG_PRINT(("sector %d cylin %d head %d addr %x sts %x\n",
+ sector, cylin, head, xfer->databuf,
+ inb(wdc->sc_iobase + wd_altsts)));
+
+ } else if (xfer->c_nblks > 1) {
+ /* The number of blocks in the last stretch may be smaller. */
+ nblks = xfer->c_bcount / d_link->sc_lp->d_secsize;
+ if (xfer->c_nblks > nblks) {
+ xfer->c_nblks = nblks;
+ xfer->c_nbytes = xfer->c_bcount;
+ }
+ }
+
+ /* If this was a write and not using DMA, push the data. */
+ if (d_link->sc_mode != WDM_DMA &&
+ (xfer->c_flags & (B_READ|B_WRITE)) == B_WRITE) {
+ if (wait_for_drq(wdc) < 0) {
+ wderror(d_link, NULL,
+ "wdc_ata_start: timeout waiting for drq");
+ wdcunwedge(wdc);
+ return;
+ }
+
+ /* Push out data. */
+ if ((d_link->sc_flags & WDF_32BIT) == 0)
+ outsw(wdc->sc_iobase + wd_data,
+ xfer->databuf + xfer->c_skip,
+ xfer->c_nbytes >> 1);
+ else
+ outsl(wdc->sc_iobase + wd_data,
+ xfer->databuf + xfer->c_skip,
+ xfer->c_nbytes >> 2);
+ }
+
+ wdc->sc_flags |= WDCF_IRQ_WAIT;
+ WDDEBUG_PRINT(("wdc_ata_start: timeout "));
+ timeout(wdctimeout, wdc, WAITTIME);
+ WDDEBUG_PRINT(("done\n"));
+}
+
+void
+wdc_atapi_start(wdc, xfer)
+ struct wdc_softc *wdc;
+ struct wdc_xfer *xfer;
+{
+ struct atapi_command_packet *acp = xfer->atapi_cmd;
+
+#ifdef ATAPI_DEBUG_WDC
+ printf("wdc_atapi_start, acp flags %lx\n",acp->flags);
+#endif
+ if (wdc->sc_errors >= WDIORETRIES) {
+ acp->status |= ERROR;
+ acp->error = inb (wdc->sc_iobase + wd_error);
+ wdc_atapi_done(wdc, xfer);
+ return;
+ }
+ if (wait_for_unbusy(wdc) != 0) {
+ if ((wdc->sc_status & WDCS_ERR) == 0) {
+ printf("wdc_atapi_start: not ready, st = %02x\n",
+ wdc->sc_status);
+ acp->status = ERROR;
+ return;
+ }
+ }
+
+ if (wdccommand((struct wd_link*)xfer->d_link, ATAPI_PACKET_COMMAND,
+ acp->drive, acp->data_size, 0, 0, 0) != 0) {
+ printf("wdc_atapi_start: can't send atapi paket command\n");
+ acp->status = ERROR;
+ wdc->sc_flags |= WDCF_IRQ_WAIT;
+ return;
+ }
+ if ((acp->flags & 0x0300) != ACAP_DRQ_INTR) {
+ int i, phase;
+ for (i=20000; i>0; --i) {
+ phase = (inb(wdc->sc_iobase + wd_ireason) &
+ (WDCI_CMD | WDCI_IN)) |
+ (inb(wdc->sc_iobase + wd_status) & WDCS_DRQ);
+ if (phase == PHASE_CMDOUT)
+ break;
+ delay(10);
+ }
+ if (phase != PHASE_CMDOUT ) {
+ printf("wdc_atapi_start: timout waiting PHASE_CMDOUT");
+ acp->status = ERROR;
+ wdc_atapi_done(wdc, xfer);
+ return;
+ }
+ outsw(wdc->sc_iobase + wd_data, acp->command,
+ acp->command_size / sizeof(short));
+ }
+ wdc->sc_flags |= WDCF_IRQ_WAIT;
+
+#ifdef ATAPI_DEBUG2
+ printf("wdc_atapi_start: timeout\n");
+#endif
+ timeout(wdctimeout, wdc, WAITTIME);
+ return;
+}
+
+
+/*
+ * Interrupt routine for the controller. Acknowledge the interrupt, check for
+ * errors on the current operation, mark it done if necessary, and start the
+ * next request. Also check for a partially done transfer, and continue with
+ * the next chunk if so.
+ */
+int
+wdcintr(arg)
+ void *arg;
+{
+ struct wdc_softc *wdc = arg;
+ struct wdc_xfer *xfer;
+
+ if ((wdc->sc_flags & WDCF_IRQ_WAIT) == 0) {
+ /* Clear the pending interrupt and abort. */
+ u_char s = inb(wdc->sc_iobase+wd_status);
+ u_char e = inb(wdc->sc_iobase+wd_error);
+ u_char i = inb(wdc->sc_iobase+wd_seccnt);
+
+#ifdef ATAPI_DEBUG_WDC
+ printf("wdcintr: inactive controller, "
+ "punting st=%02x er=%02x irr=%02x\n", s, e, i);
+#endif
+
+ if (s & WDCS_DRQ) {
+ int len = inb (wdc->sc_iobase + wd_cyl_lo) +
+ 256 * inb (wdc->sc_iobase + wd_cyl_hi);
+#ifdef ATAPI_DEBUG_WDC
+ printf ("wdcintr: clearing up %d bytes\n", len);
+#endif
+ wdcbit_bucket (wdc, len);
+ }
+ return 0;
+ }
+
+ WDDEBUG_PRINT(("wdcintr\n"));
+
+ wdc->sc_flags &= ~WDCF_IRQ_WAIT;
+ xfer = wdc->sc_xfer.tqh_first;
+ if (xfer->c_flags & C_ATAPI) {
+ (void) wdc_atapi_intr(wdc,xfer);
+ return 0;
+ } else {
+ return wdc_ata_intr(wdc,xfer);
+ }
+}
+
+
+int
+wdc_ata_intr(wdc,xfer)
+ struct wdc_softc *wdc;
+ struct wdc_xfer *xfer;
+{
+ struct wd_link *d_link;
+
+ d_link = xfer->d_link;
+
+ if (wait_for_unbusy(wdc) < 0) {
+ wdcerror(wdc, "wdcintr: timeout waiting for unbusy");
+ wdc->sc_status |= WDCS_ERR; /* XXX */
+ }
+
+ untimeout(wdctimeout, wdc);
+
+ /* Is it not a transfer, but a control operation? */
+ if (d_link->sc_state < OPEN) {
+ if (wdccontrol(d_link) == 0) {
+ /* The drive is busy. Wait. */
+ return 1;
+ }
+ WDDEBUG_PRINT(("wdc_ata_start from wdc_ata_intr(open) flags %d\n",
+ dc->sc_flags));
+ wdc_ata_start(wdc,xfer);
+ return 1;
+ }
+
+ /* Turn off the DMA channel and unbounce the buffer. */
+ if (d_link->sc_mode == WDM_DMA)
+ isa_dmadone(xfer->c_flags & B_READ ?
+ DMAMODE_READ : DMAMODE_WRITE,
+ xfer->databuf + xfer->c_skip, xfer->c_nbytes,
+ wdc->sc_drq);
+
+ /* Have we an error? */
+ if (wdc->sc_status & WDCS_ERR) {
+#ifdef WDDEBUG
+ wderror(d_link, NULL, "wdc_ata_start");
+#endif
+ if ((wdc->sc_flags & WDCF_SINGLE) == 0) {
+ wdc->sc_flags |= WDCF_ERROR;
+ goto restart;
+ }
+
+#ifdef B_FORMAT
+ if (bp->b_flags & B_FORMAT)
+ goto bad;
+#endif
+
+ wdcunwedge(wdc);
+ if (++wdc->sc_errors < WDIORETRIES)
+ return 1;
+
+ wderror(d_link, xfer->c_bp, "hard error");
+
+#ifdef B_FORMAT
+ bad:
+#endif
+ xfer->c_flags |= C_ERROR;
+ goto done;
+ }
+
+ /* If this was a read and not using DMA, fetch the data. */
+ if (d_link->sc_mode != WDM_DMA &&
+ (xfer->c_flags & (B_READ|B_WRITE)) == B_READ) {
+ if ((wdc->sc_status & (WDCS_DRDY | WDCS_DSC | WDCS_DRQ))
+ != (WDCS_DRDY | WDCS_DSC | WDCS_DRQ)) {
+ wderror(d_link, NULL, "wdcintr: read intr before drq");
+ wdcunwedge(wdc);
+ return 1;
+ }
+
+ /* Pull in data. */
+ if ((d_link->sc_flags & WDF_32BIT) == 0)
+ insw(wdc->sc_iobase+wd_data,
+ xfer->databuf + xfer->c_skip, xfer->c_nbytes >> 1);
+ else
+ insl(wdc->sc_iobase+wd_data,
+ xfer->databuf + xfer->c_skip, xfer->c_nbytes >> 2);
+ }
+
+ /* If we encountered any abnormalities, flag it as a soft error. */
+ if (wdc->sc_errors > 0 ||
+ (wdc->sc_status & WDCS_CORR) != 0) {
+ wderror(d_link, xfer->c_bp, "soft error (corrected)");
+ wdc->sc_errors = 0;
+ }
+
+ /* Adjust pointers for the next block, if any. */
+ xfer->c_blkno += xfer->c_nblks;
+ xfer->c_skip += xfer->c_nbytes;
+ xfer->c_bcount -= xfer->c_nbytes;
+
+ /* See if this transfer is complete. */
+ if (xfer->c_bcount > 0)
+ goto restart;
+
+done:
+ /* Done with this transfer, with or without error. */
+ wdc_ata_done(wdc, xfer);
+ return 0;
+
+restart:
+ /* Start the next operation */
+ WDDEBUG_PRINT(("wdc_ata_start from wdcintr flags %d\n",
+ wdc->sc_flags));
+ wdc_ata_start(wdc, xfer);
+
+ return 1;
+}
+
+int
+wdcreset(wdc)
+ struct wdc_softc *wdc;
+{
+ int iobase = wdc->sc_iobase;
+
+ /* Reset the device. */
+ outb(iobase+wd_ctlr, WDCTL_RST | WDCTL_IDS);
+ delay(1000);
+ outb(iobase+wd_ctlr, WDCTL_IDS);
+ delay(1000);
+ (void) inb(iobase+wd_error);
+ outb(iobase+wd_ctlr, WDCTL_4BIT);
+
+ if (wait_for_unbusy(wdc) < 0) {
+ printf("%s: reset failed\n", wdc->sc_dev.dv_xname);
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+wdcrestart(arg)
+ void *arg;
+{
+ struct wdc_softc *wdc = arg;
+ int s;
+
+ s = splbio();
+ wdcstart(wdc);
+ splx(s);
+}
+
+/*
+ * Unwedge the controller after an unexpected error. We do this by resetting
+ * it, marking all drives for recalibration, and stalling the queue for a short
+ * period to give the reset time to finish.
+ * NOTE: We use a timeout here, so this routine must not be called during
+ * autoconfig or dump.
+ */
+void
+wdcunwedge(wdc)
+ struct wdc_softc *wdc;
+{
+ int unit;
+
+#ifdef ATAPI_DEBUG
+ printf("wdcunwedge\n");
+#endif
+
+ untimeout(wdctimeout, wdc);
+ wdc->sc_flags &= ~WDCF_IRQ_WAIT;
+ (void) wdcreset(wdc);
+
+ /* Schedule recalibrate for all drives on this controller. */
+ for (unit = 0; unit < 2; unit++) {
+ if (!wdc->d_link[unit]) continue;
+ if (wdc->d_link[unit]->sc_state > RECAL)
+ wdc->d_link[unit]->sc_state = RECAL;
+ }
+
+ wdc->sc_flags |= WDCF_ERROR;
+ ++wdc->sc_errors;
+
+ /* Wake up in a little bit and restart the operation. */
+ WDDEBUG_PRINT(("wdcrestart from wdcunwedge\n"));
+ wdc->sc_flags &= ~WDCF_ACTIVE;
+ timeout(wdcrestart, wdc, RECOVERYTIME);
+}
+
+int
+wdcwait(wdc, mask)
+ struct wdc_softc *wdc;
+ int mask;
+{
+ int iobase = wdc->sc_iobase;
+ int timeout = 0;
+ u_char status;
+#ifdef WDCNDELAY_DEBUG
+ extern int cold;
+#endif
+
+ WDDEBUG_PRINT(("wdcwait\n"));
+
+ for (;;) {
+ wdc->sc_status = status = inb(iobase+wd_status);
+ /*
+ * XXX
+ * If a single slave ATAPI device is attached, it may
+ * have released the bus. Select it and try again.
+ */
+ if (status == 0xff && wdc->sc_flags & WDCF_ONESLAVE) {
+ outb(iobase+wd_sdh, WDSD_IBM | 0x10);
+ wdc->sc_status = status = inb(iobase+wd_status);
+ }
+ if ((status & WDCS_BSY) == 0 && (status & mask) == mask)
+ break;
+ if (++timeout > WDCNDELAY) {
+#ifdef ATAPI_DEBUG2
+ printf("wdcwait: timeout, status %x\n", status);
+#endif
+ return -1;
+ }
+ delay(WDCDELAY);
+ }
+ if (status & WDCS_ERR) {
+ wdc->sc_error = inb(iobase+wd_error);
+ return WDCS_ERR;
+ }
+#ifdef WDCNDELAY_DEBUG
+ /* After autoconfig, there should be no long delays. */
+ if (!cold && timeout > WDCNDELAY_DEBUG) {
+ struct wdc_xfer *xfer = wdc->sc_xfer.tqh_first;
+ if (xfer == NULL)
+ printf("%s: warning: busy-wait took %dus\n",
+ wdc->sc_dev.dv_xname, WDCDELAY * timeout);
+ else
+ printf("%s(%s): warning: busy-wait took %dus\n",
+ wdc->sc_dev.dv_xname,
+ ((struct device*)xfer->d_link->wd_softc)->dv_xname,
+ WDCDELAY * timeout);
+ }
+#endif
+ return 0;
+}
+
+void
+wdctimeout(arg)
+ void *arg;
+{
+ struct wdc_softc *wdc = (struct wdc_softc *)arg;
+ int s;
+
+ WDDEBUG_PRINT(("wdctimeout\n"));
+
+ s = splbio();
+ if ((wdc->sc_flags & WDCF_IRQ_WAIT) != 0) {
+ wdc->sc_flags &= ~WDCF_IRQ_WAIT;
+ wdcerror(wdc, "lost interrupt");
+ wdcunwedge(wdc);
+ } else
+ wdcerror(wdc, "missing untimeout");
+ splx(s);
+}
+
+/*
+ * Wait for the drive to become ready and send a command.
+ * Return -1 if busy for too long or 0 otherwise.
+ * Assumes interrupts are blocked.
+ */
+int
+wdccommand(d_link, command, drive, cylin, head, sector, count)
+ struct wd_link *d_link;
+ int command;
+ int drive, cylin, head, sector, count;
+{
+ struct wdc_softc *wdc = (void*)d_link->wdc_softc;
+ int iobase = wdc->sc_iobase;
+ int stat;
+
+ WDDEBUG_PRINT(("wdccommand drive %d\n", drive));
+
+#if defined(DIAGNOSTIC) && defined(WDCDEBUG)
+ if ((wdc->sc_flags & WDCF_ACTIVE) == 0)
+ printf("wdccommand: controler not active (drive %d)\n", drive);
+#endif
+
+ /* Select drive, head, and addressing mode. */
+ outb(iobase+wd_sdh, WDSD_IBM | (drive << 4) | head);
+
+ /* Wait for it to become ready to accept a command. */
+ if (command == WDCC_IDP || d_link->type == BUS)
+ stat = wait_for_unbusy(wdc);
+ else
+ stat = wdcwait(wdc, WDCS_DRDY);
+
+ if (stat < 0) {
+#ifdef ATAPI_DEBUG
+ printf("wdcommand: xfer failed (wait_for_unbusy) status %d\n",
+ stat);
+#endif
+ return -1;
+ }
+
+ /* Load parameters. */
+ if (d_link->type == DRIVE && d_link->sc_lp->d_type == DTYPE_ST506)
+ outb(iobase + wd_precomp, d_link->sc_lp->d_precompcyl / 4);
+ else
+ outb(iobase + wd_features, 0);
+ outb(iobase + wd_cyl_lo, cylin);
+ outb(iobase + wd_cyl_hi, cylin >> 8);
+ outb(iobase + wd_sector, sector);
+ outb(iobase + wd_seccnt, count);
+
+ /* Send command. */
+ outb(iobase + wd_command, command);
+
+ return 0;
+}
+
+/*
+ * Simplified version of wdccommand().
+ */
+int
+wdccommandshort(wdc, drive, command)
+ struct wdc_softc *wdc;
+ int drive;
+ int command;
+{
+ int iobase = wdc->sc_iobase;
+
+ WDDEBUG_PRINT(("wdccommandshort\n"));
+
+#if defined(DIAGNOSTIC) && defined(WDCDEBUG)
+ if ((wdc->sc_flags & WDCF_ACTIVE) == 0)
+ printf("wdccommandshort: controler not active (drive %d)\n",
+ drive);
+#endif
+
+ /* Select drive. */
+ outb(iobase + wd_sdh, WDSD_IBM | (drive << 4));
+
+ if (wdcwait(wdc, WDCS_DRDY) < 0)
+ return -1;
+
+ outb(iobase + wd_command, command);
+
+ return 0;
+}
+
+void
+wdc_ata_done(wdc, xfer)
+ struct wdc_softc *wdc;
+ struct wdc_xfer *xfer;
+{
+ struct buf *bp = xfer->c_bp;
+ struct wd_link *d_link = xfer->d_link;
+ int s;
+
+ WDDEBUG_PRINT(("wdc_ata_done\n"));
+
+ /* remove this command from xfer queue */
+ s = splbio();
+ TAILQ_REMOVE(&wdc->sc_xfer, xfer, c_xferchain);
+ wdc->sc_flags &= ~(WDCF_SINGLE | WDCF_ERROR | WDCF_ACTIVE);
+ wdc->sc_errors = 0;
+ if (bp) {
+ if (xfer->c_flags & C_ERROR) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ }
+ bp->b_resid = xfer->c_bcount;
+ wddone(d_link, bp);
+ biodone(bp);
+ } else {
+ wakeup(xfer->databuf);
+ }
+ xfer->c_skip = 0;
+ wdc_free_xfer(xfer);
+ d_link->openings++;
+ wdstart((void*)d_link->wd_softc);
+ WDDEBUG_PRINT(("wdcstart from wdc_ata_done, flags %d\n",
+ wdc->sc_flags));
+ wdcstart(wdc);
+ splx(s);
+}
+
+void
+wdc_exec_xfer(d_link, xfer)
+ struct wd_link *d_link;
+ struct wdc_xfer *xfer;
+{
+ struct wdc_softc *wdc=(struct wdc_softc *)d_link->wdc_softc;
+ int s;
+
+ WDDEBUG_PRINT(("wdc_exec_xfer\n"));
+
+ s = splbio();
+
+ /* insert at the end of command list */
+ TAILQ_INSERT_TAIL(&wdc->sc_xfer,xfer , c_xferchain)
+ WDDEBUG_PRINT(("wdcstart from wdc_exec_xfer, flags %d\n",
+ wdc->sc_flags));
+ wdcstart(wdc);
+ splx(s);
+}
+
+struct wdc_xfer *
+wdc_get_xfer(c_link,flags)
+ struct wdc_link *c_link;
+ int flags;
+{
+ struct wdc_xfer *xfer;
+ int s;
+
+ s = splbio();
+ if (xfer = xfer_free_list.lh_first) {
+ LIST_REMOVE(xfer, free_list);
+ splx(s);
+#ifdef DIAGNOSTIC
+ if ((xfer->c_flags & C_INUSE) != 0)
+ panic("wdc_get_xfer: xfer already in use\n");
+#endif
+ } else {
+ splx(s);
+#ifdef ATAPI_DEBUG
+ printf("wdc:making xfer %d\n",wdc_nxfer);
+#endif
+ xfer = malloc(sizeof(*xfer), M_DEVBUF,
+ ((flags & IDE_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK));
+ if (xfer == NULL)
+ return 0;
+
+#ifdef DIAGNOSTIC
+ xfer->c_flags &= ~C_INUSE;
+#endif
+#ifdef ATAPI_DEBUG
+ wdc_nxfer++;
+#endif
+ }
+#ifdef DIAGNOSTIC
+ if ((xfer->c_flags & C_INUSE) != 0)
+ panic("wdc_get_xfer: xfer already in use\n");
+#endif
+ bzero(xfer,sizeof(struct wdc_xfer));
+ xfer->c_flags = C_INUSE;
+ xfer->c_link = c_link;
+ return xfer;
+}
+
+void
+wdc_free_xfer(xfer)
+ struct wdc_xfer *xfer;
+{
+ int s;
+
+ s = splbio();
+ xfer->c_flags &= ~C_INUSE;
+ LIST_INSERT_HEAD(&xfer_free_list, xfer, free_list);
+ splx(s);
+}
+
+/*
+ * Implement operations needed before read/write.
+ * Returns 0 if operation still in progress, 1 if completed.
+ */
+int
+wdccontrol(d_link)
+ struct wd_link *d_link;
+{
+ struct wdc_softc *wdc = (void *)d_link->wdc_softc;
+
+ WDDEBUG_PRINT(("wdccontrol\n"));
+
+ switch (d_link->sc_state) {
+ case RECAL: /* Set SDH, step rate, do recal. */
+ if (wdccommandshort(wdc, d_link->sc_drive, WDCC_RECAL) != 0) {
+ wderror(d_link, NULL, "wdccontrol: recal failed (1)");
+ goto bad;
+ }
+ d_link->sc_state = RECAL_WAIT;
+ break;
+
+ case RECAL_WAIT:
+ if (wdc->sc_status & WDCS_ERR) {
+ wderror(d_link, NULL, "wdccontrol: recal failed (2)");
+ goto bad;
+ }
+ /* fall through */
+
+ case GEOMETRY:
+ if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0)
+ goto multimode;
+ if (wdsetctlr(d_link) != 0) {
+ /* Already printed a message. */
+ goto bad;
+ }
+ d_link->sc_state = GEOMETRY_WAIT;
+ break;
+
+ case GEOMETRY_WAIT:
+ if (wdc->sc_status & WDCS_ERR) {
+ wderror(d_link, NULL, "wdccontrol: geometry failed");
+ goto bad;
+ }
+ /* fall through */
+
+ case MULTIMODE:
+ multimode:
+ if (d_link->sc_mode != WDM_PIOMULTI)
+ goto open;
+ outb(wdc->sc_iobase + wd_seccnt, d_link->sc_multiple);
+ if (wdccommandshort(wdc, d_link->sc_drive,
+ WDCC_SETMULTI) != 0) {
+ wderror(d_link, NULL,
+ "wdccontrol: setmulti failed (1)");
+ goto bad;
+ }
+ d_link->sc_state = MULTIMODE_WAIT;
+ break;
+
+ case MULTIMODE_WAIT:
+ if (wdc->sc_status & WDCS_ERR) {
+ wderror(d_link, NULL,
+ "wdccontrol: setmulti failed (2)");
+ goto bad;
+ }
+ /* fall through */
+
+ case OPEN:
+ open:
+ wdc->sc_errors = 0;
+ d_link->sc_state = OPEN;
+ /*
+ * The rest of the initialization can be done by normal means.
+ */
+ return 1;
+
+ bad:
+ wdcunwedge(wdc);
+ return 0;
+ }
+
+ wdc->sc_flags |= WDCF_IRQ_WAIT;
+ timeout(wdctimeout, wdc, WAITTIME);
+ return 0;
+}
+
+/*
+ * Get the drive parameters, if ESDI or ATA, or create fake ones for ST506.
+ */
+int
+wdc_get_parms(d_link)
+ struct wd_link *d_link;
+{
+ struct wdc_softc *wdc = (struct wdc_softc *)d_link->wdc_softc;
+ int i;
+ char tb[DEV_BSIZE];
+ int s, error;
+
+ /*
+ * XXX
+ * The locking done here, and the length of time this may keep the rest
+ * of the system suspended, is a kluge. This should be rewritten to
+ * set up a transfer and queue it through wdstart(), but it's called
+ * infrequently enough that this isn't a pressing matter.
+ */
+
+ s = splbio();
+
+ while ((wdc->sc_flags & WDCF_ACTIVE) != 0) {
+ wdc->sc_flags |= WDCF_WANTED;
+ if ((error = tsleep(wdc, PRIBIO | PCATCH, "wdprm", 0)) != 0) {
+ splx(s);
+ return error;
+ }
+ }
+
+ wdc->sc_flags |= WDCF_ACTIVE;
+
+ if (wdccommandshort(wdc, d_link->sc_drive, WDCC_IDENTIFY) != 0 ||
+ wait_for_drq(wdc) != 0) {
+ /*
+ * 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(d_link->sc_lp->d_typename, "ST506",
+ sizeof d_link->sc_lp->d_typename);
+ d_link->sc_lp->d_type = DTYPE_ST506;
+
+ strncpy(d_link->sc_params.wdp_model, "unknown",
+ sizeof d_link->sc_params.wdp_model);
+ d_link->sc_params.wdp_config = WD_CFG_FIXED;
+ d_link->sc_params.wdp_cylinders = 1024;
+ d_link->sc_params.wdp_heads = 8;
+ d_link->sc_params.wdp_sectors = 17;
+ d_link->sc_params.wdp_maxmulti = 0;
+ d_link->sc_params.wdp_usedmovsd = 0;
+ d_link->sc_params.wdp_capabilities = 0;
+ } else {
+ strncpy(d_link->sc_lp->d_typename, "ESDI/IDE",
+ sizeof d_link->sc_lp->d_typename);
+ d_link->sc_lp->d_type = DTYPE_ESDI;
+
+ /* Read in parameter block. */
+ insw(wdc->sc_iobase + wd_data, tb, sizeof(tb) / sizeof(short));
+ bcopy(tb, &d_link->sc_params, sizeof(struct wdparams));
+
+ /* Shuffle string byte order. */
+ for (i = 0; i < sizeof(d_link->sc_params.wdp_model); i += 2) {
+ u_short *p;
+ p = (u_short *)(d_link->sc_params.wdp_model + i);
+ *p = ntohs(*p);
+ }
+ }
+
+ /* Clear any leftover interrupt. */
+ (void) inb(wdc->sc_iobase + wd_status);
+
+ /* Restart the queue. */
+ WDDEBUG_PRINT(("wdcstart from wdc_get_parms flags %d\n",
+ wdc->sc_flags));
+ wdc->sc_flags &= ~WDCF_ACTIVE;
+ wdcstart(wdc);
+
+ splx(s);
+ return 0;
+}
+
+void
+wdcerror(wdc, msg)
+ struct wdc_softc *wdc;
+ char *msg;
+{
+ struct wdc_xfer *xfer = wdc->sc_xfer.tqh_first;
+ if (xfer == NULL)
+ printf("%s: %s\n", wdc->sc_dev.dv_xname, msg);
+ else
+ printf("%s(%s): %s\n", wdc->sc_dev.dv_xname,
+ ((struct device*)xfer->d_link->wd_softc)->dv_xname, msg);
+}
+
+int
+wdc_atapi_get_params(ab_link, drive, id)
+ struct bus_link *ab_link;
+ u_int8_t drive;
+ struct atapi_identify *id;
+{
+ struct wdc_softc *wdc = (void*)ab_link->wdc_softc;
+ int status, len, excess = 0;
+ int s, error;
+
+ if (wdc->d_link[drive] != 0) {
+#ifdef ATAPI_DEBUG_PROBE
+ printf("wdc_atapi_get_params: WD drive %d\n", drive);
+#endif
+ return 0;
+ }
+
+ /*
+ * If there is only one ATAPI slave ion the bus,don't probe
+ * drive 0 (master)
+ */
+
+ if (wdc->sc_flags & WDCF_ONESLAVE && drive != 1)
+ return 0;
+
+#ifdef ATAPI_DEBUG_PROBE
+ printf("wdc_atapi_get_params: probing drive %d\n", drive);
+#endif
+
+ /*
+ * XXX
+ * The locking done here, and the length of time this may keep the rest
+ * of the system suspended, is a kluge. This should be rewritten to
+ * set up a transfer and queue it through wdstart(), but it's called
+ * infrequently enough that this isn't a pressing matter.
+ */
+
+ s = splbio();
+
+ while ((wdc->sc_flags & WDCF_ACTIVE) != 0) {
+ wdc->sc_flags |= WDCF_WANTED;
+ if ((error = tsleep(wdc, PRIBIO | PCATCH, "atprm", 0)) != 0) {
+ splx(s);
+ return error;
+ }
+ }
+
+ wdc->sc_flags |= WDCF_ACTIVE;
+ error = 1;
+ (void)wdcreset(wdc);
+ if ((status = wdccommand((struct wd_link*)ab_link,
+ ATAPI_SOFT_RESET, drive, 0, 0, 0, 0)) != 0) {
+#ifdef ATAPI_DEBUG
+ printf("wdc_atapi_get_params: ATAPI_SOFT_RESET"
+ "failed for drive %d: status %d error %d\n",
+ drive, status, wdc->sc_error);
+#endif
+ error = 0;
+ goto end;
+ }
+ if ((status = wait_for_unbusy(wdc)) != 0) {
+#ifdef ATAPI_DEBUG
+ printf("wdc_atapi_get_params: wait_for_unbusy failed "
+ "for drive %d: status %d error %d\n",
+ drive, status, wdc->sc_error);
+#endif
+ error = 0;
+ goto end;
+ }
+
+ if (wdccommand((struct wd_link*)ab_link, ATAPI_IDENTIFY_DEVICE,
+ drive, sizeof(struct atapi_identify), 0, 0, 0) != 0 ||
+ atapi_ready(wdc) != 0) {
+#ifdef ATAPI_DEBUG_PROBE
+ printf("ATAPI_IDENTIFY_DEVICE failed for drive %d\n", drive);
+#endif
+ error = 0;
+ goto end;
+ }
+ len = inb(wdc->sc_iobase + wd_cyl_lo) + 256 *
+ inb(wdc->sc_iobase + wd_cyl_hi);
+ if (len != sizeof(struct atapi_identify)) {
+ printf("Warning drive %d returned %d/%d of "
+ "indentify device data\n", drive, len,
+ sizeof(struct atapi_identify));
+ excess = len - sizeof(struct atapi_identify);
+ if (excess < 0)
+ excess = 0;
+ }
+ insw(wdc->sc_iobase + wd_data, id,
+ sizeof(struct atapi_identify)/sizeof(short));
+ wdcbit_bucket(wdc, excess);
+
+ end: /* Restart the queue. */
+ WDDEBUG_PRINT(("wdcstart from wdc_atapi_get_parms flags %d\n",
+ wdc->sc_flags));
+ wdc->sc_flags &= ~WDCF_ACTIVE;
+ wdcstart(wdc);
+ splx(s);
+ return error;
+}
+
+void
+wdc_atapi_send_command_packet(ab_link, acp)
+ struct bus_link *ab_link;
+ struct atapi_command_packet *acp;
+{
+ struct wdc_softc *wdc = (void*)ab_link->wdc_softc;
+ struct wdc_xfer *xfer;
+ u_int8_t flags = acp->flags & 0xff;
+ int s;
+
+ if (flags & A_POLLED) { /* Must use the queue and wdc_atapi_start */
+ struct wdc_xfer xfer_s;
+ int i;
+#ifdef ATAPI_DEBUG_WDC
+ printf("wdc_atapi_send_cmd: "
+ "flags %ld drive %d cmdlen %d datalen %d",
+ acp->flags, acp->drive, acp->command_size, acp->data_size);
+#endif
+ xfer = &xfer_s;
+ bzero(xfer, sizeof(xfer_s));
+ xfer->c_flags = C_INUSE|C_ATAPI|acp->flags;
+ xfer->d_link = (struct wd_link *)ab_link;
+ xfer->c_link = ab_link->ctlr_link;
+ xfer->c_bp = acp->bp;
+ xfer->atapi_cmd = acp;
+ xfer->c_blkno = 0;
+ xfer->databuf = acp->databuf;
+ xfer->c_bcount = acp->data_size;
+ if (wait_for_unbusy (wdc) != 0) {
+ if ((wdc->sc_status & WDCS_ERR) == 0) {
+ printf("wdc_atapi_send_command: not ready, "
+ "st = %02x\n", wdc->sc_status);
+ acp->status = ERROR;
+ return;
+ }
+ }
+
+ if (wdccommand((struct wd_link*)ab_link,
+ ATAPI_PACKET_COMMAND, acp->drive, acp->data_size,
+ 0, 0, 0) != 0) {
+ printf("can't send atapi paket command\n");
+ acp->status = ERROR;
+ return;
+ }
+
+ /* Wait for cmd i/o phase. */
+ for (i = 20000; i > 0; --i) {
+ int phase;
+ phase = (inb(wdc->sc_iobase + wd_ireason) &
+ (WDCI_CMD | WDCI_IN)) |
+ (inb(wdc->sc_iobase + wd_status) & WDCS_DRQ);
+ if (phase == PHASE_CMDOUT)
+ break;
+ delay(10);
+ }
+#ifdef ATAPI_DEBUG_WDC
+ printf("Wait for cmd i/o phase: i = %d\n", i);
+#endif
+
+ outsw(wdc->sc_iobase + wd_data, acp->command,
+ acp->command_size/ sizeof (short));
+
+ /* Wait for data i/o phase. */
+ for ( i= 20000; i > 0; --i) {
+ int phase;
+ phase = (inb(wdc->sc_iobase + wd_ireason) &
+ (WDCI_CMD | WDCI_IN)) |
+ (inb(wdc->sc_iobase + wd_status) & WDCS_DRQ);
+ if (phase != PHASE_CMDOUT)
+ break;
+ delay(10);
+ }
+
+#ifdef ATAPI_DEBUG_WDC
+ printf("Wait for data i/o phase: i = %d\n", i);
+#endif
+ while (wdc_atapi_intr(wdc, xfer)) {
+ for (i = 2000; i > 0; --i)
+ if ((inb(wdc->sc_iobase + wd_status)
+ & WDCS_DRQ) == 0)
+ break;
+#ifdef ATAPI_DEBUG_WDC
+ printf("wdc_atapi_intr: i = %d\n", i);
+#endif
+ }
+ wdc->sc_flags &= ~(WDCF_IRQ_WAIT | WDCF_SINGLE | WDCF_ERROR);
+ wdc->sc_errors = 0;
+ xfer->c_skip = 0;
+ return;
+ } else { /* POLLED */
+ xfer = wdc_get_xfer(ab_link->ctlr_link,
+ flags & A_NOSLEEP ? IDE_NOSLEEP : 0);
+ if (xfer == NULL) {
+ acp->status = ERROR;
+ return;
+ }
+ xfer->c_flags |= C_ATAPI|acp->flags;
+ xfer->d_link = (struct wd_link*) ab_link;
+ xfer->c_link = ab_link->ctlr_link;
+ xfer->c_bp = acp->bp;
+ xfer->atapi_cmd = acp;
+ xfer->c_blkno = 0;
+ xfer->databuf = acp->databuf;
+ xfer->c_bcount = acp->data_size;
+ wdc_exec_xfer((struct wd_link*)ab_link,xfer);
+ return;
+ }
+}
+
+
+/*
+ * the bit bucket
+ */
+void
+wdcbit_bucket(wdc, size)
+ struct wdc_softc *wdc;
+ int size;
+{
+ int iobase = wdc->sc_iobase;
+ int i;
+
+ for (i = 0 ; i < size / 2 ; i++) {
+ short null;
+ (void)insw(iobase + wd_data, &null, 1);
+ }
+
+ if (size % 2)
+ (void)inb(iobase + wd_data);
+}
+
+int
+wdc_atapi_intr(wdc, xfer)
+ struct wdc_softc *wdc;
+ struct wdc_xfer *xfer;
+{
+ struct atapi_command_packet *acp = xfer->atapi_cmd;
+ int len, phase, i;
+ int err, st, ire;
+
+ if (wait_for_unbusy(wdc) < 0) {
+ printf("wdc_atapi_intr: controller busy\n");
+ acp->status = ERROR;
+ acp->error = inb (wdc->sc_iobase + wd_error);
+ return 0;
+ }
+
+#ifdef ATAPI_DEBUG2
+ printf("wdc_atapi_intr: %s\n", wdc->sc_dev.dv_xname);
+#endif
+
+ len = inb(wdc->sc_iobase + wd_cyl_lo) +
+ 256 * inb(wdc->sc_iobase + wd_cyl_hi);
+
+ st = inb(wdc->sc_iobase + wd_status);
+ err = inb(wdc->sc_iobase + wd_error);
+ ire = inb(wdc->sc_iobase + wd_ireason);
+
+ phase = (ire & (WDCI_CMD | WDCI_IN)) | (st & WDCS_DRQ);
+#ifdef ATAPI_DEBUG_WDC
+ printf("wdc_atapi_intr: len %d st %d err %d ire %d :",
+ len, st, err, ire);
+#endif
+ switch (phase) {
+ case PHASE_CMDOUT:
+ /* send packet command */
+#ifdef ATAPI_DEBUG_WDC
+ printf("PHASE_CMDOUT\n");
+#endif
+
+#ifdef ATAPI_DEBUG_WDC
+ {
+ int i;
+ char *c = (char *)acp->command;
+ printf("wdc_atapi_intr: cmd ");
+ for (i = 0; i < acp->command_size; i++)
+ printf("%x ", c[i]);
+ printf("\n");
+ }
+#endif
+
+ wdc->sc_flags |= WDCF_IRQ_WAIT;
+ outsw(wdc->sc_iobase + wd_data, acp->command,
+ acp->command_size/ sizeof (short));
+ return 1;
+
+ case PHASE_DATAOUT:
+ /* write data */
+#ifdef ATAPI_DEBUG_WDC
+ printf("PHASE_DATAOUT\n");
+#endif
+ if ((acp->flags & (B_READ|B_WRITE)) != B_WRITE) {
+ printf("wdc_atapi_intr: bad data phase\n");
+ acp->status = ERROR;
+ return 1;
+ }
+ wdc->sc_flags |= WDCF_IRQ_WAIT;
+ if (xfer->c_bcount < len) {
+ printf("wdc_atapi_intr: warning: write only "
+ "%d of %d requested bytes\n", xfer->c_bcount, len);
+ outsw(wdc->sc_iobase + wd_data,
+ xfer->databuf + xfer->c_skip,
+ xfer->c_bcount / sizeof(short));
+ for (i = xfer->c_bcount; i < len; i += sizeof(short))
+ outw(wdc->sc_iobase + wd_data, 0);
+ xfer->c_bcount = 0;
+ return 1;
+ } else {
+ outsw(wdc->sc_iobase + wd_data,
+ xfer->databuf + xfer->c_skip, len / sizeof(short));
+ xfer->c_skip += len;
+ xfer->c_bcount -= len;
+ return 1;
+ }
+
+ case PHASE_DATAIN:
+ /* Read data */
+#ifdef ATAPI_DEBUG_WDC
+ printf("PHASE_DATAIN\n");
+#endif
+ if ((acp->flags & (B_READ|B_WRITE)) != B_READ) {
+ printf("wdc_atapi_intr: bad data phase\n");
+ acp->status = ERROR;
+ return 1;
+ }
+ wdc->sc_flags |= WDCF_IRQ_WAIT;
+ if (xfer->c_bcount < len) {
+ printf("wdc_atapi_intr: warning: reading only "
+ "%d of %d bytes\n", xfer->c_bcount, len);
+ insw(wdc->sc_iobase + wd_data,
+ xfer->databuf + xfer->c_skip,
+ xfer->c_bcount / sizeof(short));
+ wdcbit_bucket(wdc, len - xfer->c_bcount);
+ xfer->c_bcount = 0;
+ return 1;
+ } else {
+ insw(wdc->sc_iobase + wd_data,
+ xfer->databuf + xfer->c_skip, len / sizeof(short));
+ xfer->c_skip += len;
+ xfer->c_bcount -=len;
+ return 1;
+ }
+
+ case PHASE_ABORTED:
+ case PHASE_COMPLETED:
+#ifdef ATAPI_DEBUG_WDC
+ printf("PHASE_COMPLETED\n");
+#endif
+ if (st & WDCS_ERR) {
+ acp->error = inb(wdc->sc_iobase + wd_error);
+ acp->status = ERROR;
+ }
+#ifdef ATAPI_DEBUG_WDC
+ if (xfer->c_bcount != 0) {
+ printf("wdc_atapi_intr warning: bcount value "
+ "is %d after io\n", xfer->c_bcount);
+ }
+#endif
+ break;
+
+ default:
+ printf("wdc_atapi_intr: unknown phase %d\n", phase);
+ acp->status = ERROR;
+ }
+
+ wdc_atapi_done(wdc, xfer);
+ return (0);
+}
+
+
+void
+wdc_atapi_done(wdc, xfer)
+ struct wdc_softc *wdc;
+ struct wdc_xfer *xfer;
+{
+ struct atapi_command_packet *acp = xfer->atapi_cmd;
+ int s;
+
+ acp->data_size = xfer->c_bcount;
+
+ s = splbio();
+
+ /* remove this command from xfer queue */
+ wdc->sc_errors = 0;
+ xfer->c_skip = 0;
+ if ((xfer->c_flags & A_POLLED) == 0) {
+ untimeout(wdctimeout, wdc);
+ TAILQ_REMOVE(&wdc->sc_xfer, xfer, c_xferchain);
+ wdc->sc_flags &= ~(WDCF_SINGLE | WDCF_ERROR | WDCF_ACTIVE);
+ wdc_free_xfer(xfer);
+#ifdef ATAPI_DEBUG
+ printf("wdc_atapi_done: atapi_done\n");
+#endif
+ atapi_done(acp);
+#ifdef WDDEBUG
+ printf("wdcstart from wdc_atapi_intr, flags %d\n",
+ wdc->sc_flags);
+#endif
+ wdcstart(wdc);
+ } else
+ wdc->sc_flags &= ~(WDCF_SINGLE | WDCF_ERROR | WDCF_ACTIVE);
+
+ splx(s);
+}
diff --git a/sys/dev/isa/wdlink.h b/sys/dev/isa/wdlink.h
new file mode 100644
index 00000000000..0a6ba4e6362
--- /dev/null
+++ b/sys/dev/isa/wdlink.h
@@ -0,0 +1,151 @@
+/* $NetBSD: $ */
+
+/*
+ * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
+ *
+ * DMA and multi-sector PIO handling are derived from code contributed by
+ * Onno van der Linden.
+ *
+ * Atapi support added by Manuel Bouyer.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Charles M. Hannum.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+
+#undef WDDEBUG
+/* #undef DIAGNOSTIC */
+
+struct wdc_link {
+ int flags;
+ int openings;
+};
+
+struct wdc_softc {
+ struct device sc_dev;
+ void *sc_ih;
+ struct wd_link *d_link[2];
+ struct bus_link *ab_link;
+ struct wdc_link ctlr_link;
+ int sc_iobase; /* I/O port base */
+ int sc_drq; /* DMA channel */
+
+ TAILQ_HEAD(xferhead, wdc_xfer) sc_xfer;
+ int sc_flags;
+#define WDCF_ACTIVE 0x01 /* controller is active */
+#define WDCF_SINGLE 0x02 /* sector at a time mode */
+#define WDCF_ERROR 0x04 /* processing a disk error */
+#define WDCF_WANTED 0x08 /* XXX locking for wd_get_parms() */
+#define WDCF_IRQ_WAIT 0x10 /* controller is waiting for irq */
+#define WDCF_ONESLAVE 0x20 /* ctrl. has one ATAPI slave attached */
+ int sc_errors; /* errors during current transfer */
+ u_char sc_status; /* copy of status register */
+ u_char sc_error; /* copy of error register */
+};
+
+struct wd_link {
+ u_char type;
+#define DRIVE 0
+#define BUS 1
+ caddr_t wdc_softc;
+ caddr_t wd_softc;
+ struct wdc_link *ctlr_link;
+ struct wdparams sc_params;
+
+ /* Long-term state: */
+ u_int8_t openings;
+ int sc_drive; /* physical unit number */
+ int sc_state; /* control state */
+#define RECAL 0 /* recalibrate */
+#define RECAL_WAIT 1 /* done recalibrating */
+#define GEOMETRY 2 /* upload geometry */
+#define GEOMETRY_WAIT 3 /* done uploading geometry */
+#define MULTIMODE 4 /* set multiple mode */
+#define MULTIMODE_WAIT 5 /* done setting multiple mode */
+#define OPEN 6 /* done with open */
+ int sc_mode; /* transfer mode */
+#define WDM_PIOSINGLE 0 /* single-sector PIO */
+#define WDM_PIOMULTI 1 /* multi-sector PIO */
+#define WDM_DMA 2 /* DMA */
+ int sc_multiple; /* multiple for WDM_PIOMULTI */
+ int sc_flags; /* drive characteistics found */
+#define WDF_LOCKED 0x01
+#define WDF_WANTED 0x02
+#define WDF_WLABEL 0x04 /* label is writable */
+#define WDF_LABELLING 0x08 /* writing label */
+
+/*
+ * XXX Nothing resets this yet, but disk change sensing will when ATAPI is
+ * implemented.
+ */
+#define WDF_LOADED 0x10 /* parameters loaded */
+#define WDF_32BIT 0x20 /* can do 32-bit transfer */
+#define WDF_WAIT 0x40 /* waiting for resourses */
+
+ daddr_t sc_badsect[127]; /* 126 plus trailing -1 marker */
+ struct disklabel *sc_lp; /* label info for this disk */
+};
+
+struct wdc_xfer {
+ struct wdc_link *c_link; /* controller structure info */
+ struct wd_link *d_link; /* drive/bus structure info */
+ volatile long c_flags; /* handle also B_READ and B_WRITE */
+#define C_INUSE 0x01
+#define C_ATAPI 0x02
+#define C_ERROR 0x04
+
+ /* Information about the current transfer */
+ struct buf *c_bp;
+ void *atapi_cmd;
+ void *databuf;
+ daddr_t c_blkno; /* starting block number */
+ int c_bcount; /* byte count left */
+ int c_skip; /* bytes already transferred */
+ int c_nblks; /* number of blocks currently transferring */
+ int c_nbytes; /* number of bytes currently transferring */
+ u_long c_p_offset; /* offset of the partition */
+ TAILQ_ENTRY(wdc_xfer) c_xferchain;
+ LIST_ENTRY(wdc_xfer) free_list;
+};
+
+void wdc_exec_xfer __P((struct wd_link *, struct wdc_xfer *));
+struct wdc_xfer *wdc_get_xfer __P((struct wdc_link *, int));
+int wdc_get_parms __P((struct wd_link *));
+void wderror __P((struct wd_link* , struct buf *, char *));
+void wddone __P((struct wd_link*, struct buf*));
+int wdccommand __P((struct wd_link *, int, int, int, int, int, int));
+int wdccommandshort __P((struct wdc_softc *, int, int));
+int wdcwait __P((struct wdc_softc *, int));
+
+/*
+ * ST506 spec says that if READY or SEEKCMPLT go off, then the read or write
+ * command is aborted.
+ */
+#define wait_for_drq(d) wdcwait(d, WDCS_DRDY | WDCS_DSC | WDCS_DRQ)
+#define wait_for_unbusy(d) wdcwait(d, 0)
+#define wait_for_ready(d) wdcwait(d, WDCS_DRDY | WDCS_DSC)
+#define atapi_ready(d) wdcwait(d, WDCS_DRQ)
+
+#define IDE_NOSLEEP 0x01
diff --git a/sys/dev/isa/wdreg.h b/sys/dev/isa/wdreg.h
index 9e3954eca8d..ad3bb8bb107 100644
--- a/sys/dev/isa/wdreg.h
+++ b/sys/dev/isa/wdreg.h
@@ -46,6 +46,7 @@
#define wd_precomp 0x001 /* write precompensation (W) */
#define wd_features 0x001 /* features (W) */
#define wd_seccnt 0x002 /* sector count (R/W) */
+#define wd_ireason 0x002 /* interrupt reason (R/W) (for atapi) */
#define wd_sector 0x003 /* first sector number (R/W) */
#define wd_cyl_lo 0x004 /* cylinder address, low byte (R/W) */
#define wd_cyl_hi 0x005 /* cylinder address, high byte (R/W) */
@@ -118,6 +119,52 @@
#define WDSD_CHS 0x00 /* cylinder/head/sector addressing */
#define WDSD_LBA 0x40 /* logical block addressing */
+/* Commands for ATAPI devices */
+#define ATAPI_CHECK_POWER_MODE 0xe5
+#define ATAPI_EXEC_DRIVE_DIAGS 0x90
+#define ATAPI_IDLE_IMMEDIATE 0xe1
+#define ATAPI_NOP 0x00
+#define ATAPI_PACKET_COMMAND 0xa0
+#define ATAPI_IDENTIFY_DEVICE 0xa1
+#define ATAPI_SOFT_RESET 0x08
+#define ATAPI_SET_FEATURES 0xef
+#define ATAPI_SLEEP 0xe6
+#define ATAPI_STANDBY_IMMEDIATE 0xe0
+
+/* ireason */
+#define WDCI_CMD 0x01 /* command(1) or data(0) */
+#define WDCI_IN 0x02 /* transfer to(1) or from(0) the host */
+#define WDCI_RELEASE 0x04 /* bus released until completion */
+
+#define PHASE_CMDOUT (WDCS_DRQ | WDCI_CMD)
+#define PHASE_DATAIN (WDCS_DRQ | WDCI_IN)
+#define PHASE_DATAOUT WDCS_DRQ
+#define PHASE_COMPLETED (WDCI_IN | WDCI_CMD)
+#define PHASE_ABORTED 0
+
+/* Commands for ATAPI devices */
+#define ATAPI_CHECK_POWER_MODE 0xe5
+#define ATAPI_EXEC_DRIVE_DIAGS 0x90
+#define ATAPI_IDLE_IMMEDIATE 0xe1
+#define ATAPI_NOP 0x00
+#define ATAPI_PACKET_COMMAND 0xa0
+#define ATAPI_IDENTIFY_DEVICE 0xa1
+#define ATAPI_SOFT_RESET 0x08
+#define ATAPI_SET_FEATURES 0xef
+#define ATAPI_SLEEP 0xe6
+#define ATAPI_STANDBY_IMMEDIATE 0xe0
+
+/* ireason */
+#define WDCI_CMD 0x01 /* command(1) or data(0) */
+#define WDCI_IN 0x02 /* transfer to(1) or from(0) the host */
+#define WDCI_RELEASE 0x04 /* bus released until completion */
+
+#define PHASE_CMDOUT (WDCS_DRQ | WDCI_CMD)
+#define PHASE_DATAIN (WDCS_DRQ | WDCI_IN)
+#define PHASE_DATAOUT WDCS_DRQ
+#define PHASE_COMPLETED (WDCI_IN | WDCI_CMD)
+#define PHASE_ABORTED 0
+
#ifdef _KERNEL
/*