summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/conf/NOTES20
-rw-r--r--sys/conf/files.i3867
-rw-r--r--sys/dev/ata/ata-all.c658
-rw-r--r--sys/dev/ata/ata-all.h209
-rw-r--r--sys/dev/ata/ata-disk.c585
-rw-r--r--sys/dev/ata/ata-disk.h57
-rw-r--r--sys/dev/ata/atapi-all.c439
-rw-r--r--sys/dev/ata/atapi-all.h180
-rw-r--r--sys/dev/ata/atapi-cd.c1447
-rw-r--r--sys/dev/ata/atapi-cd.h358
-rw-r--r--sys/dev/ata/atapi-tape.c567
-rw-r--r--sys/dev/ata/atapi-tape.h112
-rw-r--r--sys/i386/conf/LINT20
-rw-r--r--sys/i386/conf/NOTES20
-rw-r--r--sys/i386/conf/files.i3867
15 files changed, 4681 insertions, 5 deletions
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 3175d27d5a70..1df34a06df83 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -2,7 +2,7 @@
# LINT -- config file for checking all the sources, tries to pull in
# as much of the source tree as it can.
#
-# $Id: LINT,v 1.559 1999/02/21 16:23:23 n_hibma Exp $
+# $Id: LINT,v 1.560 1999/02/22 18:19:57 des Exp $
#
# NB: You probably don't want to try running a kernel built from this
# file. Instead, you should start from GENERIC, and add options from
@@ -1006,6 +1006,24 @@ controller aha0 at isa? port ? cam irq ?
#!CAM# controller aic0 at isa? port 0x340 bio irq 11
+#
+# ATA and ATAPI devices
+# This is work in progress, use at your own risk.
+# It currently reuses the majors of wd.c and freinds.
+# It cannot co-exist with the old system in one kernel.
+# You only need one "controller ata0" for it to find all
+# PCI devices on modern machines.
+#controller ata0
+#device atadisk0 # ATA disk drives
+#device atapicd0 # ATAPI CDROM drives
+#device atapist0 # ATAPI tape drives
+#
+# If you need ISA only devices, this is the lines to add:
+#controller ata1 at isa? port "IO_WD1" bio irq 14
+#controller ata2 at isa? port "IO_WD2" bio irq 15
+#
+# All the controller lines can coexist, the driver will
+# find out which ones are there.
#
# ST-506, ESDI, and IDE hard disks: `wdc' and `wd'
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
index 8ed556251811..e5ff6f0ecafa 100644
--- a/sys/conf/files.i386
+++ b/sys/conf/files.i386
@@ -1,7 +1,7 @@
# This file tells config what files go into building a kernel,
# files marked standard are always included.
#
-# $Id: files.i386,v 1.222 1999/02/11 07:11:00 gibbs Exp $
+# $Id: files.i386,v 1.223 1999/02/20 11:17:59 julian Exp $
#
# The long compile-with and dependency lines are required because of
# limitations in config: backslash-newline doesn't work in strings, and
@@ -24,6 +24,11 @@ font8x16.o optional std8x16font \
no-implicit-rule before-depend \
clean "${STD8X16FONT}-8x16 font8x16.c"
#
+dev/ata/ata-all.c optional ata device-driver
+dev/ata/atapi-all.c optional ata device-driver
+dev/ata/ata-disk.c optional atadisk device-driver
+dev/ata/atapi-cd.c optional atapicd device-driver
+dev/ata/atapi-tape.c optional atapist device-driver
dev/fb/fb.c optional fb device-driver
dev/fb/fb.c optional vga device-driver
dev/fb/splash.c optional splash
diff --git a/sys/dev/ata/ata-all.c b/sys/dev/ata/ata-all.c
new file mode 100644
index 000000000000..50a82b2dbc46
--- /dev/null
+++ b/sys/dev/ata/ata-all.c
@@ -0,0 +1,658 @@
+/*-
+ * Copyright (c) 1998,1999 Søren Schmidt
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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. 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.
+ *
+ * $Id: ata-all.c,v 1.4 1999/03/01 21:03:15 sos Exp sos $
+ */
+
+#include "ata.h"
+#if NATA > 0
+#include "pci.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/interrupt.h>
+#include <sys/conf.h>
+#include <sys/buf.h>
+#include <sys/malloc.h>
+#include <sys/devicestat.h>
+#include <machine/clock.h>
+#include <pci/pcivar.h>
+#include <pci/pcireg.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/ata-disk.h>
+#include <dev/ata/atapi-all.h>
+
+/* misc defines */
+#define UNIT(dev) (dev>>3 & 0x1f) /* assume 8 minor # per unit */
+
+/* prototypes */
+void ataintr(int32_t);
+static int32_t ata_isaprobe(struct isa_device *);
+static int32_t ata_isaattach(struct isa_device *);
+static const char *ata_pciprobe(pcici_t, pcidi_t);
+static void ata_pciattach(pcici_t, int32_t);
+static int32_t ata_probe(int32_t, int32_t, int32_t *);
+static int32_t ata_attach(int32_t);
+static void promise_intr(int32_t);
+static int32_t ata_reset(struct ata_softc *);
+static int32_t ata_device_attach(struct ata_softc *, int32_t);
+static int32_t atapi_device_attach(struct ata_softc *, int32_t);
+static void bswap(int8_t *, int32_t);
+static void btrim(int8_t *, int32_t);
+
+static int32_t atanlun, sysctrl = 0;
+struct ata_softc *atadevices[MAXATA];
+struct isa_driver atadriver = { ata_isaprobe, ata_isaattach, "ata" };
+
+static int32_t
+ata_isaprobe(struct isa_device *devp)
+{
+ int32_t ctlr, res;
+
+ for (ctlr = 0; ctlr < atanlun; ctlr++) {
+ if (atadevices[ctlr]->ioaddr == devp->id_iobase) {
+ printf("ata-isa%d: already registered as ata%d\n",
+ devp->id_unit, ctlr);
+ return 0;
+ }
+ }
+ res=ata_probe(devp->id_iobase, devp->id_iobase+ATA_ALTPORT, &devp->id_unit);
+ if (res)
+ devp->id_intr = ataintr;
+ return res;
+}
+
+static int32_t
+ata_isaattach(struct isa_device *devp)
+{
+ return ata_attach(devp->id_unit);
+}
+
+static u_long ata_pcicount;
+static struct pci_device ata_pcidevice = {
+ "ata-pci", ata_pciprobe, ata_pciattach, &ata_pcicount, 0
+};
+
+DATA_SET(pcidevice_set, ata_pcidevice);
+
+static const char *
+ata_pciprobe(pcici_t tag, pcidi_t type)
+{
+ u_int32_t data;
+
+ data = pci_conf_read(tag, PCI_CLASS_REG);
+ if ((data & PCI_CLASS_MASK) == PCI_CLASS_MASS_STORAGE &&
+ ((data & PCI_SUBCLASS_MASK) == 0x00010000 ||
+ ((data & PCI_SUBCLASS_MASK) == 0x00040000))) {
+ switch (type) {
+ case 0x71118086:
+ return "Intel PIIX4 IDE controller";
+ case 0x70108086:
+ return "Intel PIIX3 IDE controller";
+ case 0x12308086:
+ return "Intel PIIX IDE controller";
+ case 0x4d33105a:
+ return "Promise Ultra/33 IDE controller";
+ case 0x05711106:
+ return "VIA Apollo IDE controller";
+ case 0x01021078:
+ return "Cyrix 5530 IDE controller";
+ case 0x522910b9:
+ return "Acer Aladdin IV/V IDE controller";
+ default:
+ return ("Unknown PCI IDE controller");
+ }
+ }
+ return NULL;
+}
+
+static void
+ata_pciattach(pcici_t tag, int32_t unit)
+{
+ pcidi_t type, class, cmd;
+ int32_t iobase_1, iobase_2, altiobase_1, altiobase_2, irq1, irq2;
+ int32_t lun;
+
+ /* set up vendor-specific stuff */
+ type = pci_conf_read(tag, PCI_ID_REG);
+ class = pci_conf_read(tag, PCI_CLASS_REG);
+ cmd = pci_conf_read(tag, PCI_COMMAND_STATUS_REG);
+
+#ifdef ATA_DEBUG
+ printf("ata: type=%08x class=%08x cmd=%08x\n", type, class, cmd);
+#endif
+
+ switch (type) {
+ case 0x71118086:
+ case 0x70108086:
+ case 0x12308086: /* Intel PIIX, PIIX3, PIIX4 */
+ break;
+
+ case 0x05711106: /* VIA Apollo chipset family */
+ break;
+
+ case 0x4d33105a: /* Promise controllers */
+ break;
+
+ case 0x01021078: /* Cyrix 5530 */
+ break;
+
+ case 0x522910B9: /* Acer Aladdin IV/V (M5229) */
+ break;
+ default:
+ /* everybody else */
+ break;
+ }
+
+ if (type == 0x4d33105a) { /* the Promise is special */
+ iobase_1 = pci_conf_read(tag, 0x10) & 0xfffc;
+ altiobase_1 = pci_conf_read(tag, 0x14) & 0xfffc;
+ iobase_2 = pci_conf_read(tag, 0x18) & 0xfffc;
+ altiobase_2 = pci_conf_read(tag, 0x1c) & 0xfffc;
+ irq1 = irq2 = pci_conf_read(tag, PCI_INTERRUPT_REG) & 0xff;
+ sysctrl = (pci_conf_read(tag, 0x20) & 0xfffc) + 0x1c;
+ }
+ else {
+ if ((class & 0x100) == 0) {
+ iobase_1 = IO_WD1;
+ altiobase_1 = iobase_1 + ATA_ALTPORT;
+ irq1 = 14;
+ }
+ else {
+ iobase_1 = pci_conf_read(tag, 0x10) & 0xfffc;
+ altiobase_1 = pci_conf_read(tag, 0x14) & 0xfffc;
+ irq1 = pci_conf_read(tag, PCI_INTERRUPT_REG) & 0xff;
+ }
+ if ((class & 0x400) == 0) {
+ iobase_2 = IO_WD2;
+ altiobase_2 = iobase_2 + ATA_ALTPORT;
+ irq2 = 15;
+ }
+ else {
+ iobase_2 = pci_conf_read(tag, 0x18) & 0xfffc;
+ altiobase_2 = pci_conf_read(tag, 0x1c) & 0xfffc;
+ irq2 = pci_conf_read(tag, PCI_INTERRUPT_REG) & 0xff;
+ }
+ }
+
+ /* now probe the addresse found for "real" ATA/ATAPI hardware */
+ if (ata_probe(iobase_1, altiobase_1, &lun)) {
+ if (iobase_1 == IO_WD1)
+ register_intr(irq1, 0, 0, (inthand2_t *)ataintr, &bio_imask, lun);
+ else {
+ if (sysctrl)
+ pci_map_int(tag, promise_intr, (void *)lun, &bio_imask);
+ else
+ pci_map_int(tag, ataintr, (void *)lun, &bio_imask);
+ }
+ printf("ata%d at 0x%04x irq %d on ata-pci%d\n",
+ lun, iobase_1, irq1, unit);
+ ata_attach(lun);
+ }
+ if (ata_probe(iobase_2, altiobase_2, &lun)) {
+ if (iobase_2 == IO_WD2)
+ register_intr(irq2, 0, 0, (inthand2_t *)ataintr, &bio_imask, lun);
+ else {
+ if (!sysctrl)
+ pci_map_int(tag, ataintr, (void *) lun, &bio_imask);
+ }
+ printf("ata%d at 0x%04x irq %d on ata-pci%d\n",
+ lun, iobase_2, irq2, unit);
+ ata_attach(lun);
+ }
+}
+
+static void
+promise_intr(int32_t unit)
+{
+ if (inl(sysctrl) & 0x00000400)
+ ataintr(unit);
+ if (inl(sysctrl) & 0x00004000)
+ ataintr(unit+1);
+}
+
+static int32_t
+ata_probe(int32_t ioaddr, int32_t altioaddr, int32_t *unit)
+{
+ struct ata_softc *scp = atadevices[atanlun];
+ u_int8_t status0, status1;
+ int32_t mask = 0;
+ int32_t timeout;
+
+ if (atanlun > MAXATA) {
+ printf("ata: unit of of range(%d)\n", atanlun);
+ return(0);
+ }
+ if (scp) {
+ printf("ata%d: unit already attached\n", atanlun);
+ return(0);
+ }
+ scp = malloc(sizeof(struct ata_softc), M_DEVBUF, M_NOWAIT);
+ if (scp == NULL) {
+ printf("ata%d: failed to allocate driver storage\n", atanlun);
+ return(0);
+ }
+ bzero(scp, sizeof(struct ata_softc));
+
+ scp->unit = atanlun;
+ scp->ioaddr = ioaddr;
+ scp->altioaddr = altioaddr;
+
+#ifdef ATA_DEBUG
+ printf("ata%d: iobase=0x%04x altiobase=0x%04x\n",
+ atanlun, scp->ioaddr, scp->altioaddr);
+#endif
+
+ /* do we have any signs of ATA/ATAPI HW being present ? */
+ outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
+ DELAY(1);
+ status0 = inb(scp->ioaddr + ATA_STATUS);
+ outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | ATA_SLAVE);
+ DELAY(1);
+ status1 = inb(scp->ioaddr + ATA_STATUS);
+ if ((status0 & 0xf8) != 0xf8)
+ mask |= 0x01;
+ if ((status1 & 0xf8) != 0xf8)
+ mask |= 0x02;
+#ifdef ATA_DEBUG
+ printf("ata%d: mask=%02x status0=%02x status1=%02x\n",
+ atanlun, mask, status0, status1);
+#endif
+ if (!mask) {
+ free(scp, M_DEVBUF);
+ return 0;
+ }
+ /* assert reset for devices and wait for completition */
+ outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
+ DELAY(1);
+ outb(scp->altioaddr, ATA_A_IDS | ATA_A_RESET);
+ DELAY(1000);
+ outb(scp->altioaddr, ATA_A_IDS);
+ DELAY(1000);
+ inb(scp->ioaddr + ATA_ERROR);
+ outb(scp->altioaddr, ATA_A_4BIT);
+ DELAY(1);
+
+ /* wait for BUSY to go inactive */
+ for (timeout = 0; timeout < 30000*10; timeout++) {
+ outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
+ DELAY(1);
+ status0 = inb(scp->ioaddr + ATA_STATUS);
+ outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | ATA_SLAVE);
+ DELAY(1);
+ status1 = inb(scp->ioaddr + ATA_STATUS);
+ if (mask == 0x01) /* wait for master only */
+ if (!(status0 & ATA_S_BSY))
+ break;
+ if (mask == 0x02) /* wait for slave only */
+ if (!(status1 & ATA_S_BSY))
+ break;
+ if (mask == 0x03) /* wait for both master & slave */
+ if (!(status0 & ATA_S_BSY) && !(status1 & ATA_S_BSY))
+ break;
+ DELAY(100);
+ }
+ if (status0 & ATA_S_BSY)
+ mask &= ~0x01;
+ if (status1 & ATA_S_BSY)
+ mask &= ~0x02;
+#ifdef ATA_DEBUG
+ printf("ata%d: mask=%02x status0=%02x status1=%02x\n",
+ atanlun, mask, status0, status1);
+#endif
+ if (!mask) {
+ free(scp, M_DEVBUF);
+ return 0;
+ }
+ /*
+ * OK, we have at least one device on the chain,
+ * check for ATAPI signatures, if none check if its
+ * a good old ATA device.
+ */
+
+ outb(scp->ioaddr + ATA_DRIVE, (ATA_D_IBM | ATA_MASTER));
+ DELAY(1);
+ if (inb(scp->ioaddr + ATA_CYL_LSB) == ATAPI_MAGIC_LSB &&
+ inb(scp->ioaddr + ATA_CYL_MSB) == ATAPI_MAGIC_MSB) {
+ scp->devices |= ATA_ATAPI_MASTER;
+ }
+ outb(scp->ioaddr + ATA_DRIVE, (ATA_D_IBM | ATA_SLAVE));
+ DELAY(1);
+ if (inb(scp->ioaddr + ATA_CYL_LSB) == ATAPI_MAGIC_LSB &&
+ inb(scp->ioaddr + ATA_CYL_MSB) == ATAPI_MAGIC_MSB) {
+ scp->devices |= ATA_ATAPI_SLAVE;
+ }
+ if (status0 != 0x00 && !(scp->devices & ATA_ATAPI_MASTER)) {
+ outb(scp->ioaddr + ATA_DRIVE, (ATA_D_IBM | ATA_MASTER));
+ DELAY(1);
+ outb(scp->ioaddr + ATA_ERROR, 0x58);
+ outb(scp->ioaddr + ATA_CYL_LSB, 0xa5);
+ if (inb(scp->ioaddr + ATA_ERROR) != 0x58 &&
+ inb(scp->ioaddr + ATA_CYL_LSB) == 0xa5) {
+ scp->devices |= ATA_ATA_MASTER;
+ }
+ }
+ if (status1 != 0x00 && !(scp->devices & ATA_ATAPI_SLAVE)) {
+ outb(scp->ioaddr + ATA_DRIVE, (ATA_D_IBM | ATA_SLAVE));
+ DELAY(1);
+ outb(scp->ioaddr + ATA_ERROR, 0x58);
+ outb(scp->ioaddr + ATA_CYL_LSB, 0xa5);
+ if (inb(scp->ioaddr + ATA_ERROR) != 0x58 &&
+ inb(scp->ioaddr + ATA_CYL_LSB) == 0xa5) {
+ scp->devices |= ATA_ATA_SLAVE;
+ }
+ }
+#ifdef ATA_DEBUG
+ printf("ata%d: devices = 0x%x\n", atanlun, scp->devices);
+#endif
+ if (!(scp->devices & (ATA_ATA_MASTER|ATA_ATAPI_MASTER)))
+ scp->flags |= ATA_F_SLAVE_ONLY;
+ if (!scp->devices) {
+ free(scp, M_DEVBUF);
+ return 0;
+ }
+ bufq_init(&scp->ata_queue);
+ TAILQ_INIT(&scp->atapi_queue);
+ *unit = atanlun;
+ atadevices[atanlun++] = scp;
+ return ATA_IOSIZE;
+}
+
+static int32_t
+ata_attach(int32_t unit)
+{
+ struct ata_softc *scp;
+
+ if (unit > atanlun)
+ return 0;
+
+ scp = atadevices[unit];
+
+ if (scp->devices & ATA_ATA_MASTER)
+ if (ata_device_attach(scp, ATA_MASTER))
+ scp->devices &= ~ATA_ATA_MASTER;
+ if (scp->devices & ATA_ATA_SLAVE)
+ if (ata_device_attach(scp, ATA_SLAVE))
+ scp->devices &= ~ATA_ATA_SLAVE;
+ if (scp->devices & ATA_ATAPI_MASTER)
+ if (atapi_device_attach(scp, ATA_MASTER))
+ scp->devices &= ~ATA_ATAPI_MASTER;
+ if (scp->devices & ATA_ATAPI_SLAVE)
+ if (atapi_device_attach(scp, ATA_SLAVE))
+ scp->devices &= ~ATA_ATAPI_SLAVE;
+ return scp->devices;
+}
+
+void
+ataintr(int32_t unit)
+{
+ struct ata_softc *scp;
+ struct atapi_request *atapi_request;
+ struct buf *ata_request;
+
+ static int32_t intcount = 0;
+
+#ifdef ATA_DEBUG
+ printf("ataintr: entered unit=%d\n", unit);
+#endif
+ if (unit < 0 || unit > atanlun) {
+ printf("ataintr: unit %d unusable\n", unit);
+ return;
+ }
+
+ scp = atadevices[unit];
+
+ /* find & call the responsible driver to process this interrupt */
+ switch (scp->active) {
+ case ATA_IDLE:
+ if (intcount++ < 5)
+ printf("ata%d: unwanted interrupt\n", unit);
+ break;
+
+ case ATA_ACTIVE_ATA:
+ if ((ata_request = bufq_first(&scp->ata_queue)))
+ ad_interrupt(ata_request);
+ break;
+
+ case ATA_ACTIVE_ATAPI:
+ if ((atapi_request = TAILQ_FIRST(&scp->atapi_queue)))
+ atapi_interrupt(atapi_request);
+ break;
+
+ case ATA_IGNORE_INTR:
+ scp->active = ATA_IDLE;
+ break;
+ }
+}
+
+void
+ata_start(struct ata_softc *scp)
+{
+ struct buf *ata_request;
+ struct atapi_request *atapi_request;
+
+#ifdef ATA_DEBUG
+ printf("ata_start: entered\n");
+#endif
+ if (scp->active) {
+ printf("ata: unwanted ata_start\n");
+ return;
+ }
+
+ /* find & call the responsible driver if anything on ATA queue */
+ if ((ata_request = bufq_first(&scp->ata_queue))) {
+ scp->active = ATA_ACTIVE_ATA;
+ ad_transfer(ata_request);
+ }
+
+ /* find & call the responsible driver if anything on ATAPI queue */
+ if ((atapi_request = TAILQ_FIRST(&scp->atapi_queue))) {
+ scp->active = ATA_ACTIVE_ATAPI;
+ atapi_transfer(atapi_request);
+ }
+}
+
+int32_t
+ata_wait(struct ata_softc *scp, u_int8_t mask)
+{
+ u_int8_t status;
+ u_int32_t timeout = 0;
+
+ while (timeout++ <= 50000) { /* timeout 5 secs */
+ status = inb(scp->ioaddr + ATA_STATUS);
+ if ((status == 0xff) && (scp->flags & ATA_F_SLAVE_ONLY)) {
+ outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | ATA_SLAVE);
+ status = inb(scp->ioaddr + ATA_STATUS);
+ }
+ if (status == 0xff)
+ return -1;
+ scp->status = status;
+ if (!(status & ATA_S_BSY)) {
+ if (status & ATA_S_ERROR)
+ scp->error = inb(scp->ioaddr + ATA_ERROR);
+ if ((status & mask) == mask)
+ return (status & ATA_S_ERROR);
+ }
+ if (timeout > 1000)
+ DELAY(1000);
+ else
+ DELAY(10);
+ }
+ return -1;
+}
+
+static int32_t
+ata_reset(struct ata_softc *scp)
+{
+ outb(scp->altioaddr, ATA_A_RESET | ATA_A_IDS);
+ DELAY(10000);
+ outb(scp->altioaddr, ATA_A_IDS);
+ DELAY(10000);
+ inb(scp->ioaddr + ATA_ERROR);
+ outb(scp->altioaddr, ATA_A_4BIT);
+ if (ata_wait(scp, 0) < 0) {
+ printf("ata%d: RESET failed\n", scp->unit);
+ return 1;
+ }
+ return 0;
+}
+
+static int32_t
+ata_device_attach(struct ata_softc *scp, int32_t device)
+{
+ struct ata_params *ata_parm;
+ int8_t buffer[DEV_BSIZE];
+
+ outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | device);
+ if (ata_wait(scp, 0) < 0)
+ return -1;
+ outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | device); /* XXX SOS */
+ scp->active = ATA_IGNORE_INTR;
+ outb(scp->ioaddr + ATA_CMD, ATA_C_ATA_IDENTIFY);
+ if (ata_wait(scp, ATA_S_DRDY | ATA_S_DSC | ATA_S_DRQ))
+ return -1;
+
+ insw(scp->ioaddr + ATA_DATA, buffer, sizeof(buffer)/sizeof(int16_t));
+ ata_parm = malloc(sizeof(struct ata_params), M_DEVBUF, M_NOWAIT);
+ if (!ata_parm)
+ return -1;
+ bcopy(buffer, ata_parm, sizeof(struct ata_params));
+ bswap(ata_parm->model, sizeof(ata_parm->model));
+ btrim(ata_parm->model, sizeof(ata_parm->model));
+ bswap(ata_parm->revision, sizeof(ata_parm->revision));
+ btrim(ata_parm->revision, sizeof(ata_parm->revision));
+ scp->ata_parm[device == ATA_SLAVE] = ata_parm;
+ return 0;
+}
+
+int32_t
+atapi_wait(struct ata_softc *scp, u_int8_t mask)
+{
+ u_int8_t status;
+ u_int32_t timeout = 0;
+
+ while (timeout++ <= 500000) { /* timeout 5 secs */
+ status = inb(scp->ioaddr + ATA_STATUS);
+ if ((status == 0xff) && (scp->flags & ATA_F_SLAVE_ONLY)) {
+ outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | ATA_SLAVE);
+ status = inb(scp->ioaddr + ATA_STATUS);
+ }
+
+ if (!(status & ATA_S_BSY))
+ break;
+ DELAY (10);
+ }
+ if (timeout <= 0)
+ return -1;
+ if (!mask)
+ return (status & ATA_S_ERROR);
+
+ /* Wait 50 msec for bits wanted. */
+ for (timeout=5000; timeout>0; --timeout) {
+ status = inb(scp->ioaddr + ATA_STATUS);
+ if ((status & mask) == mask)
+ return (status & ATA_S_ERROR);
+ DELAY (10);
+ }
+ return -1;
+}
+
+static int32_t
+atapi_device_attach(struct ata_softc *scp, int32_t device)
+{
+ struct atapi_params *atapi_parm;
+ int8_t buffer[DEV_BSIZE];
+
+ outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | device);
+ if (atapi_wait(scp, 0) < 0)
+ return -1;
+ outb(scp->ioaddr + ATA_DRIVE, ATA_D_IBM | device); /* XXX SOS */
+ outb(scp->ioaddr + ATA_CMD, ATA_C_ATAPI_IDENTIFY);
+ if (atapi_wait(scp, ATA_S_DRQ))
+ return -1;
+
+ insw(scp->ioaddr + ATA_DATA, buffer, sizeof(buffer)/sizeof(int16_t));
+ atapi_parm = malloc(sizeof(struct atapi_params), M_DEVBUF, M_NOWAIT);
+ if (!atapi_parm)
+ return -1;
+
+ bcopy(buffer, atapi_parm, sizeof(struct atapi_params));
+ if (!((atapi_parm->model[0] == 'N' && atapi_parm->model[1] == 'E') ||
+ (atapi_parm->model[0] == 'F' && atapi_parm->model[1] == 'X')))
+ bswap(atapi_parm->model, sizeof(atapi_parm->model));
+ btrim(atapi_parm->model, sizeof(atapi_parm->model));
+ bswap(atapi_parm->revision, sizeof(atapi_parm->revision));
+ btrim(atapi_parm->revision, sizeof(atapi_parm->revision));
+ bswap(atapi_parm->serial, sizeof(atapi_parm->serial)); /* unused SOS */
+ btrim(atapi_parm->serial, sizeof(atapi_parm->serial)); /* unused SOS */
+ scp->atapi_parm[device == ATA_SLAVE] = atapi_parm;
+ return 0;
+}
+
+static void
+bswap(int8_t *buf, int32_t len)
+{
+ u_int16_t *p = (u_int16_t*)(buf + len);
+
+ while (--p >= (u_int16_t*)buf)
+ *p = ntohs(*p);
+}
+
+static void
+btrim(int8_t *buf, int32_t len)
+{
+ int8_t *p;
+
+ for (p = buf; p < buf+len; ++p)
+ if (!*p)
+ *p = ' ';
+ for (p = buf + len - 1; p >= buf && *p == ' '; --p)
+ *p = 0;
+}
+
+void
+bpack(int8_t *src, int8_t *dst, int32_t len)
+{
+ int32_t i, j, blank;
+
+ for (i = j = blank = 0 ; i < len-1; i++) {
+ if (blank && src[i] == ' ') continue;
+ if (blank && src[i] != ' ') {
+ dst[j++] = src[i];
+ blank = 0;
+ continue;
+ }
+ if (src[i] == ' ')
+ blank = 1;
+ dst[j++] = src[i];
+ }
+ dst[j] = 0x00;
+}
+#endif
diff --git a/sys/dev/ata/ata-all.h b/sys/dev/ata/ata-all.h
new file mode 100644
index 000000000000..df6791de0210
--- /dev/null
+++ b/sys/dev/ata/ata-all.h
@@ -0,0 +1,209 @@
+/*-
+ * Copyright (c) 1998,1999 Søren Schmidt
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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. 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.
+ *
+ * $Id: ata-all.h,v 1.3 1999/03/01 21:03:15 sos Exp sos $
+ */
+
+/* ATA register defines */
+
+#define ATA_DATA 0x00 /* data register */
+#define ATA_ERROR 0x01 /* (R) error register */
+#define ATA_PRECOMP 0x01 /* (W) precompensation */
+#define ATA_COUNT 0x02 /* sector count */
+#define ATA_I_CMD 0x01 /* cmd (1) | data (0) */
+#define ATA_I_IN 0x02 /* read (1) | write (0) */
+#define ATA_I_RELEASE 0x04 /* released bus (1) */
+
+#define ATA_SECTOR 0x03 /* sector # */
+#define ATA_CYL_LSB 0x04 /* cylinder# LSB */
+#define ATA_CYL_MSB 0x05 /* cylinder# MSB */
+#define ATA_DRIVE 0x06 /* Sector/Drive/Head register */
+#define ATA_D_IBM 0xa0 /* 512 byte sectors, ECC */
+
+#define ATA_CMD 0x07 /* command register */
+#define ATA_C_ATA_IDENTIFY 0xec /* get ATA params */
+#define ATA_C_ATAPI_IDENTIFY 0xa1 /* get ATAPI params*/
+#define ATA_C_READ 0x20 /* read command */
+#define ATA_C_WRITE 0x30 /* write command */
+#define ATA_C_READ_MULTI 0xc4 /* read multi command */
+#define ATA_C_WRITE_MULTI 0xc5 /* write multi command */
+#define ATA_C_SET_MULTI 0xc6 /* set multi size command */
+#define ATA_C_PACKET_CMD 0xa0 /* set multi size command */
+
+#define ATA_STATUS 0x07 /* status register */
+#define ATA_S_ERROR 0x01 /* error */
+#define ATA_S_INDEX 0x02 /* index */
+#define ATA_S_CORR 0x04 /* data corrected */
+#define ATA_S_DRQ 0x08 /* data request */
+#define ATA_S_DSC 0x10 /* drive Seek Completed */
+#define ATA_S_DWF 0x20 /* drive write fault */
+#define ATA_S_DRDY 0x40 /* drive ready */
+#define ATA_S_BSY 0x80 /* busy */
+
+#define ATA_ALTPORT 0x206 /* alternate Status register */
+#define ATA_A_IDS 0x02 /* disable interrupts */
+#define ATA_A_RESET 0x04 /* RESET controller */
+#define ATA_A_4BIT 0x08 /* 4 head bits */
+
+/* Misc defines */
+#define ATA_MASTER 0x00
+#define ATA_SLAVE 0x10
+#define ATA_IOSIZE 0x08
+
+/* Devices types */
+#define ATA_ATA_MASTER 0x01
+#define ATA_ATA_SLAVE 0x02
+#define ATA_ATAPI_MASTER 0x04
+#define ATA_ATAPI_SLAVE 0x08
+
+struct ata_params {
+ int16_t config; /* general configuration bits */
+ u_int16_t cylinders; /* number of cylinders */
+ int16_t reserved2;
+ u_int16_t heads; /* # heads */
+ int16_t unfbytespertrk; /* # unformatted bytes/track */
+ int16_t unfbytes; /* # unformatted bytes/sector */
+ u_int16_t sectors; /* # sectors/track */
+ int16_t vendorunique[3];
+ int8_t serial[20]; /* serial number */
+ int16_t buffertype; /* buffer type */
+#define ATA_BT_SINGLEPORTSECTOR 1 /* 1 port, 1 sector buffer */
+#define ATA_BT_DUALPORTMULTI 2 /* 2 port, mult sector buffer */
+#define ATA_BT_DUALPORTMULTICACHE 3 /* above plus track cache */
+
+ int16_t buffersize; /* buf size, 512-byte units */
+ int16_t necc; /* ecc bytes appended */
+ int8_t revision[8]; /* firmware revision */
+ int8_t model[40]; /* model name */
+ int8_t nsecperint; /* sectors per interrupt */
+ int8_t vendorunique1;
+ int16_t usedmovsd; /* double word read/write? */
+ int8_t vendorunique2;
+ int8_t capability; /* various capability bits */
+ int16_t cap_validate; /* validation for above */
+ int8_t vendorunique3;
+ int8_t opiomode; /* PIO modes 0-2 */
+ int8_t vendorunique4;
+ int8_t odmamode; /* old DMA modes, not ATA-3 */
+ int16_t atavalid; /* fields valid */
+ int16_t currcyls;
+ int16_t currheads;
+ int16_t currsectors;
+ int16_t currsize0;
+ int16_t currsize1;
+ int8_t currmultsect;
+ int8_t multsectvalid;
+ int lbasize;
+ int16_t dmasword; /* obsolete in ATA-3 */
+ int16_t dmamword; /* multiword DMA modes */
+ int16_t eidepiomodes; /* advanced PIO modes */
+ int16_t eidedmamin; /* fastest DMA timing */
+ int16_t eidedmanorm; /* recommended DMA timing */
+ int16_t eidepioblind; /* fastest possible blind PIO */
+ int16_t eidepioacked; /* fastest possible IORDY PIO */
+ int16_t reserved69;
+ int16_t reserved70;
+ int16_t reserved71;
+ int16_t reserved72;
+ int16_t reserved73;
+ int16_t reserved74;
+ int16_t queuelen;
+ int16_t reserved76;
+ int16_t reserved77;
+ int16_t reserved78;
+ int16_t reserved79;
+ int16_t versmajor;
+ int16_t versminor;
+ int16_t featsupp1;
+ int16_t featsupp2;
+ int16_t featsupp3;
+ int16_t featenab1;
+ int16_t featenab2;
+ int16_t featenab3;
+ int16_t udmamode; /* UltraDMA modes */
+ int16_t erasetime;
+ int16_t enherasetime;
+ int16_t apmlevel;
+ int16_t reserved92[34];
+ int16_t rmvcap;
+ int16_t securelevel;
+};
+
+/*
+ * Structure describing an ATA device
+ */
+struct ata_softc {
+ u_int32_t unit; /* this instance's number */
+ u_int32_t ioaddr; /* port addr */
+ u_int32_t altioaddr; /* alternate port addr */
+ void *dmacookie; /* handle for DMA services */
+ int32_t flags; /* controller flags */
+#define ATA_F_SLAVE_ONLY 0x0001
+
+ int32_t devices; /* what is present */
+ u_int8_t status; /* last controller status */
+ u_int8_t error; /* last controller error */
+
+ int32_t active; /* active processing request */
+#define ATA_IDLE 0x0
+#define ATA_ACTIVE_ATA 0x1
+#define ATA_ACTIVE_ATAPI 0x2
+#define ATA_IGNORE_INTR 0x3
+
+ struct buf_queue_head ata_queue; /* head of ATA queue */
+ struct ata_params *ata_parm[2]; /* ata device params */
+ TAILQ_HEAD(, atapi_request) atapi_queue; /* head of ATAPI queue */
+ struct atapi_params *atapi_parm[2]; /* atapi device params */
+
+#ifdef DEVFS
+ static void *devfs_token;
+#endif
+};
+
+struct ata_request {
+ struct ad_softc *driver; /* ptr to parent device */
+ /*bla request bla*/
+ u_int32_t flags; /* drive flags */
+#define A_READ 0x0001
+
+ u_int32_t bytecount; /* bytes to transfer */
+ u_int32_t donecount; /* bytes transferred */
+ u_int32_t currentsize; /* size of current transfer */
+ struct buf *bp; /* associated buf ptr */
+ TAILQ_ENTRY(ata_request) chain; /* list management */
+};
+
+#define MAXATA 8
+
+extern struct ata_softc *atadevices[];
+
+/* public prototypes */
+void ata_start(struct ata_softc *);
+int32_t ata_wait(struct ata_softc *, u_int8_t);
+int32_t atapi_wait(struct ata_softc *, u_int8_t);
+void bpack(int8_t *, int8_t *, int32_t);
+
diff --git a/sys/dev/ata/ata-disk.c b/sys/dev/ata/ata-disk.c
new file mode 100644
index 000000000000..bfbf877e791c
--- /dev/null
+++ b/sys/dev/ata/ata-disk.c
@@ -0,0 +1,585 @@
+/*-
+ * Copyright (c) 1998,1999 Søren Schmidt
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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. 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.
+ *
+ * $Id: ata-disk.c,v 1.14 1999/03/01 21:03:15 sos Exp sos $
+ */
+
+#include "ata.h"
+#include "atadisk.h"
+#include "opt_devfs.h"
+
+#if NATA > 0 && NATADISK > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/disklabel.h>
+#include <sys/diskslice.h>
+#include <sys/devicestat.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif
+#include <machine/clock.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/ata-disk.h>
+
+static d_open_t adopen;
+static d_close_t adclose;
+static d_write_t adwrite;
+static d_read_t adread;
+static d_ioctl_t adioctl;
+static d_strategy_t adstrategy;
+static d_psize_t adpsize;
+
+#define BDEV_MAJOR 0
+#define CDEV_MAJOR 3
+static struct cdevsw ad_cdevsw = {
+ adopen, adclose, adread, adwrite,
+ adioctl, nostop, nullreset, nodevtotty,
+ seltrue, nommap, adstrategy, "ad",
+ NULL, -1, nodump, adpsize,
+ D_DISK, 0, -1
+};
+
+/* misc defines */
+#define UNIT(dev) (dev>>3 & 0x1f) /* assume 8 minor # per unit */
+#define NUNIT 16 /* max # of devices */
+
+/* prototypes */
+static void ad_attach(void *);
+static void ad_strategy(struct buf *);
+static void ad_start(struct ad_softc *);
+static void ad_sleep(struct ad_softc *, int8_t *);
+static int32_t ad_command(struct ad_softc *, u_int32_t, u_int32_t, u_int32_t, u_int32_t, u_int32_t);
+static int8_t ad_version(u_int16_t);
+static void ad_drvinit(void);
+
+static struct ad_softc *adtab[NUNIT];
+static int32_t adnlun = 0; /* number of config'd drives */
+static struct intr_config_hook *ad_attach_hook;
+
+static void
+ad_attach(void *notused)
+{
+ struct ad_softc *adp;
+ int32_t ctlr, dev;
+ int8_t model_buf[40+1];
+ int8_t revision_buf[8+1];
+
+ /* now, run through atadevices and look for ATA disks */
+ for (ctlr=0; ctlr<MAXATA && atadevices[ctlr]; ctlr++) {
+ for (dev=0; dev<2; dev++) {
+ if (atadevices[ctlr]->ata_parm[dev]) {
+ adp = adtab[adnlun];
+ if (adp)
+ printf("ad%d: unit already attached\n", adnlun);
+ adp = malloc(sizeof(struct ad_softc), M_DEVBUF, M_NOWAIT);
+ if (adp == NULL)
+ printf("ad%d: failed to allocate driver storage\n", adnlun);
+ bzero(adp, sizeof(struct ad_softc));
+ adp->controller = atadevices[ctlr];
+ adp->ata_parm = atadevices[ctlr]->ata_parm[dev];
+ adp->unit = (dev == 0) ? ATA_MASTER : ATA_SLAVE;
+ adp->cylinders = adp->ata_parm->cylinders;
+ adp->heads = adp->ata_parm->heads;
+ adp->sectors = adp->ata_parm->sectors;
+ adp->total_secs = adp->ata_parm->lbasize;
+
+ /* support multiple sectors / interrupt ? */
+ if (ad_command(adp, ATA_C_SET_MULTI, 0, 0, 0, 16))
+ adp->transfersize = DEV_BSIZE;
+ else {
+ if (ata_wait(adp->controller, ATA_S_DRDY) < 0)
+ adp->transfersize = DEV_BSIZE;
+ else
+ adp->transfersize = 16*DEV_BSIZE;
+ }
+ bpack(adp->ata_parm->model, model_buf, sizeof(model_buf));
+ bpack(adp->ata_parm->revision, revision_buf,
+ sizeof(revision_buf));
+ printf("ad%d: <%s/%s> ATA-%c disk at ata%d as %s\n",
+ adnlun,
+ model_buf, revision_buf,
+ ad_version(adp->ata_parm->versmajor),
+ ctlr,
+ (adp->unit == ATA_MASTER) ? "master" : "slave ");
+ printf("ad%d: %luMB (%u sectors), "
+ "%u cyls, %u heads, %u S/T, %u B/S\n",
+ adnlun,
+ adp->total_secs / ((1024L * 1024L) / DEV_BSIZE),
+ adp->total_secs,
+ adp->cylinders,
+ adp->heads,
+ adp->sectors,
+ DEV_BSIZE);
+ printf("ad%d: %d secs/int, %d depth queue \n",
+ adnlun, adp->transfersize / DEV_BSIZE,
+ adp->ata_parm->queuelen & 0x1f);
+ devstat_add_entry(&adp->stats, "ad", adnlun, DEV_BSIZE,
+ DEVSTAT_NO_ORDERED_TAGS,
+ DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_IDE,
+ 0x180);
+ bufq_init(&adp->queue);
+ adtab[adnlun++] = adp;
+ }
+ }
+ }
+ config_intrhook_disestablish(ad_attach_hook);
+}
+
+static int32_t
+adopen(dev_t dev, int32_t flags, int32_t fmt, struct proc *p)
+{
+ int32_t lun = UNIT(dev);
+ struct ad_softc *adp;
+ struct disklabel label;
+ int32_t error;
+
+#ifdef AD_DEBUG
+printf("adopen: lun=%d adnlun=%d\n", lun, adnlun);
+#endif
+ if (lun >= adnlun || !(adp = adtab[lun]))
+ return ENXIO;
+
+ /* spinwait if anybody else is reading the disk label */
+ while (adp->flags & AD_F_LABELLING)
+ tsleep((caddr_t)&adp->flags, PZERO - 1, "adop1", 1);
+
+ /* protect agains label race */
+ adp->flags |= AD_F_LABELLING;
+
+ /* build disklabel and initilize slice tables */
+ bzero(&label, sizeof label);
+ label.d_secsize = DEV_BSIZE;
+ label.d_nsectors = adp->sectors;
+ label.d_ntracks = adp->heads;
+ label.d_ncylinders = adp->cylinders;
+ label.d_secpercyl = adp->sectors * adp->heads;
+ label.d_secperunit = adp->total_secs;
+
+ error = dsopen("ad", dev, fmt, 0, &adp->slices, &label, ad_strategy,
+ (ds_setgeom_t *)NULL, &ad_cdevsw);
+
+ adp->flags &= ~AD_F_LABELLING;
+ ad_sleep(adp, "adop2");
+ return error;
+}
+
+static int32_t
+adclose(dev_t dev, int32_t flags, int32_t fmt, struct proc *p)
+{
+ int32_t lun = UNIT(dev);
+ struct ad_softc *adp;
+
+#ifdef AD_DEBUG
+printf("adclose: lun=%d adnlun=%d\n", lun, adnlun);
+#endif
+ if (lun >= adnlun || !(adp = adtab[lun]))
+ return ENXIO;
+
+ dsclose(dev, fmt, adp->slices);
+ return 0;
+}
+
+static int32_t
+adread(dev_t dev, struct uio *uio, int32_t ioflag)
+{
+ return physio(adstrategy, NULL, dev, 1, minphys, uio);
+}
+
+static int32_t
+adwrite(dev_t dev, struct uio *uio, int32_t ioflag)
+{
+ return physio(adstrategy, NULL, dev, 0, minphys, uio);
+}
+
+static int32_t
+adioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flags, struct proc *p)
+{
+ struct ad_softc *adp;
+ int32_t lun = UNIT(dev);
+ int32_t error = 0;
+
+ if (lun >= adnlun || !(adp = adtab[lun]))
+ return ENXIO;
+
+ ad_sleep(adp, "adioct");
+ error = dsioctl("sd", dev, cmd, addr, flags, &adp->slices,
+ ad_strategy, (ds_setgeom_t *)NULL);
+
+ if (error != ENOIOCTL)
+ return error;
+ return ENOTTY;
+}
+
+static int32_t
+adpsize(dev_t dev)
+{
+ struct ad_softc *adp;
+ int32_t lun = UNIT(dev);
+
+ if (lun >= adnlun || !(adp = adtab[lun]))
+ return -1;
+ return (dssize(dev, &adp->slices, adopen, adclose));
+}
+
+static void
+adstrategy(struct buf *bp)
+{
+ struct ad_softc *adp;
+ int32_t lun = UNIT(bp->b_dev);
+ int32_t s;
+
+#ifdef AD_DEBUG
+printf("adstrategy: entered\n");
+#endif
+ if (lun >= adnlun || bp->b_blkno < 0 || !(adp = adtab[lun])
+ || bp->b_bcount % DEV_BSIZE != 0) {
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto done;
+ }
+
+ if (dscheck(bp, adp->slices) <= 0)
+ goto done;
+
+ s = splbio();
+
+ /* hang around if somebody else is labelling */
+ if (adp->flags & AD_F_LABELLING)
+ ad_sleep(adp, "adlab");
+
+ bufqdisksort(&adp->queue, bp);
+
+ if (!adp->active)
+ ad_start(adp);
+
+ if (!adp->controller->active)
+ ata_start(adp->controller);
+
+ devstat_start_transaction(&adp->stats);
+
+ splx(s);
+ return;
+
+done:
+ s = splbio();
+ biodone(bp);
+ splx(s);
+}
+
+static void
+ad_strategy(struct buf *bp)
+{
+ adstrategy(bp);
+}
+
+static void
+ad_start(struct ad_softc *adp)
+{
+ struct buf *bp;
+
+#ifdef AD_DEBUG
+printf("ad_start:\n");
+#endif
+ /* newer called when adp->active != 0 SOS */
+ if (adp->active)
+ return;
+
+ if (!(bp = bufq_first(&adp->queue)))
+ return;
+
+ bp->b_driver1 = adp;
+ bufq_remove(&adp->queue, bp);
+
+ /* link onto controller queue */
+ bufq_insert_tail(&adp->controller->ata_queue, bp);
+
+ /* mark the drive as busy */
+ adp->active = 1;
+}
+
+void
+ad_transfer(struct buf *bp)
+{
+ struct ad_softc *adp;
+ u_int32_t blknum, secsprcyl;
+ u_int32_t cylinder, head, sector, count, command;
+
+ /* get request params */
+ adp = bp->b_driver1;
+
+ /* calculate transfer details */
+ blknum = bp->b_pblkno + (adp->donecount / DEV_BSIZE);
+
+#ifdef AD_DEBUG
+ printf("ad_transfer: blknum=%d\n", blknum);
+#endif
+ if (adp->donecount == 0) {
+
+ /* setup transfer parameters */
+ adp->bytecount = bp->b_bcount;
+ secsprcyl = adp->sectors * adp->heads;
+ cylinder = blknum / secsprcyl;
+ head = (blknum % secsprcyl) / adp->sectors;
+ sector = blknum % adp->sectors;
+ count = howmany(adp->bytecount, DEV_BSIZE);
+
+ if (count > 255) /* SOS */
+ printf("ad_transfer: count=%d\n", count);
+
+
+ /* setup transfer length if multible sector access present */
+ adp->currentsize = min(adp->bytecount, adp->transfersize);
+ if (adp->currentsize > DEV_BSIZE)
+ command = (bp->b_flags&B_READ) ? ATA_C_READ_MULTI:ATA_C_WRITE_MULTI;
+ else
+ command = (bp->b_flags&B_READ) ? ATA_C_READ : ATA_C_WRITE;
+
+ /* ready to issue command ? */
+ while (ata_wait(adp->controller, 0) < 0) {
+ printf("ad_transfer: timeout waiting to give command");
+ /*ata_unwedge(adp->controller); SOS */
+ }
+
+ outb(adp->controller->ioaddr + ATA_DRIVE, ATA_D_IBM | adp->unit | head);
+ outb(adp->controller->ioaddr + ATA_PRECOMP, 0); /* no precompensation */
+ outb(adp->controller->ioaddr + ATA_CYL_LSB, cylinder);
+ outb(adp->controller->ioaddr + ATA_CYL_MSB, cylinder >> 8);
+ outb(adp->controller->ioaddr + ATA_SECTOR, sector + 1);
+ outb(adp->controller->ioaddr + ATA_COUNT, count);
+/*
+ if (ata_wait(adp->controller, ATA_S_DRDY) < 0)
+ printf("ad_transfer: timeout waiting to send command");
+*/
+ outb(adp->controller->ioaddr + ATA_CMD, command);
+ }
+
+ /* if this is a read operation, return and wait for interrupt */
+ if (bp->b_flags & B_READ) {
+#ifdef AD_DEBUG
+ printf("ad_transfer: return waiting to read data\n");
+#endif
+ return;
+ }
+
+ /* ready to write data ? */
+ if (ata_wait(adp->controller, ATA_S_DRDY | ATA_S_DSC | ATA_S_DRQ) < 0) {
+ printf("ad_transfer: timeout waiting for DRQ");
+ }
+
+ /* calculate transfer length */
+ adp->currentsize = min(adp->bytecount, adp->transfersize);
+
+ /* output the data */
+#if 0
+ outsw(adp->controller->ioaddr + ATA_DATA,
+ (void *)((int32_t)bp->b_data + adp->donecount),
+ adp->currentsize / sizeof(int16_t));
+#else
+ outsl(adp->controller->ioaddr + ATA_DATA,
+ (void *)((int32_t)bp->b_data + adp->donecount),
+ adp->currentsize / sizeof(int32_t));
+#endif
+ adp->bytecount -= adp->currentsize;
+#ifdef AD_DEBUG
+ printf("ad_transfer: return wrote data\n");
+#endif
+}
+
+void
+ad_interrupt(struct buf *bp)
+{
+ struct ad_softc *adp = bp->b_driver1;
+
+ /* finish DMA stuff */
+
+ /* get drive status */
+ if (ata_wait(adp->controller, 0) < 0)
+ printf("ad_interrupt: timeout waiting for status");
+
+ if (adp->controller->status & (ATA_S_ERROR | ATA_S_CORR)) {
+oops:
+ printf("ad%d: status=%02x error=%02x\n",
+ adp->unit, adp->controller->status, adp->controller->error);
+ if (adp->controller->status & ATA_S_ERROR) {
+ printf("ad_interrupt: hard error");
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ }
+ if (adp->controller->status & ATA_S_CORR)
+ printf("ad_interrupt: soft ECC");
+ }
+ /* if this was a read operation, get the data */
+ if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && adp->active) {
+
+ /* ready to receive data? */
+ if ((adp->controller->status & (ATA_S_DRDY | ATA_S_DSC | ATA_S_DRQ))
+ != (ATA_S_DRDY | ATA_S_DSC | ATA_S_DRQ))
+ printf("ad_interrupt: read interrupt arrived early");
+
+ if (ata_wait(adp->controller, ATA_S_DRDY | ATA_S_DSC | ATA_S_DRQ) != 0){
+ printf("ad_interrupt: read error detected late");
+ goto oops;
+ }
+
+ /* calculate transfer length */
+ adp->currentsize = min(adp->bytecount, adp->currentsize);
+
+ /* data are ready, get them */
+#if 0
+ insw(adp->controller->ioaddr + ATA_DATA,
+ (void *)((int32_t)bp->b_data + adp->donecount),
+ adp->currentsize / sizeof(int16_t));
+#else
+ insl(adp->controller->ioaddr + ATA_DATA,
+ (void *)((int32_t)bp->b_data + adp->donecount),
+ adp->currentsize / sizeof(int32_t));
+#endif
+ adp->bytecount -= adp->currentsize;
+#ifdef AD_DEBUG
+ printf("ad_interrupt: read in data\n");
+#endif
+ }
+
+ /* finish up this tranfer, check for more work on this buffer */
+ if (adp->controller->active) {
+ if ((bp->b_flags & B_ERROR) == 0) {
+ adp->donecount += adp->currentsize;
+#ifdef AD_DEBUG
+ printf("ad_interrupt: %s operation OK\n", (bp->b_flags & B_READ)?"R":"W");
+#endif
+ if (adp->bytecount > 0) {
+ ad_transfer(bp); /* MESSY!! only needed for W */
+ return;
+ }
+ }
+ bufq_remove(&adp->controller->ata_queue, bp);
+ bp->b_resid = bp->b_bcount - adp->donecount;
+ adp->donecount = 0;
+ devstat_end_transaction(&adp->stats, bp->b_bcount - bp->b_resid,
+ DEVSTAT_TAG_NONE,
+ (bp->b_flags & B_READ) ?
+ DEVSTAT_READ : DEVSTAT_WRITE);
+ biodone(bp);
+ adp->active = 0;
+ }
+ adp->controller->active = ATA_IDLE;
+ ad_start(adp);
+#ifdef AD_DEBUG
+ printf("ad_interrupt: completed\n");
+#endif
+ ata_start(adp->controller);
+}
+
+static void
+ad_sleep(struct ad_softc *adp, int8_t *mesg)
+{
+ int32_t s = splbio();
+
+ while (adp->controller->active)
+ tsleep((caddr_t)&adp->controller->active, PZERO - 1, mesg, 1);
+ splx(s);
+}
+
+static int32_t
+ad_command(struct ad_softc *adp, u_int32_t command,
+ u_int32_t cylinder, u_int32_t head, u_int32_t sector,
+ u_int32_t count)
+{
+ /* ready to issue command ? */
+ while (ata_wait(adp->controller, 0) < 0) {
+ printf("ad_transfer: timeout waiting to give command");
+ return -1;
+ }
+
+ outb(adp->controller->ioaddr + ATA_DRIVE, ATA_D_IBM | adp->unit | head);
+ outb(adp->controller->ioaddr + ATA_PRECOMP, 0); /* no precompensation */
+ outb(adp->controller->ioaddr + ATA_CYL_LSB, cylinder);
+ outb(adp->controller->ioaddr + ATA_CYL_MSB, cylinder >> 8);
+ outb(adp->controller->ioaddr + ATA_SECTOR, sector + 1);
+ outb(adp->controller->ioaddr + ATA_COUNT, count);
+/*
+ if (ata_wait(adp->controller, ATA_S_DRDY) < 0) {
+ printf("ad_transfer: timeout waiting to send command");
+ return -1;
+ }
+*/
+ adp->controller->active = ATA_IGNORE_INTR;
+ outb(adp->controller->ioaddr + ATA_CMD, command);
+ return 0;
+}
+
+static int8_t
+ad_version(u_int16_t version)
+{
+ int32_t bit;
+
+ if (version == 0xffff)
+ return '?';
+ for (bit = 15; bit >= 0; bit--)
+ if (version & (1<<bit))
+ return ('0' + bit);
+ return '?';
+}
+
+static void
+ad_drvinit(void)
+{
+ static ad_devsw_installed = 0;
+
+ if (!ad_devsw_installed) {
+ cdevsw_add_generic(BDEV_MAJOR, CDEV_MAJOR, &ad_cdevsw);
+ ad_devsw_installed = 1;
+ }
+ /* register callback for when interrupts are enabled */
+ if (!(ad_attach_hook =
+ (struct intr_config_hook *)malloc(sizeof(struct intr_config_hook),
+ M_TEMP, M_NOWAIT))) {
+ printf("ad: malloc attach_hook failed\n");
+ return;
+ }
+ bzero(ad_attach_hook, sizeof(struct intr_config_hook));
+
+ ad_attach_hook->ich_func = ad_attach;
+ if (config_intrhook_establish(ad_attach_hook) != 0) {
+ printf("ad: config_intrhook_establish failed\n");
+ free(ad_attach_hook, M_TEMP);
+ }
+}
+
+SYSINIT(addev, SI_SUB_DRIVERS, SI_ORDER_SECOND, ad_drvinit, NULL)
+#endif /* NATA && NATADISK */
diff --git a/sys/dev/ata/ata-disk.h b/sys/dev/ata/ata-disk.h
new file mode 100644
index 000000000000..618b8e2c1b82
--- /dev/null
+++ b/sys/dev/ata/ata-disk.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 1998,1999 Søren Schmidt
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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. 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.
+ *
+ * $Id: ata-disk.h,v 1.5 1999/03/01 12:11:01 sos Exp $
+ */
+
+/*
+ * Structure describing an ATA disk
+ */
+struct ad_softc {
+ struct ata_softc *controller; /* ptr to parent ctrl */
+ struct ata_params *ata_parm; /* ata device params */
+ struct diskslices *slices;
+ int32_t unit; /* ATA_MASTER or ATA_SLAVE */
+ u_int16_t cylinders; /* disk geometry (probed) */
+ u_int8_t heads;
+ u_int8_t sectors;
+ u_int32_t total_secs; /* total # of sectors (LBA) */
+ u_int32_t transfersize; /* size of each transfer */
+ u_int32_t currentsize; /* size of current transfer */
+ struct buf_queue_head queue; /* head of request queue */
+ u_int32_t bytecount; /* bytes to transfer */
+ u_int32_t donecount; /* bytes transferred */
+ u_int32_t active; /* active processing request */
+ u_int32_t flags; /* drive flags */
+#define AD_F_LABELLING 0x0001
+
+ struct devstat stats; /* devstat entry */
+};
+
+void ad_transfer(struct buf *);
+void ad_interrupt(struct buf *);
+
diff --git a/sys/dev/ata/atapi-all.c b/sys/dev/ata/atapi-all.c
new file mode 100644
index 000000000000..a5c9fdd1ec56
--- /dev/null
+++ b/sys/dev/ata/atapi-all.c
@@ -0,0 +1,439 @@
+/*-
+ * Copyright (c) 1998,1999 Søren Schmidt
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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. 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.
+ *
+ * $Id: atapi-all.c,v 1.5 1999/03/01 21:03:15 sos Exp sos $
+ */
+
+#include "ata.h"
+#include "atapicd.h"
+#include "atapist.h"
+/*#include "atapifd.h"*/
+#include "opt_devfs.h"
+
+#if NATA > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/buf.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/devicestat.h>
+#include <machine/clock.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/atapi-all.h>
+
+/* prototypes */
+static void atapi_attach(void *);
+static int8_t *atapi_type(int32_t);
+static int8_t *atapi_cmd2str(u_int8_t);
+static void atapi_init(void);
+
+/* extern references */
+int32_t acdattach(struct atapi_softc *);
+int32_t afdattach(struct atapi_softc *);
+int32_t astattach(struct atapi_softc *);
+
+static struct intr_config_hook *atapi_attach_hook;
+
+static void
+atapi_attach(void *notused)
+{
+ int32_t ctlr, dev;
+ int8_t model_buf[40+1];
+ int8_t revision_buf[8+1];
+
+ /* now, run through atadevices and look for ATAPI devices */
+ for (ctlr=0; ctlr<MAXATA && atadevices[ctlr]; ctlr++) {
+ for (dev=0; dev<2; dev++) {
+ struct ata_softc *scp = atadevices[ctlr];
+
+ if (scp->atapi_parm[dev]) {
+ struct atapi_softc *atp;
+
+ atp = malloc(sizeof(struct atapi_softc), M_DEVBUF, M_NOWAIT);
+ bzero(atp, sizeof(struct atapi_softc));
+ atp->controller = scp;
+ atp->atapi_parm = scp->atapi_parm[dev];
+ atp->unit = (dev) ? ATA_SLAVE : ATA_MASTER;
+
+ switch (scp->atapi_parm[dev]->device_type) {
+#if NATAPICD > 0
+ case ATAPI_TYPE_CDROM:
+ if (acdattach(atp))
+ goto notfound;
+ break;
+#endif
+#if NATAPIFD > 0
+ case ATAPI_TYPE_DIRECT:
+ if (afdattach(atp))
+ goto notfound;
+ break;
+#endif
+#if NATAPIST > 0
+ case ATAPI_TYPE_TAPE:
+ if (astattach(atp))
+ goto notfound;
+ break;
+#endif
+notfound:
+ default:
+ free(atp, M_DEVBUF);
+ bpack(scp->atapi_parm[dev]->model, model_buf,
+ sizeof(model_buf));
+ bpack(scp->atapi_parm[dev]->revision, revision_buf,
+ sizeof(revision_buf));
+ printf("atapi: <%s/%s> %s device at ata%d as %s "
+ "- NO DRIVER!\n",
+ model_buf, revision_buf,
+ atapi_type(scp->atapi_parm[dev]->device_type),
+ ctlr,
+ (dev) ? "slave" : "master ");
+ }
+ }
+ }
+ }
+ config_intrhook_disestablish(atapi_attach_hook);
+}
+
+int32_t
+atapi_queue_cmd(struct atapi_softc *atp, int8_t *ccb, void *data,
+ int32_t count, int32_t flags,
+ atapi_callback_t callback, void *driver, struct buf *bp)
+{
+ struct atapi_request *request;
+ int32_t error = 0;
+ int32_t s = splbio();
+
+ if (!(request = malloc(sizeof(struct atapi_request), M_DEVBUF, M_NOWAIT))) {
+ splx(s);
+ return -1;
+ }
+ bzero(request, sizeof(struct atapi_request));
+ request->device = atp;
+ request->data = data;
+ request->bytecount = count;
+ request->flags = flags;
+ if (callback) {
+ request->callback = callback;
+ request->bp = bp;
+ request->driver = driver;
+ }
+ request->ccbsize = (atp->atapi_parm->cmdsize) ? 16 : 12;
+ bcopy(ccb, request->ccb, request->ccbsize);
+
+ /* link onto controller queue */
+ TAILQ_INSERT_TAIL(&atp->controller->atapi_queue, request, chain);
+#ifdef ATAPI_DEBUG
+ printf("atapi: trying to start %s command\n", atapi_cmd2str(ccb[0]));
+#endif
+ /* try to start controller */
+ if (!atp->controller->active)
+ ata_start(atp->controller);
+
+ if (!callback) {
+ /* wait for command to complete */
+ tsleep((caddr_t)request, PRIBIO, "atprq", 0);
+#ifdef ATAPI_DEBUG
+ printf("atapi: phew, got back from tsleep\n");
+#endif
+ error = request->result;
+ free(request, M_DEVBUF);
+ }
+ splx(s);
+ return error;
+}
+
+void
+atapi_transfer(struct atapi_request *request)
+{
+ struct atapi_softc *atp;
+ int32_t timeout;
+ int8_t int_reason; /* not needed really */
+
+ /* get device params */
+ atp = request->device;
+
+ /* start ATAPI operation */
+ outb(atp->controller->ioaddr + ATA_DRIVE, ATA_D_IBM | atp->unit);
+ if (atapi_wait(atp->controller, 0) < 0) {
+ printf ("atapi_transfer: timeout waiting to send PACKET command\n");
+ /* now what ? SOS */
+ }
+ outb(atp->controller->ioaddr + ATA_PRECOMP, 0);
+ outb(atp->controller->ioaddr + ATA_COUNT, 0);
+ outb(atp->controller->ioaddr + ATA_SECTOR, 0);
+ outb(atp->controller->ioaddr + ATA_CYL_LSB, request->bytecount);
+ outb(atp->controller->ioaddr + ATA_CYL_MSB, request->bytecount >> 8);
+ outb(atp->controller->ioaddr + ATA_CMD, ATA_C_PACKET_CMD);
+
+ /* wait for interrupt ?? not supported yet. */
+ /* just return then and let atapi_interrupt handle it */
+
+ /* ready to write ATAPI command */
+ timeout = 5000; /* might be less for fast devices */
+ while (timeout--) {
+ int_reason = inb(atp->controller->ioaddr + ATA_COUNT);
+ atp->controller->status = inb(atp->controller->ioaddr + ATA_STATUS);
+ if (((int_reason & (ATA_I_CMD | ATA_I_IN)) |
+ (atp->controller->status&(ATA_S_DRQ|ATA_S_BSY))) == ATAPI_P_CMDOUT)
+ break;
+ DELAY(20);
+ }
+ if (timeout <= 0) {
+ atp->controller->error = inb(atp->controller->ioaddr + ATA_ERROR);
+ printf("atapi_transfer: bad command phase\n");
+ /* now what ?? SOS atapi-done & again */
+ }
+
+ /* send actual command */
+ outsw(atp->controller->ioaddr + ATA_DATA, request->ccb,
+ request->ccbsize / sizeof(int16_t));
+}
+
+void
+atapi_interrupt(struct atapi_request *request)
+{
+ struct atapi_softc *atp;
+ int32_t length, reason, resid;
+
+#ifdef ATAPI_DEBUG
+printf("atapi_interrupt: enter\n");
+#endif
+ /* get device params */
+ atp = request->device;
+
+ /* get drive status */
+ if (atapi_wait(atp->controller, 0) < 0) {
+ printf("atapi_interrupt: timeout waiting for status");
+ /* maybe check sense code ?? SOS */
+ return;
+ }
+ atp->controller->status = inb(atp->controller->ioaddr + ATA_STATUS);
+ atp->controller->error = inb(atp->controller->ioaddr + ATA_ERROR);
+
+ length = inb(atp->controller->ioaddr + ATA_CYL_LSB);
+ length |= inb(atp->controller->ioaddr + ATA_CYL_MSB) << 8;
+ reason = (inb(atp->controller->ioaddr + ATA_COUNT)&(ATA_I_CMD|ATA_I_IN)) |
+ (atp->controller->status & ATA_S_DRQ);
+
+#ifdef ATAPI_DEBUG
+printf("atapi_interrupt: length=%d reason=0x%02x\n", length, reason);
+#endif
+
+ switch (reason) {
+
+ case ATAPI_P_CMDOUT:
+ printf("atapi_interrupt: command interrupt, not supported yet\n");
+#if notyet
+ /* send actual command */
+ if (!(atp->status & ATA_S_DRQ))
+ printf("atapi_interrupt: command interrupt, but no DRQ\n");
+ else
+ outsw(atp->controller->ioaddr + ATA_DATA, request->ccb,
+ request->ccdsize / sizeof(int16_t));
+#endif
+ return;
+
+ case ATAPI_P_WRITE:
+ if (request->flags & A_READ) {
+ printf("atapi_interrupt: trying to write on read buffer\n");
+ break;
+ }
+ if (request->bytecount < length) {
+ printf("atapi_interrupt: write data underrun %d/%d\n",
+ length, request->bytecount);
+ outsw(atp->controller->ioaddr + ATA_DATA,
+ (void *)((int32_t)request->data), /* + donecount ?? */
+ length / sizeof(int16_t));
+ for (resid=request->bytecount; resid<length; resid+=sizeof(int16_t))
+ outw(atp->controller->ioaddr + ATA_DATA, 0);
+ }
+ else {
+ outsw(atp->controller->ioaddr + ATA_DATA,
+ (void *)((int32_t)request->data), /* + donecount ?? */
+ length / sizeof(int16_t));
+ }
+ request->bytecount -= length;
+ request->data += length;
+ return;
+
+ case ATAPI_P_READ:
+ if (!(request->flags & A_READ)) {
+ printf("atapi_interrupt: trying to read on write buffer\n");
+ break;
+ }
+ if (request->bytecount < length) {
+ printf("atapi_interrupt: read data overrun %d/%d\n",
+ length, request->bytecount);
+ insw(atp->controller->ioaddr + ATA_DATA,
+ (void *)((int32_t)request->data), /* + donecount ?? */
+ length / sizeof(int16_t));
+ for (resid=request->bytecount; resid<length; resid+=sizeof(int16_t))
+ inw(atp->controller->ioaddr + ATA_DATA);
+ }
+ else {
+ insw(atp->controller->ioaddr + ATA_DATA,
+ (void *)((int32_t)request->data), /* + donecount ?? */
+ length / sizeof(int16_t));
+ }
+ request->bytecount -= length;
+ request->data += length;
+ return;
+
+ case ATAPI_P_ABORT:
+ case ATAPI_P_DONE:
+ request->result = 0;
+ if (atp->controller->status & (ATA_S_ERROR | ATA_S_DWF)) {
+ /* check sense !! SOS */
+ request->result = atp->controller->error;
+ }
+#ifdef ATAPI_DEBUG
+ if (request->bytecount > 0) {
+ printf("atapi_interrupt: %s size problem, %d bytes residue\n",
+ (request->flags & A_READ) ? "read" : "write",
+ request->bytecount);
+ }
+#endif
+ break;
+ default:
+ printf("atapi_interrupt: unknown transfer phase\n");
+ }
+
+ TAILQ_REMOVE(&atp->controller->atapi_queue, request, chain);
+#ifdef ATAPI_DEBUG
+printf("atapi_interrupt: error=%02x\n", request->result);
+#endif
+ if (request->callback) {
+ (request->callback)(request);
+ free(request, M_DEVBUF);
+ }
+ else
+ wakeup((caddr_t)request);
+ atp->controller->active = ATA_IDLE;
+ ata_start(atp->controller);
+}
+
+void
+atapi_error(struct atapi_softc *atp, int32_t error)
+{
+ printf("atapi: error = %02x\n", error);
+}
+
+void
+atapi_dump(int8_t *label, void *data, int32_t len)
+{
+ u_int8_t *p = data;
+
+ printf ("atapi: %s %x", label, *p++);
+ while (--len > 0) printf ("-%x", *p++);
+ printf ("\n");
+}
+
+static int8_t *
+atapi_type(int32_t type)
+{
+ switch (type) {
+ case ATAPI_TYPE_CDROM:
+ return "CDROM";
+ case ATAPI_TYPE_DIRECT:
+ return "floppy";
+ case ATAPI_TYPE_TAPE:
+ return "tape";
+ case ATAPI_TYPE_OPTICAL:
+ return "optical";
+ default:
+ return "Unknown";
+ }
+}
+
+static int8_t *
+atapi_cmd2str(u_int8_t cmd)
+{
+ switch (cmd) {
+ case 0x00: return ("TEST_UNIT_READY");
+ case 0x01: return ("REZERO_UNIT");
+ case 0x03: return ("REQUEST_SENSE");
+ case 0x04: return ("FORMAT_UNIT");
+ case 0x1a: return ("TAPE_MODE_SENSE");
+ case 0x1b: return ("START_STOP");
+ case 0x1e: return ("PREVENT_ALLOW");
+ case 0x25: return ("READ_CAPACITY");
+ case 0x28: return ("READ_BIG");
+ case 0x2a: return ("WRITE_BIG");
+ case 0x35: return ("SYNCHRONIZE_CACHE");
+ case 0x42: return ("READ_SUBCHANNEL");
+ case 0x43: return ("READ_TOC");
+ case 0x51: return ("READ_DISC_INFO");
+ case 0x52: return ("READ_TRACK_INFO");
+ case 0x53: return ("RESERVE_TRACK");
+ case 0x54: return ("SEND_OPC_INFO");
+ case 0x55: return ("MODE_SELECT");
+ case 0x58: return ("REPAIR_TRACK");
+ case 0x59: return ("READ_MASTER_CUE");
+ case 0x5a: return ("MODE_SENSE");
+ case 0x5b: return ("CLOSE_TRACK/SESSION");
+ case 0x5c: return ("READ_BUFFER_CAPACITY");
+ case 0x5d: return ("SEND_CUE_SHEET");
+ case 0x47: return ("PLAY_MSF");
+ case 0x4b: return ("PAUSE");
+ case 0x48: return ("PLAY_TRACK");
+ case 0xa1: return ("BLANK_CMD");
+ case 0xa5: return ("PLAY_BIG");
+ case 0xb4: return ("PLAY_CD");
+ case 0xbd: return ("MECH_STATUS");
+ case 0xbe: return ("READ_CD");
+ default: {
+ static int8_t buffer[16];
+ sprintf(buffer, "Unknown 0x%02x", cmd);
+ return buffer;
+ }
+ }
+}
+
+static void
+atapi_init(void)
+{
+ /* register callback for when interrupts are enabled */
+ if (!(atapi_attach_hook =
+ (struct intr_config_hook *)malloc(sizeof(struct intr_config_hook),
+ M_TEMP, M_NOWAIT))) {
+ printf("atapi: malloc attach_hook failed\n");
+ return;
+ }
+ bzero(atapi_attach_hook, sizeof(struct intr_config_hook));
+
+ atapi_attach_hook->ich_func = atapi_attach;
+ if (config_intrhook_establish(atapi_attach_hook) != 0) {
+ printf("atapi: config_intrhook_establish failed\n");
+ free(atapi_attach_hook, M_TEMP);
+ }
+
+}
+
+SYSINIT(atconf, SI_SUB_CONFIGURE, SI_ORDER_SECOND, atapi_init, NULL)
+#endif /* NATA */
diff --git a/sys/dev/ata/atapi-all.h b/sys/dev/ata/atapi-all.h
new file mode 100644
index 000000000000..ba0e42e1f853
--- /dev/null
+++ b/sys/dev/ata/atapi-all.h
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 1998,1999 Søren Schmidt
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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. 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.
+ *
+ * $Id: atapi-all.h,v 1.2 1999/03/01 12:11:01 sos Exp $
+ */
+
+/* ATAPI misc defines */
+#define ATAPI_MAGIC_LSB 0x14
+#define ATAPI_MAGIC_MSB 0xeb
+#define ATAPI_P_READ (ATA_S_DRQ | ATA_I_IN)
+#define ATAPI_P_WRITE (ATA_S_DRQ)
+#define ATAPI_P_CMDOUT (ATA_S_DRQ | ATA_I_CMD)
+#define ATAPI_P_ABORT 0
+#define ATAPI_P_DONE (ATA_I_IN | ATA_I_CMD)
+
+/* error register bits */
+#define ATAPI_E_ILI 0x01 /* illegal length indication */
+#define ATAPI_E_EOM 0x02 /* end of media detected */
+#define ATAPI_E_ABRT 0x04 /* command aborted */
+#define ATAPI_E_MCR 0x08 /* media change requested */
+#define ATAPI_SK_MASK 0xf0 /* sense key mask */
+#define ATAPI_SK_NO_SENSE 0x00 /* no specific sense key info */
+#define ATAPI_SK_RECOVERED_ERROR 0x10 /* command OK, data recovered */
+#define ATAPI_SK_NOT_READY 0x20 /* no access to drive */
+#define ATAPI_SK_MEDIUM_ERROR 0x30 /* non-recovered data error */
+#define ATAPI_SK_HARDWARE_ERROR 0x40 /* non-recoverable HW failure */
+#define ATAPI_SK_ILLEGAL_REQUEST 0x50 /* invalid command param(s) */
+#define ATAPI_SK_UNIT_ATTENTION 0x60 /* media changed */
+#define ATAPI_SK_DATA_PROTECT 0x70 /* reading read-protected sec */
+#define ATAPI_SK_BLANK_CHECK 0x80 /* blank check */
+#define ATAPI_SK_VENDOR_SPECIFIC 0x90 /* vendor specific skey */
+#define ATAPI_SK_COPY_ABORTED 0xa0 /* copy aborted */
+#define ATAPI_SK_ABORTED_COMMAND 0xb0 /* command aborted, try again */
+#define ATAPI_SK_EQUAL 0xc0 /* equal */
+#define ATAPI_SK_VOLUME_OVERFLOW 0xd0 /* volume overflow */
+#define ATAPI_SK_MISCOMPARE 0xe0 /* data dont match the medium */
+#define ATAPI_SK_RESERVED 0xf0
+
+/* ATAPI commands */
+#define ATAPI_TEST_UNIT_READY 0x00 /* check if device is ready */
+#define ATAPI_REZERO_UNIT 0x01 /* reinit device */
+#define ATAPI_REQUEST_SENSE 0x03 /* get sense data */
+#define ATAPI_START_STOP 0x1b /* start/stop the media */
+#define ATAPI_PREVENT_ALLOW 0x1e /* media removal */
+#define ATAPI_READ_CAPACITY 0x25 /* get volume capacity */
+#define ATAPI_READ_BIG 0x28 /* read data */
+#define ATAPI_WRITE_BIG 0x2a /* write data */
+#define ATAPI_SYNCHRONIZE_CACHE 0x35 /* flush buf, close channel */
+#define ATAPI_READ_SUBCHANNEL 0x42 /* get subchannel info */
+#define ATAPI_READ_TOC 0x43 /* get table of contents */
+#define ATAPI_PLAY_MSF 0x47 /* play by MSF address */
+#define ATAPI_PLAY_TRACK 0x48 /* play by track number */
+#define ATAPI_PAUSE 0x4b /* stop/start audio operation */
+#define ATAPI_READ_TRACK_INFO 0x52 /* get track info structure */
+#define ATAPI_MODE_SELECT 0x55 /* set device parameters */
+#define ATAPI_MODE_SENSE 0x5a /* get device parameters */
+#define ATAPI_CLOSE_TRACK 0x5b /* close track/session */
+#define ATAPI_BLANK 0xa1 /* blank (erase) media */
+#define ATAPI_PLAY_BIG 0xa5 /* play by lba */
+#define ATAPI_LOAD_UNLOAD 0xa6 /* changer control command */
+#define ATAPI_PLAY_CD 0xb4 /* universal play command */
+#define ATAPI_MECH_STATUS 0xbd /* get changer status */
+#define ATAPI_READ_CD 0xbe /* read data */
+
+/* ATAPI device parameter information */
+struct atapi_params {
+ u_int cmdsize :2; /* packet command size */
+#define ATAPI_PSIZE_12 0 /* 12 bytes */
+#define ATAPI_PSIZE_16 1 /* 16 bytes */
+
+ u_int :3;
+ u_int drqtype :2; /* DRQ type */
+#define ATAPI_DRQT_MPROC 0 /* cpu 3 ms delay */
+#define ATAPI_DRQT_INTR 1 /* intr 10 ms delay */
+#define ATAPI_DRQT_ACCEL 2 /* accel 50 us delay */
+
+ u_int removable :1; /* device is removable */
+ u_int device_type :5; /* device type */
+#define ATAPI_TYPE_DIRECT 0 /* disk/floppy */
+#define ATAPI_TYPE_TAPE 1 /* streaming tape */
+#define ATAPI_TYPE_CDROM 5 /* CD-ROM device */
+#define ATAPI_TYPE_OPTICAL 7 /* optical disk */
+
+ u_int :1;
+ u_int proto :2; /* command protocol */
+#define ATAPI_PROTO_ATAPI 2
+
+ int16_t reserved1[9];
+ int8_t serial[20]; /* serial number */
+ int16_t reserved2[3];
+ int8_t revision[8]; /* firmware revision */
+ int8_t model[40]; /* model name */
+ int16_t reserved3[2];
+ u_int8_t vendor_cap; /* vendor capabilities */
+ u_int8_t dmaflag :1; /* DMA supported */
+ u_int8_t lbaflag :1; /* LBA supported - always 1 */
+ u_int8_t iordydis :1; /* IORDY can be disabled */
+ u_int8_t iordyflag :1; /* IORDY supported */
+ u_int8_t :1;
+ u_int8_t ovlapflag :1; /* overlap supported */
+ u_int8_t :1;
+ u_int8_t idmaflag :1; /* interleaved DMA supported */
+ int16_t reserved4;
+ u_int16_t pio_timing; /* PIO cycle timing */
+ u_int16_t dma_timing; /* DMA cycle timing */
+ u_int16_t flags;
+#define ATAPI_FLAG_54_58 1 /* words 54-58 valid */
+#define ATAPI_FLAG_64_70 2 /* words 64-70 valid */
+
+ int16_t reserved5[8];
+ u_int8_t swdma_flag; /* singleword DMA supported */
+ u_int8_t swdma_active; /* singleword DMA active */
+ u_int8_t mwdma_flag; /* multiword DMA supported */
+ u_int8_t mwdma_active; /* multiword DMA active */
+ u_int8_t apio_flag; /* advanced PIO supported */
+ u_int8_t reserved6;
+ u_int16_t mwdma_min; /* min. M/W DMA time/word ns */
+ u_int16_t mwdma_dflt; /* rec. M/W DMA time ns */
+ u_int16_t pio_nfctl_min; /* min. PIO cycle w/o flow */
+ u_int16_t pio_iordy_min; /* min. PIO cycle IORDY flow */
+ int16_t reserved7[2];
+ u_int16_t rls_ovlap; /* rel time (us) for overlap */
+ u_int16_t rls_service; /* rel time (us) for service */
+};
+
+struct atapi_softc {
+ struct ata_softc *controller; /* ptr to parent ctrl */
+ struct atapi_params *atapi_parm; /* ata device params */
+ int32_t unit; /* ATA_MASTER or ATA_SLAVE */
+ u_int32_t flags; /* drive flags */
+};
+
+typedef void atapi_callback_t(struct atapi_request *);
+
+struct atapi_request {
+ struct atapi_softc *device; /* ptr to parent device */
+ void *driver; /* ptr to calling driver */
+ u_int8_t ccb[16]; /* command control block */
+ int32_t ccbsize; /* size of ccb (12 | 16) */
+ int32_t flags;
+#define A_READ 0x0001
+
+ u_int32_t bytecount; /* bytes to transfer */
+ u_int32_t result; /* result code */
+ int8_t *data; /* pointer to data buf */
+ struct buf *bp; /* associated buf ptr */
+ atapi_callback_t *callback; /* ptr to callback func */
+ TAILQ_ENTRY(atapi_request) chain; /* list management */
+};
+
+void atapi_transfer(struct atapi_request *);
+void atapi_interrupt(struct atapi_request *);
+int atapi_queue_cmd(struct atapi_softc *, int8_t [], void *, int32_t, int32_t, atapi_callback_t, void *, struct buf *);
+void atapi_error(struct atapi_softc *, int32_t);
+void atapi_dump(int8_t *, void *, int32_t);
+
diff --git a/sys/dev/ata/atapi-cd.c b/sys/dev/ata/atapi-cd.c
new file mode 100644
index 000000000000..6c16dd803ca5
--- /dev/null
+++ b/sys/dev/ata/atapi-cd.c
@@ -0,0 +1,1447 @@
+/*-
+ * Copyright (c) 1998,1999 Søren Schmidt
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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. 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.
+ *
+ * $Id: atapi-cd.c,v 1.5 1999/03/01 21:03:15 sos Exp sos $
+ */
+
+#include "ata.h"
+#include "atapicd.h"
+#include "opt_devfs.h"
+
+#if NATA > 0 && NATAPICD > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/disklabel.h>
+#include <sys/devicestat.h>
+#include <sys/cdio.h>
+#include <sys/wormio.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif
+#include <dev/ata/ata-all.h>
+#include <dev/ata/atapi-all.h>
+#include <dev/ata/atapi-cd.h>
+
+static d_open_t acdopen;
+static d_close_t acdclose;
+static d_write_t acdwrite;
+static d_read_t acdread;
+static d_ioctl_t acdioctl;
+static d_strategy_t acdstrategy;
+
+#define CDEV_MAJOR 69
+#define BDEV_MAJOR 19
+static struct cdevsw acd_cdevsw = {
+ acdopen, acdclose, acdread, acdwrite,
+ acdioctl, nostop, nullreset, nodevtotty,
+ seltrue, nommap, acdstrategy, "acd",
+ NULL, -1, nodump, nopsize,
+ D_DISK, 0, -1
+};
+
+#define NUNIT 16 /* Max # of devices */
+
+#define F_BOPEN 0x0001 /* The block device is opened */
+#define F_MEDIA_CHANGED 0x0002 /* The media have changed since open */
+#define F_LOCKED 0x0004 /* This unit is locked (or should be) */
+#define F_TRACK_PREP 0x0008 /* Track should be prep'ed */
+#define F_TRACK_PREPED 0x0010 /* Track has been prep'ed */
+#define F_DISK_PREPED 0x0020 /* Disk has been prep'ed */
+#define F_WRITTEN 0x0040 /* The medium has been written to */
+
+static struct acd_softc *acdtab[NUNIT];
+static int32_t acdnlun = 0; /* Number of configured drives */
+
+int32_t acdattach(struct atapi_softc *);
+static struct acd_softc *acd_init_lun(struct atapi_softc *, int, struct devstat *);
+static void acd_start(struct acd_softc *);
+static void acd_done(struct atapi_request *);
+static int32_t acd_test_unit_ready (struct acd_softc *);
+static int32_t acd_lock_device (struct acd_softc *, int32_t);
+static int32_t acd_start_device (struct acd_softc *, int32_t);
+static int32_t acd_pause_device (struct acd_softc *, int32_t);
+static int32_t acd_mode_sense (struct acd_softc *, u_int8_t, void *, int32_t);
+static int32_t acd_mode_select (struct acd_softc *, void *, int32_t);
+static int32_t acd_read_toc(struct acd_softc *);
+static void acd_describe(struct acd_softc *);
+static int32_t acd_setchan(struct acd_softc *, u_int8_t, u_int8_t, u_int8_t, u_int8_t);
+static int32_t acd_eject(struct acd_softc *, int);
+static void acd_select_slot(struct acd_softc *);
+static int32_t acd_rezero_unit(struct acd_softc *);
+static int32_t acd_open_disk(struct acd_softc *, int);
+static int32_t acd_open_track(struct acd_softc *, struct wormio_prepare_track *);
+static int32_t acd_close_track(struct acd_softc *);
+static int32_t acd_close_disk(struct acd_softc *);
+static int32_t acd_read_track_info(struct acd_softc *, int, struct acd_track_info*);
+static int32_t acd_blank_disk(struct acd_softc *);
+
+int
+acdattach(struct atapi_softc *atp)
+{
+ struct acd_softc *cdp;
+ struct changer *chp;
+ int32_t error, count;
+
+ if (acdnlun >= NUNIT) {
+ printf("acd: too many units\n");
+ return -1;
+ }
+ if ((cdp = acd_init_lun(atp, acdnlun, NULL)) == NULL) {
+ printf("acd: out of memory\n");
+ return -1;
+ }
+
+ /* Get drive capabilities, some drives needs this repeated */
+ for (count = 0 ; count < 5 ; count++) {
+ if (!(error = acd_mode_sense(cdp, ATAPI_CDROM_CAP_PAGE,
+ &cdp->cap, sizeof(cdp->cap))))
+ break;
+ }
+ if (error) {
+ free(cdp, M_TEMP);
+ return -1;
+ }
+ cdp->cap.max_speed = ntohs(cdp->cap.max_speed);
+ cdp->cap.max_vol_levels = ntohs(cdp->cap.max_vol_levels);
+ cdp->cap.buf_size = ntohs(cdp->cap.buf_size);
+ cdp->cap.cur_speed = ntohs(cdp->cap.cur_speed);
+ acd_describe(cdp);
+
+ /* If this is a changer device, allocate the neeeded lun's */
+ if (cdp->cap.mech == MST_MECH_CHANGER) {
+ int8_t ccb[16] = { ATAPI_MECH_STATUS,
+ 0, 0, 0, 0, 0, 0, 0,
+ sizeof(struct changer)>>8, sizeof(struct changer),
+ 0, 0, 0, 0, 0, 0 };
+
+ chp = malloc(sizeof(struct changer), M_TEMP, M_NOWAIT);
+ if (chp == NULL) {
+ printf("acd: out of memory\n");
+ return 0;
+ }
+ bzero(chp, sizeof(struct changer));
+ error = atapi_queue_cmd(cdp->atp, ccb, chp, sizeof(struct changer),
+ A_READ, NULL, NULL, NULL);
+
+#ifdef ACD_DEBUG
+ printf("error=%02x curr=%02x slots=%d len=%d\n",
+ error, chp->current_slot, chp->slots, htons(chp->table_length));
+#endif
+
+ if (!error) {
+ struct acd_softc *tmpcdp = cdp;
+ int32_t count;
+ int8_t string[16];
+
+ chp->table_length = htons(chp->table_length);
+ for (count = 0; count < chp->slots && acdnlun < NUNIT; count++) {
+ if (count > 0) {
+ tmpcdp = acd_init_lun(atp, acdnlun, cdp->stats);
+ if (!tmpcdp) {
+ printf("acd: out of memory\n");
+ return -1;
+ }
+ }
+ tmpcdp->slot = count;
+ tmpcdp->changer_info = chp;
+ printf("acd%d: changer slot %d %s\n", acdnlun, count,
+ (chp->slot[count].present ? "CD present" : "empty"));
+ acdtab[acdnlun++] = tmpcdp;
+ }
+ if (acdnlun >= NUNIT) {
+ printf("acd: too many units\n");
+ return 0;
+ }
+ sprintf(string, "acd%d-", cdp->lun);
+ devstat_add_entry(cdp->stats, string, tmpcdp->lun, DEV_BSIZE,
+ DEVSTAT_NO_ORDERED_TAGS,
+ DEVSTAT_TYPE_CDROM | DEVSTAT_TYPE_IF_IDE,
+ 0x178);
+ }
+ }
+ else {
+ acdtab[acdnlun++] = cdp;
+ devstat_add_entry(cdp->stats, "acd", cdp->lun, DEV_BSIZE,
+ DEVSTAT_NO_ORDERED_TAGS,
+ DEVSTAT_TYPE_CDROM | DEVSTAT_TYPE_IF_IDE,
+ 0x178);
+ }
+ return 0;
+}
+
+struct acd_softc *
+acd_init_lun(struct atapi_softc *atp, int32_t lun, struct devstat *stats)
+{
+ struct acd_softc *acd;
+
+ if (!(acd = malloc(sizeof(struct acd_softc), M_TEMP, M_NOWAIT)))
+ return NULL;
+ bzero(acd, sizeof(struct acd_softc));
+ bufq_init(&acd->buf_queue);
+ acd->atp = atp;
+ acd->lun = lun;
+ acd->flags = F_MEDIA_CHANGED;
+ acd->flags &= ~(F_WRITTEN|F_TRACK_PREP|F_TRACK_PREPED);
+ acd->block_size = 2048;
+ acd->refcnt = 0;
+ acd->slot = -1;
+ acd->changer_info = NULL;
+ if (stats == NULL) {
+ if (!(acd->stats = malloc(sizeof(struct devstat),
+ M_TEMP, M_NOWAIT)))
+ return NULL;
+ bzero(acd->stats, sizeof(struct devstat));
+ }
+ else
+ acd->stats = stats;
+#ifdef DEVFS
+ acd->ra_devfs_token =
+ devfs_add_devswf(&acd_cdevsw, dkmakeminor(lun, 0, 0),
+ DV_CHR, UID_ROOT, GID_OPERATOR, 0640,
+ "racd%da", lun);
+ acd->rc_devfs_token =
+ devfs_add_devswf(&acd_cdevsw, dkmakeminor(lun, 0, RAW_PART),
+ DV_CHR, UID_ROOT, GID_OPERATOR, 0640,
+ "racd%dc", lun);
+ acd->a_devfs_token =
+ devfs_add_devswf(&acd_cdevsw, dkmakeminor(lun, 0, 0),
+ DV_BLK, UID_ROOT, GID_OPERATOR, 0640,
+ "acd%da", lun);
+ acd->c_devfs_token =
+ devfs_add_devswf(&acd_cdevsw, dkmakeminor(lun, 0, RAW_PART),
+ DV_BLK, UID_ROOT, GID_OPERATOR, 0640,
+ "acd%dc", lun);
+#endif
+ return acd;
+}
+
+void
+acd_describe(struct acd_softc *cdp)
+{
+ int32_t comma;
+ int8_t *mechanism;
+ int8_t model_buf[40+1];
+ int8_t revision_buf[8+1];
+
+ bpack(cdp->atp->atapi_parm->model, model_buf, sizeof(model_buf));
+ bpack(cdp->atp->atapi_parm->revision, revision_buf, sizeof(revision_buf));
+ printf("acd%d: <%s/%s> CDROM drive at ata%d as %s\n",
+ cdp->lun, model_buf, revision_buf,
+ cdp->atp->controller->unit,
+ (cdp->atp->unit == ATA_MASTER) ? "master" : "slave ");
+
+ printf("acd%d: drive speed ", cdp->lun);
+ if (cdp->cap.cur_speed != cdp->cap.max_speed)
+ printf("%d - ", cdp->cap.cur_speed * 1000 / 1024);
+ printf("%dKB/sec", cdp->cap.max_speed * 1000 / 1024);
+ if (cdp->cap.buf_size)
+ printf(", %dKB cache\n", cdp->cap.buf_size);
+
+ printf("acd%d: supported read types:", cdp->lun);
+ comma = 0;
+ if (cdp->cap.read_cdr) {
+ printf(" CD-R"); comma = 1;
+ }
+ if (cdp->cap.read_cdrw) {
+ printf("%s CD-RW", comma ? "," : ""); comma = 1;
+ }
+ if (cdp->cap.cd_da) {
+ printf("%s CD-DA", comma ? "," : ""); comma = 1;
+ }
+ if (cdp->cap.method2)
+ printf("%s packet track", comma ? "," : "");
+ if (cdp->cap.write_cdr || cdp->cap.write_cdrw) {
+ printf("\nacd%d: supported write types:", cdp->lun);
+ comma = 0;
+ if (cdp->cap.write_cdr) {
+ printf(" CD-R" ); comma = 1;
+ }
+ if (cdp->cap.write_cdrw) {
+ printf("%s CD-RW", comma ? "," : ""); comma = 1;
+ }
+ if (cdp->cap.test_write) {
+ printf("%s test write", comma ? "," : ""); comma = 1;
+ }
+ }
+ if (cdp->cap.audio_play) {
+ printf("\nacd%d: Audio: ", cdp->lun);
+ if (cdp->cap.audio_play)
+ printf("play");
+ if (cdp->cap.max_vol_levels)
+ printf(", %d volume levels", cdp->cap.max_vol_levels);
+ }
+ printf("\nacd%d: Mechanism: ", cdp->lun);
+ switch (cdp->cap.mech) {
+ case MST_MECH_CADDY:
+ mechanism = "caddy"; break;
+ case MST_MECH_TRAY:
+ mechanism = "tray"; break;
+ case MST_MECH_POPUP:
+ mechanism = "popup"; break;
+ case MST_MECH_CHANGER:
+ mechanism = "changer"; break;
+ case MST_MECH_CARTRIDGE:
+ mechanism = "cartridge"; break;
+ default:
+ mechanism = 0; break;
+ }
+ if (mechanism)
+ printf("%s%s", cdp->cap.eject ? "ejectable " : "", mechanism);
+ else if (cdp->cap.eject)
+ printf("ejectable");
+
+ if (cdp->cap.mech != MST_MECH_CHANGER) {
+ printf("\nacd%d: Medium: ", cdp->lun);
+ switch (cdp->cap.medium_type & MST_TYPE_MASK_HIGH) {
+ case MST_CDROM:
+ printf("CD-ROM "); break;
+ case MST_CDR:
+ printf("CD-R "); break;
+ case MST_CDRW:
+ printf("CD-RW "); break;
+ case MST_DOOR_OPEN:
+ printf("door open"); break;
+ case MST_NO_DISC:
+ printf("no/blank disc inside"); break;
+ case MST_FMT_ERROR:
+ printf("medium format error"); break;
+ }
+ if ((cdp->cap.medium_type & MST_TYPE_MASK_HIGH) < MST_TYPE_MASK_HIGH) {
+ switch (cdp->cap.medium_type & MST_TYPE_MASK_LOW) {
+ case MST_DATA_120:
+ printf("120mm data disc loaded"); break;
+ case MST_AUDIO_120:
+ printf("120mm audio disc loaded"); break;
+ case MST_COMB_120:
+ printf("120mm data/audio disc loaded"); break;
+ case MST_PHOTO_120:
+ printf("120mm photo disc loaded"); break;
+ case MST_DATA_80:
+ printf("80mm data disc loaded"); break;
+ case MST_AUDIO_80:
+ printf("80mm audio disc loaded"); break;
+ case MST_COMB_80:
+ printf("80mm data/audio disc loaded"); break;
+ case MST_PHOTO_80:
+ printf("80mm photo disc loaded"); break;
+ case MST_FMT_NONE:
+ switch (cdp->cap.medium_type & MST_TYPE_MASK_HIGH) {
+ case MST_CDROM:
+ printf("unknown medium"); break;
+ case MST_CDR:
+ case MST_CDRW:
+ printf("blank medium"); break;
+ }
+ break;
+ default:
+ printf("unknown type=0x%x", cdp->cap.medium_type); break;
+ }
+ }
+ }
+ if (cdp->cap.lock)
+ printf(cdp->cap.locked ? ", locked" : ", unlocked");
+ if (cdp->cap.prevent)
+ printf(", lock protected");
+ printf("\n");
+}
+
+static int
+acdopen(dev_t dev, int32_t flags, int32_t fmt, struct proc *p)
+{
+ int32_t lun = dkunit(dev);
+ struct acd_softc *cdp;
+
+ if (lun >= acdnlun || !(cdp = acdtab[lun]))
+ return ENXIO;
+
+ if (!(cdp->flags & F_BOPEN) && !cdp->refcnt) {
+ acd_lock_device(cdp, 1); /* Prevent user eject */
+ cdp->flags |= F_LOCKED;
+ }
+ if (fmt == S_IFBLK)
+ cdp->flags |= F_BOPEN;
+ else
+ ++cdp->refcnt;
+
+ if ((flags & O_NONBLOCK) == 0) {
+ if ((flags & FWRITE) != 0) {
+ /* read/write */
+ if (acd_rezero_unit(cdp)) {
+ printf("acd%d: rezero failed\n", lun);
+ return EIO;
+ }
+ } else {
+ /* read only */
+ if (acd_read_toc(cdp) != 0) {
+ printf("acd%d: read_toc failed\n", lun);
+ /* return EIO; */
+ }
+ }
+ }
+ return 0;
+}
+
+int32_t
+acdclose(dev_t dev, int32_t flags, int32_t fmt, struct proc *p)
+{
+ int32_t lun = dkunit(dev);
+ struct acd_softc *cdp;
+
+ if (lun >= acdnlun || !(cdp = acdtab[lun]))
+ return ENXIO;
+
+ if (fmt == S_IFBLK)
+ cdp->flags &= ~F_BOPEN;
+ else
+ --cdp->refcnt;
+
+ /* Are we the last open ?? */
+ if (!(cdp->flags & F_BOPEN) && !cdp->refcnt) {
+ /* Yup, do we need to close any written tracks */
+ if ((flags & FWRITE) != 0) {
+ if ((cdp->flags & F_TRACK_PREPED) != 0) {
+ acd_close_track(cdp);
+ cdp->flags &= ~(F_TRACK_PREPED | F_TRACK_PREP);
+ }
+ }
+ acd_lock_device(cdp, 0); /* Allow the user eject */
+ }
+ cdp->flags &= ~F_LOCKED;
+ return 0;
+}
+
+static int
+acdread(dev_t dev, struct uio *uio, int32_t ioflag)
+{
+ return physio(acdstrategy, NULL, dev, 1, minphys, uio);
+}
+
+static int
+acdwrite(dev_t dev, struct uio *uio, int32_t ioflag)
+{
+ return physio(acdstrategy, NULL, dev, 0, minphys, uio);
+}
+
+void
+acdstrategy(struct buf *bp)
+{
+ int32_t lun = dkunit(bp->b_dev);
+ struct acd_softc *cdp = acdtab[lun];
+ int32_t x;
+
+#ifdef NOTYET
+ /* allow write only on CD-R/RW media */ /* all for now SOS */
+ if (!(bp->b_flags & B_READ) && !(writeable_media)) {
+ bp->b_error = EROFS;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+#endif
+
+ if (bp->b_bcount == 0) {
+ bp->b_resid = 0;
+ biodone(bp);
+ return;
+ }
+
+ /* check for valid blocksize SOS */
+
+ bp->b_pblkno = bp->b_blkno;
+ bp->b_resid = bp->b_bcount;
+
+ x = splbio();
+ bufqdisksort(&cdp->buf_queue, bp);
+ acd_start(cdp);
+ splx(x);
+}
+
+static void
+acd_start(struct acd_softc *cdp)
+{
+ struct buf *bp = bufq_first(&cdp->buf_queue);
+ u_long lba, count;
+ int8_t ccb[16];
+
+ if (!bp)
+ return;
+ bzero(ccb, sizeof(ccb));
+ bufq_remove(&cdp->buf_queue, bp);
+
+ /* Should reject all queued entries if media have changed. */
+ if (cdp->flags & F_MEDIA_CHANGED) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+ acd_select_slot(cdp);
+ if ((bp->b_flags & B_READ) == B_WRITE) {
+ if ((cdp->flags & F_TRACK_PREPED) == 0) {
+ if ((cdp->flags & F_TRACK_PREP) == 0) {
+ printf("acd%d: sequence error\n", cdp->lun);
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ } else {
+ if (acd_open_track(cdp, &cdp->preptrack) != 0) {
+ biodone(bp);
+ return;
+ }
+ cdp->flags |= F_TRACK_PREPED;
+ }
+ }
+ }
+ if (bp->b_flags & B_READ) {
+ lba = bp->b_blkno / (cdp->block_size / DEV_BSIZE);
+ ccb[0] = ATAPI_READ_BIG;
+ }
+ else {
+ lba = cdp->next_writeable_lba + (bp->b_offset / cdp->block_size);
+ ccb[0] = ATAPI_WRITE_BIG;
+ }
+ count = (bp->b_bcount + (cdp->block_size - 1)) / cdp->block_size;
+
+ ccb[1] = 0;
+ ccb[2] = lba>>24;
+ ccb[3] = lba>>16;
+ ccb[4] = lba>>8;
+ ccb[5] = lba;
+ ccb[7] = count>>8;
+ ccb[8] = count;
+
+ devstat_start_transaction(cdp->stats);
+
+ atapi_queue_cmd(cdp->atp, ccb, bp->b_data, bp->b_bcount,
+ (bp->b_flags&B_READ)?A_READ : 0, acd_done, cdp, (void *)bp);
+}
+
+static void
+acd_done(struct atapi_request *request)
+{
+ struct buf *bp = request->bp;
+ struct acd_softc *cdp = request->driver;
+
+ devstat_end_transaction(cdp->stats, bp->b_bcount-request->bytecount,
+ DEVSTAT_TAG_NONE,
+ (bp->b_flags&B_READ) ? DEVSTAT_READ:DEVSTAT_WRITE);
+ if (request->result) {
+ printf("acd_done: ");
+ atapi_error(request->device, request->result);
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ }
+ else {
+ bp->b_resid = request->bytecount;
+ if ((bp->b_flags & B_READ) == B_WRITE)
+ cdp->flags |= F_WRITTEN;
+ }
+ biodone(bp);
+ acd_start(cdp);
+}
+
+static __inline void
+lba2msf(int32_t lba, u_int8_t *m, u_int8_t *s, u_int8_t *f)
+{
+ lba += 150;
+ lba &= 0xffffff;
+ *m = lba / (60 * 75);
+ lba %= (60 * 75);
+ *s = lba / 75;
+ *f = lba % 75;
+}
+
+static __inline int32_t
+msf2lba(u_int8_t m, u_int8_t s, u_int8_t f)
+{
+ return (m * 60 + s) * 75 + f - 150;
+}
+
+int32_t
+acdioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
+{
+ int32_t lun = dkunit(dev);
+ struct acd_softc *cdp = acdtab[lun];
+ int32_t error = 0;
+
+ if (cdp->flags & F_MEDIA_CHANGED)
+ switch (cmd) {
+ case CDIOCRESET:
+ break;
+ default:
+ acd_read_toc(cdp);
+ acd_lock_device(cdp, 1);
+ cdp->flags |= F_LOCKED;
+ break;
+ }
+ switch (cmd) {
+
+ case CDIOCRESUME:
+ return acd_pause_device(cdp, 1);
+
+ case CDIOCPAUSE:
+ return acd_pause_device(cdp, 0);
+
+ case CDIOCSTART:
+ return acd_start_device(cdp, 1);
+
+ case CDIOCSTOP:
+ return acd_start_device(cdp, 0);
+
+ case CDIOCALLOW:
+ acd_select_slot(cdp);
+ cdp->flags &= ~F_LOCKED;
+ return acd_lock_device(cdp, 0);
+
+ case CDIOCPREVENT:
+ acd_select_slot(cdp);
+ cdp->flags |= F_LOCKED;
+ return acd_lock_device(cdp, 1);
+
+ case CDIOCRESET:
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error)
+ return (error);
+ return acd_test_unit_ready(cdp);
+
+ case CDIOCEJECT:
+ if ((cdp->flags & F_BOPEN) && cdp->refcnt)
+ return EBUSY;
+ return acd_eject(cdp, 0);
+
+ case CDIOCCLOSE:
+ if ((cdp->flags & F_BOPEN) && cdp->refcnt)
+ return 0;
+ return acd_eject(cdp, 1);
+
+ case CDIOREADTOCHEADER:
+ if (!cdp->toc.hdr.ending_track)
+ return EIO;
+ bcopy(&cdp->toc.hdr, addr, sizeof(cdp->toc.hdr));
+ break;
+
+ case CDIOREADTOCENTRYS:
+ {
+ struct ioc_read_toc_entry *te = (struct ioc_read_toc_entry *)addr;
+ struct toc *toc = &cdp->toc;
+ struct toc buf;
+ u_long len;
+ u_int8_t starting_track = te->starting_track;
+
+ if (!cdp->toc.hdr.ending_track)
+ return EIO;
+
+ if (te->data_len < sizeof(toc->tab[0]) ||
+ (te->data_len % sizeof(toc->tab[0])) != 0 ||
+ (te->address_format != CD_MSF_FORMAT &&
+ te->address_format != CD_LBA_FORMAT))
+ return EINVAL;
+
+ if (!starting_track)
+ starting_track = toc->hdr.starting_track;
+ else if (starting_track == 170)
+ starting_track = toc->hdr.ending_track + 1;
+ else if (starting_track < toc->hdr.starting_track ||
+ starting_track > toc->hdr.ending_track + 1)
+ return EINVAL;
+
+ len = ((toc->hdr.ending_track + 1 - starting_track) + 1) *
+ sizeof(toc->tab[0]);
+ if (te->data_len < len)
+ len = te->data_len;
+ if (len > sizeof(toc->tab))
+ return EINVAL;
+
+ if (te->address_format == CD_MSF_FORMAT) {
+ struct cd_toc_entry *entry;
+
+ buf = cdp->toc;
+ toc = &buf;
+ entry = toc->tab + (toc->hdr.ending_track + 1 -
+ toc->hdr.starting_track) + 1;
+ while (--entry >= toc->tab)
+ lba2msf(ntohl(entry->addr.lba), &entry->addr.msf.minute,
+ &entry->addr.msf.second, &entry->addr.msf.frame);
+ }
+ return copyout(toc->tab + starting_track - toc->hdr.starting_track,
+ te->data, len);
+ }
+ case CDIOREADTOCENTRY:
+ {
+ struct ioc_read_toc_single_entry *te =
+ (struct ioc_read_toc_single_entry *)addr;
+ struct toc *toc = &cdp->toc;
+ struct toc buf;
+ u_int8_t track = te->track;
+
+ if (!cdp->toc.hdr.ending_track)
+ return EIO;
+
+ if (te->address_format != CD_MSF_FORMAT &&
+ te->address_format != CD_LBA_FORMAT)
+ return EINVAL;
+
+ if (!track)
+ track = toc->hdr.starting_track;
+ else if (track == 170)
+ track = toc->hdr.ending_track + 1;
+ else if (track < toc->hdr.starting_track ||
+ track > toc->hdr.ending_track + 1)
+ return EINVAL;
+
+ if (te->address_format == CD_MSF_FORMAT) {
+ struct cd_toc_entry *entry;
+
+ buf = cdp->toc;
+ toc = &buf;
+ entry = toc->tab + (track - toc->hdr.starting_track);
+ lba2msf(ntohl(entry->addr.lba), &entry->addr.msf.minute,
+ &entry->addr.msf.second, &entry->addr.msf.frame);
+ }
+ bcopy(toc->tab + track - toc->hdr.starting_track,
+ &te->entry, sizeof(struct cd_toc_entry));
+ }
+ break;
+
+ case CDIOCREADSUBCHANNEL:
+ {
+ struct ioc_read_subchannel *args =
+ (struct ioc_read_subchannel *)addr;
+ struct cd_sub_channel_info data;
+ u_long len = args->data_len;
+ int32_t abslba, rellba;
+ int8_t ccb[16] = { ATAPI_READ_SUBCHANNEL, 0, 0x40, 1, 0, 0, 0,
+ sizeof(cdp->subchan)>>8, sizeof(cdp->subchan),
+ 0, 0, 0, 0, 0, 0, 0 };
+
+ if (len > sizeof(data) ||
+ len < sizeof(struct cd_sub_channel_header))
+ return EINVAL;
+
+ if (atapi_queue_cmd(cdp->atp, ccb, &cdp->subchan,
+ sizeof(cdp->subchan), A_READ, NULL, NULL, NULL))
+ return EIO;
+#ifdef ACD_DEBUG
+ atapi_dump("acd: subchan", &cdp->subchan, sizeof(cdp->subchan));
+#endif
+
+ abslba = cdp->subchan.abslba;
+ rellba = cdp->subchan.rellba;
+ if (args->address_format == CD_MSF_FORMAT) {
+ lba2msf(ntohl(abslba),
+ &data.what.position.absaddr.msf.minute,
+ &data.what.position.absaddr.msf.second,
+ &data.what.position.absaddr.msf.frame);
+ lba2msf(ntohl(rellba),
+ &data.what.position.reladdr.msf.minute,
+ &data.what.position.reladdr.msf.second,
+ &data.what.position.reladdr.msf.frame);
+ } else {
+ data.what.position.absaddr.lba = abslba;
+ data.what.position.reladdr.lba = rellba;
+ }
+ data.header.audio_status = cdp->subchan.audio_status;
+ data.what.position.control = cdp->subchan.control & 0xf;
+ data.what.position.addr_type = cdp->subchan.control >> 4;
+ data.what.position.track_number = cdp->subchan.track;
+ data.what.position.index_number = cdp->subchan.indx;
+ return copyout(&data, args->data, len);
+ }
+
+ case CDIOCPLAYMSF:
+ {
+ struct ioc_play_msf *args = (struct ioc_play_msf *)addr;
+ int8_t ccb[16] = { ATAPI_PLAY_MSF, 0, 0,
+ args->start_m, args->start_s, args->start_f,
+ args->end_m, args->end_s, args->end_f,
+ 0, 0, 0, 0, 0, 0, 0 };
+
+ return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+ }
+
+ case CDIOCPLAYBLOCKS:
+ {
+ struct ioc_play_blocks *args = (struct ioc_play_blocks *)addr;
+ int8_t ccb[16] = { ATAPI_PLAY_BIG, 0,
+ args->blk>>24, args->blk>>16, args->blk>>8,
+ args->blk, args->len>>24, args->len>>16,
+ args->len>>8, args->len,
+ 0, 0, 0, 0, 0, 0 };
+
+ return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+ }
+
+ case CDIOCPLAYTRACKS:
+ {
+ struct ioc_play_track *args = (struct ioc_play_track *)addr;
+ u_long start, len;
+ int32_t t1, t2;
+ int8_t ccb[16];
+
+ if (!cdp->toc.hdr.ending_track)
+ return EIO;
+
+ if (args->end_track < cdp->toc.hdr.ending_track + 1)
+ ++args->end_track;
+ if (args->end_track > cdp->toc.hdr.ending_track + 1)
+ args->end_track = cdp->toc.hdr.ending_track + 1;
+ t1 = args->start_track - cdp->toc.hdr.starting_track;
+ t2 = args->end_track - cdp->toc.hdr.starting_track;
+ if (t1 < 0 || t2 < 0)
+ return EINVAL;
+ start = ntohl(cdp->toc.tab[t1].addr.lba);
+ len = ntohl(cdp->toc.tab[t2].addr.lba) - start;
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_PLAY_BIG;
+ ccb[2] = start>>24;
+ ccb[3] = start>>16;
+ ccb[4] = start>>8;
+ ccb[5] = start;
+ ccb[6] = len>>24;
+ ccb[7] = len>>16;
+ ccb[8] = len>>8;
+ ccb[9] = len;
+
+ return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+ }
+
+ case CDIOCREADAUDIO:
+ {
+ struct ioc_read_audio *args = (struct ioc_read_audio *)addr;
+ int32_t lba, frames, error = 0;
+ u_int8_t *buffer, *ubuf = args->buffer;
+ int8_t ccb[16];
+
+ if (!cdp->toc.hdr.ending_track)
+ return EIO;
+
+ if ((frames = args->nframes) < 0)
+ return EINVAL;
+
+ if (args->address_format == CD_LBA_FORMAT)
+ lba = args->address.lba;
+ else if (args->address_format == CD_MSF_FORMAT)
+ lba = msf2lba(args->address.msf.minute,
+ args->address.msf.second,
+ args->address.msf.frame);
+ else
+ return EINVAL;
+#ifndef CD_BUFFER_BLOCKS
+#define CD_BUFFER_BLOCKS 8
+#endif
+ if (!(buffer = malloc(CD_BUFFER_BLOCKS * 2352,
+ M_TEMP,M_NOWAIT)))
+ return ENOMEM;
+ bzero(ccb, sizeof(ccb));
+ while (frames > 0) {
+ int32_t size;
+ u_int8_t blocks;
+
+ blocks = (frames>CD_BUFFER_BLOCKS) ? CD_BUFFER_BLOCKS : frames;
+ size = blocks * 2352;
+
+ ccb[0] = ATAPI_READ_CD;
+ ccb[1] = 4;
+ ccb[2] = lba>>24;
+ ccb[3] = lba>>16;
+ ccb[4] = lba>>8;
+ ccb[5] = lba;
+ ccb[8] = blocks;
+ ccb[9] = 0xf0;
+ if ((error = atapi_queue_cmd(cdp->atp, ccb, buffer, size,
+ A_READ, NULL, NULL, NULL)))
+ break;
+
+ if ((error = copyout(buffer, ubuf, size)))
+ break;
+
+ ubuf += size;
+ frames -= blocks;
+ lba += blocks;
+ }
+
+ free(buffer, M_TEMP);
+ if (args->address_format == CD_LBA_FORMAT)
+ args->address.lba = lba;
+ else if (args->address_format == CD_MSF_FORMAT)
+ lba2msf(lba, &args->address.msf.minute,
+ &args->address.msf.second,
+ &args->address.msf.frame);
+ return error;
+ }
+
+ case CDIOCGETVOL:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+
+ if ((error = acd_mode_sense(cdp, ATAPI_CDROM_AUDIO_PAGE,
+ &cdp->au, sizeof(cdp->au))))
+ return error;
+ if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE)
+ return EIO;
+ arg->vol[0] = cdp->au.port[0].volume;
+ arg->vol[1] = cdp->au.port[1].volume;
+ arg->vol[2] = cdp->au.port[2].volume;
+ arg->vol[3] = cdp->au.port[3].volume;
+ }
+ break;
+
+ case CDIOCSETVOL:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+
+ if ((error = acd_mode_sense(cdp, ATAPI_CDROM_AUDIO_PAGE,
+ &cdp->au, sizeof(cdp->au))))
+ return error;
+ if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE)
+ return EIO;
+ if ((error = acd_mode_sense(cdp, ATAPI_CDROM_AUDIO_PAGE_MASK,
+ &cdp->aumask, sizeof(cdp->aumask))))
+ return error;
+ cdp->au.data_length = 0;
+ cdp->au.port[0].channels = CHANNEL_0;
+ cdp->au.port[1].channels = CHANNEL_1;
+ cdp->au.port[0].volume = arg->vol[0] & cdp->aumask.port[0].volume;
+ cdp->au.port[1].volume = arg->vol[1] & cdp->aumask.port[1].volume;
+ cdp->au.port[2].volume = arg->vol[2] & cdp->aumask.port[2].volume;
+ cdp->au.port[3].volume = arg->vol[3] & cdp->aumask.port[3].volume;
+ return acd_mode_select(cdp, &cdp->au, sizeof(cdp->au));
+ }
+ case CDIOCSETPATCH:
+ {
+ struct ioc_patch *arg = (struct ioc_patch *)addr;
+
+ return acd_setchan(cdp, arg->patch[0], arg->patch[1],
+ arg->patch[2], arg->patch[3]);
+ }
+
+ case CDIOCSETMONO:
+ return acd_setchan(cdp, CHANNEL_0|CHANNEL_1, CHANNEL_0|CHANNEL_1, 0, 0);
+
+ case CDIOCSETSTEREO:
+ return acd_setchan(cdp, CHANNEL_0, CHANNEL_1, 0, 0);
+
+ case CDIOCSETMUTE:
+ return acd_setchan(cdp, 0, 0, 0, 0);
+
+ case CDIOCSETLEFT:
+ return acd_setchan(cdp, CHANNEL_0, CHANNEL_0, 0, 0);
+
+ case CDIOCSETRIGHT:
+ return acd_setchan(cdp, CHANNEL_1, CHANNEL_1, 0, 0);
+
+ case CDRIOCNEXTWRITEABLEADDR:
+ {
+ struct acd_track_info track_info;
+
+ if ((error = acd_read_track_info(cdp, 0xff, &track_info)))
+ break;
+ if (!track_info.nwa_valid)
+ return EINVAL;
+ cdp->next_writeable_lba = track_info.next_writeable_addr;
+ *(int*)addr = track_info.next_writeable_addr;
+ }
+ break;
+
+ case WORMIOCPREPDISK:
+ {
+ struct wormio_prepare_disk *w = (struct wormio_prepare_disk *)addr;
+
+ if (w->dummy != 0 && w->dummy != 1)
+ error = EINVAL;
+ else {
+ error = acd_open_disk(cdp, w->dummy);
+ if (error == 0) {
+ cdp->flags |= F_DISK_PREPED;
+ cdp->dummy = w->dummy;
+ cdp->speed = w->speed;
+ }
+ }
+ }
+ break;
+
+ case WORMIOCPREPTRACK:
+ {
+ struct wormio_prepare_track *w =(struct wormio_prepare_track *)addr;
+
+ if (w->audio != 0 && w->audio != 1)
+ error = EINVAL;
+ else if (w->audio == 0 && w->preemp)
+ error = EINVAL;
+ else if ((cdp->flags & F_DISK_PREPED) == 0) {
+ error = EINVAL;
+ printf("acd%d: sequence error (PREP_TRACK)\n", cdp->lun);
+ } else {
+ cdp->flags |= F_TRACK_PREP;
+ cdp->preptrack = *w;
+ }
+ }
+ break;
+
+ case WORMIOCFINISHTRACK:
+ if ((cdp->flags & F_TRACK_PREPED) != 0)
+ error = acd_close_track(cdp);
+ cdp->flags &= ~(F_TRACK_PREPED | F_TRACK_PREP);
+ break;
+
+ case WORMIOCFIXATION:
+ {
+ struct wormio_fixation *w =
+ (struct wormio_fixation *)addr;
+
+ if ((cdp->flags & F_WRITTEN) == 0)
+ error = EINVAL;
+ else if (w->toc_type < 0 /* WORM_TOC_TYPE_AUDIO */ ||
+ w->toc_type > 4 /* WORM_TOC_TYPE_CDI */ )
+ error = EINVAL;
+ else if (w->onp != 0 && w->onp != 1)
+ error = EINVAL;
+ else {
+ /* no fixation needed if dummy write */
+ if (cdp->dummy == 0)
+ error = acd_close_disk(cdp);
+ cdp->flags &=
+ ~(F_WRITTEN|F_DISK_PREPED|F_TRACK_PREP|F_TRACK_PREPED);
+ }
+ }
+ break;
+
+ case CDRIOCBLANK:
+ return acd_blank_disk(cdp);
+
+ default:
+ return ENOTTY;
+ }
+ return error;
+}
+
+static int32_t
+acd_test_unit_ready(struct acd_softc *cdp)
+{
+ int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+}
+
+static int32_t
+acd_lock_device(struct acd_softc *cdp, int32_t lock)
+{
+ int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+}
+
+static int32_t
+acd_start_device(struct acd_softc *cdp, int32_t start)
+{
+ int8_t ccb[16] = { ATAPI_START_STOP, 0, 0, 0, start,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+}
+
+static int32_t
+acd_pause_device(struct acd_softc *cdp, int32_t pause)
+{
+ int8_t ccb[16] = { ATAPI_START_STOP, 0, 0, 0, 0, 0, 0, 0, pause,
+ 0, 0, 0, 0, 0, 0, 0 };
+
+ return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+}
+
+static int32_t
+acd_mode_sense(struct acd_softc *cdp, u_int8_t page,
+ void *pagebuf, int32_t pagesize)
+{
+ int32_t error;
+ int8_t ccb[16] = { ATAPI_MODE_SENSE, 0, page, 0, 0, 0, 0,
+ pagesize>>8, pagesize, 0, 0, 0, 0, 0, 0, 0 };
+
+ error = atapi_queue_cmd(cdp->atp, ccb, pagebuf, pagesize, A_READ,
+ NULL, NULL, NULL);
+#ifdef AST_DEBUG
+ atapi_dump("acd: mode sense ", &cdp->au, sizeof(cdp->au));
+#endif
+ return error;
+}
+
+static int32_t
+acd_mode_select(struct acd_softc *cdp, void *pagebuf, int32_t pagesize)
+{
+ int8_t ccb[16] = { ATAPI_MODE_SELECT, 0x10, 0, 0, 0, 0, 0,
+ pagesize>>8, pagesize, 0, 0, 0, 0, 0, 0, 0 };
+
+#ifdef AST_DEBUG
+ atapi_dump("acd: mode select ", &cdp->au, sizeof(cdp->au));
+#endif
+ return atapi_queue_cmd(cdp->atp, ccb, pagebuf, pagesize, 0,
+ NULL, NULL, NULL);
+}
+
+static int32_t
+acd_read_toc(struct acd_softc *cdp)
+{
+ int32_t error, ntracks, len;
+ int8_t ccb[16];
+
+ bzero(&cdp->toc, sizeof(cdp->toc));
+ bzero(&cdp->info, sizeof(cdp->info));
+ bzero(ccb, sizeof(ccb));
+
+ acd_select_slot(cdp);
+
+ error = acd_test_unit_ready(cdp);
+ if ((error & ATAPI_SK_MASK) == ATAPI_SK_UNIT_ATTENTION) {
+ cdp->flags |= F_MEDIA_CHANGED;
+ cdp->flags &= ~(F_WRITTEN | F_TRACK_PREP | F_TRACK_PREPED);
+ error = acd_test_unit_ready(cdp);
+ }
+
+ if (error) {
+ atapi_error(cdp->atp, error);
+ return EIO;
+ }
+
+ cdp->flags &= ~F_MEDIA_CHANGED;
+
+ len = sizeof(struct ioc_toc_header) + sizeof(struct cd_toc_entry);
+ ccb[0] = ATAPI_READ_TOC;
+ ccb[7] = len>>8;
+ ccb[8] = len;
+ if (atapi_queue_cmd(cdp->atp, ccb, &cdp->toc, len, A_READ, NULL,NULL,NULL)){
+ bzero(&cdp->toc, sizeof(cdp->toc));
+ return 0;
+ }
+ ntracks = cdp->toc.hdr.ending_track - cdp->toc.hdr.starting_track + 1;
+ if (ntracks <= 0 || ntracks > MAXTRK) {
+ bzero(&cdp->toc, sizeof(cdp->toc));
+ return 0;
+ }
+
+ len = sizeof(struct ioc_toc_header) + ntracks * sizeof(struct cd_toc_entry);
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_READ_TOC;
+ ccb[7] = len>>8;
+ ccb[8] = len;
+ if (atapi_queue_cmd(cdp->atp, ccb, &cdp->toc, len, A_READ, NULL,NULL,NULL)){
+ bzero(&cdp->toc, sizeof(cdp->toc));
+ return 0;
+ }
+
+ cdp->toc.hdr.len = ntohs(cdp->toc.hdr.len);
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_READ_CAPACITY;
+ if (atapi_queue_cmd(cdp->atp, ccb, &cdp->info, sizeof(cdp->info),
+ A_READ, NULL, NULL, NULL))
+ bzero(&cdp->info, sizeof(cdp->info));
+
+ cdp->toc.tab[ntracks].control = cdp->toc.tab[ntracks - 1].control;
+ cdp->toc.tab[ntracks].addr_type = cdp->toc.tab[ntracks - 1].addr_type;
+ cdp->toc.tab[ntracks].track = 170;
+ cdp->toc.tab[ntracks].addr.lba = cdp->info.volsize;
+
+ cdp->info.volsize = ntohl(cdp->info.volsize);
+ cdp->info.blksize = ntohl(cdp->info.blksize);
+
+#ifdef ACD_DEBUG
+ if (cdp->info.volsize && cdp->toc.hdr.ending_track) {
+ printf("acd%d: ", cdp->lun);
+ if (cdp->toc.tab[0].control & 4)
+ printf("%dMB ", cdp->info.volsize / 512);
+ else
+ printf("%d:%d audio ", cdp->info.volsize / 75 / 60,
+ cdp->info.volsize / 75 % 60);
+ printf("(%d sectors (%d bytes)), %d tracks\n",
+ cdp->info.volsize, cdp->info.blksize,
+ cdp->toc.hdr.ending_track - cdp->toc.hdr.starting_track + 1);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * Set up the audio channel masks.
+ */
+static int32_t
+acd_setchan(struct acd_softc *cdp,
+ u_int8_t c0, u_int8_t c1, u_int8_t c2, u_int8_t c3)
+{
+ int32_t error;
+
+ if ((error = acd_mode_sense(cdp, ATAPI_CDROM_AUDIO_PAGE, &cdp->au,
+ sizeof(cdp->au))))
+ return error;
+ if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE)
+ return EIO;
+ cdp->au.data_length = 0;
+ cdp->au.port[0].channels = c0;
+ cdp->au.port[1].channels = c1;
+ cdp->au.port[2].channels = c2;
+ cdp->au.port[3].channels = c3;
+ return acd_mode_select(cdp, &cdp->au, sizeof(cdp->au));
+}
+
+static int32_t
+acd_eject(struct acd_softc *cdp, int32_t close)
+{
+ int32_t error;
+
+ acd_select_slot(cdp);
+
+ error = acd_start_device(cdp, 0);
+
+ if ((error & ATAPI_SK_MASK) &&
+ ((error & ATAPI_SK_MASK) == ATAPI_SK_NOT_READY ||
+ (error & ATAPI_SK_MASK) == ATAPI_SK_UNIT_ATTENTION)) {
+
+ if (!close)
+ return 0;
+ if ((error = acd_start_device(cdp, 3)))
+ return error;
+ acd_read_toc(cdp);
+ acd_lock_device(cdp, 1);
+ cdp->flags |= F_LOCKED;
+ return 0;
+ }
+ if (error) {
+ atapi_error(cdp->atp, error);
+ return EIO;
+ }
+ if (close)
+ return 0;
+
+ tsleep((caddr_t) &lbolt, PRIBIO, "acdej1", 0);
+ tsleep((caddr_t) &lbolt, PRIBIO, "acdej2", 0);
+ acd_lock_device(cdp, 0);
+ cdp->flags &= ~F_LOCKED;
+ cdp->flags |= F_MEDIA_CHANGED;
+ cdp->flags &= ~(F_WRITTEN|F_TRACK_PREP|F_TRACK_PREPED);
+ return acd_start_device(cdp, 2);
+}
+
+static void
+acd_select_slot(struct acd_softc *cdp)
+{
+ int8_t ccb[16];
+
+ if (cdp->slot < 0 || cdp->changer_info->current_slot == cdp->slot)
+ return;
+
+ /* Unlock (might not be needed but its cheaper than asking) */
+ acd_lock_device(cdp, 0);
+
+ bzero(ccb, sizeof(ccb));
+ /* Unload the current media from player */
+ ccb[0] = ATAPI_LOAD_UNLOAD;
+ ccb[4] = 2;
+ ccb[8] = cdp->changer_info->current_slot;
+ atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+
+ /* load the wanted slot */
+ ccb[0] = ATAPI_LOAD_UNLOAD;
+ ccb[4] = 3;
+ ccb[8] = cdp->slot;
+ atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+
+ cdp->changer_info->current_slot = cdp->slot;
+
+ /* Lock the media if needed */
+ if (cdp->flags & F_LOCKED)
+ acd_lock_device(cdp, 1);
+}
+
+static int
+acd_rezero_unit(struct acd_softc *cdp)
+{
+ int8_t ccb[16];
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_REZERO_UNIT;
+ return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+}
+
+static int
+acd_open_disk(struct acd_softc *cdp, int32_t test)
+{
+ cdp->next_writeable_lba = 0;
+ return 0;
+}
+
+static int
+acd_close_disk(struct acd_softc *cdp)
+{
+ int8_t ccb[16];
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_CLOSE_TRACK;
+ ccb[2] = 2;
+ ccb[5] = 0; /* track to close (0 = last open) */
+ return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+}
+
+static int
+acd_open_track(struct acd_softc *cdp, struct wormio_prepare_track *ptp)
+{
+ struct write_param param;
+ int32_t error;
+
+ if ((error = acd_mode_sense(cdp, ATAPI_CDROM_WRITE_PARAMETERS_PAGE,
+ &param, sizeof(param))))
+ return error;
+ param.page_code = 0x05;
+ param.page_length = 0x32;
+ param.test_write = cdp->dummy ? 1 : 0;
+ param.write_type = CDR_WTYPE_TRACK;
+
+ switch (ptp->audio) {
+/* switch (data_type) { */
+
+ case 0:
+/* case CDR_DATA: */
+ cdp->block_size = 2048;
+ param.track_mode = CDR_TMODE_DATA;
+ param.data_block_type = CDR_DB_ROM_MODE1;
+ param.session_format = CDR_SESS_CDROM;
+ break;
+
+ default:
+/* case CDR_AUDIO: */
+ cdp->block_size = 2352;
+ if (ptp->preemp)
+ param.track_mode = CDR_TMODE_AUDIO;
+ else
+ param.track_mode = 0;
+ param.data_block_type = CDR_DB_RAW;
+ param.session_format = CDR_SESS_CDROM;
+ break;
+
+/*
+ case CDR_MODE2:
+ param.track_mode = CDR_TMODE_DATA;
+ param.data_block_type = CDR_DB_ROM_MODE2;
+ param.session_format = CDR_SESS_CDROM;
+ break;
+
+ case CDR_XA1:
+ param.track_mode = CDR_TMODE_DATA;
+ param.data_block_type = CDR_DB_XA_MODE1;
+ param.session_format = CDR_SESS_CDROM_XA;
+ break;
+
+ case CDR_XA2:
+ param.track_mode = CDR_TMODE_DATA;
+ param.data_block_type = CDR_DB_XA_MODE2_F1;
+ param.session_format = CDR_SESS_CDROM_XA;
+ break;
+
+ case CDR_CDI:
+ param.track_mode = CDR_TMODE_DATA;
+ param.data_block_type = CDR_DB_XA_MODE2_F1;
+ param.session_format = CDR_SESS_CDI;
+ break;
+ }
+*/
+ }
+
+ param.multi_session = CDR_MSES_NONE;
+ param.fp = 0;
+ param.packet_size = 0;
+ return acd_mode_select(cdp, &param, sizeof(param));
+}
+
+static int
+acd_close_track(struct acd_softc *cdp)
+{
+ int8_t ccb[16];
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_SYNCHRONIZE_CACHE;
+ return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+}
+
+static int
+acd_read_track_info(struct acd_softc *cdp,
+ int32_t lba, struct acd_track_info *info)
+{
+ int32_t error;
+ int8_t ccb[16] = { ATAPI_READ_TRACK_INFO, 1,
+ lba>>24, lba>>16, lba>>8, lba,
+ 0,
+ sizeof(*info)>>8, sizeof(*info),
+ 0, 0, 0, 0, 0, 0, 0 };
+
+ if ((error = atapi_queue_cmd(cdp->atp, ccb, info, sizeof(*info),
+ A_READ, NULL, NULL, NULL)))
+ return error;
+ info->track_start_addr = ntohl(info->track_start_addr);
+ info->next_writeable_addr = ntohl(info->next_writeable_addr);
+ info->free_blocks = ntohl(info->free_blocks);
+ info->fixed_packet_size = ntohl(info->fixed_packet_size);
+ info->track_length = ntohl(info->track_length);
+ return 0;
+}
+
+static int
+acd_blank_disk(struct acd_softc *cdp)
+{
+ int32_t error;
+ int8_t ccb[16];
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_BLANK;
+ ccb[1] = 1;
+ error = atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+ cdp->flags |= F_MEDIA_CHANGED;
+ cdp->flags &= ~(F_WRITTEN|F_TRACK_PREP|F_TRACK_PREPED);
+ return error;
+}
+
+static void
+acd_drvinit(void *unused)
+{
+ static acd_devsw_installed = 0;
+
+ if (!acd_devsw_installed) {
+ cdevsw_add_generic(BDEV_MAJOR, CDEV_MAJOR, &acd_cdevsw);
+ acd_devsw_installed = 1;
+ }
+}
+SYSINIT(acddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, acd_drvinit, NULL)
+#endif /* NATA && NATAPICD */
diff --git a/sys/dev/ata/atapi-cd.h b/sys/dev/ata/atapi-cd.h
new file mode 100644
index 000000000000..f602e376a10b
--- /dev/null
+++ b/sys/dev/ata/atapi-cd.h
@@ -0,0 +1,358 @@
+/*-
+ * Copyright (c) 1998,1999 Søren Schmidt
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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. 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.
+ *
+ * $Id: atapi-cd.h,v 1.3 1999/03/01 21:03:15 sos Exp sos $
+ */
+
+/*
+ * CDROM Table Of Contents
+ */
+#define MAXTRK 99
+struct toc {
+ struct ioc_toc_header hdr;
+ struct cd_toc_entry tab[MAXTRK + 1];
+};
+
+/*
+ * CDROM Audio Control Parameters Page
+ */
+struct audiopage {
+ /* Mode Page data header */
+ u_int16_t data_length;
+ u_int8_t medium_type;
+ u_int8_t dev_spec;
+ u_int8_t unused[2];
+ u_int16_t blk_desc_len;
+
+ /* Audio control page */
+ u_int8_t page_code;
+#define ATAPI_CDROM_AUDIO_PAGE 0x0e
+#define ATAPI_CDROM_AUDIO_PAGE_MASK 0x4e
+
+ u_int8_t param_len;
+ u_int8_t flags;
+#define CD_PA_SOTC 0x02
+#define CD_PA_IMMED 0x04
+
+ u_int8_t reserved3;
+ u_int8_t reserved4;
+ u_int8_t reserved5;
+ u_int16_t lb_per_sec;
+ struct port_control {
+ u_int8_t channels:4;
+#define CHANNEL_0 1
+#define CHANNEL_1 2
+#define CHANNEL_2 4
+#define CHANNEL_3 8
+ u_int8_t volume;
+ } port[4];
+};
+
+/*
+ * CDROM Capabilities and Mechanical Status Page
+ */
+struct cappage {
+ /* Mode data header */
+ u_int16_t data_length;
+ u_int8_t medium_type; /* Present media type */
+#define MST_TYPE_MASK_LOW 0x0f
+#define MST_FMT_NONE 0x00
+#define MST_DATA_120 0x01
+#define MST_AUDIO_120 0x02
+#define MST_COMB_120 0x03
+#define MST_PHOTO_120 0x04
+#define MST_DATA_80 0x05
+#define MST_AUDIO_80 0x06
+#define MST_COMB_80 0x07
+#define MST_PHOTO_80 0x08
+
+#define MST_TYPE_MASK_HIGH 0x70
+#define MST_CDROM 0x00
+#define MST_CDR 0x10
+#define MST_CDRW 0x20
+
+#define MST_NO_DISC 0x70
+#define MST_DOOR_OPEN 0x71
+#define MST_FMT_ERROR 0x72
+
+ u_int8_t dev_spec;
+ u_int8_t unused[2];
+ u_int16_t blk_desc_len;
+
+ /* Capabilities page */
+ u_int8_t page_code;
+#define ATAPI_CDROM_CAP_PAGE 0x2a
+
+ u_int8_t param_len;
+ u_int8_t read_cdr :1; /* Supports CD-R read */
+ u_int8_t read_cdrw :1; /* Supports CD-RW read */
+ u_int8_t method2 :1; /* Supports reading packet tracks */
+ u_int8_t byte2_37 :5;
+ u_int8_t write_cdr :1; /* Supports CD-R write */
+ u_int8_t write_cdrw :1; /* Supports CD-RW write */
+ u_int8_t test_write :1; /* Supports test writing */
+ u_int8_t byte3_37 :5;
+ u_int8_t audio_play :1; /* Audio play supported */
+ u_int8_t composite :1; /* Composite audio/video supported */
+ u_int8_t dport1 :1; /* Digital audio on port 1 */
+ u_int8_t dport2 :1; /* Digital audio on port 2 */
+ u_int8_t mode2_form1 :1; /* Mode 2 form 1 (XA) read */
+ u_int8_t mode2_form2 :1; /* Mode 2 form 2 format */
+ u_int8_t multisession :1; /* Multi-session photo-CD */
+ u_int8_t :1;
+ u_int8_t cd_da :1; /* Audio-CD read supported */
+ u_int8_t cd_da_stream :1; /* CD-DA streaming */
+ u_int8_t rw :1; /* Combined R-W subchannels */
+ u_int8_t rw_corr :1; /* R-W subchannel data corrected */
+ u_int8_t c2 :1; /* C2 error pointers supported */
+ u_int8_t isrc :1; /* Can return the ISRC info */
+ u_int8_t upc :1; /* Can return the catalog number UPC */
+ u_int8_t :1;
+ u_int8_t lock :1; /* Can be locked */
+ u_int8_t locked :1; /* Current lock state */
+ u_int8_t prevent :1; /* Prevent jumper installed */
+ u_int8_t eject :1; /* Can eject */
+ u_int8_t :1;
+ u_int8_t mech :3; /* Loading mechanism type */
+#define MST_MECH_CADDY 0
+#define MST_MECH_TRAY 1
+#define MST_MECH_POPUP 2
+#define MST_MECH_CHANGER 4
+#define MST_MECH_CARTRIDGE 5
+
+ u_int8_t sep_vol :1; /* Independent volume of channels */
+ u_int8_t sep_mute :1; /* Independent mute of channels */
+ u_int8_t:6;
+
+ u_int16_t max_speed; /* Max raw data rate in bytes/1000 */
+ u_int16_t max_vol_levels; /* Number of discrete volume levels */
+ u_int16_t buf_size; /* Internal buffer size in bytes/1024 */
+ u_int16_t cur_speed; /* Current data rate in bytes/1000 */
+
+ u_int8_t reserved3;
+ u_int8_t bckf :1; /* Data valid on failing edge of BCK */
+ u_int8_t rch :1; /* High LRCK indicates left channel */
+ u_int8_t lsbf :1; /* Set if LSB first */
+ u_int8_t dlen :2;
+#define MST_DLEN_32 0
+#define MST_DLEN_16 1
+#define MST_DLEN_24 2
+#define MST_DLEN_24_I2S 3
+
+ u_int8_t :3;
+ u_int8_t reserved4[2];
+};
+
+/*
+ * CDROM Changer mechanism status structure
+ */
+struct changer {
+ u_int8_t current_slot :5; /* Active changer slot */
+ u_int8_t mech_state :2; /* Current changer state */
+#define CH_READY 0
+#define CH_LOADING 1
+#define CH_UNLOADING 2
+#define CH_INITIALIZING 3
+
+ u_int8_t fault :1; /* Fault in last operation */
+ u_int8_t reserved0 :5;
+ u_int8_t cd_state :3; /* Current mechanism state */
+#define CD_IDLE 0
+#define CD_AUDIO_ACTIVE 1
+#define CD_AUDIO_SCAN 2
+#define CD_HOST_ACTIVE 3
+#define CD_NO_STATE 7
+
+ u_int8_t current_lba[3]; /* Current LBA */
+ u_int8_t slots; /* Number of available slots */
+ u_int16_t table_length; /* Slot table length */
+ struct {
+ u_int8_t changed :1; /* Media has changed in this slot */
+ u_int8_t unused :6;
+ u_int8_t present :1; /* Slot has a CD present */
+ u_int8_t reserved0;
+ u_int8_t reserved1;
+ u_int8_t reserved2;
+ } slot[32];
+};
+
+/*
+ * CDROM Write Parameters Mode Page (Burners ONLY)
+ */
+struct write_param {
+ /* Mode Page data header */
+ u_int16_t data_length;
+ u_int8_t medium_type;
+ u_int8_t dev_spec;
+ u_int8_t unused[2];
+ u_int16_t blk_desc_len;
+
+ /* Write Parameters mode page */
+ u_int8_t page_code;
+#define ATAPI_CDROM_WRITE_PARAMETERS_PAGE 0x0e
+
+ u_int8_t page_length; /* 0x32 */
+ u_int8_t write_type :4; /* Write stream type */
+#define CDR_WTYPE_PACKET 0x00
+#define CDR_WTYPE_TRACK 0x01
+#define CDR_WTYPE_SESSION 0x02
+#define CDR_WTYPE_RAW 0x03
+
+ u_int8_t test_write :1; /* Test write enable */
+ u_int8_t reserved2_567 :3;
+ u_int8_t track_mode :4; /* Track mode */
+#define CDR_TMODE_AUDIO 0x01
+#define CDR_TMODE_INCR_DATA 0x01
+#define CDR_TMODE_ALLOW_COPY 0x02
+#define CDR_TMODE_DATA 0x04
+#define CDR_TMODE_QUAD_AUDIO 0x08
+
+ u_int8_t copy :1; /* Generation stamp */
+ u_int8_t fp :1; /* Fixed packet type */
+ u_int8_t multi_session :2; /* Multi-session type */
+#define CDR_MSES_NONE 0x00
+#define CDR_MSES_FINAL 0x01
+#define CDR_MSES_RESERVED 0x02
+#define CDR_MSES_NULTI 0x03
+
+ u_int8_t data_block_type :4; /* Data block type code */
+#define CDR_DB_RAW 0x0 /* 2352 bytes of raw data */
+#define CDR_DB_RAW_PQ 0x1 /* 2368 bytes raw data + P/Q subchan */
+#define CDR_DB_RAW_PW 0x2 /* 2448 bytes raw data + P-W subchan */
+#define CDR_DB_RAW_PW_R 0x3 /* 2448 bytes raw data + P-W raw sub */
+#define CDR_DB_RES_4 0x4 /* Reserved */
+#define CDR_DB_RES_5 0x5 /* Reserved */
+#define CDR_DB_RES_6 0x6 /* Reserved */
+#define CDR_DB_VS_7 0x7 /* Vendor specific */
+#define CDR_DB_ROM_MODE1 0x8 /* 2048 bytes Mode 1 (ISO/IEC 10149) */
+#define CDR_DB_ROM_MODE2 0x9 /* 2336 bytes Mode 2 (ISO/IEC 10149) */
+#define CDR_DB_XA_MODE1 0x10 /* 2048 bytes Mode 1 (CD-ROM XA 1) */
+#define CDR_DB_XA_MODE2_F1 0x11 /* 2056 bytes Mode 2 (CD-ROM XA 1) */
+#define CDR_DB_XA_MODE2_F2 0x12 /* 2324 bytes Mode 2 (CD-ROM XA 2) */
+#define CDR_DB_XA_MODE2_MIX 0x13 /* 2332 bytes Mode 2 (CD-ROM XA 1/2) */
+#define CDR_DB_RES_14 0x14 /* Reserved */
+#define CDR_DB_VS_15 0x15 /* Vendor specific */
+
+ u_int8_t reserved4_4567 :4;
+ u_int8_t reserved5;
+ u_int8_t reserved6;
+ u_int8_t host_app_code :6; /* Host application code */
+ u_int8_t reserved7_67 :2;
+ u_int8_t session_format; /* Session format */
+#define CDR_SESS_CDROM 0x00
+#define CDR_SESS_CDI 0x10
+#define CDR_SESS_CDROM_XA 0x20
+
+ u_int8_t reserved9;
+ u_int32_t packet_size; /* Packet size in bytes */
+ u_int16_t audio_pause_length; /* Audio pause length in secs */
+ u_int8_t media_catalog_number[16];
+ u_int8_t isr_code[16];
+ u_int8_t sub_hdr_byte0;
+ u_int8_t sub_hdr_byte1;
+ u_int8_t sub_hdr_byte2;
+ u_int8_t sub_hdr_byte3;
+/*
+ u_int8_t vendor_specific_byte0;
+ u_int8_t vendor_specific_byte1;
+ u_int8_t vendor_specific_byte2;
+ u_int8_t vendor_specific_byte3;
+*/
+
+} __attribute__((packed));
+/*
+ * CDROM Read Track Information structure
+ */
+struct acd_track_info {
+ u_int16_t data_length;
+ u_int8_t track_number; /* Current track number */
+ u_int8_t session_number; /* Current session number */
+ u_int8_t reserved4;
+ u_int8_t track_mode :4; /* Mode of this track */
+ u_int8_t copy :1; /* Generation stamp */
+ u_int8_t damage :1; /* Damaged track */
+ u_int8_t reserved5_67 :2;
+ u_int8_t data_mode :4; /* Data mode of this disc */
+ u_int8_t fp :1; /* Fixed packet */
+ u_int8_t packet :1; /* Packet track */
+ u_int8_t blank :1; /* Blank (empty) track */
+ u_int8_t rt :1; /* Reserved track */
+ u_int8_t nwa_valid :1; /* next_writeable_addr field valid */
+ u_int8_t reserved7_17 :7;
+ u_int track_start_addr; /* Start of this track */
+ u_int next_writeable_addr; /* Next writeable addr on this disc */
+ u_int free_blocks; /* Free block on this disc */
+ u_int fixed_packet_size; /* Size of packets on this track */
+ u_int track_length; /* Length of this track */
+};
+
+/*
+ * Structure describing an ATAPI CDROM device
+ */
+struct acd_softc {
+ struct atapi_softc *atp; /* Controller structure */
+ int32_t lun; /* Logical device unit */
+ int32_t flags; /* Device state flags */
+ int32_t refcnt; /* The number of raw opens */
+ struct buf_queue_head buf_queue; /* Queue of i/o requests */
+ struct toc toc; /* Table of disc contents */
+ struct {
+ u_int32_t volsize; /* Volume size in blocks */
+ u_int32_t blksize; /* Block size in bytes */
+ } info;
+ struct audiopage au; /* Audio page info */
+ struct cappage cap; /* Capabilities page info */
+ struct audiopage aumask; /* Audio page mask */
+ struct { /* Subchannel info */
+ u_int8_t void0;
+ u_int8_t audio_status;
+ u_int16_t data_length;
+ u_int8_t data_format;
+ u_int8_t control;
+ u_int8_t track;
+ u_int8_t indx;
+ u_int32_t abslba;
+ u_int32_t rellba;
+ } subchan;
+ struct changer *changer_info; /* Changer info */
+ int32_t slot; /* This lun's slot number */
+ u_int32_t block_size; /* Blocksize currently used */
+ u_int8_t dummy; /* Use dummy writes */
+ u_int8_t speed; /* Select drive speed */
+ u_int32_t next_writeable_lba; /* Next writable position */
+ struct wormio_prepare_track preptrack; /* Scratch region */
+ struct devstat *stats; /* devstat entry */
+#ifdef DEVFS
+ void *ra_devfs_token;
+ void *rc_devfs_token;
+ void *a_devfs_token;
+ void *c_devfs_token;
+#endif
+};
+
+#define CDRIOCBLANK _IO('c',100) /* Blank a CDRW disc */
+#define CDRIOCNEXTWRITEABLEADDR _IOR('c',101,int)
diff --git a/sys/dev/ata/atapi-tape.c b/sys/dev/ata/atapi-tape.c
new file mode 100644
index 000000000000..ea21e99c4def
--- /dev/null
+++ b/sys/dev/ata/atapi-tape.c
@@ -0,0 +1,567 @@
+/*-
+ * Copyright (c) 1998,1999 Søren Schmidt
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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. 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.
+ *
+ * $Id: atapi-tape.c,v 1.5 1999/03/01 21:03:15 sos Exp sos $
+ */
+
+#include "ata.h"
+#include "atapist.h"
+#include "opt_devfs.h"
+
+#if NATA > 0 && NATAPIST > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/mtio.h>
+#include <sys/devicestat.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif
+#include <machine/clock.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/atapi-all.h>
+#include <dev/ata/atapi-tape.h>
+
+static d_open_t astopen;
+static d_read_t astread;
+static d_write_t astwrite;
+static d_close_t astclose;
+static d_ioctl_t astioctl;
+static d_strategy_t aststrategy;
+
+#define CDEV_MAJOR 90
+#define BDEV_MAJOR 24
+
+static struct cdevsw ast_cdevsw = {
+ astopen, astclose, astread, astwrite,
+ astioctl, nostop, nullreset, nodevtotty,
+ seltrue, nommap, aststrategy, "ast",
+ NULL, -1 };
+
+static u_int32_t ast_total = 0;
+
+#define NUNIT 8
+#define UNIT(d) ((minor(d) >> 3) & 3)
+
+#define F_OPEN 0x0001 /* The device is opened */
+#define F_MEDIA_CHANGED 0x0002 /* The media have changed */
+#define F_DATA_WRITTEN 0x0004 /* Data has been written */
+#define F_FM_WRITTEN 0x0008 /* Filemark has been written */
+#define F_CTL_WARN 0x0010 /* Have we warned about CTL wrong? */
+
+static struct ast_softc *asttab[NUNIT]; /* Drive info by unit number */
+static int32_t astnlun = 0; /* Number of config'd drives */
+
+int32_t astattach(struct atapi_softc *);
+static int32_t ast_sense(struct ast_softc *);
+static void ast_describe(struct ast_softc *);
+static void ast_start(struct ast_softc *);
+static void ast_done(struct atapi_request *);
+static void ast_drvinit(void *);
+static int32_t ast_space_cmd(struct ast_softc *, u_int8_t, u_int32_t);
+static int32_t ast_write_filemark(struct ast_softc *, u_int8_t);
+static int32_t ast_erase(struct ast_softc *);
+static int32_t ast_load_unload(struct ast_softc *, u_int8_t);
+static int32_t ast_rewind(struct ast_softc *);
+
+int32_t
+astattach(struct atapi_softc *atp)
+{
+ struct ast_softc *stp;
+
+ if (astnlun >= NUNIT) {
+ printf("ast: too many units\n");
+ return -1;
+ }
+ stp = malloc(sizeof(struct ast_softc), M_TEMP, M_NOWAIT);
+ if (!stp) {
+ printf("ast: out of memory\n");
+ return -1;
+ }
+ bzero(stp, sizeof(struct ast_softc));
+ bufq_init(&stp->buf_queue);
+ stp->atp = atp;
+ stp->lun = astnlun;
+ stp->flags = F_MEDIA_CHANGED;
+
+ if (ast_sense(stp)) {
+ free(stp, M_TEMP);
+ return -1;
+ }
+
+ ast_describe(stp);
+ asttab[astnlun++] = stp;
+ devstat_add_entry(&stp->stats, "ast", stp->lun, DEV_BSIZE,
+ DEVSTAT_NO_ORDERED_TAGS,
+ DEVSTAT_TYPE_SEQUENTIAL | DEVSTAT_TYPE_IF_IDE,
+ 0x178);
+
+
+#ifdef DEVFS
+ t->cdevs = devfs_add_devswf(&ast_cdevsw, 0, DV_CHR, UID_ROOT, GID_OPERATOR,
+ 0640, "rast%d", t->lun);
+#endif /* DEVFS */
+ return 0;
+}
+
+static int32_t
+ast_sense(struct ast_softc *stp)
+{
+ int32_t error, count;
+ int8_t buffer[256];
+ int8_t ccb[16] = { ATAPI_TAPE_MODE_SENSE,
+ 8, /* DBD = 1 no block descr */
+ ATAPI_TAPE_CAP_PAGE,
+ sizeof(buffer)>>8, sizeof(buffer) & 0xff,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ bzero(buffer, sizeof(buffer));
+ /* Get drive capabilities, some drives needs this repeated */
+ for (count = 0 ; count < 5 ; count++) {
+ if (!(error = atapi_queue_cmd(stp->atp, ccb, buffer, sizeof(buffer),
+ A_READ, NULL, NULL, NULL)))
+ break;
+ }
+#ifdef AST_DEBUG
+ atapi_dump("ast: sense", buffer, sizeof(buffer));
+#endif
+ if (error)
+ return error;
+ bcopy(buffer, &stp->header, sizeof(struct ast_header));
+ bcopy(buffer+sizeof(struct ast_header), &stp->cap,
+ sizeof(struct ast_cappage));
+ if (stp->cap.page_code != ATAPI_TAPE_CAP_PAGE)
+ return 1;
+ stp->cap.max_speed = ntohs(stp->cap.max_speed);
+ stp->cap.max_defects = ntohs(stp->cap.max_defects);
+ stp->cap.ctl = ntohs(stp->cap.ctl);
+ stp->cap.speed = ntohs(stp->cap.speed);
+ stp->cap.buffer_size = ntohs(stp->cap.buffer_size);
+ stp->blksize = (stp->cap.blk512 ? 512 : (stp->cap.blk1024 ? 1024 : 0));
+ return 0;
+}
+
+static void
+ast_describe(struct ast_softc *stp)
+{
+ int8_t model_buf[40+1];
+ int8_t revision_buf[8+1];
+
+ bpack(stp->atp->atapi_parm->model, model_buf, sizeof(model_buf));
+ bpack(stp->atp->atapi_parm->revision, revision_buf, sizeof(revision_buf));
+ printf("ast%d: <%s/%s> tape drive at ata%d as %s\n",
+ stp->lun, model_buf, revision_buf,
+ stp->atp->controller->unit,
+ (stp->atp->unit == ATA_MASTER) ? "master" : "slave ");
+ printf("ast%d: ", stp->lun);
+ switch (stp->header.medium_type) {
+ case 0x00: printf("Drive empty"); break;
+ case 0x17: printf("Travan 1 (400 Mbyte) media"); break;
+ case 0xb6: printf("Travan 4 (4 Gbyte) media"); break;
+ default: printf("Unknown media (0x%x)", stp->header.medium_type);
+ }
+ if (stp->cap.readonly) printf(", readonly");
+ if (stp->cap.reverse) printf(", reverse");
+ if (stp->cap.eformat) printf(", eformat");
+ if (stp->cap.qfa) printf(", qfa");
+ if (stp->cap.lock) printf(", lock");
+ if (stp->cap.locked) printf(", locked");
+ if (stp->cap.prevent) printf(", prevent");
+ if (stp->cap.eject) printf(", eject");
+ if (stp->cap.disconnect) printf(", disconnect");
+ if (stp->cap.ecc) printf(", ecc");
+ if (stp->cap.compress) printf(", compress");
+ if (stp->cap.blk512) printf(", 512b");
+ if (stp->cap.blk1024) printf(", 1024b");
+ if (stp->cap.slowb) printf(", slowb");
+ printf("\nast%d: ", stp->lun);
+ printf("Max speed=%dKb/s, ", stp->cap.max_speed);
+ printf("Transfer limit=%d blocks, ", stp->cap.ctl);
+ printf("Buffer size=%d blocks", stp->cap.buffer_size);
+ printf("\n");
+}
+
+int
+astopen(dev_t dev, int32_t flags, int32_t fmt, struct proc *p)
+{
+ int32_t lun = UNIT(dev);
+ struct ast_softc *stp;
+
+ if (lun >= astnlun || !(stp = asttab[lun]))
+ return(ENXIO);
+ if (stp->flags == F_OPEN)
+ return EBUSY;
+ if (ast_sense(stp))
+ printf("ast%d: sense media type failed\n", stp->lun);
+ stp->flags &= ~F_MEDIA_CHANGED;
+ stp->flags &= ~(F_DATA_WRITTEN | F_FM_WRITTEN);
+ stp->flags |= F_OPEN;
+ ast_total = 0;
+ return(0);
+}
+
+int32_t
+astclose(dev_t dev, int32_t flags, int32_t fmt, struct proc *p)
+{
+ int32_t lun = UNIT(dev);
+ struct ast_softc *stp;
+
+ if (lun >= astnlun || !(stp = asttab[lun]))
+ return(ENXIO);
+
+ /* Flush buffers, some drives fail here, but they should report ctl = 0 */
+ if (stp->cap.ctl && (stp->flags & F_DATA_WRITTEN))
+ ast_write_filemark(stp, 0);
+
+ /* Write filemark if data written to tape */
+ if ((stp->flags & (F_DATA_WRITTEN | F_FM_WRITTEN)) == F_DATA_WRITTEN)
+ ast_write_filemark(stp, WEOF_WRITE_MASK);
+
+ /* If minor is even rewind on close */
+ if (!(minor(dev) & 0x01))
+ ast_rewind(stp);
+
+ stp->flags &= ~F_OPEN;
+#ifdef AST_DEBUG
+ printf("ast%d: %ud total bytes transferred\n", stp->lun, ast_total);
+#endif
+ stp->flags &= ~F_CTL_WARN;
+ return(0);
+}
+
+static int
+astread(dev_t dev, struct uio *uio, int32_t ioflag)
+{
+ return (physio(aststrategy, NULL, dev, 1, minphys, uio));
+}
+
+static int
+astwrite(dev_t dev, struct uio *uio, int32_t ioflag)
+{
+ return (physio(aststrategy, NULL, dev, 0, minphys, uio));
+}
+
+void
+aststrategy(struct buf *bp)
+{
+ int32_t lun = UNIT(bp->b_dev);
+ struct ast_softc *stp = asttab[lun];
+ int32_t x;
+
+ /* If it's a null transfer, return immediatly. */
+ if (bp->b_bcount == 0) {
+ bp->b_resid = 0;
+ biodone(bp);
+ return;
+ }
+
+ /* Check for != blocksize requests */
+ if (bp->b_bcount % stp->blksize) {
+ printf("ast%d: bad request, must be multiple of %d\n",
+ lun, stp->blksize);
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+ if (bp->b_bcount > stp->blksize * stp->cap.ctl) {
+ if ((stp->flags & F_CTL_WARN) == 0) {
+ printf("ast%d: WARNING: CTL exceeded %ld>%d\n",
+ lun, bp->b_bcount, stp->blksize * stp->cap.ctl);
+ stp->flags |= F_CTL_WARN;
+ }
+ }
+
+ x = splbio();
+ ast_total += bp->b_bcount;
+ bufq_insert_tail(&stp->buf_queue, bp);
+ ast_start(stp);
+ splx(x);
+}
+
+static void
+ast_start(struct ast_softc *stp)
+{
+ struct buf *bp = bufq_first(&stp->buf_queue);
+ u_long blkcount;
+ int8_t ccb[16];
+
+ if (!bp)
+ return;
+ bzero(ccb, sizeof(ccb));
+ bufq_remove(&stp->buf_queue, bp);
+ blkcount = bp->b_bcount / stp->blksize;
+ if (bp->b_flags & B_READ) {
+ ccb[0] = ATAPI_TAPE_READ_CMD;
+ } else {
+ ccb[0] = ATAPI_TAPE_WRITE_CMD;
+ stp->flags |= F_DATA_WRITTEN;
+ }
+ ccb[1] = 1;
+ ccb[2] = blkcount>>16;
+ ccb[3] = blkcount>>8;
+ ccb[4] = blkcount;
+
+ devstat_start_transaction(&stp->stats);
+
+ atapi_queue_cmd(stp->atp, ccb, bp->b_data, bp->b_bcount,
+ (bp->b_flags & B_READ) ? A_READ : 0, ast_done, stp, bp);
+}
+
+static void
+ast_done(struct atapi_request *request)
+{
+ struct buf *bp = request->bp;
+ struct ast_softc *stp = request->driver;
+
+ devstat_end_transaction(&stp->stats, bp->b_bcount-request->bytecount,
+ DEVSTAT_TAG_NONE,
+ (bp->b_flags&B_READ) ? DEVSTAT_READ:DEVSTAT_WRITE);
+
+ if (request->result) {
+ printf("ast_done: ");
+ atapi_error(request->device, request->result);
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ }
+ else
+ bp->b_resid = request->bytecount;
+ biodone(bp);
+ ast_start(stp);
+}
+
+int32_t
+astioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
+{
+ int32_t lun = UNIT(dev);
+ int32_t error = 0;
+ struct ast_softc *stp;
+
+ if (lun >= astnlun || !(stp = asttab[lun]))
+ return ENXIO;
+
+ switch (cmd) {
+ case MTIOCGET:
+ {
+ struct mtget *g = (struct mtget *) addr;
+
+ bzero(g, sizeof(struct mtget));
+ g->mt_type = 7;
+ g->mt_density = 1;
+ g->mt_blksiz = stp->blksize;
+ g->mt_comp = stp->cap.compress;
+ g->mt_density0 = 0; g->mt_density1 = 0;
+ g->mt_density2 = 0; g->mt_density3 = 0;
+ g->mt_blksiz0 = 0; g->mt_blksiz1 = 0;
+ g->mt_blksiz2 = 0; g->mt_blksiz3 = 0;
+ g->mt_comp0 = 0; g->mt_comp1 = 0;
+ g->mt_comp2 = 0; g->mt_comp3 = 0;
+ break;
+ }
+ case MTIOCTOP:
+ {
+ int32_t i;
+ struct mtop *mt = (struct mtop *)addr;
+
+ switch ((int16_t) (mt->mt_op)) {
+ case MTWEOF:
+ for (i=0; i < mt->mt_count && !error; i++)
+ error = ast_write_filemark(stp, WEOF_WRITE_MASK);
+ break;
+ case MTFSF:
+ if (mt->mt_count)
+ error = ast_space_cmd(stp, SP_FM, mt->mt_count);
+ break;
+ case MTBSF:
+ if (mt->mt_count)
+ error = ast_space_cmd(stp, SP_FM, -(mt->mt_count));
+ break;
+ case MTFSR:
+ error = EINVAL; break;
+ case MTBSR:
+ error = EINVAL; break;
+ case MTREW:
+ error = ast_rewind(stp);
+ break;
+ case MTOFFL:
+ if ((error = ast_rewind(stp)))
+ break;
+ error = ast_load_unload(stp, !LU_LOAD_MASK);
+ break;
+ case MTNOP:
+ error = ast_write_filemark(stp, 0);
+ break;
+ case MTCACHE:
+ error = EINVAL; break;
+ case MTNOCACHE:
+ error = EINVAL; break;
+ case MTSETBSIZ:
+ error = EINVAL; break;
+ case MTSETDNSTY:
+ error = EINVAL; break;
+ case MTERASE:
+ error = ast_erase(stp);
+ break;
+ case MTEOD:
+ error = ast_space_cmd(stp, SP_EOD, 0);
+ break;
+ case MTCOMP:
+ error = EINVAL; break;
+ case MTRETENS:
+ error = ast_load_unload(stp, LU_RETENSION_MASK|LU_LOAD_MASK);
+ break;
+ default:
+ error = EINVAL;
+ }
+ return error;
+ }
+ default:
+ return(ENOTTY);
+ }
+ return(error);
+}
+
+static int
+ast_space_cmd(struct ast_softc *stp, u_int8_t function, u_int32_t count)
+{
+ int32_t error;
+ int8_t ccb[16];
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_TAPE_SPACE_CMD;
+ ccb[1] = function;
+ ccb[2] = count>>16;
+ ccb[3] = count>>8;
+ ccb[4] = count;
+
+ if ((error = atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL))){
+ printf("ast_space_cmd: ");
+ atapi_error(stp->atp, error);
+ return EIO;
+ }
+ return 0;
+}
+
+static int
+ast_write_filemark(struct ast_softc *stp, u_int8_t function)
+{
+ int32_t error;
+ int8_t ccb[16];
+
+ if (function) {
+ if (stp->flags & F_FM_WRITTEN)
+ stp->flags &= ~F_DATA_WRITTEN;
+ else
+ stp->flags |= F_FM_WRITTEN;
+ }
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_TAPE_WEOF;
+ ccb[4] = function;
+
+ if ((error = atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL))){
+ printf("ast_write_filemark: ");
+ atapi_error(stp->atp, error);
+ return EIO;
+ }
+ return 0;
+}
+
+static int
+ast_load_unload(struct ast_softc *stp, u_int8_t function)
+{
+ int32_t error;
+ int8_t ccb[16];
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_TAPE_LOAD_UNLOAD;
+ ccb[4] = function;
+
+ if ((error = atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL))){
+ printf("ast_load_unload: ");
+ atapi_error(stp->atp, error);
+ return EIO;
+ }
+ return 0;
+}
+
+static int
+ast_erase(struct ast_softc *stp)
+{
+ int32_t error;
+ int8_t ccb[16];
+
+ if ((error = ast_rewind(stp)))
+ return error;
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_TAPE_ERASE;
+ ccb[1] = 3;
+
+ if ((error = atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL))){
+ printf("ast_erase: ");
+ atapi_error(stp->atp, error);
+ return EIO;
+ }
+ return 0;
+}
+
+static int
+ast_rewind(struct ast_softc *stp)
+{
+ int32_t error;
+ int8_t ccb[16];
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_TAPE_REWIND;
+
+ if ((error = atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL))){
+ printf("ast_rewind: ");
+ atapi_error(stp->atp, error);
+ return EIO;
+ }
+ return 0;
+}
+
+static void
+ast_drvinit(void *unused)
+{
+ static ast_devsw_installed = 0;
+
+ if (!ast_devsw_installed) {
+ dev_t dev = makedev(CDEV_MAJOR, 0);
+
+ cdevsw_add(&dev, &ast_cdevsw, NULL);
+ ast_devsw_installed = 1;
+ }
+}
+
+SYSINIT(astdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, ast_drvinit, NULL)
+#endif /* NATA & NATAPIST */
diff --git a/sys/dev/ata/atapi-tape.h b/sys/dev/ata/atapi-tape.h
new file mode 100644
index 000000000000..fe0ecfa069cd
--- /dev/null
+++ b/sys/dev/ata/atapi-tape.h
@@ -0,0 +1,112 @@
+
+/* ATAPI tape commands not in std ATAPI command set */
+#define ATAPI_TAPE_REWIND 0x01
+#define ATAPI_TAPE_REQUEST_SENSE 0x03
+#define ATAPI_TAPE_READ_CMD 0x08
+#define ATAPI_TAPE_WRITE_CMD 0x0a
+#define ATAPI_TAPE_WEOF 0x10
+#define WEOF_WRITE_MASK 0x01
+#define ATAPI_TAPE_SPACE_CMD 0x11
+#define SP_FM 0x01
+#define SP_EOD 0x03
+#define ATAPI_TAPE_ERASE 0x19
+#define ATAPI_TAPE_MODE_SENSE 0x1a
+#define ATAPI_TAPE_LOAD_UNLOAD 0x1b
+#define LU_LOAD_MASK 0x01
+#define LU_RETENSION_MASK 0x02
+#define LU_EOT_MASK 0x04
+
+#define DSC_POLL_INTERVAL 10
+
+/*
+ * MODE SENSE parameter header
+ */
+struct ast_header {
+ u_int8_t data_length; /* Total length of data */
+ u_int8_t medium_type; /* Medium type (if any) */
+ u_int8_t dsp; /* Device specific parameter */
+ u_int8_t bdl; /* Block Descriptor Length */
+};
+
+/*
+ * ATAPI tape drive Capabilities and Mechanical Status Page
+ */
+#define ATAPI_TAPE_CAP_PAGE 0x2a
+
+struct ast_cappage {
+ u_int8_t page_code :6; /* Page code == 0x2a */
+ u_int8_t reserved1_67 :2;
+ u_int8_t page_length; /* Page Length == 0x12 */
+ u_int8_t reserved2;
+ u_int8_t reserved3;
+ u_int8_t readonly :1; /* Read Only Mode */
+ u_int8_t reserved4_1234 :4;
+ u_int8_t reverse :1; /* Supports reverse direction */
+ u_int8_t reserved4_67 :2;
+ u_int8_t reserved5_012 :3;
+ u_int8_t eformat :1; /* Supports ERASE formatting */
+ u_int8_t reserved5_4 :1;
+ u_int8_t qfa :1; /* Supports QFA formats */
+ u_int8_t reserved5_67 :2;
+ u_int8_t lock :1; /* Supports locking media */
+ u_int8_t locked :1; /* The media is locked */
+ u_int8_t prevent :1; /* Defaults to prevent state */
+ u_int8_t eject :1; /* Supports eject */
+ u_int8_t disconnect :1; /* Can break request > ctl */
+ u_int8_t reserved6_5 :1;
+ u_int8_t ecc :1; /* Supports error correction */
+ u_int8_t compress :1; /* Supports data compression */
+ u_int8_t reserved7_0 :1;
+ u_int8_t blk512 :1; /* Supports 512b block size */
+ u_int8_t blk1024 :1; /* Supports 1024b block size */
+ u_int8_t reserved7_3456 :4;
+ u_int8_t slowb :1; /* Restricts byte count */
+ u_int16_t max_speed; /* Supported speed in KBps */
+ u_int16_t max_defects; /* Max stored defect entries */
+ u_int16_t ctl; /* Continuous Transfer Limit */
+ u_int16_t speed; /* Current Speed, in KBps */
+ u_int16_t buffer_size; /* Buffer Size, in 512 bytes */
+ u_int8_t reserved18;
+ u_int8_t reserved19;
+};
+
+/*
+ * REQUEST SENSE structure
+ */
+struct ast_reqsense {
+ u_int8_t error_code :7; /* Current or deferred errors */
+ u_int8_t valid :1; /* Follows QIC-157C */
+ u_int8_t reserved1; /* Segment Number - Reserved */
+ u_int8_t sense_key :4; /* Sense Key */
+ u_int8_t reserved2_4 :1; /* Reserved */
+ u_int8_t ili :1; /* Incorrect Length Indicator */
+ u_int8_t eom :1; /* End Of Medium */
+ u_int8_t filemark :1; /* Filemark */
+ u_int8_t info __attribute__((packed)); /* Cmd specific info */
+ u_int8_t asl; /* Additional sense length (n-7) */
+ u_int8_t command_specific; /* Additional cmd specific info */
+ u_int8_t asc; /* Additional Sense Code */
+ u_int8_t ascq; /* Additional Sense Code Qualifier */
+ u_int8_t replaceable_unit_code; /* Field Replaceable Unit Code */
+ u_int8_t sk_specific1 :7; /* Sense Key Specific */
+ u_int8_t sksv :1; /* Sense Key Specific info valid */
+ u_int8_t sk_specific2; /* Sense Key Specific */
+ u_int8_t sk_specific3; /* Sense Key Specific */
+ u_int8_t pad[2]; /* Padding */
+};
+
+struct ast_softc {
+ struct atapi_softc *atp; /* Controller structure */
+ int32_t lun; /* Logical device unit */
+ int32_t flags; /* Device state flags */
+ int32_t blksize; /* Block size (512 | 1024) */
+ struct buf_queue_head buf_queue; /* Queue of i/o requests */
+ struct atapi_params *param; /* Drive parameters table */
+ struct ast_header header; /* MODE SENSE param header */
+ struct ast_cappage cap; /* Capabilities page info */
+ struct devstat stats; /* devstat entry */
+#ifdef DEVFS
+ void *cdevs;
+ void *bdevs;
+#endif
+};
diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT
index 3175d27d5a70..1df34a06df83 100644
--- a/sys/i386/conf/LINT
+++ b/sys/i386/conf/LINT
@@ -2,7 +2,7 @@
# LINT -- config file for checking all the sources, tries to pull in
# as much of the source tree as it can.
#
-# $Id: LINT,v 1.559 1999/02/21 16:23:23 n_hibma Exp $
+# $Id: LINT,v 1.560 1999/02/22 18:19:57 des Exp $
#
# NB: You probably don't want to try running a kernel built from this
# file. Instead, you should start from GENERIC, and add options from
@@ -1006,6 +1006,24 @@ controller aha0 at isa? port ? cam irq ?
#!CAM# controller aic0 at isa? port 0x340 bio irq 11
+#
+# ATA and ATAPI devices
+# This is work in progress, use at your own risk.
+# It currently reuses the majors of wd.c and freinds.
+# It cannot co-exist with the old system in one kernel.
+# You only need one "controller ata0" for it to find all
+# PCI devices on modern machines.
+#controller ata0
+#device atadisk0 # ATA disk drives
+#device atapicd0 # ATAPI CDROM drives
+#device atapist0 # ATAPI tape drives
+#
+# If you need ISA only devices, this is the lines to add:
+#controller ata1 at isa? port "IO_WD1" bio irq 14
+#controller ata2 at isa? port "IO_WD2" bio irq 15
+#
+# All the controller lines can coexist, the driver will
+# find out which ones are there.
#
# ST-506, ESDI, and IDE hard disks: `wdc' and `wd'
diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES
index 3175d27d5a70..1df34a06df83 100644
--- a/sys/i386/conf/NOTES
+++ b/sys/i386/conf/NOTES
@@ -2,7 +2,7 @@
# LINT -- config file for checking all the sources, tries to pull in
# as much of the source tree as it can.
#
-# $Id: LINT,v 1.559 1999/02/21 16:23:23 n_hibma Exp $
+# $Id: LINT,v 1.560 1999/02/22 18:19:57 des Exp $
#
# NB: You probably don't want to try running a kernel built from this
# file. Instead, you should start from GENERIC, and add options from
@@ -1006,6 +1006,24 @@ controller aha0 at isa? port ? cam irq ?
#!CAM# controller aic0 at isa? port 0x340 bio irq 11
+#
+# ATA and ATAPI devices
+# This is work in progress, use at your own risk.
+# It currently reuses the majors of wd.c and freinds.
+# It cannot co-exist with the old system in one kernel.
+# You only need one "controller ata0" for it to find all
+# PCI devices on modern machines.
+#controller ata0
+#device atadisk0 # ATA disk drives
+#device atapicd0 # ATAPI CDROM drives
+#device atapist0 # ATAPI tape drives
+#
+# If you need ISA only devices, this is the lines to add:
+#controller ata1 at isa? port "IO_WD1" bio irq 14
+#controller ata2 at isa? port "IO_WD2" bio irq 15
+#
+# All the controller lines can coexist, the driver will
+# find out which ones are there.
#
# ST-506, ESDI, and IDE hard disks: `wdc' and `wd'
diff --git a/sys/i386/conf/files.i386 b/sys/i386/conf/files.i386
index 8ed556251811..e5ff6f0ecafa 100644
--- a/sys/i386/conf/files.i386
+++ b/sys/i386/conf/files.i386
@@ -1,7 +1,7 @@
# This file tells config what files go into building a kernel,
# files marked standard are always included.
#
-# $Id: files.i386,v 1.222 1999/02/11 07:11:00 gibbs Exp $
+# $Id: files.i386,v 1.223 1999/02/20 11:17:59 julian Exp $
#
# The long compile-with and dependency lines are required because of
# limitations in config: backslash-newline doesn't work in strings, and
@@ -24,6 +24,11 @@ font8x16.o optional std8x16font \
no-implicit-rule before-depend \
clean "${STD8X16FONT}-8x16 font8x16.c"
#
+dev/ata/ata-all.c optional ata device-driver
+dev/ata/atapi-all.c optional ata device-driver
+dev/ata/ata-disk.c optional atadisk device-driver
+dev/ata/atapi-cd.c optional atapicd device-driver
+dev/ata/atapi-tape.c optional atapist device-driver
dev/fb/fb.c optional fb device-driver
dev/fb/fb.c optional vga device-driver
dev/fb/splash.c optional splash