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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DEVFS +#include +#endif +#include +#include +#include +#include +#include + +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; ctlrata_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<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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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; ctlratapi_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; residcontroller->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; residcontroller->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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DEVFS +#include +#endif +#include +#include +#include + +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, + ¶m, 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, ¶m, 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 +#include +#include +#include +#include +#include +#include +#include +#ifdef DEVFS +#include +#endif +#include +#include +#include +#include + +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